firer.ts 8.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 35 36 37 38 39
const PuppetWebFirer = {
  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 = {
Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
53
  friendConfirm:  /^You have added (.+) as your WeChat contact. Start chatting!$/
54

Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
55 56
  , roomJoin:     /^"?(.+?)"? invited "(.+)" to the group chat$/
  , roomLeave:    /^You removed "(.+)" from the group chat$/
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57
  , roomTopic:    /^"?(.+?)"? changed the group name to "(.+)"$/
58 59
}

60
async function fireFriendRequest(m: Message) {
61
  const info = m.rawObj.RecommendInfo
62
  log.verbose('PuppetWebFirer', 'fireFriendRequest(%s)', info)
63 64 65

  const request = new FriendRequest()
  request.receive(info)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
66 67

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

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  this.emit('friend', request.contact, request)
}

/**
 * try to find FriendRequest Confirmation Message
 */
function checkFriendConfirm(content) {
  const re = regexConfig.friendConfirm
  if (re.test(content)) {
    return true
  } else {
    return false
  }
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87
async function fireFriendConfirm(m: Message) {
88
  const content = m.content()
89
  log.silly('PuppetWebFirer', 'fireFriendConfirm(%s)', content)
90 91 92 93 94

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
98
  await contact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
99 100 101
  if (!contact.isReady()) {
    log.warn('PuppetWebFirer', 'fireFriendConfirm() contact still not ready after `ready()` call')
  }
102 103 104 105 106 107
  this.emit('friend', contact)
}

/**
 * try to find 'join' event for Room
 *
108 109 110 111 112 113 114
 * 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 (李卓桓) 已提交
115
function checkRoomJoin(content: string): [string[], string] {
116 117
  log.verbose('PuppetWebFirer', 'checkRoomJoin()')

118 119 120 121
  const re = regexConfig.roomJoin

  const found = content.match(re)
  if (!found) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
122
    throw new Error('checkRoomJoin() not found')
123
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124

125
  const [inviter, inviteeStr] = [ found[1], found[2] ]
126 127 128 129 130

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

  return [inviteeList, inviter] // put invitee at first place
131 132
}

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

136 137 138
  const room    = m.room()
  const content = m.content()

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
139 140 141
  let inviteeList: string[], inviter: string
  try {
    [inviteeList, inviter] = checkRoomJoin(content)
142 143
  } catch (e) {
    // not a room join message
144 145
    return
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
146 147 148 149
  log.silly('PuppetWebFirer', 'fireRoomJoin() inviteeList: %s, inviter: %s'
                            , inviteeList.join(',')
                            , inviter
          )
150

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
151 152
  let inviterContact: Contact
  let inviteeContactList: Contact[] = []
153

154
  try {
155 156 157 158
    if (inviter === "You've") {
      inviterContact = Contact.load(this.userId)
    }

159 160 161 162
    const max = 20
    const backoff = 300
    const timeout = max * (backoff * max) / 2
    // 20 / 300 => 63,000
163 164 165
    // max = (2*totalTime/backoff) ^ (1/2)
    // timeout = 11,250 for {max: 15, backoff: 100}

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

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
      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(',')
                            , inviterContact.name()
              )
204
    })
205

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206
    if (!inviterContact) {
207
      log.error('PuppetWebFirer', 'firmRoomJoin() inivter not found for %s , `room-join` & `join` event will not fired', inviter)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208 209
      return
    }
210
    if (!inviteeContactList.every(c => c instanceof Contact)) {
211 212 213
      log.error('PuppetWebFirer', 'firmRoomJoin() inviteeList not all found for %s , only part of them will in the `room-join` or `join` event'
                                 , inviteeContactList.join(',')
              )
214
      inviteeContactList = inviteeContactList.filter(c => (c instanceof Contact))
215 216 217 218
      if (inviteeContactList.length < 1) {
        log.error('PuppetWebFirer', 'firmRoomJoin() inviteeList empty.  `room-join` & `join` event will not fired')
        return
      }
219
    }
220

221 222
    await Promise.all(inviteeContactList.map(c => c.ready()))
    await inviterContact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
223
    await room.ready()
224 225

    if (inviteeContactList.length === 1) {
226 227
      this.emit('room-join', room , inviteeContactList[0] , inviterContact)
      room.emit('join'            , inviteeContactList[0] , inviterContact)
228
    } else {
229 230
      this.emit('room-join', room , inviteeContactList    , inviterContact)
      room.emit('join'            , inviteeContactList    , inviterContact)
231
    }
232

233
  } catch (e) {
234
    log.error('PuppetWebFirer', 'exception: %s', e.stack)
235 236 237
  }

  return
238 239
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
240
function checkRoomLeave(content: string): string {
241 242 243 244
  const re = regexConfig.roomLeave

  const found = content.match(re)
  if (!found) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
245
    return null
246
  }
247
  return found[1] // leaver
248 249 250 251 252
}

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

256 257 258 259
  const leaver = checkRoomLeave(m.content())
  if (!leaver) {
    return
  }
260 261
  log.silly('PuppetWebFirer', 'fireRoomLeave() got leaver: %s', leaver)

262
  const room = m.room()
263 264
  let leaverContact = room.member(leaver)

265
  if (!leaverContact) {
266
    log.error('PuppetWebFirer', 'fireRoomLeave() leaver %s not found, event `room-leave` & `leave` will not be fired')
267 268
    return
  }
269

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
270 271 272 273 274
  await leaverContact.ready()
  await room.ready()
  this.emit('room-leave', room, leaverContact)
  room.emit('leave'           , leaverContact)
  await room.refresh()
275 276
}

277
function checkRoomTopic(content: string): [string, string] {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
278 279 280 281
  const re = regexConfig.roomTopic

  const found = content.match(re)
  if (!found) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
282
    throw new Error('checkRoomTopic() not found')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
283
  }
284
  const [, changer, topic] = found
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
285 286 287
  return [topic, changer]
}

288
async function fireRoomTopic(m: Message) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
289 290 291 292
  let  topic, changer
  try {
    [topic, changer] = checkRoomTopic(m.content())
  } catch (e) { // not found
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
293 294 295 296 297 298
    return
  }

  const room = m.room()
  const oldTopic = room.topic()

299
  let changerContact: Contact
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
300 301 302 303 304
  if (/^You$/.test(changer)) {
    changerContact = Contact.load(this.userId)
  } else {
    changerContact = room.member(changer)
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
305 306 307 308 309 310

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

311 312 313
  // co.call(this, function* () {
  try {
    await changerContact.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
314
    await room.ready()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
315
    this.emit('room-topic', room, topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
316
    room.emit('topic'           , topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
317
    room.refresh()
318 319
  // }).catch(e => {
  } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
320
    log.error('PuppetWebFirer', 'fireRoomTopic() co exception: %s', e.stack)
321
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
322 323
}

324 325
// module.exports = PuppetWebFirer
export default PuppetWebFirer