puppet-padchat.ts 32.5 KB
Newer Older
ruiruibupt's avatar
init  
ruiruibupt 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/**
 *   Wechaty - https://github.com/chatie/wechaty
 *
 *   @copyright 2016-2018 Huan LI <zixia@zixia.net>
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

20
import path  from 'path'
21 22
// import fs    from 'fs'
// import cuid from 'cuid'
ruiruibupt's avatar
init  
ruiruibupt 已提交
23

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
24 25
import LRU      from 'lru-cache'
import flatten  from 'array-flatten'
26

ruiruibupt's avatar
init  
ruiruibupt 已提交
27 28
import {
  FileBox,
29
}               from 'file-box'
ruiruibupt's avatar
init  
ruiruibupt 已提交
30

31 32
import Misc from '../misc'

ruiruibupt's avatar
init  
ruiruibupt 已提交
33
import {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
34 35
  ContactPayload,

36 37
  MessagePayload,
  MessageType,
ruiruibupt's avatar
init  
ruiruibupt 已提交
38 39

  RoomPayload,
40
  RoomMemberPayload,
ruiruibupt's avatar
init  
ruiruibupt 已提交
41 42 43

  Puppet,
  PuppetOptions,
44

ruiruibupt's avatar
init  
ruiruibupt 已提交
45
  Receiver,
46

47 48
  FriendshipPayload,
  FriendshipPayloadReceive,
49 50

  WATCHDOG_TIMEOUT,
51
}                                 from '../puppet/'
ruiruibupt's avatar
init  
ruiruibupt 已提交
52

53
import {
54
  contactRawPayloadParser,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
55

56
  fileBoxToQrcode,
57
  // friendRequestEventMessageParser,
58
  friendshipRawPayloadParser,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
59 60 61 62

  isStrangerV1,
  isStrangerV2,

63
  messageRawPayloadParser,
64 65 66

  roomRawPayloadParser,

67 68 69
  roomJoinEventMessageParser,
  roomLeaveEventMessageParser,
  roomTopicEventMessageParser,
70 71 72 73

  friendshipConfirmEventMessageParser,
  friendshipReceiveEventMessageParser,
  friendshipVerifyEventMessageParser,
74
}                                         from './pure-function-helpers'
75

ruiruibupt's avatar
init  
ruiruibupt 已提交
76 77
import {
  log,
78 79
  qrCodeForChatie,
}                   from '../config'
ruiruibupt's avatar
init  
ruiruibupt 已提交
80

81
import {
82
  padchatToken,
83 84 85
  WECHATY_PUPPET_PADCHAT_ENDPOINT,
}                                   from './config'

ruiruibupt's avatar
init  
ruiruibupt 已提交
86
import {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87 88
  PadchatManager,
}                       from './padchat-manager'
ruiruibupt's avatar
init  
ruiruibupt 已提交
89 90

import {
91
  PadchatContactPayload,
92
  PadchatMessagePayload,
93
  PadchatRoomPayload,
94
  PadchatRoomMemberPayload,
95
  PadchatMessageType,
96
}                           from './padchat-schemas'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
97

E
Egg 已提交
98 99 100 101
import {
  WXSearchContactType,
  WXSearchContactTypeStatus,
}                           from './padchat-rpc.type'
ruiruibupt's avatar
init  
ruiruibupt 已提交
102 103

export class PuppetPadchat extends Puppet {
104

105 106 107
  // in seconds, 4 minute for padchat
  protected [WATCHDOG_TIMEOUT] = 4 * 60

108
  // private readonly cachePadchatContactPayload       : LRU.Cache<string, PadchatContactRawPayload>
109
  // private readonly cachePadchatFriendshipPayload : LRU.Cache<string, PadchatMessagePayload>
110
  private readonly cachePadchatMessagePayload    : LRU.Cache<string, PadchatMessagePayload>
111
  // private readonly cachePadchatRoomPayload          : LRU.Cache<string, PadchatRoomRawPayload>
112

113
  public padchatManager?:  PadchatManager
ruiruibupt's avatar
init  
ruiruibupt 已提交
114 115 116 117 118 119

  constructor(
    public options: PuppetOptions,
  ) {
    super(options)

120 121 122
    const lruOptions: LRU.Options = {
      max: 1000,
      // length: function (n) { return n * 2},
123
      dispose: function (key: string, val: any) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124
        log.silly('PuppetPadchat', 'constructor() lruOptions.dispose(%s, %s)', key, JSON.stringify(val))
125 126 127 128
      },
      maxAge: 1000 * 60 * 60,
    }

129
    // this.cachePadchatContactPayload       = new LRU<string, PadchatContactRawPayload>(lruOptions)
130
    // this.cachePadchatFriendshipPayload = new LRU<string, PadchatMessagePayload>(lruOptions)
131
    this.cachePadchatMessagePayload       = new LRU<string, PadchatMessagePayload>(lruOptions)
132
    // this.cachePadchatRoomPayload          = new LRU<string, PadchatRoomRawPayload>(lruOptions)
ruiruibupt's avatar
init  
ruiruibupt 已提交
133 134 135
  }

  public toString() {
136
    return `PuppetPadchat<${this.options.memory.name}>`
ruiruibupt's avatar
init  
ruiruibupt 已提交
137 138 139 140 141 142
  }

  public ding(data?: any): Promise<string> {
    return data
  }

ruiruibupt's avatar
ruiruibupt 已提交
143
  public startWatchdog(): void {
ruiruibupt's avatar
ruiruibupt 已提交
144
    log.verbose('PuppetPadchat', 'initWatchdogForPuppet()')
ruiruibupt's avatar
init  
ruiruibupt 已提交
145

146
    if (!this.padchatManager) {
147 148
      throw new Error('no bridge')
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
149

ruiruibupt's avatar
ruiruibupt 已提交
150 151 152
    // clean the dog because this could be re-inited
    this.watchdog.removeAllListeners()

153
    /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
154
     * Use bridge's heartbeat to feed dog
155
     */
