firer.ts 9.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/**
 *
 * Wechaty: Wechat for Bot. Connecting ChatBots
 *
 * Class PuppetWeb Firer
 *
 * Process the Message to find which event to FIRE
 *
 * Licenst: ISC
 * https://github.com/wechaty/wechaty
 *
 * Firer for Class PuppetWeb
 *
 * here `this` is a PuppetWeb Instance
 *
16 17 18 19 20
 */
// import * as util  from 'util'
// import * as fs    from 'fs'

/* tslint:disable:no-var-requires */
21
const retryPromise  = require('retry-promise').default
22

23 24 25 26
import {
  // RecommendInfo
}                     from '../config'

27
import Contact        from '../contact'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
28
import Message        from '../message'
29
import log            from '../brolog-env'
30

31 32 33
import FriendRequest  from './friend-request'

/* tslint:disable:variable-name */
34
export const PuppetWebFirer = {
35 36 37 38 39
  fireFriendConfirm
  , fireFriendRequest

  , fireRoomJoin
  , fireRoomLeave
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40
  , fireRoomTopic
41

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
42 43 44
  /**
   * for testing
   */
45
  , checkFriendConfirm
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
46

47 48
  , checkRoomJoin
  , checkRoomLeave
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
49
  , checkRoomTopic
50 51 52
}

const regexConfig = {
53 54
  friendConfirm: [
      /^You have added (.+) as your WeChat contact. Start chatting!$/
55
    , /^你已添加了(.+),现在可以开始聊天了。$/
56 57 58 59 60 61 62 63 64 65 66 67 68 69
  ]

  , roomJoin: [
      /^"?(.+?)"? invited "(.+)" to the group chat$/
    , /^"?(.+?)"?邀请"(.+)"加入了群聊$/
  ]
  , roomLeave: [
      /^You removed "(.+)" from the group chat$/
    , /^你将"(.+)"移出了群聊$/
  ]
  , roomTopic: [
      /^"?(.+?)"? changed the group name to "(.+)"$/
    , /^"?(.+?)"?修改群名为“(.+)”$/
  ]
70 71
}

72
async function fireFriendRequest(m: Message) {
73 74 75
  if (!m.rawObj) {
    throw new Error('message empty')
  }
76
  const info = m.rawObj.RecommendInfo
77
  log.verbose('PuppetWebFirer', 'fireFriendRequest(%s)', info)
78

79 80 81
  if (!info) {
    throw new Error('no info')
  }
82 83
  const request = new FriendRequest()
  request.receive(info)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
84 85

  await request.contact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86 87 88 89
  if (!request.contact.isReady()) {
    log.warn('PuppetWebFirer', 'fireFriendConfirm() contact still not ready after `ready()` call')
  }

90 91 92 93 94 95 96
  this.emit('friend', request.contact, request)
}

/**
 * try to find FriendRequest Confirmation Message
 */
