puppet-padchat.ts 32.3 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,
              )
255 256 257 258 259 260 261 262
    /**
     * 0. Discard messages when not logged in
     */
    if (!this.id) {
      log.warn('PuppetPadchat', 'onPadchatMessage({id=%s, type=%s(%s)}) discarded message because puppet is not logged-in')
      return
    }

ruiruibupt's avatar
ruiruibupt 已提交
263
    /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
264
     * 1. Sometimes will get duplicated same messages from rpc, drop the same message from here.
ruiruibupt's avatar
ruiruibupt 已提交
265 266 267 268 269 270
     */
    if (this.cachePadchatMessagePayload.has(rawPayload.msg_id)) {
      log.warn('PuppetPadchat', 'onPadchatMessage({id=%s, type=%s(%s)}) duplicate message')
      return
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
271 272 273 274 275 276 277 278
    /**
     * 2. Save message for future usage
     */
    this.cachePadchatMessagePayload.set(
      rawPayload.msg_id,
      rawPayload,
    )

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
281 282 283
    /**
     * 3. Check for Different Message Types
     */
284 285
    switch (rawPayload.sub_type) {
      case PadchatMessageType.VerifyMsg:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
286
        this.emit('friendship', rawPayload.msg_id)
287 288
        break

289 290 291 292 293 294 295
      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 (李卓桓) 已提交
296
         *  sub_type: 10002}
297 298
         */
        await Promise.all([
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
299
          this.onPadchatMessageRoomEventJoin(rawPayload),
300 301
        ])
        break
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
302
      case PadchatMessageType.Sys:
303 304
        await Promise.all([
          this.onPadchatMessageFriendshipEvent(rawPayload),
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
305 306 307 308
          ////////////////////////////////////////////////
          this.onPadchatMessageRoomEventJoin(rawPayload),
          this.onPadchatMessageRoomEventLeave(rawPayload),
          this.onPadchatMessageRoomEventTopic(rawPayload),
309
        ])
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
310 311
        break

312 313 314 315 316 317
      case PadchatMessageType.App:
      case PadchatMessageType.Emoticon:
      case PadchatMessageType.Image:
      case PadchatMessageType.MicroVideo:
      case PadchatMessageType.Video:
      case PadchatMessageType.Voice:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
318
        // TODO: the above types are filel type
319

320 321 322 323 324 325
      default:
        this.emit('message', rawPayload.msg_id)
        break
    }
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
326 327 328 329 330 331
  /**
   * 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 (李卓桓) 已提交
332 333 334 335 336 337
    const roomJoinEvent = roomJoinEventMessageParser(rawPayload)

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

339 340 341 342 343 344 345 346
      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 (李卓桓) 已提交
347
          ),
348 349 350 351 352 353
        )

        if (tryIdList.length) {
          return tryIdList
        }

354
        if (!this.padchatManager) {
355 356 357 358 359 360
          throw new Error('no manager')
        }

        /**
         * PURGE Cache and Reload
         */
361
        this.padchatManager.purgeRoomMemberIdList(roomId)
362 363 364 365 366 367 368 369

        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 (李卓桓) 已提交
370
      const inviterIdList = await this.roomMemberSearch(roomId, inviterName)
371

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
372 373 374
      if (inviterIdList.length < 1) {
        throw new Error('no inviterId found')
      } else if (inviterIdList.length > 1) {
375
        log.warn('PuppetPadchat', 'onPadchatMessageRoomEvent() case PadchatMesssageSys: inviterId found more than 1, use the first one.')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
376
      }
377

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

380
      this.emit('room-join', roomId, inviteeIdList,  inviterId)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
