io-client.ts 6.5 KB
Newer Older
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 *
 * wechaty: Wechat for Bot. and for human who talk to bot/robot
 *
 * Class IoClient
 * http://www.wechaty.io
 *
 * Licenst: ISC
 * https://github.com/wechaty/wechaty
 *
 */

/**
 * DO NOT use `require('../')` here!
 * because it will casue a LOOP require ERROR
 */
17 18
// import Brolog   from 'brolog'

19 20 21 22 23
import { Config }       from './config'
import { Io }           from './io'
import { StateMonitor } from './state-monitor'
import { Wechaty }      from './wechaty'
import { Brolog }       from './brolog-env'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
24

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
25
export class IoClient {
26

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
27 28
  private wechaty: Wechaty
  private io: Io // XXX keep io `null-able` or not? 20161026
29

30 31
  private state = new StateMonitor<'online', 'offline'>('IoClient', 'offline')

32
  constructor(
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33 34
    private token: string = Config.token || Config.DEFAULT_TOKEN,
    private log: any = new Brolog(),
35
  ) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
36 37 38 39
    if (!log) {
      const e = new Error('constructor() log(npmlog/brolog) must be set')
      throw e
    }
40
    this.log.verbose('IoClient', 'constructor() with token: %s', token)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
41

42
    if (!token) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
43 44 45 46
      const e = new Error('constructor() token must be set')
      this.log.error('IoClient', e.message)
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
47 48

    this.wechaty = Wechaty.instance({
L
lijiarui 已提交
49
      profile: token,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
50 51 52
    })

