wechaty.ts 17.8 KB
Newer Older
1
/**
2
 *   Wechaty - https://github.com/chatie/wechaty
3
 *
4
 *   @copyright 2016-2017 Huan LI <zixia@zixia.net>
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
9
 *
10
 *       http://www.apache.org/licenses/LICENSE-2.0
11
 *
12 13 14 15 16
 *   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.
L
lijiarui 已提交
17 18
 *
 *  @ignore
19
 */
20
import { EventEmitter } from 'events'
21 22 23 24 25
import StateSwitch      from 'state-switch'
import {
  callerResolve,
  hotImport,
}                       from 'hot-import'
26

27
import {
28
  config,
29 30
  HeadName,
  PuppetName,
31
  Raven,
32 33 34
  Sayable,
  log,
}                         from './config'
35

36 37
import { Contact }        from './contact'
import { FriendRequest }  from './friend-request'
M
Mukaiu 已提交
38 39 40 41
import {
  Message,
  MediaMessage,
}                         from './message'
42 43 44 45
import { Puppet }         from './puppet'
import { PuppetWeb }      from './puppet-web/'
import { Room }           from './room'
import { UtilLib }        from './util-lib'
46

47
export interface PuppetSetting {
L
lijiarui 已提交
48 49 50
  head?:    HeadName,
  puppet?:  PuppetName,
  profile?: string,
51
}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
52

53 54 55 56 57 58 59 60 61 62
export type WechatyEventName = 'error'
                              | 'friend'
                              | 'heartbeat'
                              | 'login'
                              | 'logout'
                              | 'message'
                              | 'room-join'
                              | 'room-leave'
                              | 'room-topic'
                              | 'scan'
63

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
64
/**
L
lijiarui 已提交
65
 * Main bot class.
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
66
 *
L
lijiarui 已提交
67
 * [The World's Shortest ChatBot Code: 6 lines of JavaScript]{@link #wechatyinstance}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
68
 *
L
lijiarui 已提交
69 70 71
 * [Wechaty Starter Project]{@link https://github.com/lijiarui/wechaty-getting-started}
 * @example
 * import { Wechaty } from 'wechaty'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
73
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
74
export class Wechaty extends EventEmitter implements Sayable {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
75 76
  /**
   * singleton _instance
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
77
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
78
   */
79
  private static _instance: Wechaty
80

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
81 82
  /**
   * the puppet
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
83
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
84
   */
85
  public puppet: Puppet | null
86

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87 88
  /**
   * the state
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
89
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
90
   */
91
  private state = new StateSwitch<'standby', 'ready'>('Wechaty', 'standby', log)
92

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
93 94
  /**
   * the uuid
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
95
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
96
   */
97
  public uuid:        string
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
98

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
99
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
100
   * get the singleton instance of Wechaty
L
lijiarui 已提交
101 102 103 104 105 106 107 108 109
   *
   * @example <caption>The World's Shortest ChatBot Code: 6 lines of JavaScript</caption>
   * const { Wechaty } = require('wechaty')
   *
   * Wechaty.instance() // Singleton
   * .on('scan', (url, code) => console.log(`Scan QR Code to login: ${code}\n${url}`))
   * .on('login',       user => console.log(`User ${user} logined`))
   * .on('message',  message => console.log(`Message: ${message}`))
   * .init()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