381
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
382 383 384 385 386 387 388 389
  }

  /**
   * 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 (李卓桓) 已提交
390 391 392 393 394 395
    const roomLeaveEvent = roomLeaveEventMessageParser(rawPayload)

    if (roomLeaveEvent) {
      const leaverNameList = roomLeaveEvent.leaverNameList
      const removerName    = roomLeaveEvent.removerName
      const roomId         = roomLeaveEvent.roomId
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

      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]

412 413 414 415 416 417 418 419 420 421
      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 (李卓桓) 已提交
422 423
      this.emit('room-leave',  roomId, leaverIdList, removerId)
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
424 425 426 427 428 429 430 431
  }

  /**
   * 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 (李卓桓) 已提交
432 433 434 435 436 437
    const roomTopicEvent = roomTopicEventMessageParser(rawPayload)

    if (roomTopicEvent) {
      const changerName = roomTopicEvent.changerName
      const newTopic    = roomTopicEvent.topic
      const roomId      = roomTopicEvent.roomId
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
438 439 440 441 442 443 444 445 446 447 448 449

      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]

450 451 452 453 454 455 456 457 458 459 460
      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 (李卓桓) 已提交
461 462
      this.emit('room-topic',  roomId, newTopic, oldTopic, changerId)
    }
463 464 465
  }

  protected async onPadchatMessageFriendshipEvent(rawPayload: PadchatMessagePayload): Promise<void> {
466
    log.verbose('PuppetPadchat', 'onPadchatMessageFriendshipEvent({id=%s})', rawPayload.msg_id)
467
    /**
468
     * 1. Look for friendship confirm event
469
     */
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
    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 (李卓桓) 已提交
486 487
  }

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

491
    if (!this.padchatManager) {
492 493
      throw new Error('no bridge')
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
494 495

    if (this.state.off()) {
496
      log.warn('PuppetPadchat', 'stop() is called on a OFF puppet. await ready(off) and return.')
ruiruibupt's avatar
init  
ruiruibupt 已提交
497 498 499 500 501
      await this.state.ready('off')
      return
    }

    this.state.off('pending')
ruiruibupt's avatar
ruiruibupt 已提交
502 503 504

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

506 507
    setImmediate(() => this.padchatManager && this.padchatManager.removeAllListeners())
    await this.padchatManager.stop()
ruiruibupt's avatar
ruiruibupt 已提交
508

ruiruibupt's avatar
init  
ruiruibupt 已提交
509 510
    // await some tasks...
    this.state.off(true)
511
    this.emit('stop')
ruiruibupt's avatar
init  
ruiruibupt 已提交
512 513
  }

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

517
    if (!this.id) {
518 519 520 521 522
      log.warn('PuppetPadchat', 'logout() this.id not exist')
      // throw new Error('logout before login?')
      return
    }

523
    if (!this.padchatManager) {
524
      throw new Error('no bridge')
ruiruibupt's avatar
init  
ruiruibupt 已提交
525 526
    }

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

530 531 532 533
    // if (!passive) {
    //   await this.bridge.WXLogout()
    // }

534
    await this.padchatManager.logout()
ruiruibupt's avatar
init  
ruiruibupt 已提交
535 536 537 538 539 540 541 542 543 544 545 546 547 548
  }

  /**
   *
   * 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 已提交
549
      const payload = await this.contactPayload(contactId)
ruiruibupt's avatar
ruiruibupt 已提交
550
      return payload.alias || ''
ruiruibupt's avatar
init  
ruiruibupt 已提交
551
    }
ruiruibupt's avatar
ruiruibupt 已提交
552

553
    if (!this.padchatManager) {
554 555 556
      throw new Error('no bridge')
    }

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

ruiruibupt's avatar
init  
ruiruibupt 已提交
559 560 561
    return
  }

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

565
    if (!this.padchatManager) {
566 567 568
      throw new Error('no bridge')
    }

569
    const contactIdList = this.padchatManager.getContactIdList()
ruiruibupt's avatar
ruiruibupt 已提交
570 571 572 573

    return contactIdList
  }

574 575
  public async contactAvatar(contactId: string)                : Promise<FileBox>
  public async contactAvatar(contactId: string, file: FileBox) : Promise<void>
ruiruibupt's avatar
init  
ruiruibupt 已提交
576

577
  public async contactAvatar(contactId: string, file?: FileBox): Promise<void | FileBox> {
578 579 580 581
    log.verbose('PuppetPadchat', 'contactAvatar(%s%s)',
                                  contactId,
                                  file ? (', ' + file.name) : '',
                )
582 583 584 585 586 587 588 589

    /**
     * 1. set avatar for user self
     */
    if (file) {
      if (contactId !== this.selfId()) {
        throw new Error('can not set avatar for others')
      }
590
      if (!this.padchatManager) {
591 592
        throw new Error('no bridge')
      }
593
      await this.padchatManager.WXSetHeadImage(await file.toBase64())
594 595 596 597 598 599
      return
    }

    /**
     * 2. get avatar
     */
