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

H
hcz 已提交
173 174 175
  /**
   * @todo document me
   */
176 177
  public version(forceNpm?) {
    return Wechaty.version(forceNpm)
178
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
179

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

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

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

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

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

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

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

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302
    // 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)
    // })
303

304
    return this
305 306
  }

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

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

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

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

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

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

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

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

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

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

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

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

381
    const puppetBeforeDie = this.puppet
382
    this.puppet     = null
383
    config.puppetInstance(null)
384

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

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

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

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

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

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

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

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

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

export default Wechaty