    this.io = new Io({
L
lijiarui 已提交
53 54
      wechaty: this.wechaty,
      token: this.token,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
55 56
    })

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57 58
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
59
  public async init(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
60 61
    this.log.verbose('IoClient', 'init()')

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
62
    if (this.state.inprocess()) {
63 64 65
      const e = new Error('state.inprocess(), skip init')
      this.log.warn('IoClient', 'init() with %s', e.message)
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
66 67
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
68 69
    this.state.target('online')
    this.state.current('online', false)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
70

71
    try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72 73
      await this.initIo()
      await this.initWechaty()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
74
      this.state.current('online')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
75
      return
76
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
77
      this.log.error('IoClient', 'init() exception: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
78
      this.state.current('offline')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
79
      throw e
80
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
81 82
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
83
  private async initWechaty(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
84 85
    this.log.verbose('IoClient', 'initWechaty()')

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86
    if (this.state.target() !== 'online') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87 88 89
      const e = new Error('state.target() is not `online`, skipped')
      this.log.warn('IoClient', 'initWechaty() %s', e.message)
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
90 91 92 93
    }

    const wechaty = this.wechaty

94 95 96 97
    if (!wechaty) {
      throw new Error('no Wechaty')
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
98
    wechaty
99 100 101
    .on('login'	     , user => this.log.info('IoClient', `${user.name()} logined`))
    .on('logout'	   , user => this.log.info('IoClient', `${user.name()} logouted`))
    .on('scan', (url, code) => this.log.info('IoClient', `[${code}] ${url}`))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
102
    .on('message'     , msg => this.onMessage(msg))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
103

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
104 105 106 107 108 109 110 111 112
    try {
      await wechaty.init()
      this.log.verbose('IoClient', 'wechaty.init() done')
    } catch (e) {
      this.log.error('IoClient', 'init() init fail: %s', e)
      wechaty.quit()
      throw e
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
113
    return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
114 115
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
116
  private async initIo(): Promise<void> {
117
    this.log.verbose('IoClient', 'initIo() with token %s', this.token)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
118

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
119
    if (this.state.target() !== 'online') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
120 121 122
      const e = new Error('initIo() targetState is not `connected`, skipped')
      this.log.warn('IoClient', e.message)
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
123 124
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
125 126 127 128 129 130
    try {
      await this.io.init()
    } catch (e) {
      this.log.verbose('IoClient', 'initIo() init fail: %s', e.message)
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
131

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
132
    return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
133 134
  }

135
  public initWeb(port = Config.httpPort) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
136 137 138 139 140 141 142 143
//    if (process.env.DYNO) {
//    }
    const app = require('express')()

    app.get('/', function (req, res) {
      res.send('Wechaty IO Bot Alive!')
    })

144
    return new Promise((resolve) => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145 146 147 148 149 150 151 152 153
      app.listen(port, () => {
        this.log.verbose('IoClient', 'initWeb() Wechaty IO Bot listening on port ' + port + '!')

        return resolve(this)

      })
    })
  }

154 155 156 157 158
  private onMessage(m) {
    // const from = m.from()
    // const to = m.to()
    // const content = m.toString()
    // const room = m.room()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
159

160
    // this.log.info('Bot', '%s<%s>:%s'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
161
    //               , (room ? '['+room.topic()+']' : '')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
162 163 164 165
    //               , from.name()
    //               , m.toStringDigest()
    //         )

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
166
    if (/^wechaty|botie/i.test(m.content()) && !m.self()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
167
      m.say('https://www.wechaty.io')
168
        .then(_ => this.log.info('Bot', 'REPLIED to magic word "wechaty"'))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
169 170
    }
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
171

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
172
  public async start(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
173 174
    this.log.verbose('IoClient', 'start()')

175 176 177
    if (!this.wechaty) {
      return this.init()
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
178

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
179
    if (this.state.inprocess()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
180 181 182
      this.log.warn('IoClient', 'start() with a pending state, not the time')
      return Promise.reject('pending')
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
183

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
184 185
    this.state.target('online')
    this.state.current('online', false)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
186

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
187 188 189 190 191 192 193 194 195
    try {
      await this.initIo()
      this.state.current('online')
      return
    } catch (e) {
      this.log.error('IoClient', 'start() exception: %s', e.message)
      this.state.current('offline')
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
196 197
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
198
  public async stop(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
199
    this.log.verbose('IoClient', 'stop()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
200 201
    this.state.target('offline')
    this.state.current('offline', false)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
202

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203
    // XXX
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
204 205
    if (!this.io) {
      this.log.warn('IoClient', 'stop() without this.io')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206 207
      // this.currentState('connected')
      this.state.current('online')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208 209 210
      return Promise.resolve()
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
211
    await this.io.quit()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
212
                    // .then(_ => this.currentState('disconnected'))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
213 214 215 216
                    // .then(_ => this.state.current('offline'))
    this.state.current('offline')

    // XXX 20161026
217
    // this.io = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
218
    return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
219 220
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
221
  public async restart(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
222 223
    this.log.verbose('IoClient', 'restart()')

224 225 226
    try {
      await this.stop()
      await this.start()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
227
      return
228
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
229 230
      this.log.error('IoClient', 'restart() exception %s', e.message)
      throw e
231
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
232 233
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
234
  public async quit(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235
    this.log.verbose('IoClient', 'quit()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
236

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
237
    if (this.state.current() === 'offline' && this.state.inprocess()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
238 239 240 241
      this.log.warn('IoClient', 'quit() with currentState() = `disconnecting`, skipped')
      return Promise.reject('quit() with currentState = `disconnecting`')
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
242 243
    this.state.target('offline')
    this.state.current('offline', false)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
244

245
    try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
246
      if (this.wechaty) {
247
        await this.wechaty.quit()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
248
        // this.wechaty = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
249 250 251
      } else { this.log.warn('IoClient', 'quit() no this.wechaty') }

      if (this.io) {
252
        await this.io.quit()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
253
        // this.io = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
254 255
      } else { this.log.warn('IoClient', 'quit() no this.io') }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
256 257
      this.state.current('offline')

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
258 259
      return

260
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
261
      this.log.error('IoClient', 'exception: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
262

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
263
      // XXX fail safe?
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
264
      this.state.current('offline')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
265

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
266
      throw e
267
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
268
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
269
}