wechaty.ts 12.5 KB
Newer Older
1
/**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
2
 *   Wechaty - https://github.com/chatie/wechaty
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *   Copyright 2016-2017 Huan LI <zixia@zixia.net>
 *
 *   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
 *
 *       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.
 *
 */
19
import { EventEmitter } from 'events'
20

21 22
import { StateSwitch }  from 'state-switch'

23
import {
24
  config,
25 26
  HeadName,
  PuppetName,
27
  Raven,
28 29 30
  Sayable,
  log,
}                         from './config'
31

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

43
export interface PuppetSetting {
L
lijiarui 已提交
44 45 46
  head?:    HeadName,
  puppet?:  PuppetName,
  profile?: string,
47
}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
48

49 50 51 52 53 54 55 56 57 58 59
export type WechatyEventName = 'error'
                              | 'friend'
                              | 'heartbeat'
                              | 'login'
                              | 'logout'
                              | 'message'
                              | 'room-join'
                              | 'room-leave'
                              | 'room-topic'
                              | 'scan'
                              | 'EVENT_PARAM_ERROR'
60

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
61 62 63 64 65 66 67 68 69 70
/**
 *
 * Wechaty: Wechat for ChatBots.
 * Connect ChatBots
 *
 * Class Wechaty
 *
 * Licenst: ISC
 * https://github.com/zixia/wechaty
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83
 *
 * **Example**
 *
 * ```ts
 * // The World's Shortest ChatBot Code: 6 lines of JavaScript
 * 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 (李卓桓) 已提交
84
 * @see The <a href="https://github.com/lijiarui/wechaty-getting-started">Wechaty Starter Project</a>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
85
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86
export class Wechaty extends EventEmitter implements Sayable {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87 88
  /**
   * singleton _instance
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
89
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
90
   */
91
  private static _instance: Wechaty
92

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
99 100
  /**
   * the state
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
101
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
102
   */
103
  private state = new StateSwitch<'standby', 'ready'>('Wechaty', 'standby', log)
104

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
105 106
  /**
   * the uuid
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
107
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
108
   */
109
  public uuid:        string
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
110

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
111
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
112
   * get the singleton instance of Wechaty
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
113
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
114
  public static instance(setting?: PuppetSetting) {
115
    if (setting && this._instance) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
116
      throw new Error('there has already a instance. no params will be allowed any more')
117 118 119 120 121 122 123
    }
    if (!this._instance) {
      this._instance = new Wechaty(setting)
    }
    return this._instance
  }

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

131 132 133
    setting.head    = setting.head    || config.head
    setting.puppet  = setting.puppet  || config.puppet
    setting.profile = setting.profile || config.profile
134

135
    // setting.port    = setting.port    || Config.port
136

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

143
    this.uuid = UtilLib.guid()
144 145
  }

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
151 152
  /**
   * Return version of Wechaty
Huan (李卓桓)'s avatar
doc  
Huan (李卓桓) 已提交
153
   *
Huan (李卓桓)'s avatar
dodc  
Huan (李卓桓) 已提交
154 155
   * @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 (李卓桓) 已提交
156
   * @returns {string}                  - the version number
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
157 158
   * @example
   *  console.log(Wechaty.instance().version())
159
   *  // '#git[af39df]'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
160
   *  console.log(Wechaty.instance().version(true))
161
   *  // '0.7.9'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
162
   */
163
  public static version(forceNpm = false): string {
164
    if (!forceNpm) {
165 166
      const revision = config.gitVersion()
      if (revision) {
167
        return `#git[${revision}]`
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
168
      }
169
    }
170 171
    return config.npmVersion()
  }
172