110
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
111
  public static instance(setting?: PuppetSetting) {
112
    if (setting && this._instance) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
113
      throw new Error('there has already a instance. no params will be allowed any more')
114 115 116 117 118 119 120
    }
    if (!this._instance) {
      this._instance = new Wechaty(setting)
    }
    return this._instance
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
121
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
122
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
123
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124
  private constructor(private setting: PuppetSetting = {}) {
125
    super()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
126 127
    log.verbose('Wechaty', 'contructor()')

128 129 130
    setting.head    = setting.head    || config.head
    setting.puppet  = setting.puppet  || config.puppet
    setting.profile = setting.profile || config.profile
131

132
    // setting.port    = setting.port    || Config.port
133

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
134 135
    if (setting.profile) {
      setting.profile  = /\.wechaty\.json$/i.test(setting.profile)
136 137
                        ? setting.profile
                        : setting.profile + '.wechaty.json'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
138
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
139

140
    this.uuid = UtilLib.guid()
141 142
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
143
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
144
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
146
  public toString() { return 'Class Wechaty(' + this.setting.puppet + ')'}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
147

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
148 149
  /**
   * Return version of Wechaty
Huan (李卓桓)'s avatar
doc  
Huan (李卓桓) 已提交
150
   *
Huan (李卓桓)'s avatar
dodc  
Huan (李卓桓) 已提交
151 152
   * @param {boolean} [forceNpm=false]  - if set to true, will only return the version in package.json.
   *                                      otherwise will return git commit hash if .git exists.
Huan (李卓桓)'s avatar
doc  
Huan (李卓桓) 已提交
153
   * @returns {string}                  - the version number
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
154
   * @example
L
lijiarui 已提交
155 156
   * console.log(Wechaty.instance().version())       // return '#git[af39df]'
   * console.log(Wechaty.instance().version(true))   // return '0.7.9'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
157
   */
158
  public static version(forceNpm = false): string {
159
    if (!forceNpm) {
160 161
      const revision = config.gitVersion()
      if (revision) {
162
        return `#git[${revision}]`
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
163
      }
164
    }
165 166
    return config.npmVersion()
  }
167

H
hcz 已提交
168
  /**
L
lijiarui 已提交
169
   * @private
H
hcz 已提交
170
   */
171 172
  public version(forceNpm?) {
    return Wechaty.version(forceNpm)
173
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
174

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
175
  /**
L
lijiarui 已提交
176
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
177
   */
Huan (李卓桓)'s avatar
fix #54  
Huan (李卓桓) 已提交
178
  public user(): Contact {
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
179 180
    log.warn('Wechaty', 'user() DEPRECATED. use self() instead.')

Huan (李卓桓)'s avatar
fix #54  
Huan (李卓桓) 已提交
181 182 183 184 185
    if (!this.puppet || !this.puppet.user) {
      throw new Error('no user')
    }
    return this.puppet.user
  }
186

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
187
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
188
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
189
   */
190
  public async reset(reason?: string): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
191
    log.verbose('Wechaty', 'reset() because %s', reason)
192 193 194
    if (!this.puppet) {
      throw new Error('no puppet')
    }
195 196
    await this.puppet.reset(reason)
    return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
197
  }
198

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
199
  /**
L
lijiarui 已提交
200 201 202 203 204 205
   * Initialize the bot, return Promise.
   *
   * @returns {Promise<void>}
   * @example
   * await bot.init()
   * // do other stuff with bot here
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
207
  public async init(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208
    log.info('Wechaty', 'v%s initializing...' , this.version())
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
209
    log.verbose('Wechaty', 'puppet: %s'       , this.setting.puppet)
210 211
    log.verbose('Wechaty', 'head: %s'         , this.setting.head)
    log.verbose('Wechaty', 'profile: %s'      , this.setting.profile)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
212
    log.verbose('Wechaty', 'uuid: %s'         , this.uuid)
213

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
214
    if (this.state.current() === 'ready') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
215
      log.error('Wechaty', 'init() already inited. return and do nothing.')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
216
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
217 218
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
219
    this.state.target('ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
220 221
    this.state.current('ready', false)

222 223 224
    try {
      await this.initPuppet()
    } catch (e) {
Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
225
      log.error('Wechaty', 'init() exception: %s', e && e.message)
226
      Raven.captureException(e)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
227
      throw e
228
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
229 230

    this.state.current('ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
231
    return
232
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
233

234 235 236 237 238 239 240 241 242 243
  public on(event: 'error'      , listener: string | ((this: Wechaty, error: Error) => void)):                                                  this
  public on(event: 'friend'     , listener: string | ((this: Wechaty, friend: Contact, request?: FriendRequest) => void)):                      this
  public on(event: 'heartbeat'  , listener: string | ((this: Wechaty, data: any) => void)):                                                     this
  public on(event: 'logout'     , listener: string | ((this: Wechaty, user: Contact) => void)):                                                 this
  public on(event: 'login'      , listener: string | ((this: Wechaty, user: Contact) => void)):                                                 this
  public on(event: 'message'    , listener: string | ((this: Wechaty, message: Message) => void)):                                              this
  public on(event: 'room-join'  , listener: string | ((this: Wechaty, room: Room, inviteeList: Contact[],  inviter: Contact) => void)):         this
  public on(event: 'room-leave' , listener: string | ((this: Wechaty, room: Room, leaverList: Contact[]) => void)):                             this
  public on(event: 'room-topic' , listener: string | ((this: Wechaty, room: Room, topic: string, oldTopic: string, changer: Contact) => void)): this
  public on(event: 'scan'       , listener: string | ((this: Wechaty, url: string, code: number) => void)):                                     this
244 245
  // guard for the above event: make sure it includes all the possible values
  public on(event: never,         listener: any): this
L
lijiarui 已提交
246

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
247
  /**
L
lijiarui 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
   * @desc       Wechaty Class Event Type
   * @typedef    WechatyEventName
   * @property   {string}  error      - When the bot get error, there will be a Wechaty error event fired.
   * @property   {string}  login      - After the bot login full successful, the event login will be emitted, with a Contact of current logined user.
   * @property   {string}  logout     - Logout will be emitted when bot detected log out, with a Contact of the current login user.
   * @property   {string}  scan       - A scan event will be emitted when the bot needs to show you a QR Code for scanning.
   * @property   {string}  heartbeat  - Get bot's heartbeat.
   * @property   {string}  friend     - When someone sends you a friend request, there will be a Wechaty friend event fired.
   * @property   {string}  message    - Emit when there's a new message.
   * @property   {string}  room-join  - Emit when anyone join any room.
   * @property   {string}  room-topic - Get topic event, emitted when someone change room topic.
   * @property   {string}  room-leave - Emit when anyone leave the room.<br>
   *                                    If someone leaves the room by themselves, wechat will not notice other people in the room, so the bot will never get the "leave" event.
   */

  /**
   * @desc       Wechaty Class Event Function
   * @typedef    WechatyEventFunction
   * @property   {Function} error           -(this: Wechaty, error: Error) => void callback function
   * @property   {Function} login           -(this: Wechaty, user: Contact)=> void
   * @property   {Function} logout          -(this: Wechaty, user: Contact) => void
   * @property   {Function} scan            -(this: Wechaty, url: string, code: number) => void <br>
   * <ol>
   * <li>URL: {String} the QR code image URL</li>
   * <li>code: {Number} the scan status code. some known status of the code list here is:</li>
   * </ol>
   * <ul>
   * <li>0 initial_</li>
   * <li>200 login confirmed</li>
   * <li>201 scaned, wait for confirm</li>
   * <li>408 waits for scan</li>
   * </ul>
   * @property   {Function} heartbeat       -(this: Wechaty, data: any) => void
   * @property   {Function} friend          -(this: Wechaty, friend: Contact, request?: FriendRequest) => void
   * @property   {Function} message         -(this: Wechaty, message: Message) => void
   * @property   {Function} room-join       -(this: Wechaty, room: Room, inviteeList: Contact[],  inviter: Contact) => void
   * @property   {Function} room-topic      -(this: Wechaty, room: Room, topic: string, oldTopic: string, changer: Contact) => void
   * @property   {Function} room-leave      -(this: Wechaty, room: Room, leaverList: Contact[]) => void
   */

  /**
   * @listens Wechaty
   * @param   {WechatyEventName}      event      - Emit WechatyEvent
   * @param   {WechatyEventFunction}  listener   - Depends on the WechatyEvent
   * @return  {Wechaty}                          - this for chain
   *
   * More Example Gist: [Example/Friend-Bot]{@link https://github.com/wechaty/wechaty/blob/master/example/friend-bot.ts}
   *
   * @example <caption>Event:scan </caption>
   * wechaty.on('scan', (url: string, code: number) => {
   *   console.log(`[${code}] Scan ${url} to login.` )
   * })
   *
   * @example <caption>Event:login </caption>
   * bot.on('login', (user: Contact) => {
   *   console.log(`user ${user} login`)
   * })
   *
   * @example <caption>Event:logout </caption>
   * bot.on('logout', (user: Contact) => {
   *   console.log(`user ${user} logout`)
   * })
   *
   * @example <caption>Event:message </caption>
   * wechaty.on('message', (message: Message) => {
   *   console.log(`message ${message} received`)
   * })
   *
   * @example <caption>Event:friend </caption>
   * bot.on('friend', (contact: Contact, request: FriendRequest) => {
   *   if(request){ // 1. request to be friend from new contact
   *     let result = await request.accept()
   *       if(result){
   *         console.log(`Request from ${contact.name()} is accept succesfully!`)
   *       } else{
   *         console.log(`Request from ${contact.name()} failed to accept!`)
   *       }
   * 	  } else { // 2. confirm friend ship
   *       console.log(`new friendship confirmed with ${contact.name()}`)
   *    }
   *  })
   *
   * @example <caption>Event:room-join </caption>
   * bot.on('room-join', (room: Room, inviteeList: Contact[], inviter: Contact) => {
   *   const nameList = inviteeList.map(c => c.name()).join(',')
   *   console.log(`Room ${room.topic()} got new member ${nameList}, invited by ${inviter}`)
   * })
   *
   * @example <caption>Event:room-leave </caption>
   * bot.on('room-leave', (room: Room, leaverList: Contact[]) => {
   *   const nameList = leaverList.map(c => c.name()).join(',')
   *   console.log(`Room ${room.topic()} lost member ${nameList}`)
   * })
   *
   * @example <caption>Event:room-topic </caption>
   * bot.on('room-topic', (room: Room, topic: string, oldTopic: string, changer: Contact) => {
   *   console.log(`Room ${room.topic()} topic changed from ${oldTopic} to ${topic} by ${changer.name()}`)
   * })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
346
   */
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
  public on(event: WechatyEventName, listener: string | ((...args: any[]) => any)): this {
    log.verbose('Wechaty', 'on(%s, %s)',
                            event,
                            typeof listener === 'string'
                              ? listener
                              : typeof listener,
                )

    if (typeof listener === 'string') {
      const absoluteFilename = callerResolve(listener, __filename)
      log.verbose('Wechaty', 'on() hotImpor(%s)', absoluteFilename)
      hotImport(absoluteFilename)
        .then(func => super.on(event, func.bind(this)))
        .catch(e => {
          log.error('Wechaty', 'on(%s, %s) hotImport() exception: %s',
                                event, listener, e)
          this.emit('error', e)
        })
      return this
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
367
    super.on(event, listener) // `this: Wechaty` is Sayable
368
    return this
369 370
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
371
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
372
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
373
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
374
  public async initPuppet(): Promise<Puppet> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
375
    let puppet: Puppet
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
376 377 378 379 380

    if (!this.setting.head) {
      throw new Error('no head')
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
381
    switch (this.setting.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
382
      case 'web':
383
        puppet = new PuppetWeb({
L
lijiarui 已提交
384 385
          head:     this.setting.head,
          profile:  this.setting.profile,
386
        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
387
        break
388

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
389
      default:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
390
        throw new Error('Puppet unsupport(yet?): ' + this.setting.puppet)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
391
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
392

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
393
    const eventList: WechatyEventName[] = [
L
lijiarui 已提交
394 395 396 397 398 399 400 401 402 403
      'error',
      'friend',
      'heartbeat',
      'login',
      'logout',
      'message',
      'room-join',
      'room-leave',
      'room-topic',
      'scan',
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
404 405 406
    ]

    eventList.map(e => {
407 408
      // https://strongloop.com/strongblog/an-introduction-to-javascript-es6-arrow-functions/
      // We’ve lost () around the argument list when there’s just one argument (rest arguments are an exception, eg (...args) => ...)
409
      puppet.on(e, (...args: any[]) => {
410 411
        // this.emit(e, data)
        this.emit.apply(this, [e, ...args])
412
      })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
413
    })
414

415
    // set puppet before init, because we need this.puppet if we quit() before init() finish
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
416
    this.puppet = <Puppet>puppet // force to use base class Puppet interface for better encapsolation
417 418

    // set puppet instance to Wechaty Static variable, for using by Contact/Room/Message/FriendRequest etc.
419
    config.puppetInstance(puppet)
420

421
    await puppet.init()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
422
    return puppet
423 424
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
425
  /**
L
lijiarui 已提交
426 427 428 429 430
   * Quit the bot
   *
   * @returns {Promise<void>}
   * @example
   * await bot.quit()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
431
   */
432
  public async quit(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
433
    log.verbose('Wechaty', 'quit()')
434 435 436 437 438 439 440

    if (this.state.current() !== 'ready' || this.state.inprocess()) {
      const err = new Error('quit() must run on a inited instance.')
      log.error('Wechaty', err.message)
      throw err
    }
    this.state.target('standby')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
441
    this.state.current('standby', false)
442

443 444
    if (!this.puppet) {
      log.warn('Wechaty', 'quit() without this.puppet')
445
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
446 447
    }

448
    const puppetBeforeDie = this.puppet
449
    this.puppet     = null
450
    config.puppetInstance(null)
451

452 453 454 455 456 457 458
    try {
      await puppetBeforeDie.quit()
    } catch (e) {
      log.error('Wechaty', 'quit() exception: %s', e.message)
      Raven.captureException(e)
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
459
    this.state.current('standby')
460
    return
461
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
462

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
463
  /**
L
lijiarui 已提交
464 465 466 467 468
   * Logout the bot
   *
   * @returns {Promise<void>}
   * @example
   * await bot.logout()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
469
   */
470
  public async logout(): Promise<void>  {
471 472 473
    if (!this.puppet) {
      throw new Error('no puppet')
    }
474 475 476 477 478 479 480
    try {
      await this.puppet.logout()
    } catch (e) {
      log.error('Wechaty', 'logout() exception: %s', e.message)
      Raven.captureException(e)
      throw e
    }
481
    return
482
  }
483

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
484
  /**
L
lijiarui 已提交
485 486 487 488 489 490
   * Get current user
   *
   * @returns {Contact}
   * @example
   * const contact = bot.self()
   * console.log(`Bot is ${contact.name()}`)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
491
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
492
  public self(): Contact {
493
    if (!this.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
494
      throw new Error('Wechaty.self() no puppet')
495
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
496
    return this.puppet.self()
497
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
498

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
499
  /**
L
lijiarui 已提交
500
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
501
   */
502
  public async send(message: Message | MediaMessage): Promise<boolean> {
503 504 505
    if (!this.puppet) {
      throw new Error('no puppet')
    }
506 507 508 509 510 511 512
    try {
      return await this.puppet.send(message)
    } catch (e) {
      log.error('Wechaty', 'send() exception: %s', e.message)
      Raven.captureException(e)
      throw e
    }
513
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
514

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
515
  /**
L
lijiarui 已提交
516 517 518 519
   * Send message to filehelper
   *
   * @param {string} content
   * @returns {Promise<boolean>}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
520
   */
521
  public async say(content: string): Promise<boolean> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
522 523
    log.verbose('Wechaty', 'say(%s)', content)

524 525 526
    if (!this.puppet) {
      throw new Error('no puppet')
    }
527
    return await this.puppet.say(content)
528 529
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
530
  /**
L
lijiarui 已提交
531
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
532
   */
533
  public static async sleep(millisecond: number): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
534
    await new Promise(resolve => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
535 536 537 538
      setTimeout(resolve, millisecond)
    })
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
539
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
540
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
541
   */
542
  public async ding(): Promise<string> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
543 544 545 546
    if (!this.puppet) {
      return Promise.reject(new Error('wechaty cant ding coz no puppet'))
    }

547 548 549 550 551 552 553
    try {
      return await this.puppet.ding() // should return 'dong'
    } catch (e) {
      log.error('Wechaty', 'ding() exception: %s', e.message)
      Raven.captureException(e)
      throw e
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
554
  }
555
}
556 557

export default Wechaty