ruiruibupt's avatar
ruiruibupt 已提交
600 601 602 603 604 605
    const payload = await this.contactPayload(contactId)

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

606 607 608 609
    const fileBox = FileBox.fromUrl(
      payload.avatar,
      `wechaty-contact-avatar-${payload.name}.jpg`,
    )
610 611 612
    return fileBox
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
613
  public async contactQrcode(contactId: string): Promise<string> {
614 615 616
    if (contactId !== this.selfId()) {
      throw new Error('can not set avatar for others')
    }
617
    if (!this.padchatManager) {
618 619
      throw new Error('no bridge')
    }
620
    const base64 = await this.padchatManager.WXGetUserQRCode(contactId, 0)
621
    const qrcode = await fileBoxToQrcode(base64)
622
    return qrcode
ruiruibupt's avatar
init  
ruiruibupt 已提交
623 624
  }

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

628
    if (!this.padchatManager) {
629 630
      throw new Error('no bridge')
    }
631
    const rawPayload = await this.padchatManager.contactRawPayload(contactId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
632 633 634
    return rawPayload
  }

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

638
    const payload: ContactPayload = contactRawPayloadParser(rawPayload)
ruiruibupt's avatar
init  
ruiruibupt 已提交
639 640 641 642 643 644 645 646
    return payload
  }

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

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

ruiruibupt's avatar
ruiruibupt 已提交
651 652
    // TODO

653
    if (!this.padchatManager) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
654 655 656 657 658 659
      throw new Error('no bridge')
    }

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

660 661
    const rawText        = JSON.stringify(rawPayload)
    const attachmentName = payload.filename || payload.id
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
662 663 664 665 666 667 668 669

    let result

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

      case MessageType.Audio:
670
        result = await this.padchatManager.WXGetMsgVoice(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
671
        console.log(result)
672
        return FileBox.fromBase64(result.voice, `${attachmentName}.slk`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
673 674

      case MessageType.Emoticon:
675
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
676
        console.log(result)
677
        return FileBox.fromBase64(result.image, `${attachmentName}.gif`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
678 679

      case MessageType.Image:
680
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
681
        console.log(result)
682
        return FileBox.fromBase64(result.image, `${attachmentName}.jpg`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
683 684

      case MessageType.Video:
685
        result = await this.padchatManager.WXGetMsgVideo(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
686
        console.log(result)
687
        return FileBox.fromBase64(result.video, `${attachmentName}.mp4`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
688 689 690 691 692

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

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

696
    const file = FileBox.fromBase64(
ruiruibupt's avatar
ruiruibupt 已提交
697 698 699 700 701 702 703
      base64,
      filename,
    )

    return file
  }

704
  public async messageRawPayload(id: string): Promise<PadchatMessagePayload> {
705 706 707 708 709
    // throw Error('should not call messageRawPayload: ' + id)

    /**
     * Issue #1249
     */
ruiruibupt's avatar
ruiruibupt 已提交
710 711 712 713 714

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

716
    const rawPayload = this.cachePadchatMessagePayload.get(id)
717 718 719 720 721 722

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

    return rawPayload
ruiruibupt's avatar
init  
ruiruibupt 已提交
723 724
  }

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
730
    log.silly('PuppetPadchat', 'messagePayload(%s)', JSON.stringify(payload))
ruiruibupt's avatar
init  
ruiruibupt 已提交
731 732 733 734 735 736 737
    return payload
  }

  public async messageSendText(
    receiver : Receiver,
    text     : string,
  ): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
738
    log.verbose('PuppetPadchat', 'messageSend(%s, %s)', JSON.stringify(receiver), text)
739 740 741 742

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

ruiruibupt's avatar
ruiruibupt 已提交
743
    if (!id) {
744
      throw Error('no id')
ruiruibupt's avatar
ruiruibupt 已提交
745
    }
746
    if (!this.padchatManager) {
747 748
      throw new Error('no bridge')
    }
749
    await this.padchatManager.WXSendMsg(id, text)
ruiruibupt's avatar
init  
ruiruibupt 已提交
750 751 752 753 754 755
  }

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

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

ruiruibupt's avatar
ruiruibupt 已提交
761 762 763 764
    if (!id) {
      throw new Error('no id!')
    }

765
    if (!this.padchatManager) {
766 767 768
      throw new Error('no bridge')
    }

769 770 771 772
    const type = file.mimeType || path.extname(file.name)
    switch (type) {
      case '.slk':
        // 发送语音消息(微信silk格式语音)
773
        await this.padchatManager.WXSendVoice(
774 775 776 777 778 779 780
          id,
          await file.toBase64(),
          60,
        )
        break

      default:
781
        await this.padchatManager.WXSendImage(
782 783 784 785 786
          id,
          await file.toBase64(),
        )
        break
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
787 788
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
789 790 791 792 793 794
  public async messageSendContact(
    receiver  : Receiver,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'messageSend("%s", %s)', JSON.stringify(receiver), contactId)

795
    if (!this.padchatManager) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
796 797 798
      throw new Error('no bridge')
    }

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
802 803 804 805 806 807
    if (!id) {
      throw Error('no id')
    }

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

ruiruibupt's avatar
init  
ruiruibupt 已提交
811 812 813 814 815
  public async messageForward(
    receiver  : Receiver,
    messageId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'messageForward(%s, %s)',
816
                              JSON.stringify(receiver),
ruiruibupt's avatar
init  
ruiruibupt 已提交
817 818
                              messageId,
              )
819
    const payload = await this.messagePayload(messageId)
ruiruibupt's avatar
ruiruibupt 已提交
820

821 822 823 824
    if (payload.type === MessageType.Text) {
      if (!payload.text) {
        throw new Error('no text')
      }
ruiruibupt's avatar
ruiruibupt 已提交
825 826
      await this.messageSendText(
        receiver,
827
        payload.text,
ruiruibupt's avatar
ruiruibupt 已提交
828 829 830 831
      )
    } else {
      await this.messageSendFile(
        receiver,
832
        await this.messageFile(messageId),
ruiruibupt's avatar
ruiruibupt 已提交
833 834
      )
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
835 836 837 838 839 840 841
  }

  /**
   *
   * Room
   *
   */
842 843 844 845 846
  public async roomMemberRawPayload(
    roomId    : string,
    contactId : string,
  ): Promise<PadchatRoomMemberPayload> {
    log.silly('PuppetPadchat', 'roomMemberRawPayload(%s)', roomId)
ruiruibupt's avatar
ruiruibupt 已提交
847

848
    if (!this.padchatManager) {
849 850 851
      throw new Error('no bridge')
    }

852
    const rawPayload = await this.padchatManager.roomMemberRawPayload(roomId, contactId)
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    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
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
870 871 872
  public async roomRawPayload(
    roomId: string,
  ): Promise<PadchatRoomPayload> {
873 874
    log.verbose('PuppetPadchat', 'roomRawPayload(%s)', roomId)

875
    if (!this.padchatManager) {
876 877 878
      throw new Error('no bridge')
    }

879
    const rawPayload = await this.padchatManager.roomRawPayload(roomId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
880 881 882
    return rawPayload
  }

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

886 887
    // const memberIdList = await this.bridge.getRoomMemberIdList()
    //  WXGetChatRoomMember(rawPayload.user_name)
888

889
    const payload: RoomPayload = roomRawPayloadParser(rawPayload)
ruiruibupt's avatar
init  
ruiruibupt 已提交
890 891 892 893

    return payload
  }

894 895 896
  public async roomMemberList(roomId: string): Promise<string[]> {
    log.verbose('PuppetPadchat', 'roomMemberList(%s)', roomId)

897
    if (!this.padchatManager) {
898 899 900
      throw new Error('no bridge')
    }

901
    const memberIdList = await this.padchatManager.getRoomMemberIdList(roomId)
902 903 904 905 906
    log.silly('PuppetPadchat', 'roomMemberList()=%d', memberIdList.length)

    return memberIdList
  }

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

910
    if (!this.padchatManager) {
911 912 913
      throw new Error('no bridge')
    }

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

ruiruibupt's avatar
ruiruibupt 已提交
917
    return roomIdList
ruiruibupt's avatar
init  
ruiruibupt 已提交
918 919 920 921 922 923 924
  }

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

926
    if (!this.padchatManager) {
927 928 929
      throw new Error('no bridge')
    }

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
934
  public async roomQrcode(roomId: string): Promise<string> {
935 936 937 938 939 940 941 942
    log.verbose('PuppetPadchat', 'roomQrCode(%s)', roomId)

    // TODO

    throw new Error('not support')

  }

943 944 945 946 947 948 949 950 951 952 953 954 955
  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 已提交
956 957 958 959 960
  public async roomAdd(
    roomId    : string,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'roomAdd(%s, %s)', roomId, contactId)
961

962
    if (!this.padchatManager) {
963 964 965
      throw new Error('no bridge')
    }

966 967 968 969
    // 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)
970
      await this.padchatManager.WXAddChatRoomMember(roomId, contactId)
971 972 973 974 975
    } 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)
976
      await this.padchatManager.WXInviteChatRoomMember(roomId, contactId)
977
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
978 979
  }

