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
import Contact        from '../contact'
24
// import Message        from '../message'
25
import log            from '../brolog-env'
26

27 28 29
import FriendRequest  from './friend-request'

/* tslint:disable:variable-name */
30 31 32 33 34 35
const PuppetWebFirer = {
  fireFriendConfirm
  , fireFriendRequest

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
38 39 40
  /**
   * for testing
   */
41
  , checkFriendConfirm
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
42

43 44
  , checkRoomJoin
  , checkRoomLeave
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
45
  , checkRoomTopic
46 47 48
}

const regexConfig = {
Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
49
  friendConfirm:  /^You have added (.+) as your WeChat contact. Start chatting!$/
50

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

function fireFriendRequest(m) {
  const info = m.rawObj.RecommendInfo
58
  log.verbose('PuppetWebFirer', 'fireFriendRequest(%s)', info)
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

  const request = new FriendRequest()
  request.receive(info)
  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
  }
}

function fireFriendConfirm(m) {
  const content = m.content()
79
  log.silly('PuppetWebFirer', 'fireFriendConfirm(%s)', content)
80 81 82 83 84 85 86 87 88 89 90 91 92 93

  if (!checkFriendConfirm(content)) {
    return
  }
  const request = new FriendRequest()
  const contact = Contact.load(m.get('from'))
  request.confirm(contact)

  this.emit('friend', contact)
}

/**
 * try to find 'join' event for Room
 *
94 95 96 97 98 99 100 101
 * 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
 */
function checkRoomJoin(content): [string|string[], string] | boolean {
102 103
  log.verbose('PuppetWebFirer', 'checkRoomJoin()')

104 105 106 107 108 109
  const re = regexConfig.roomJoin

  const found = content.match(re)
  if (!found) {
    return false
  }
110
  const [, inviter, inviteeStr] = found
111 112 113 114 115

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

  return [inviteeList, inviter] // put invitee at first place
116 117 118
}

function fireRoomJoin(m) {
119 120
  log.verbose('PuppetWebFirer', 'fireRoomJoin()')

121 122 123
  const room    = m.room()
  const content = m.content()

Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
124 125
  let result = checkRoomJoin(content)
  if (!result) {
126 127
    return
  }
128
  const [inviteeList, inviter] = <[string[], string]>result
129

130
  let inviterContact, inviteeContactList = []
131

132 133 134 135 136
  co.call(this, function* () {
    if (inviter === "You've") {
      inviterContact = Contact.load(this.userId)
    }

137 138 139 140
    const max = 20
    const backoff = 300
    const timeout = max * (backoff * max) / 2
    // 20 / 300 => 63,000
141 142 143 144
    // max = (2*totalTime/backoff) ^ (1/2)
    // timeout = 11,250 for {max: 15, backoff: 100}

    yield retryPromise({ max: max, backoff: backoff }, attempt => {
145
      log.silly('PuppetWebFirer', 'fireRoomJoin() retryPromise() attempt %d with timeout %d', attempt, timeout)
146 147 148

      return room.refresh()
                  .then(_ => {
149 150 151 152 153 154 155 156 157 158 159 160 161 162
                    log.silly('PuppetWebFirer', 'inviteeList: %s, inviter: %s'
                                              , inviteeList.join(',')
                                              , inviter
                            )

                    let iDone, allDone = true

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

164 165 166
                    if (!inviterContact) {
                      inviterContact = room.member(inviter)
                    }
167 168 169 170

                    if (allDone && inviterContact) {
                      log.silly('PuppetWebFirer', 'fireRoomJoin() resolve() inviteeContactList: %s, inviterContact: %s'
                                                , inviteeContactList.join(',')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
171 172
                                                , inviterContact
                              )
173 174
                      return Promise.resolve()
                    } else {
175 176
                      log.silly('PuppetWebFirer', 'fireRoomJoin() reject() inviteeContactList: %s, inviterContact: %s'
                                                  , inviteeContactList.join(',')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
177 178
                                                  , inviterContact
                                )
179
                      return Promise.reject('not found(yet)')
180 181 182
                    }
                  })
                  .catch(e => {
183
                    log.error('PuppetWebFirer', 'fireRoomJoin9() retryPromise() room.refresh() rejected: %s', e.stack)
184 185 186 187
                    throw e
                  })
    })
    .catch(e => { /* fail safe */ })
188

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
189 190 191 192
    if (!inviterContact) {
      log.error('PuppetWebFirer', 'firmRoomJoin() inivter not found for %s', inviter)
      return
    }
193
    if (!inviteeContactList.every(c => c instanceof Contact)) {
194 195
      log.error('PuppetWebFirer', 'firmRoomJoin() inviteeList not all found for %s', inviteeContactList.join(','))
      inviteeContactList = inviteeContactList.filter(c => (c instanceof Contact))
196
    }
197

198
    yield Promise.all(inviteeContactList.map(c => c.ready()))
199
    yield inviterContact.ready()
200 201 202 203 204 205 206 207

    if (inviteeContactList.length === 1) {
      this.emit('room-join', room , inviteeContactList[0], inviterContact)
      room.emit('join'            , inviteeContactList[0], inviterContact)
    } else {
      this.emit('room-join', room , inviteeContactList, inviterContact)
      room.emit('join'            , inviteeContactList, inviterContact)
    }
208 209 210

  }).catch(e => {
    log.error('PuppetWebFirer', 'retryPromise() rejected: %s', e.stack)
211
  })
212 213 214 215 216 217 218 219 220
}