173 174
  public version(forceNpm?) {
    return Wechaty.version(forceNpm)
175
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
176

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
177 178
  /**
   * @todo document me
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
179
   * @returns {Contact}
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
180
   * @deprecated
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
181
   */
Huan (李卓桓)'s avatar
fix #54  
Huan (李卓桓) 已提交
182
  public user(): Contact {
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
183 184
    log.warn('Wechaty', 'user() DEPRECATED. use self() instead.')

Huan (李卓桓)'s avatar
fix #54  
Huan (李卓桓) 已提交
185 186 187 188 189
    if (!this.puppet || !this.puppet.user) {
      throw new Error('no user')
    }
    return this.puppet.user
  }
190

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203 204 205
  /**
   * @todo document me
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206
  public async init(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
207
    log.info('Wechaty', 'v%s initializing...' , this.version())
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208
    log.verbose('Wechaty', 'puppet: %s'       , this.setting.puppet)
209 210
    log.verbose('Wechaty', 'head: %s'         , this.setting.head)
    log.verbose('Wechaty', 'profile: %s'      , this.setting.profile)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
211
    log.verbose('Wechaty', 'uuid: %s'         , this.uuid)
212

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

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

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

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

233
  // public on(event: WechatyEventName, listener: Function): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
234
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
235
   * @listens Wechaty#error
Huan (李卓桓)'s avatar
dodc  
Huan (李卓桓) 已提交
236 237 238
   * @param   {string}    [event='error'] - the `error` event name
   * @param   {Function}  listener        - (error) => void callback function
   * @return  {Wechaty}                   - this for chain
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
239
   */
240
  public on(event: 'error'      , listener: (this: Wechaty, error: Error) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
241 242 243
  /**
   * @todo document me
   */
244
  public on(event: 'friend'     , listener: (this: Wechaty, friend: Contact, request?: FriendRequest) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
245 246 247
  /**
   * @todo document me
   */
248
  public on(event: 'heartbeat'  , listener: (this: Wechaty, data: any) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
249 250 251
  /**
   * @todo document me
   */
252
  public on(event: 'logout'     , listener: (this: Wechaty, user: Contact) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
253 254 255
  /**
   * @todo document me
   */
256
  public on(event: 'login'      , listener: (this: Wechaty, user: Contact) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
257 258 259
  /**
   * @todo document me
   */
260
  public on(event: 'message'    , listener: (this: Wechaty, message: Message) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
261 262 263
  /**
   * @todo document me
   */
264
  public on(event: 'room-join'  , listener: (this: Wechaty, room: Room, inviteeList: Contact[],  inviter: Contact) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
265 266 267
  /**
   * @todo document me
   */
268
  public on(event: 'room-leave' , listener: (this: Wechaty, room: Room, leaverList: Contact[]) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
269 270 271
  /**
   * @todo document me
   */
272
  public on(event: 'room-topic' , listener: (this: Wechaty, room: Room, topic: string, oldTopic: string, changer: Contact) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
273 274 275
  /**
   * @todo document me
   */
276
  public on(event: 'scan'       , listener: (this: Wechaty, url: string, code: number) => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
277 278 279
  /**
   * @todo document me
   */
280
  public on(event: 'EVENT_PARAM_ERROR', listener: () => void): this
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
281 282 283
  /**
   * @todo document me
   */
284

285
  public on(event: WechatyEventName, listener: (args: any) => any): this {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
286
    log.verbose('Wechaty', 'addListener(%s, %s)', event, typeof listener)
287

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300
    // const thisWithSay: Sayable = {
    //   say: (content: string) => {
    //     return Config.puppetInstance()
    //                   .say(content)
    //   }
    // }

    super.on(event, listener) // `this: Wechaty` is Sayable

    // (...args) => {
    //
    //   return listener.apply(this, args)
    // })
301

302
    return this
303 304
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
305 306
  /**
   * @todo document me
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
307
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
308
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
309
  public async initPuppet(): Promise<Puppet> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
310
    let puppet: Puppet
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
311 312 313 314 315

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
316
    switch (this.setting.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
317
      case 'web':
318
        puppet = new PuppetWeb({
L
lijiarui 已提交
319 320
          head:     this.setting.head,
          profile:  this.setting.profile,
321
        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
322
        break
323

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
324
      default:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
325
        throw new Error('Puppet unsupport(yet?): ' + this.setting.puppet)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
326
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
327

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
328
    const eventList: WechatyEventName[] = [
L
lijiarui 已提交
329 330 331 332 333 334 335 336 337 338
      'error',
      'friend',
      'heartbeat',
      'login',
      'logout',
      'message',
      'room-join',
      'room-leave',
      'room-topic',
      'scan',
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
339 340 341
    ]

    eventList.map(e => {
342 343
      // 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) => ...)
344
      puppet.on(e, (...args: any[]) => {
345 346
        // this.emit(e, data)
        this.emit.apply(this, [e, ...args])
347
      })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
348
    })
349

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

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

356
    await puppet.init()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
357
    return puppet
358 359
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
360 361 362
  /**
   * @todo document me
   */
363
  public async quit(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
364
    log.verbose('Wechaty', 'quit()')
365 366 367 368 369 370 371

    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 (李卓桓) 已提交
372
    this.state.current('standby', false)
373

374 375
    if (!this.puppet) {
      log.warn('Wechaty', 'quit() without this.puppet')
376
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
377 378
    }

379
    const puppetBeforeDie = this.puppet
380
    this.puppet     = null
381
    config.puppetInstance(null)
382

383 384 385
    await puppetBeforeDie.quit()
                        .catch(e => {
                          log.error('Wechaty', 'quit() exception: %s', e.message)
386
                          Raven.captureException(e)
387 388
                          throw e
                        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
389
    this.state.current('standby')
390
    return
391
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
392

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
393 394 395
  /**
   * @todo document me
   */
396
  public async logout(): Promise<void>  {
397 398 399
    if (!this.puppet) {
      throw new Error('no puppet')
    }
400 401 402
    await this.puppet.logout()
                    .catch(e => {
                      log.error('Wechaty', 'logout() exception: %s', e.message)
403
                      Raven.captureException(e)
404 405 406
                      throw e
                    })
    return
407
  }
408

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
409
  /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
410
   * get current user
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
411
   * @returns {Contact} current logined user
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
412
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
413
  public self(): Contact {
414
    if (!this.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
415
      throw new Error('Wechaty.self() no puppet')
416
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
417
    return this.puppet.self()
418
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
419

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
420 421 422
  /**
   * @todo document me
   */
423
  public async send(message: Message | MediaMessage): Promise<boolean> {
424 425 426
    if (!this.puppet) {
      throw new Error('no puppet')
    }
427 428 429
    return await this.puppet.send(message)
                            .catch(e => {
                              log.error('Wechaty', 'send() exception: %s', e.message)
430
                              Raven.captureException(e)
431 432
                              throw e
                            })
433
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
434

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
435 436 437
  /**
   * @todo document me
   */
438
  public async say(content: string): Promise<boolean> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
439 440
    log.verbose('Wechaty', 'say(%s)', content)

441 442 443
    if (!this.puppet) {
      throw new Error('no puppet')
    }
444
    return await this.puppet.say(content)
445 446
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
447 448
  /**
   * @todo document me
449 450
   * @static
   *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
451
   */
452
  public static async sleep(millisecond: number): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
453
    await new Promise(resolve => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
454 455 456 457
      setTimeout(resolve, millisecond)
    })
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
458 459
  /**
   * @todo document me
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
460
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
461
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
462
  public ding() {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
463 464 465 466
    if (!this.puppet) {
      return Promise.reject(new Error('wechaty cant ding coz no puppet'))
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
467
    return this.puppet.ding() // should return 'dong'
468 469
                      .catch(e => {
                        log.error('Wechaty', 'ding() exception: %s', e.message)
470
                        Raven.captureException(e)
471 472
                        throw e
                      })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
473
  }
474
}
475 476

export default Wechaty