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,
              )
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
  public async messageFile(messageId: string): Promise<FileBox> {
638
    log.warn('PuppetPadchat', 'messageFile(%s) not fully implemented yet, PR is welcome!', messageId)
ruiruibupt's avatar
ruiruibupt 已提交
639

ruiruibupt's avatar
ruiruibupt 已提交
640 641
    // TODO

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

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

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

    let result

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

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

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

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

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

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

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

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

    return file
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return payload
  }

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

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

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

    return memberIdList
  }

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

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

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

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

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

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

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

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

    // TODO

    throw new Error('not support')

  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

export default PuppetPadchat