156
    this.padchatManager.on('heartbeat', (data: string) => {
157 158 159 160 161
      log.silly('PuppetPadchat', 'startWatchdog() bridge.on(heartbeat)')
      this.watchdog.feed({
        data,
      })
    })
ruiruibupt's avatar
ruiruibupt 已提交
162
    this.watchdog.on('feed', async food => {
163
      log.silly('PuppetPadchat', 'startWatchdog() watchdog.on(feed, food={type=%s, data=%s})', food.type, food.data)
ruiruibupt's avatar
ruiruibupt 已提交
164 165
    })

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
166
    this.watchdog.on('reset', async (food, timeout) => {
167 168 169 170 171
      log.warn('PuppetPadchat', 'startWatchdog() dog.on(reset) last food:%s, timeout:%s',
                                food.data,
                                timeout,
              )
      await this.restart('watchdog.on(reset)')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
172
    })
ruiruibupt's avatar
ruiruibupt 已提交
173 174 175 176 177

    this.emit('watchdog', {
      data: 'inited',
    })

ruiruibupt's avatar
ruiruibupt 已提交
178
  }
ruiruibupt's avatar
init  
ruiruibupt 已提交
179 180

  public async start(): Promise<void> {
ruiruibupt's avatar
ruiruibupt 已提交
181
    log.verbose('PuppetPadchat', `start() with ${this.options.memory.name}`)
ruiruibupt's avatar
init  
ruiruibupt 已提交
182

183 184 185 186 187 188
    if (this.state.on()) {
      log.warn('PuppetPadchat', 'start() already on(pending)?')
      await this.state.ready('on')
      return
    }

ruiruibupt's avatar
ruiruibupt 已提交
189 190 191 192 193 194 195
    /**
     * state has two main state: ON / OFF
     * ON (pending)
     * OFF (pending)
     */
    this.state.on('pending')

196
    const bridge = this.padchatManager = new PadchatManager({
197 198 199 200 201 202
      memory   : this.options.memory,
      token    : padchatToken(),
      endpoint : WECHATY_PUPPET_PADCHAT_ENDPOINT,
    })

    await this.startBridge(bridge)
ruiruibupt's avatar
ruiruibupt 已提交
203
    await this.startWatchdog()
ruiruibupt's avatar
ruiruibupt 已提交
204

ruiruibupt's avatar
ruiruibupt 已提交
205
    this.state.on(true)
206
    this.emit('start')
207
  }
ruiruibupt's avatar
init  
ruiruibupt 已提交
208

