puppet-padchat.ts 32.2 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 roomJoin = roomJoinEventMessageParser(rawPayload)
    if (roomJoin) {
      const inviteeNameList = roomJoin.inviteeNameList
      const inviterName     = roomJoin.inviterName
      const roomId          = roomJoin.roomId

330 331 332 333 334 335 336 337
      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 (李卓桓) 已提交
338
          ),
339 340 341 342 343 344
        )

        if (tryIdList.length) {
          return tryIdList
        }

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

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

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

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

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

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

  /**
   * 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 (李卓桓) 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    const roomLeave = roomLeaveEventMessageParser(rawPayload)
    if (roomLeave) {
      const leaverNameList = roomLeave.leaverNameList
      const removerName    = roomLeave.removerName
      const roomId         = roomLeave.roomId

      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]

402 403 404 405 406 407 408 409 410 411
      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 (李卓桓) 已提交
412 413
      this.emit('room-leave',  roomId, leaverIdList, removerId)
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
414 415 416 417 418 419 420 421
  }

  /**
   * 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 (李卓桓) 已提交
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    const roomTopic = roomTopicEventMessageParser(rawPayload)
    if (roomTopic) {
      const changerName = roomTopic.changerName
      const newTopic    = roomTopic.topic
      const roomId      = roomTopic.roomId

      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]

439 440 441 442 443 444 445 446 447 448 449
      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 (李卓桓) 已提交
450 451
      this.emit('room-topic',  roomId, newTopic, oldTopic, changerId)
    }
452 453 454
  }

  protected async onPadchatMessageFriendshipEvent(rawPayload: PadchatMessagePayload): Promise<void> {
455
    log.verbose('PuppetPadchat', 'onPadchatMessageFriendshipEvent({id=%s})', rawPayload.msg_id)
456
    /**
457
     * 1. Look for friendship confirm event
458
     */
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
    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 (李卓桓) 已提交
475 476
  }

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

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

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

    this.state.off('pending')
ruiruibupt's avatar
ruiruibupt 已提交
491 492 493

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

495 496
    setImmediate(() => this.padchatManager && this.padchatManager.removeAllListeners())
    await this.padchatManager.stop()
ruiruibupt's avatar
ruiruibupt 已提交
497

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

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

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

512
    if (!this.padchatManager) {
513
      throw new Error('no bridge')
ruiruibupt's avatar
init  
ruiruibupt 已提交
514 515
    }

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

519 520 521 522
    // if (!passive) {
    //   await this.bridge.WXLogout()
    // }

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

  /**
   *
   * 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 已提交
538
      const payload = await this.contactPayload(contactId)
ruiruibupt's avatar
ruiruibupt 已提交
539
      return payload.alias || ''
ruiruibupt's avatar
init  
ruiruibupt 已提交
540
    }
ruiruibupt's avatar
ruiruibupt 已提交
541

542
    if (!this.padchatManager) {
543 544 545
      throw new Error('no bridge')
    }

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

ruiruibupt's avatar
init  
ruiruibupt 已提交
548 549 550
    return
  }

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

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

558
    const contactIdList = this.padchatManager.getContactIdList()
ruiruibupt's avatar
ruiruibupt 已提交
559 560 561 562

    return contactIdList
  }

563 564
  public async contactAvatar(contactId: string)                : Promise<FileBox>
  public async contactAvatar(contactId: string, file: FileBox) : Promise<void>
ruiruibupt's avatar
init  
ruiruibupt 已提交
565

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

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

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

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

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

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

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

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

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

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
637 638
  public async messageFile(messageId: string): Promise<FileBox> {
    log.warn('PuppetPadchat', 'messageFile(%s) not implemented yet', messageId)
639

ruiruibupt's avatar
ruiruibupt 已提交
640 641
    // const rawPayload = await this.messageRawPayload(id)

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

644
    if (!this.padchatManager) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
      throw new Error('no bridge')
    }

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

    const rawText = JSON.stringify(rawPayload)

    let result

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

      case MessageType.Audio:
660
        result = await this.padchatManager.WXGetMsgVoice(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
661 662 663 664
        console.log(result)
        return FileBox.fromBase64(result.data.image, 'test.slk')

      case MessageType.Emoticon:
665
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
666 667 668 669
        console.log(result)
        return FileBox.fromBase64(result.data.image, 'test.gif')

      case MessageType.Image:
670
        result = await this.padchatManager.WXGetMsgImage(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
671 672 673 674
        console.log(result)
        return FileBox.fromBase64(result.data.image, 'test.jpg')

      case MessageType.Video:
675
        result = await this.padchatManager.WXGetMsgVideo(rawText)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
676 677 678 679 680 681 682
        console.log(result)
        return FileBox.fromBase64(result.data.image, 'test.mp4')

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

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

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

    return file
  }

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

    /**
     * Issue #1249
     */
ruiruibupt's avatar
ruiruibupt 已提交
700 701 702 703 704

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

706
    const rawPayload = this.cachePadchatMessagePayload.get(id)
707 708 709 710 711 712

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

    return rawPayload
ruiruibupt's avatar
init  
ruiruibupt 已提交
713 714
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

842
    const rawPayload = await this.padchatManager.roomMemberRawPayload(roomId, contactId)
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
    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)

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

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

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

874 875
    // const memberIdList = await this.bridge.getRoomMemberIdList()
    //  WXGetChatRoomMember(rawPayload.user_name)
876

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

    return payload
  }

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

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

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

    return memberIdList
  }

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

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

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

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

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

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

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

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

    // TODO

    throw new Error('not support')

  }

931 932 933 934 935 936 937 938 939 940 941 942 943
  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 已提交
944 945 946 947 948
  public async roomAdd(
    roomId    : string,
    contactId : string,
  ): Promise<void> {
    log.verbose('PuppetPadchat', 'roomAdd(%s, %s)', roomId, contactId)
949

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

954 955 956 957
    // 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)
958
      await this.padchatManager.WXAddChatRoomMember(roomId, contactId)
959 960 961 962 963
    } 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)
964
      await this.padchatManager.WXInviteChatRoomMember(roomId, contactId)
965
    }
ruiruibupt's avatar
init  
ruiruibupt 已提交
966 967
  }

968 969 970
  public async roomTopic(roomId: string)                : Promise<string>
  public async roomTopic(roomId: string, topic: string) : Promise<void>

ruiruibupt's avatar
init  
ruiruibupt 已提交
971 972 973 974 975 976 977
  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 已提交
978
      const payload = await this.roomPayload(roomId)
ruiruibupt's avatar
ruiruibupt 已提交
979
      return payload.topic
ruiruibupt's avatar
init  
ruiruibupt 已提交
980
    }
ruiruibupt's avatar
ruiruibupt 已提交
981

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

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

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

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

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

1001
    const roomId = await this.padchatManager.WXCreateChatRoom(contactIdList)
1002

Huan (李卓桓)'s avatar
lint  
Huan (李卓桓) 已提交
1003
    await Misc.retry(async (retry, attempt) => {
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
      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)
      }
    })

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

1131
    const rawPayload = this.cachePadchatMessagePayload.get(friendshipId)
1132
    if (!rawPayload) {
1133
      throw new Error('no rawPayload for id ' + friendshipId)
1134
    }
ruiruibupt's avatar
ruiruibupt 已提交
1135

1136
    return rawPayload
ruiruibupt's avatar
ruiruibupt 已提交
1137 1138
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1139 1140 1141
}

export default PuppetPadchat