puppet-padchat.ts 32.7 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 870 871 872
    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)

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

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

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

884 885
    // const memberIdList = await this.bridge.getRoomMemberIdList()
    //  WXGetChatRoomMember(rawPayload.user_name)
886

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

    return payload
  }

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

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

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

    return memberIdList
  }

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

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

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

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

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

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

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

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

    // TODO

    throw new Error('not support')

  }

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

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

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

978 979 980
  public async roomTopic(roomId: string)                : Promise<string>
  public async roomTopic(roomId: string, topic: string) : Promise<void>

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

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

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

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

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

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

1011
    const roomId = await this.padchatManager.WXCreateChatRoom(contactIdList)
1012

Huan (李卓桓)'s avatar
lint  
Huan (李卓桓) 已提交
1013
    await Misc.retry(async (retry, attempt) => {
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
      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)
      }
    })

1028
    return roomId
ruiruibupt's avatar
init  
ruiruibupt 已提交
1029 1030 1031 1032
  }

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

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

1038
    await this.padchatManager.WXQuitChatRoom(roomId)
ruiruibupt's avatar
init  
ruiruibupt 已提交
1039 1040
  }

1041 1042 1043 1044 1045
  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 : '')
1046

1047
    if (!this.padchatManager) {
1048 1049 1050
      throw new Error('no bridge')
    }

1051
    if (text) {
1052
      await this.padchatManager.WXSetChatroomAnnouncement(roomId, text)
1053
    } else {
1054
      return await this.padchatManager.WXGetChatroomAnnouncement(roomId)
1055 1056 1057
    }
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1058 1059
  /**
   *
1060
   * Friendship
ruiruibupt's avatar
init  
ruiruibupt 已提交
1061 1062
   *
   */
1063
  public async friendshipVerify(
ruiruibupt's avatar
init  
ruiruibupt 已提交
1064 1065 1066
    contactId : string,
    hello     : string,
  ): Promise<void> {
1067
    log.verbose('PuppetPadchat', 'friendshipVerify(%s, %s)', contactId, hello)
ruiruibupt's avatar
ruiruibupt 已提交
1068

1069
    if (!this.padchatManager) {
E
Egg 已提交
1070 1071
      throw new Error('no bridge')
    }
ruiruibupt's avatar
ruiruibupt 已提交
1072

1073
    const rawSearchPayload: WXSearchContactType = await this.padchatManager.WXSearchContact(contactId)
1074

E
Egg 已提交
1075 1076 1077
    /**
     * If the contact is not stranger, than ussing WXSearchContact can get user_name
     */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1078
    if (rawSearchPayload.user_name !== '' && !isStrangerV1(rawSearchPayload.user_name) && !isStrangerV2(rawSearchPayload.user_name)) {
E
Egg 已提交
1079 1080 1081
      log.warn('PuppetPadchat', 'friendRequestSend %s has been friend with bot, no need to send friend request!', contactId)
      return
    }
ruiruibupt's avatar
ruiruibupt 已提交
1082

E
Egg 已提交
1083 1084
    let strangerV1
    let strangerV2
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1085
    if (isStrangerV1(rawSearchPayload.stranger)) {
E
Egg 已提交
1086 1087
      strangerV1 = rawSearchPayload.stranger
      strangerV2 = rawSearchPayload.user_name
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1088
    } else if (isStrangerV2(rawSearchPayload.stranger)) {
E
Egg 已提交
1089 1090 1091 1092
      strangerV2 = rawSearchPayload.stranger
      strangerV1 = rawSearchPayload.user_name
    } else {
      throw new Error('stranger neither v1 nor v2!')
1093 1094
    }

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

1097
    await this.padchatManager.WXAddUser(
E
Egg 已提交
1098 1099 1100
      strangerV1 || '',
      strangerV2 || '',
      WXSearchContactTypeStatus.WXID, // default
ruiruibupt's avatar
ruiruibupt 已提交
1101 1102
      hello,
    )
ruiruibupt's avatar
init  
ruiruibupt 已提交
1103 1104
  }

1105 1106
  public async friendshipAccept(
    friendshipId : string,
ruiruibupt's avatar
init  
ruiruibupt 已提交
1107
  ): Promise<void> {
1108
    log.verbose('PuppetPadchat', 'friendshipAccept(%s)', friendshipId)
ruiruibupt's avatar
ruiruibupt 已提交
1109

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

1112
    console.log('friendshipAccept: ', payload)
1113

1114 1115 1116
    if (!payload.ticket) {
      throw new Error('no ticket')
    }
1117 1118 1119
    if (!payload.stranger) {
      throw new Error('no stranger')
    }
ruiruibupt's avatar
ruiruibupt 已提交
1120

1121
    if (!this.padchatManager) {
1122 1123 1124
      throw new Error('no bridge')
    }

1125
    await this.padchatManager.WXAcceptUser(
1126
      payload.stranger,
1127 1128
      payload.ticket,
    )
ruiruibupt's avatar
init  
ruiruibupt 已提交
1129 1130
  }

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

1134
    const payload: FriendshipPayload = await friendshipRawPayloadParser(rawPayload)
1135
    return payload
ruiruibupt's avatar
ruiruibupt 已提交
1136 1137
  }

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1141 1142 1143
    /**
     * Friendship shares Cache with the Message RawPayload
     */
1144
    const rawPayload = this.cachePadchatMessagePayload.get(friendshipId)
1145
    if (!rawPayload) {
1146
      throw new Error('no rawPayload for id ' + friendshipId)
1147
    }
ruiruibupt's avatar
ruiruibupt 已提交
1148

1149
    return rawPayload
ruiruibupt's avatar
ruiruibupt 已提交
1150 1151
  }

ruiruibupt's avatar
init  
ruiruibupt 已提交
1152 1153 1154
}

export default PuppetPadchat