function checkFriendConfirm(content) {
97 98 99 100
  const reList = regexConfig.friendConfirm
  let found = false
  reList.some(re => !!(found = re.test(content)))
  if (found) {
101 102 103 104 105 106
    return true
  } else {
    return false
  }
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
107
async function fireFriendConfirm(m: Message) {
108
  const content = m.content()
109
  log.silly('PuppetWebFirer', 'fireFriendConfirm(%s)', content)
110 111 112 113 114

  if (!checkFriendConfirm(content)) {
    return
  }
  const request = new FriendRequest()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
115
  const contact = m.from()
116 117
  request.confirm(contact)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
118
  await contact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
119 120 121
  if (!contact.isReady()) {
    log.warn('PuppetWebFirer', 'fireFriendConfirm() contact still not ready after `ready()` call')
  }
122 123 124 125 126 127
  this.emit('friend', contact)
}

/**
 * try to find 'join' event for Room
 *
128 129 130 131 132 133 134
 * 1.
 *  You've invited "李卓桓" to the group chat
 *  You've invited "李卓桓.PreAngel、Bruce LEE" to the group chat
 * 2.
 *  "李卓桓.PreAngel" invited "Bruce LEE" to the group chat
 *  "凌" invited "庆次、小桔妹" to the group chat
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
135
function checkRoomJoin(content: string): [string[], string] {
136 137
  log.verbose('PuppetWebFirer', 'checkRoomJoin()')

138
  const reList = regexConfig.roomJoin
139

140 141 142
  let found: string[]|null = []
  reList.some(re => !!(found = content.match(re)))
  if (!found || !found.length) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
143
    throw new Error('checkRoomJoin() not found')
144
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145

146
  const [inviter, inviteeStr] = [ found[1], found[2] ]
147 148 149 150 151

  // "凌" invited "庆次、小桔妹" to the group chat
  const inviteeList = inviteeStr.split(/、/)

  return [inviteeList, inviter] // put invitee at first place
152 153
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
154
async function fireRoomJoin(m: Message): Promise<void> {
155
  log.verbose('PuppetWebFirer', 'fireRoomJoin(%s)', m.content())
156

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
157
  const room = m.room()
158 159 160 161 162
  if (!room) {
    log.warn('PuppetWebFirer', 'fireRoomJoin() `room` not found')
    return
  }

163 164
  const content = m.content()

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
165 166 167
  let inviteeList: string[], inviter: string
  try {
    [inviteeList, inviter] = checkRoomJoin(content)
168
  } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
169
    log.silly('PuppetWebFirer', 'fireRoomJoin() "%s" is not a join message', content)
170
    // not a room join message
171 172
    return
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
173 174 175 176
  log.silly('PuppetWebFirer', 'fireRoomJoin() inviteeList: %s, inviter: %s'
                            , inviteeList.join(',')
                            , inviter
          )
177

178
  let inviterContact: Contact | null = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
179
  let inviteeContactList: Contact[] = []
180

181
  try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
182
    if (inviter === "You've" || inviter === '') {
183 184 185
      inviterContact = Contact.load(this.userId)
    }

186 187 188 189
    const max = 20
    const backoff = 300
    const timeout = max * (backoff * max) / 2
    // 20 / 300 => 63,000
190 191 192
    // max = (2*totalTime/backoff) ^ (1/2)
    // timeout = 11,250 for {max: 15, backoff: 100}

193
    await retryPromise({ max: max, backoff: backoff }, async (attempt: number) => {
194
      log.silly('PuppetWebFirer', 'fireRoomJoin() retryPromise() attempt %d with timeout %d', attempt, timeout)
195

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
      await room.refresh()
                  // .then(_ => {
      let iDone, allDone = true

      for (let i in inviteeList) {
        iDone = inviteeContactList[i] instanceof Contact
        if (!iDone) {
          let c = room.member(inviteeList[i])
          if (c) {
            inviteeContactList[i] = c
          } else {
            allDone = false
          }
        }
      }

      if (!inviterContact) {
        inviterContact = room.member(inviter)
      }

      if (allDone && inviterContact) {
        log.silly('PuppetWebFirer', 'fireRoomJoin() resolve() inviteeContactList: %s, inviterContact: %s'
                                  , inviteeContactList.map((c: Contact) => c.name()).join(',')
                                  , inviterContact.name()
                )
        return
      }

      throw new Error('not found(yet)')

    }).catch(e => {
      log.silly('PuppetWebFirer', 'fireRoomJoin() reject() inviteeContactList: %s, inviterContact: %s'
                            , inviteeContactList.map((c: Contact) => c.name()).join(',')
229
                            , inviter
230
              )
231
    })
232

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
233
    if (!inviterContact) {
234
      log.error('PuppetWebFirer', 'firmRoomJoin() inivter not found for %s , `room-join` & `join` event will not fired', inviter)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235 236
      return
    }
237
    if (!inviteeContactList.every(c => c instanceof Contact)) {
238 239 240
      log.error('PuppetWebFirer', 'firmRoomJoin() inviteeList not all found for %s , only part of them will in the `room-join` or `join` event'
                                 , inviteeContactList.join(',')
              )
241
      inviteeContactList = inviteeContactList.filter(c => (c instanceof Contact))
242 243 244 245
      if (inviteeContactList.length < 1) {
        log.error('PuppetWebFirer', 'firmRoomJoin() inviteeList empty.  `room-join` & `join` event will not fired')
        return
      }
246
    }
247

248 249
    await Promise.all(inviteeContactList.map(c => c.ready()))
    await inviterContact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
250
    await room.ready()
251 252

    if (inviteeContactList.length === 1) {
253 254
      this.emit('room-join', room , inviteeContactList[0] , inviterContact)
      room.emit('join'            , inviteeContactList[0] , inviterContact)
255
    } else {
256 257
      this.emit('room-join', room , inviteeContactList    , inviterContact)
      room.emit('join'            , inviteeContactList    , inviterContact)
258
    }
259

260
  } catch (e) {
261
    log.error('PuppetWebFirer', 'exception: %s', e.stack)
262 263 264
  }

  return
265 266
}

267
function checkRoomLeave(content: string): string|null {
268
  const reList = regexConfig.roomLeave
269

270 271 272
  let found: string[]|null = []
  reList.some(re => !!(found = content.match(re)))
  if (!found || !found.length) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
273
    return null
274
  }
275
  return found[1] // leaver
276 277 278 279 280
}

/**
 * You removed "Bruce LEE" from the group chat
 */
281 282 283
async function fireRoomLeave(m: Message) {
  log.verbose('PuppetWebFirer', 'fireRoomLeave(%s)', m.content())

284 285 286 287
  const leaver = checkRoomLeave(m.content())
  if (!leaver) {
    return
  }
288 289
  log.silly('PuppetWebFirer', 'fireRoomLeave() got leaver: %s', leaver)

290
  const room = m.room()
291 292 293 294
  if (!room) {
    log.warn('PuppetWebFirer', 'fireRoomLeave() room not found')
    return
  }
295 296
  let leaverContact = room.member(leaver)

297
  if (!leaverContact) {
298
    log.error('PuppetWebFirer', 'fireRoomLeave() leaver %s not found, event `room-leave` & `leave` will not be fired')
299 300
    return
  }
301

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
302 303 304 305 306
  await leaverContact.ready()
  await room.ready()
  this.emit('room-leave', room, leaverContact)
  room.emit('leave'           , leaverContact)
  await room.refresh()
307 308
}

309
function checkRoomTopic(content: string): [string, string] {
310
  const reList = regexConfig.roomTopic
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
311

312 313 314
  let found: string[]|null = []
  reList.some(re => !!(found = content.match(re)))
  if (!found || !found.length) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
315
    throw new Error('checkRoomTopic() not found')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
316
  }
317
  const [, changer, topic] = found
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
318 319 320
  return [topic, changer]
}

321
async function fireRoomTopic(m: Message): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
322 323 324 325
  let  topic, changer
  try {
    [topic, changer] = checkRoomTopic(m.content())
  } catch (e) { // not found
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
326 327 328 329
    return
  }

  const room = m.room()
330 331 332 333 334
  if (!room) {
    log.warn('PuppetWebFirer', 'fireRoomLeave() room not found')
    return
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
335 336
  const oldTopic = room.topic()

337
  let changerContact: Contact | null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
338
  if (/^You$/.test(changer) || /^你$/.test(changer)) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
339 340 341 342
    changerContact = Contact.load(this.userId)
  } else {
    changerContact = room.member(changer)
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
343 344 345 346 347 348

  if (!changerContact) {
    log.error('PuppetWebFirer', 'fireRoomTopic() changer contact not found for %s', changer)
    return
  }

349 350
  try {
    await changerContact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
351
    await room.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
352
    this.emit('room-topic', room, topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
353
    room.emit('topic'           , topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
354
    room.refresh()
355
  } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
356
    log.error('PuppetWebFirer', 'fireRoomTopic() co exception: %s', e.stack)
357
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
358 359
}

360
export default PuppetWebFirer