209
  protected async login(selfId: string): Promise<void> {
210
    if (!this.padchatManager) {
211 212
      throw new Error('no bridge')
    }
213
    await super.login(selfId)
214
    this.padchatManager.syncContactsAndRooms()
ruiruibupt's avatar
init  
ruiruibupt 已提交
215 216
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
217
  public async startBridge(bridge: PadchatManager): Promise<void> {
ruiruibupt's avatar
ruiruibupt 已提交
218
    log.verbose('PuppetPadchat', 'startBridge()')
ruiruibupt's avatar
ruiruibupt 已提交
219 220

    if (this.state.off()) {
221
      throw new Error('startBridge() state is off')
ruiruibupt's avatar
ruiruibupt 已提交
222 223
    }

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    bridge.removeAllListeners()
    // bridge.on('ding'     , Event.onDing.bind(this))
    // bridge.on('error'    , e => this.emit('error', e))
    // bridge.on('log'      , Event.onLog.bind(this))
    bridge.on('scan',    (qrcode: string, status: number, data?: string) => this.emit('scan', qrcode, status, data))
    bridge.on('login',   (userId: string)                                => this.login(userId))
    bridge.on('message', (rawPayload: PadchatMessagePayload)             => this.onPadchatMessage(rawPayload))
    bridge.on('logout',  ()                                              => this.logout())

    bridge.on('destroy', reason => {
      log.warn('PuppetPadchat', 'startBridge() bridge.on(destroy) for %s. Restarting PuppetPadchat ... ', reason)
      this.restart(reason)
    })

    await bridge.start()
  }

  protected async restart(reason: string): Promise<void> {
    log.verbose('PuppetPadchat', 'restart(%s)', reason)

    await this.stop()
    await this.start()
ruiruibupt's avatar
init  
ruiruibupt 已提交
246 247 248

  }

Huan (李卓桓)'s avatar
wip...  
Huan (李卓桓) 已提交
249
  protected async onPadchatMessage(rawPayload: PadchatMessagePayload): Promise<void> {
250 251 252 253 254
    log.verbose('PuppetPadchat', 'onPadchatMessage({id=%s, type=%s(%s)})',
                                rawPayload.msg_id,
                                PadchatMessageType[rawPayload.sub_type],
                                rawPayload.msg_type,
              )
ruiruibupt's avatar
ruiruibupt 已提交
255
    /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
256
     * 1. Sometimes will get duplicated same messages from rpc, drop the same message from here.
ruiruibupt's avatar
ruiruibupt 已提交
257 258 259 260 261 262
     */
    if (this.cachePadchatMessagePayload.has(rawPayload.msg_id)) {
      log.warn('PuppetPadchat', 'onPadchatMessage({id=%s, type=%s(%s)}) duplicate message')
      return
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
263 264 265 266 267 268 269 270
    /**
     * 2. Save message for future usage
     */
    this.cachePadchatMessagePayload.set(
      rawPayload.msg_id,
      rawPayload,
    )

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
271 272
    console.log('rawPayload:', rawPayload)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
273 274 275
    /**
     * 3. Check for Different Message Types
     */
276 277
    switch (rawPayload.sub_type) {
      case PadchatMessageType.VerifyMsg:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
278
        this.emit('friendship', rawPayload.msg_id)
279 280
        break

281 282 283 284 285 286 287
      case PadchatMessageType.Recalled:
        /**
         * When someone joined the room invited by Bot,
         * the bot will receive a `recall-able` message for room event
         *
         * { content: '12740017638@chatroom:\n<sysmsg type="delchatroommember">\n\t<delchatroommember>\n\t\t<plain>
         *            <![CDATA[You invited 卓桓、Zhuohuan, 太阁_传话助手, 桔小秘 to the group chat.   ]]></plain>...,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
288
         *  sub_type: 10002}
289 290
         */
        await Promise.all([
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
291
          this.onPadchatMessageRoomEventJoin(rawPayload),
292 293
        ])
        break
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
294
      case PadchatMessageType.Sys:
295 296
        await Promise.all([
          this.onPadchatMessageFriendshipEvent(rawPayload),
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
297 298 299 300
          ////////////////////////////////////////////////
          this.onPadchatMessageRoomEventJoin(rawPayload),
          this.onPadchatMessageRoomEventLeave(rawPayload),
          this.onPadchatMessageRoomEventTopic(rawPayload),
301
        ])
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
302 303
        break

304 305 306 307 308 309
      case PadchatMessageType.App:
      case PadchatMessageType.Emoticon:
      case PadchatMessageType.Image:
      case PadchatMessageType.MicroVideo:
      case PadchatMessageType.Video:
      case PadchatMessageType.Voice:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
310
        // TODO: the above types are filel type
311

312 313 314 315 316 317
      default:
        this.emit('message', rawPayload.msg_id)
        break
    }
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
318 319 320 321 322 323
  /**
   * Look for room join event
   */
  protected async onPadchatMessageRoomEventJoin(rawPayload: PadchatMessagePayload): Promise<void> {
    log.verbose('PuppetPadchat', 'onPadchatMessageRoomEventJoin({id=%s})', rawPayload.msg_id)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
324 325 326 327 328 329
    const roomJoinEvent = roomJoinEventMessageParser(rawPayload)

    if (roomJoinEvent) {
      const inviteeNameList = roomJoinEvent.inviteeNameList
      const inviterName     = roomJoinEvent.inviterName
      const roomId          = roomJoinEvent.roomId
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
330

331 332 333 334 335 336 337 338
      const inviteeIdList = await Misc.retry(async (retry, attempt) => {
        log.verbose('PuppetPadchat', 'onPadchatMessageRoomEvent({id=%s}) roomJoin retry(attempt=%d)', attempt)

        const tryIdList = flatten<string>(
          await Promise.all(
            inviteeNameList.map(
              inviteeName => this.roomMemberSearch(roomId, inviteeName),
            ),
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
339
          ),
340 341 342 343 344 345
        )

        if (tryIdList.length) {
          return tryIdList
        }

346
        if (!this.padchatManager) {
347 348 349 350 351 352
          throw new Error('no manager')
        }

        /**
         * PURGE Cache and Reload
         */
353
        this.padchatManager.purgeRoomMemberIdList(roomId)
354 355 356 357 358 359 360 361

        return retry(new Error('roomMemberSearch() not found'))

      }).catch(e => {
        log.warn('PuppetPadchat', 'onPadchatMessageRoomEvent({id=%s}) roomJoin retry() fail: %s', e.message)
        return [] as string[]
      })

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
362
      const inviterIdList = await this.roomMemberSearch(roomId, inviterName)
363

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
364 365 366
      if (inviterIdList.length < 1) {
        throw new Error('no inviterId found')
      } else if (inviterIdList.length > 1) {
367
        log.warn('PuppetPadchat', 'onPadchatMessageRoomEvent() case PadchatMesssageSys: inviterId found more than 1, use the first one.')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
368
      }
369

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
370 371
      const inviterId = inviterIdList[0]

372
      this.emit('room-join', roomId, inviteeIdList,  inviterId)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
373
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
374 375 376 377 378 379 380 381
  }

  /**
   * Look for room leave event
   */
  protected async onPadchatMessageRoomEventLeave(rawPayload: PadchatMessagePayload): Promise<void> {
    log.verbose('PuppetPadchat', 'onPadchatMessageRoomEventLeave({id=%s})', rawPayload.msg_id)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
382 383 384 385 386 387
    const roomLeaveEvent = roomLeaveEventMessageParser(rawPayload)

    if (roomLeaveEvent) {
      const leaverNameList = roomLeaveEvent.leaverNameList
      const removerName    = roomLeaveEvent.removerName
      const roomId         = roomLeaveEvent.roomId
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

      const leaverIdList = flatten<string>(
        await Promise.all(
          leaverNameList.map(
            leaverName => this.roomMemberSearch(roomId, leaverName),
          ),
        ),
      )
      const removerIdList = await this.roomMemberSearch(roomId, removerName)
      if (removerIdList.length < 1) {
        throw new Error('no removerId found')
      } else if (removerIdList.length > 1) {
        log.warn('PuppetPadchat', 'onPadchatMessage() case PadchatMesssageSys: removerId found more than 1, use the first one.')
      }
      const removerId = removerIdList[0]

404 405 406 407 408 409 410 411 412 413
      if (!this.padchatManager) {
        throw new Error('no padchatManager')
      }

      /**
       * Update L1 Cache & PURGE L2 Cache
       */
      this.cacheRoomMemberPayload.del(roomId)
      this.padchatManager.purgeRoomMemberIdList(roomId)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
414 415
      this.emit('room-leave',  roomId, leaverIdList, removerId)
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
416 417 418 419 420 421 422 423
  }

  /**
   * Look for room topic event
   */
  protected async onPadchatMessageRoomEventTopic(rawPayload: PadchatMessagePayload): Promise<void> {
    log.verbose('PuppetPadchat', 'onPadchatMessageRoomEventTopic({id=%s})', rawPayload.msg_id)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
424 425 426 427 428 429
    const roomTopicEvent = roomTopicEventMessageParser(rawPayload)

    if (roomTopicEvent) {
      const changerName = roomTopicEvent.changerName
      const newTopic    = roomTopicEvent.topic
      const roomId      = roomTopicEvent.roomId
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
430 431 432 433 434 435 436 437 438 439 440 441

      const roomPayload = await this.roomPayload(roomId)
      const oldTopic = roomPayload.topic

      const changerIdList = await this.roomMemberSearch(roomId, changerName)
      if (changerIdList.length < 1) {
        throw new Error('no changerId found')
      } else if (changerIdList.length > 1) {
        log.warn('PuppetPadchat', 'onPadchatMessage() case PadchatMesssageSys: changerId found more than 1, use the first one.')
      }
      const changerId = changerIdList[0]

442 443 444 445 446 447 448 449 450 451 452
      if (!this.padchatManager) {
        throw new Error('no padchatManager')
      }
      /**
       * Update Room Payload to new Topic
       */
      const updateRoomPayload = await this.roomPayload(roomId)
      updateRoomPayload.topic = newTopic
      this.cacheRoomPayload.set(roomId, updateRoomPayload)
      this.padchatManager.purgeRoomMemberIdList(roomId)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
453 454
      this.emit('room-topic',  roomId, newTopic, oldTopic, changerId)
    }
455 456 457
  }

  protected async onPadchatMessageFriendshipEvent(rawPayload: PadchatMessagePayload): Promise<void> {
458
    log.verbose('PuppetPadchat', 'onPadchatMessageFriendshipEvent({id=%s})', rawPayload.msg_id)
459
    /**
460
     * 1. Look for friendship confirm event
461
     */
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
    const friendshipConfirmContactId = friendshipConfirmEventMessageParser(rawPayload)
    /**
     * 2. Look for friendship receive event
     */
    const friendshipReceiveContactId = friendshipReceiveEventMessageParser(rawPayload)
    /**
     * 3. Look for friendship verify event
     */
    const friendshipVerifyContactId = friendshipVerifyEventMessageParser(rawPayload)

    if (   friendshipConfirmContactId
        || friendshipReceiveContactId
        || friendshipVerifyContactId
    ) {
      this.emit('friendship', rawPayload.msg_id)
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
478 479
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
480
  public async stop(): Promise<void> {
481 482
    log.verbose('PuppetPadchat', 'stop()')

483
    if (!this.padchatManager) {
484 485
      throw new Error('no bridge')
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
486 487

    if (this.state.off()) {
488
      log.warn('PuppetPadchat', 'stop() is called on a OFF puppet. await ready(off) and return.')
ruiruibupt's avatar
init  
ruiruibupt 已提交
489 490 491 492 493
      await this.state.ready('off')
      return
    }

    this.state.off('pending')
ruiruibupt's avatar
ruiruibupt 已提交
494 495 496

    this.watchdog.sleep()
    await this.logout()
ruiruibupt's avatar
ruiruibupt 已提交
497

498 499
    setImmediate(() => this.padchatManager && this.padchatManager.removeAllListeners())
    await this.padchatManager.stop()
ruiruibupt's avatar
ruiruibupt 已提交
500

ruiruibupt's avatar
init  
ruiruibupt 已提交
501 502
    // await some tasks...
    this.state.off(true)
503
    this.emit('stop')
ruiruibupt's avatar
init  
ruiruibupt 已提交
504 505
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
506
  public async logout(): Promise<void> {
ruiruibupt's avatar
init  
ruiruibupt 已提交
507 508
    log.verbose('PuppetPadchat', 'logout()')

509
    if (!this.id) {
510 511 512 513 514
      log.warn('PuppetPadchat', 'logout() this.id not exist')
      // throw new Error('logout before login?')
      return
    }

515
    if (!this.padchatManager) {
516
      throw new Error('no bridge')
ruiruibupt's avatar
init  
ruiruibupt 已提交
517 518
    }

519 520
    this.emit('logout', this.id) // becore we will throw above by logonoff() when this.user===undefined
    this.id = undefined
ruiruibupt's avatar
init  
ruiruibupt 已提交
521

522 523 524 525
    // if (!passive) {
    //   await this.bridge.WXLogout()
    // }

526
    await this.padchatManager.logout()
ruiruibupt's avatar
init  
ruiruibupt 已提交
527 528 529 530 531 532 533 534 535 536 537 538 539 540
  }

  /**
   *
   * Contact
   *
   */
  public contactAlias(contactId: string)                      : Promise<string>
  public contactAlias(contactId: string, alias: string | null): Promise<void>

  public async contactAlias(contactId: string, alias?: string|null): Promise<void | string> {
    log.verbose('PuppetPadchat', 'contactAlias(%s, %s)', contactId, alias)

    if (typeof alias === 'undefined') {
ruiruibupt's avatar
ruiruibupt 已提交
541
      const payload = await this.contactPayload(contactId)
ruiruibupt's avatar
ruiruibupt 已提交
542
      return payload.alias || ''
ruiruibupt's avatar
init  
ruiruibupt 已提交
543
    }
ruiruibupt's avatar
ruiruibupt 已提交
544

545
    if (!this.padchatManager) {
546 547 548
      throw new Error('no bridge')
    }

549
    await this.padchatManager.WXSetUserRemark(contactId, alias || '')
ruiruibupt's avatar
ruiruibupt 已提交
550

ruiruibupt's avatar
init  
ruiruibupt 已提交
551 552 553
    return
  }

554 555
  public async contactList(): Promise<string[]> {
    log.verbose('PuppetPadchat', 'contactList()')
ruiruibupt's avatar
init  
ruiruibupt 已提交
556

557
    if (!this.padchatManager) {
558 559 560
      throw new Error('no bridge')
    }

561
    const contactIdList = this.padchatManager.getContactIdList()
ruiruibupt's avatar
ruiruibupt 已提交
562 563 564 565

    return contactIdList
  }

566 567
  public async contactAvatar(contactId: string)                : Promise<FileBox>
  public async contactAvatar(contactId: string, file: FileBox) : Promise<void>
ruiruibupt's avatar
init  
ruiruibupt 已提交
568

569
  public async contactAvatar(contactId: string, file?: FileBox): Promise<void | FileBox> {
570 571 572 573
    log.verbose('PuppetPadchat', 'contactAvatar(%s%s)',
                                  contactId,
                                  file ? (', ' + file.name) : '',
                )
574 575 576 577 578 579 580 581

    /**
     * 1. set avatar for user self
     */
    if (file) {
      if (contactId !== this.selfId()) {
        throw new Error('can not set avatar for others')
      }
582
      if (!this.padchatManager) {
583 584
        throw new Error('no bridge')
      }
585
      await this.padchatManager.WXSetHeadImage(await file.toBase64())
586 587 588 589 590 591
      return
    }

    /**
     * 2. get avatar
     */
ruiruibupt's avatar
ruiruibupt 已提交
592 593 594 595 596 597
    const payload = await this.contactPayload(contactId)

    if (!payload.avatar) {
      throw new Error('no avatar')
    }

598 599 600 601
    const fileBox = FileBox.fromUrl(
      payload.avatar,
      `wechaty-contact-avatar-${payload.name}.jpg`,
    )
602 603 604
    return fileBox
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
605
  public async contactQrcode(contactId: string): Promise<string> {
606 607 608
    if (contactId !== this.selfId()) {
      throw new Error('can not set avatar for others')
    }
609
    if (!this.padchatManager) {
610 611
      throw new Error('no bridge')
    }
612
    const base64 = await this.padchatManager.WXGetUserQRCode(contactId, 0)
613
    const qrcode = await fileBoxToQrcode(base64)
614
    return qrcode
ruiruibupt's avatar
init  
ruiruibupt 已提交
615 616
  }

617
  public async contactRawPayload(contactId: string): Promise<PadchatContactPayload> {
618
    log.silly('PuppetPadchat', 'contactRawPayload(%s)', contactId)
ruiruibupt's avatar
ruiruibupt 已提交
619

620
    if (!this.padchatManager) {
621 622
      throw new Error('no bridge')
    }
623
    const rawPayload = await this.padchatManager.contactRawPayload(contactId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
624 625 626
    return rawPayload
  }

627
  public async contactRawPayloadParser(rawPayload: PadchatContactPayload): Promise<ContactPayload> {
628
    log.silly('PuppetPadchat', 'contactRawPayloadParser({user_name="%s"})', rawPayload.user_name)
ruiruibupt's avatar
init  
ruiruibupt 已提交
629

630
    const payload: ContactPayload = contactRawPayloadParser(rawPayload)
ruiruibupt's avatar
init  
ruiruibupt 已提交
631 632 633 634 635 636 637 638
    return payload
  }

  /**
   *
   * Message
   *
   */
ruiruibupt's avatar
ruiruibupt 已提交
639

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
640
  public async messageFile(messageId: string): Promise<FileBox> {
641
    log.warn('PuppetPadchat', 'messageFile(%s) not fully implemented yet, PR is welcome!', messageId)
ruiruibupt's avatar
ruiruibupt 已提交
642

ruiruibupt's avatar
ruiruibupt 已提交
643 644
    // TODO

645
    if (!this.padchatManager) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
646 647 648 649 650 651
      throw new Error('no bridge')
    }

    const rawPayload = await this.messageRawPayload(messageId)
    const payload    = await this.messagePayload(messageId)

652 653
    const rawText        = JSON.stringify(rawPayload)
    const attachmentName = payload.filename || payload.id
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
654 655 656 657 658 659 660 661

    let result

    switch (payload.type) {
      case MessageType.Attachment:
        break

      case MessageType.Audio:
662
        result = await this.padchatManager.WXGetMsgVoice(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
663
        console.log(result)
664
        return FileBox.fromBase64(result.voice, `${attachmentName}.slk`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
665 666

      case MessageType.Emoticon:
667
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
668
        console.log(result)
669
        return FileBox.fromBase64(result.image, `${attachmentName}.gif`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
670 671

      case MessageType.Image:
672
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
673
        console.log(result)
674
        return FileBox.fromBase64(result.image, `${attachmentName}.jpg`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
675 676

      case MessageType.Video:
677
        result = await this.padchatManager.WXGetMsgVideo(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
678
        console.log(result)
679
        return FileBox.fromBase64(result.video, `${attachmentName}.mp4`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
680 681 682 683 684

      default:
        throw new Error('unsupport type: ' + PadchatMessageType[rawPayload.sub_type] + ':' + rawPayload.sub_type)
    }

ruiruibupt's avatar
ruiruibupt 已提交
685
    const base64 = 'cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ=='
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
686
    const filename = 'test-' + messageId + '.txt'
ruiruibupt's avatar
ruiruibupt 已提交
687

688
    const file = FileBox.fromBase64(
ruiruibupt's avatar
ruiruibupt 已提交
689 690 691 692 693 694 695
      base64,
      filename,
    )

    return file
  }

696
  public async messageRawPayload(id: string): Promise<PadchatMessagePayload> {
697 698 699 700 701
    // throw Error('should not call messageRawPayload: ' + id)

    /**
     * Issue #1249
     */
ruiruibupt's avatar
ruiruibupt 已提交
702 703 704 705 706

    // this.cachePadchatMessageRawPayload.set(id, {
    //   id: 'xxx',
    //   data: 'xxx',
    // } as any)
707

708
    const rawPayload = this.cachePadchatMessagePayload.get(id)
709 710 711 712 713 714

    if (!rawPayload) {
      throw new Error('no rawPayload')
    }

    return rawPayload
ruiruibupt's avatar
init  
ruiruibupt 已提交
715 716
  }

717
  public async messageRawPayloadParser(rawPayload: PadchatMessagePayload): Promise<MessagePayload> {
718
    log.verbose('PuppetPadChat', 'messageRawPayloadParser({msg_id="%s"})', rawPayload.msg_id)
719

720
    const payload: MessagePayload = messageRawPayloadParser(rawPayload)
ruiruibupt's avatar
ruiruibupt 已提交
721

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
722
    log.silly('PuppetPadchat', 'messagePayload(%s)', JSON.stringify(payload))
ruiruibupt's avatar
init  
ruiruibupt 已提交
723 724 725 726 727 728 729
    return payload
  }

  public async messageSendText(
    receiver : Receiver,
    text     : string,
  ): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
730
    log.verbose('PuppetPadchat', 'messageSend(%s, %s)', JSON.stringify(receiver), text)
731 732 733 734

    // roomId first, contactId second.
    const id = receiver.roomId || receiver.contactId

ruiruibupt's avatar
ruiruibupt 已提交
735
    if (!id) {
736
      throw Error('no id')
ruiruibupt's avatar
ruiruibupt 已提交
737
    }
738
    if (!this.padchatManager) {
739 740
      throw new Error('no bridge')
    }
741
    await this.padchatManager.WXSendMsg(id, text)
ruiruibupt's avatar
init  
ruiruibupt 已提交
742 743 744 745 746 747
  }

  public async messageSendFile(
    receiver : Receiver,
    file     : FileBox,
  ): Promise<void> {
748
    log.verbose('PuppetPadchat', 'messageSend("%s", %s)', JSON.stringify(receiver), file)
ruiruibupt's avatar
ruiruibupt 已提交
749

750 751 752
    // roomId first, contactId second.
    const id = receiver.roomId || receiver.contactId

ruiruibupt's avatar
ruiruibupt 已提交
753 754 755 756
    if (!id) {
      throw new Error('no id!')
    }

757
    if (!this.padchatManager) {
758 759 760
      throw new Error('no bridge')
    }

761 762 763 764
    const type = file.mimeType || path.extname(file.name)
    switch (type) {
      case '.slk':
        // 发送语音消息(微信silk格式语音)
765
        await this.padchatManager.WXSendVoice(
766 767 768 769 770 771 772
          id,
          await file.toBase64(),
          60,
        )
        break

      default:
773
        await this.padchatManager.WXSendImage(
774 775 776 777 778
          id,
          await file.toBase64(),
        )
        break
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
779 780
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
781 782 783 784 785 786
  public async messageSendContact(
    receiver  : Receiver,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'messageSend("%s", %s)', JSON.stringify(receiver), contactId)

787
    if (!this.padchatManager) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
788 789 790
      throw new Error('no bridge')
    }

791 792 793
    // roomId first, contactId second.
    const id = receiver.roomId || receiver.contactId

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
794 795 796 797 798 799
    if (!id) {
      throw Error('no id')
    }

    const payload = await this.contactPayload(contactId)
    const title = payload.name + '名片'
800
    await this.padchatManager.WXShareCard(id, contactId, title)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
801 802
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
803 804 805 806 807
  public async messageForward(
    receiver  : Receiver,
    messageId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'messageForward(%s, %s)',
808
                              JSON.stringify(receiver),
ruiruibupt's avatar
init  
ruiruibupt 已提交
809 810
                              messageId,
              )
811
    const payload = await this.messagePayload(messageId)
ruiruibupt's avatar
ruiruibupt 已提交
812

813 814 815 816
    if (payload.type === MessageType.Text) {
      if (!payload.text) {
        throw new Error('no text')
      }
ruiruibupt's avatar
ruiruibupt 已提交
817 818
      await this.messageSendText(
        receiver,
819
        payload.text,
ruiruibupt's avatar
ruiruibupt 已提交
820 821 822 823
      )
    } else {
      await this.messageSendFile(
        receiver,
824
        await this.messageFile(messageId),
ruiruibupt's avatar
ruiruibupt 已提交
825 826
      )
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
827 828 829 830 831 832 833
  }

  /**
   *
   * Room
   *
   */
834 835 836 837 838
  public async roomMemberRawPayload(
    roomId    : string,
    contactId : string,
  ): Promise<PadchatRoomMemberPayload> {
    log.silly('PuppetPadchat', 'roomMemberRawPayload(%s)', roomId)
ruiruibupt's avatar
ruiruibupt 已提交
839

840
    if (!this.padchatManager) {
841 842 843
      throw new Error('no bridge')
    }

844
    const rawPayload = await this.padchatManager.roomMemberRawPayload(roomId, contactId)
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
    return rawPayload
  }

  public async roomMemberRawPayloadParser(
    rawPayload: PadchatRoomMemberPayload,
  ): Promise<RoomMemberPayload> {
    log.silly('PuppetPadchat', 'roomMemberRawPayloadParser(%s)', rawPayload)

    const payload: RoomMemberPayload = {
      id        : rawPayload.user_name,
      inviterId : rawPayload.invited_by,
      roomAlias : rawPayload.chatroom_nick_name,
    }

    return payload
  }

  public async roomRawPayload(roomId: string): Promise<PadchatRoomPayload> {
    log.verbose('PuppetPadchat', 'roomRawPayload(%s)', roomId)

865
    if (!this.padchatManager) {
866 867 868
      throw new Error('no bridge')
    }

869
    const rawPayload = await this.padchatManager.roomRawPayload(roomId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
870 871 872
    return rawPayload
  }

873
  public async roomRawPayloadParser(rawPayload: PadchatRoomPayload): Promise<RoomPayload> {
874
    log.verbose('PuppetPadchat', 'roomRawPayloadParser(rawPayload.user_name="%s")', rawPayload.user_name)
ruiruibupt's avatar
init  
ruiruibupt 已提交
875

876 877
    // const memberIdList = await this.bridge.getRoomMemberIdList()
    //  WXGetChatRoomMember(rawPayload.user_name)
878

879
    const payload: RoomPayload = roomRawPayloadParser(rawPayload)
ruiruibupt's avatar
init  
ruiruibupt 已提交
880 881 882 883

    return payload
  }

884 885 886
  public async roomMemberList(roomId: string): Promise<string[]> {
    log.verbose('PuppetPadchat', 'roomMemberList(%s)', roomId)

887
    if (!this.padchatManager) {
888 889 890
      throw new Error('no bridge')
    }

891
    const memberIdList = await this.padchatManager.getRoomMemberIdList(roomId)
892 893 894 895 896
    log.silly('PuppetPadchat', 'roomMemberList()=%d', memberIdList.length)

    return memberIdList
  }

897
  public async roomList(): Promise<string[]> {
ruiruibupt's avatar
ruiruibupt 已提交
898
    log.verbose('PuppetPadchat', 'roomList()')
899

900
    if (!this.padchatManager) {
901 902 903
      throw new Error('no bridge')
    }

904
    const roomIdList = await this.padchatManager.getRoomIdList()
905
    log.silly('PuppetPadchat', 'roomList()=%d', roomIdList.length)
ruiruibupt's avatar
ruiruibupt 已提交
906

ruiruibupt's avatar
ruiruibupt 已提交
907
    return roomIdList
ruiruibupt's avatar
init  
ruiruibupt 已提交
908 909 910 911 912 913 914
  }

  public async roomDel(
    roomId    : string,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'roomDel(%s, %s)', roomId, contactId)
ruiruibupt's avatar
ruiruibupt 已提交
915

916
    if (!this.padchatManager) {
917 918 919
      throw new Error('no bridge')
    }

920
    // Should check whether user is in the room. WXDeleteChatRoomMember won't check if user in the room automatically
921
    await this.padchatManager.WXDeleteChatRoomMember(roomId, contactId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
922 923
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
924
  public async roomQrcode(roomId: string): Promise<string> {
925 926 927 928 929 930 931 932
    log.verbose('PuppetPadchat', 'roomQrCode(%s)', roomId)

    // TODO

    throw new Error('not support')

  }

933 934 935 936 937 938 939 940 941 942 943 944 945
  public async roomAvatar(roomId: string): Promise<FileBox> {
    log.verbose('PuppetPadchat', 'roomAvatar(%s)', roomId)

    const payload = await this.roomPayload(roomId)

    if (payload.avatar) {
      return FileBox.fromUrl(payload.avatar)
    }
    log.warn('PuppetPadchat', 'roomAvatar() avatar not found, use the chatie default.')

    return qrCodeForChatie()
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
946 947 948 949 950
  public async roomAdd(
    roomId    : string,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'roomAdd(%s, %s)', roomId, contactId)
951

952
    if (!this.padchatManager) {
953 954 955
      throw new Error('no bridge')
    }

956 957 958 959
    // XXX: did there need to calc the total number of the members in this room?
    // if n <= 40 then add() else invite() ?
    try {
      log.verbose('PuppetPadchat', 'roomAdd(%s, %s) try to Add', roomId, contactId)
960
      await this.padchatManager.WXAddChatRoomMember(roomId, contactId)
961 962 963 964 965
    } catch (e) {
      // FIXME
      console.error(e)
      log.warn('PuppetPadchat', 'roomAdd(%s, %s) Add exception: %s', e)
      log.verbose('PuppetPadchat', 'roomAdd(%s, %s) try to Invite', roomId, contactId)
966
      await this.padchatManager.WXInviteChatRoomMember(roomId, contactId)
967
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
968 969
  }

970 971 972
  public async roomTopic(roomId: string)                : Promise<string>
  public async roomTopic(roomId: string, topic: string) : Promise<void>

ruiruibupt's avatar
init  
ruiruibupt 已提交
973 974 975 976 977 978 979
  public async roomTopic(
    roomId: string,
    topic?: string,
  ): Promise<void | string> {
    log.verbose('PuppetPadchat', 'roomTopic(%s, %s)', roomId, topic)

    if (typeof topic === 'undefined') {
ruiruibupt's avatar
ruiruibupt 已提交
980
      const payload = await this.roomPayload(roomId)
ruiruibupt's avatar
ruiruibupt 已提交
981
      return payload.topic
ruiruibupt's avatar
init  
ruiruibupt 已提交
982
    }
ruiruibupt's avatar
ruiruibupt 已提交
983

984
    if (!this.padchatManager) {
985 986 987
      throw new Error('no bridge')
    }

988
    await this.padchatManager.WXSetChatroomName(roomId, topic)
ruiruibupt's avatar
ruiruibupt 已提交
989

ruiruibupt's avatar
init  
ruiruibupt 已提交
990 991 992 993 994 995 996 997 998
    return
  }

  public async roomCreate(
    contactIdList : string[],
    topic         : string,
  ): Promise<string> {
    log.verbose('PuppetPadchat', 'roomCreate(%s, %s)', contactIdList, topic)

999
    if (!this.padchatManager) {
1000 1001 1002
      throw new Error('no bridge')
    }

1003
    const roomId = await this.padchatManager.WXCreateChatRoom(contactIdList)
1004

Huan (李卓桓)'s avatar
lint  
Huan (李卓桓) 已提交
1005
    await Misc.retry(async (retry, attempt) => {
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
      log.verbose('PuppetPadchat', 'roomCreate() roomId=%s retry attempt=%d', roomId, attempt)

      try {
        const tryRoomPayload = await this.roomPayload(roomId)
        if (tryRoomPayload) {
          return tryRoomPayload
        } else {
          return retry(new Error('room create payload not ready'))
        }
      } catch (e) {
        return retry(e)
      }
    })

1020
    return roomId
ruiruibupt's avatar
init  
ruiruibupt 已提交
1021 1022 1023 1024
  }

  public async roomQuit(roomId: string): Promise<void> {
    log.verbose('PuppetPadchat', 'roomQuit(%s)', roomId)
1025

1026
    if (!this.padchatManager) {
1027 1028 1029
      throw new Error('no bridge')
    }

1030
    await this.padchatManager.WXQuitChatRoom(roomId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
1031 1032
  }

1033 1034 1035 1036 1037
  public async roomAnnounce(roomId: string)             : Promise<string>
  public async roomAnnounce(roomId: string, text: string) : Promise<void>

  public async roomAnnounce(roomId: string, text?: string): Promise<void | string> {
    log.verbose('PuppetPadchat', 'roomAnnounce(%s, %s)', roomId, text ? text : '')
1038

1039
    if (!this.padchatManager) {
1040 1041 1042
      throw new Error('no bridge')
    }

1043
    if (text) {
1044
      await this.padchatManager.WXSetChatroomAnnouncement(roomId, text)
1045
    } else {
1046
      return await this.padchatManager.WXGetChatroomAnnouncement(roomId)
1047 1048 1049
    }
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1050 1051
  /**
   *
1052
   * Friendship
ruiruibupt's avatar
init  
ruiruibupt 已提交
1053 1054
   *
   */
1055
  public async friendshipVerify(
ruiruibupt's avatar
init  
ruiruibupt 已提交
1056 1057 1058
    contactId : string,
    hello     : string,
  ): Promise<void> {
1059
    log.verbose('PuppetPadchat', 'friendshipVerify(%s, %s)', contactId, hello)
ruiruibupt's avatar
ruiruibupt 已提交
1060

1061
    if (!this.padchatManager) {
E
Egg 已提交
1062 1063
      throw new Error('no bridge')
    }
ruiruibupt's avatar
ruiruibupt 已提交
1064

1065
    const rawSearchPayload: WXSearchContactType = await this.padchatManager.WXSearchContact(contactId)
1066

E
Egg 已提交
1067 1068 1069
    /**
     * If the contact is not stranger, than ussing WXSearchContact can get user_name
     */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1070
    if (rawSearchPayload.user_name !== '' && !isStrangerV1(rawSearchPayload.user_name) && !isStrangerV2(rawSearchPayload.user_name)) {
E
Egg 已提交
1071 1072 1073
      log.warn('PuppetPadchat', 'friendRequestSend %s has been friend with bot, no need to send friend request!', contactId)
      return
    }
ruiruibupt's avatar
ruiruibupt 已提交
1074

E
Egg 已提交
1075 1076
    let strangerV1
    let strangerV2
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1077
    if (isStrangerV1(rawSearchPayload.stranger)) {
E
Egg 已提交
1078 1079
      strangerV1 = rawSearchPayload.stranger
      strangerV2 = rawSearchPayload.user_name
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1080
    } else if (isStrangerV2(rawSearchPayload.stranger)) {
E
Egg 已提交
1081 1082 1083 1084
      strangerV2 = rawSearchPayload.stranger
      strangerV1 = rawSearchPayload.user_name
    } else {
      throw new Error('stranger neither v1 nor v2!')
1085 1086
    }

E
Egg 已提交
1087
    // Issue #1252 : what's wrong here?, Trying to fix now...
1088

1089
    await this.padchatManager.WXAddUser(
E
Egg 已提交
1090 1091 1092
      strangerV1 || '',
      strangerV2 || '',
      WXSearchContactTypeStatus.WXID, // default
ruiruibupt's avatar
ruiruibupt 已提交
1093 1094
      hello,
    )
ruiruibupt's avatar
init  
ruiruibupt 已提交
1095 1096
  }

1097 1098
  public async friendshipAccept(
    friendshipId : string,
ruiruibupt's avatar
init  
ruiruibupt 已提交
1099
  ): Promise<void> {
1100
    log.verbose('PuppetPadchat', 'friendshipAccept(%s)', friendshipId)
ruiruibupt's avatar
ruiruibupt 已提交
1101

1102
    const payload = await this.friendshipPayload(friendshipId) as FriendshipPayloadReceive
ruiruibupt's avatar
ruiruibupt 已提交
1103

1104
    console.log('friendshipAccept: ', payload)
1105

1106 1107 1108
    if (!payload.ticket) {
      throw new Error('no ticket')
    }
1109 1110 1111
    if (!payload.stranger) {
      throw new Error('no stranger')
    }
ruiruibupt's avatar
ruiruibupt 已提交
1112

1113
    if (!this.padchatManager) {
1114 1115 1116
      throw new Error('no bridge')
    }

1117
    await this.padchatManager.WXAcceptUser(
1118
      payload.stranger,
1119 1120
      payload.ticket,
    )
ruiruibupt's avatar
init  
ruiruibupt 已提交
1121 1122
  }

1123 1124
  public async friendshipRawPayloadParser(rawPayload: PadchatMessagePayload) : Promise<FriendshipPayload> {
    log.verbose('PuppetPadchat', 'friendshipRawPayloadParser({id=%s})', rawPayload.msg_id)
ruiruibupt's avatar
ruiruibupt 已提交
1125

1126
    const payload: FriendshipPayload = await friendshipRawPayloadParser(rawPayload)
1127
    return payload
ruiruibupt's avatar
ruiruibupt 已提交
1128 1129
  }

1130 1131
  public async friendshipRawPayload(friendshipId: string): Promise<PadchatMessagePayload> {
    log.verbose('PuppetPadchat', 'friendshipRawPayload(%s)', friendshipId)
ruiruibupt's avatar
ruiruibupt 已提交
1132

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1133 1134 1135
    /**
     * Friendship shares Cache with the Message RawPayload
     */
1136
    const rawPayload = this.cachePadchatMessagePayload.get(friendshipId)
1137
    if (!rawPayload) {
1138
      throw new Error('no rawPayload for id ' + friendshipId)
1139
    }
ruiruibupt's avatar
ruiruibupt 已提交
1140

1141
    return rawPayload
ruiruibupt's avatar
ruiruibupt 已提交
1142 1143
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1144 1145 1146
}

export default PuppetPadchat