function checkRoomLeave(content) {
  const re = regexConfig.roomLeave

  const found = content.match(re)
  if (!found) {
    return false
  }
221
  const [, leaver] = found
222 223 224 225 226 227 228 229 230 231 232 233
  return leaver
}

/**
 * You removed "Bruce LEE" from the group chat
 */
function fireRoomLeave(m) {
  const leaver = checkRoomLeave(m.content())
  if (!leaver) {
    return
  }
  const room = m.room()
234 235
  let leaverContact = room.member(leaver)

236
  if (!leaverContact) {
237

238 239 240 241 242
    co.call(this, function* () {
      const max = 20
      const backoff = 300
      const timeout = max * (backoff * max) / 2
      // 20 / 300 => 63,000
243

244 245
      yield retryPromise({ max: max, backoff: backoff }, attempt => {
        log.silly('PuppetWebFirer', 'fireRoomLeave() retryPromise() attempt %d with timeout %d', attempt, timeout)
246

247 248 249
        return room.refresh()
                    .then(_ => {
                      log.silly('PuppetWebFirer', 'leaver: %s', leaver)
250

251 252 253 254 255
                      leaverContact = room.member(leaver)

                      if (leaverContact) {
                        log.silly('PuppetWebFirer', 'fireRoomLeave() resolve() leaverContact: %s'
                                                  , leaverContact
256
                                )
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
                        return Promise.resolve(leaverContact)
                      } else {
                        log.silly('PuppetWebFirer', 'fireRoomLeave() reject() leaver: %s'
                                                    , leaver
                                  )
                        return Promise.reject('not found(yet)')
                      }
                    })
                    .catch(e => {
                      log.error('PuppetWebFirer', 'fireRoomLeave() retryPromise() room.refresh() rejected: %s', e && e.stack || e)
                      throw e
                    })
      })
    }).catch(e => {
      log.error('PuppetWebFirer', 'fireRoomLeave() co exception: %s', e && e.stack || e)
272
    })
273
  }
274 275

  if (!leaverContact) {
276
    log.error('PuppetWebFirer', 'fireRoomLeave() leaver not found for %s', leaver)
277 278
    return
  }
279

280 281 282 283 284 285
  leaverContact.ready()
                .then(_ => {
                  this.emit('room-leave', room, leaverContact)
                  room.emit('leave'           , leaverContact)
                  room.refresh()
                })
286 287
}

288
function checkRoomTopic(content): [string, string] | boolean {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
289 290 291 292 293 294
  const re = regexConfig.roomTopic

  const found = content.match(re)
  if (!found) {
    return false
  }
295
  const [, changer, topic] = found
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
296 297 298 299 300 301 302 303 304
  return [topic, changer]
}

function fireRoomTopic(m) {
  const result = checkRoomTopic(m.content())
  if (!result) {
    return
  }

305
  const [topic, changer] = <[string, string]>result
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
306 307 308
  const room = m.room()
  const oldTopic = room.topic()

309
  let changerContact: Contact
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
310 311 312 313 314
  if (/^You$/.test(changer)) {
    changerContact = Contact.load(this.userId)
  } else {
    changerContact = room.member(changer)
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
315 316 317 318 319 320 321 322 323

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

  co.call(this, function* () {
    yield changerContact.ready()
    this.emit('room-topic', room, topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
324
    room.emit('topic'           , topic, oldTopic, changerContact)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
325 326 327 328 329 330
    room.refresh()
  }).catch(e => {
    log.error('PuppetWebFirer', 'fireRoomTopic() co exception: %s', e.stack)
  })
}

331 332
// module.exports = PuppetWebFirer
export default PuppetWebFirer