io-client.ts 4.9 KB
Newer Older
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1
/**
2
 *   Wechaty - https://github.com/wechaty/wechaty
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
3
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
4
 *   @copyright 2016-2018 Huan LI <zixia@zixia.net>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
5
 *
6 7 8
 *   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
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
9
 *
10 11 12 13 14 15 16
 *       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.
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
17 18 19 20 21 22
 *
 */
/**
 * DO NOT use `require('../')` here!
 * because it will casue a LOOP require ERROR
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
23
import { StateSwitch }  from 'state-switch'
24

25
import { Message }      from './user'
26

27
import {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
28
  log,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
29
}                      from './config'
30 31
import { Io }           from './io'
import { Wechaty }      from './wechaty'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
32

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33
export interface IoClientOptions {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
34 35
  token   : string,
  wechaty : Wechaty,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
36 37
}

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40
  private io: Io // XXX keep io `null-able` or not? 20161026
41

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
42
  private state: StateSwitch
43

44
  constructor (
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
45
    public options: IoClientOptions,
46
  ) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
47
    log.verbose('IoClient', 'constructor(%s)', JSON.stringify(options))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
48

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
49
    this.state = new StateSwitch('IoClient', log)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
50 51

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

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
61
    if (this.state.pending()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
62 63
      log.warn('IoClient', 'start() with a pending state, not the time')
      const e = new Error('state.pending() when start()')
64
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
65 66
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
67
    this.state.on('pending')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
68

69
    try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
70 71

      await this.startIo()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72
      await this.hookWechaty(this.options.wechaty)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
73

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
74
      this.state.on(true)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
75

76
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
77
      log.error('IoClient', 'init() exception: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
78
      this.state.off(true)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
79
      throw e
80
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
81 82
  }

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
92
    wechaty
93 94 95 96
      .on('login',    user => log.info('IoClient', `${user.name()} logined`))
      .on('logout',   user => log.info('IoClient', `${user.name()} logouted`))
      .on('scan',     (url, code) => log.info('IoClient', `[${code}] ${url}`))
      .on('message',  msg => this.onMessage(msg))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
97 98
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
99 100
  private async startIo (): Promise<void> {
    log.verbose('IoClient', 'startIo() with token %s', this.options.token)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
101

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
102
    if (this.state.off()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
103
      const e = new Error('startIo() state.off() is true, skipped')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
104
      log.warn('IoClient', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
105
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
106 107
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
108
    try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
109
      await this.io.start()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
110
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
111
      log.verbose('IoClient', 'startIo() init fail: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
112 113
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
114 115
  }

116 117
  private async onMessage (msg: Message) {
    log.verbose('IoClient', 'onMessage(%s)', msg)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
118

119 120 121 122
    // const from = m.from()
    // const to = m.to()
    // const content = m.toString()
    // const room = m.room()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
123

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124
    // log.info('Bot', '%s<%s>:%s'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
125
    //               , (room ? '['+room.topic()+']' : '')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
126 127 128 129
    //               , from.name()
    //               , m.toStringDigest()
    //         )

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
130 131 132 133
    // if (/^wechaty|chatie|botie/i.test(m.text()) && !m.self()) {
    //   await m.say('https://www.chatie.io')
    //     .then(_ => log.info('Bot', 'REPLIED to magic word "chatie"'))
    // }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
134
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
135

136
  public async stop (): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
137
    log.verbose('IoClient', 'stop()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
138

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
139
    this.state.off('pending')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
140

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
141
    // XXX
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
142
    if (!this.io) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
143
      log.warn('IoClient', 'stop() without this.io')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
144
      this.state.off(true)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
146 147
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
148
    await this.io.stop()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
149
    this.state.off(true)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
150 151

    // XXX 20161026
152
    // this.io = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
153 154
  }

155
  public async restart (): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
156
    log.verbose('IoClient', 'restart()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
157

158 159 160 161
    try {
      await this.stop()
      await this.start()
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
162
      log.error('IoClient', 'restart() exception %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
163
      throw e
164
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
165 166
  }

167
  public async quit (): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
168
    log.verbose('IoClient', 'quit()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
169

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
170 171 172
    if (this.state.off() === 'pending') {
      log.warn('IoClient', 'quit() with state.off() = `pending`, skipped')
      throw new Error('quit() with state.off() = `pending`')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
173 174
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
175
    this.state.off('pending')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
176

177
    try {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
178
      if (this.options.wechaty) {
Huan (李卓桓)'s avatar
wip...  
Huan (李卓桓) 已提交
179
        await this.options.wechaty.stop()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
180
        // this.wechaty = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
181
      } else { log.warn('IoClient', 'quit() no this.wechaty') }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
182 183

      if (this.io) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
184
        await this.io.stop()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
185
        // this.io = null
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
186
      } else { log.warn('IoClient', 'quit() no this.io') }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
187

188
    } catch (e) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
189
      log.error('IoClient', 'exception: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
190
      throw e
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
191 192
    } finally {
      this.state.off(true)
193
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
194
  }
195

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