980 981 982
  public async roomTopic(roomId: string)                : Promise<string>
  public async roomTopic(roomId: string, topic: string) : Promise<void>

ruiruibupt's avatar
init  
ruiruibupt 已提交
983 984 985 986 987 988 989
  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 已提交
990
      const payload = await this.roomPayload(roomId)
ruiruibupt's avatar
ruiruibupt 已提交
991
      return payload.topic
ruiruibupt's avatar
init  
ruiruibupt 已提交
992
    }
ruiruibupt's avatar
ruiruibupt 已提交
993

994
    if (!this.padchatManager) {
995 996 997
      throw new Error('no bridge')
    }

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

ruiruibupt's avatar
init  
ruiruibupt 已提交
1000 1001 1002 1003 1004 1005 1006 1007 1008
    return
  }

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

1009
    if (!this.padchatManager) {
1010 1011 1012
      throw new Error('no bridge')
    }

1013
    const roomId = await this.padchatManager.WXCreateChatRoom(contactIdList)
1014

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1015 1016
    // Load new created room payload
    await this.roomPayload(roomId)
1017

1018
    return roomId
ruiruibupt's avatar
init  
ruiruibupt 已提交
1019 1020 1021 1022
  }

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

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

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

1031 1032 1033 1034 1035
  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 : '')
1036

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

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

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

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

1063
    const rawSearchPayload: WXSearchContactType = await this.padchatManager.WXSearchContact(contactId)
1064

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

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

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

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

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

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

1102
    console.log('friendshipAccept: ', payload)
1103

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

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

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

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

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

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

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

1139
    return rawPayload
ruiruibupt's avatar
ruiruibupt 已提交
1140 1141
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1142 1143 1144
}

export default PuppetPadchat