wechaty.ts 12.6 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
/**
 *
 * Wechaty: Wechat for ChatBots.
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
65 66 67 68
 * Wechaty is a Bot Framework for Wechat **Personal** Account
 * which can help you create a bot in 6 lines of javascript
 * by easy to use API, with cross-platform support for
 * Linux/Mac/Windows.
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81
 *
 * **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 (李卓桓) 已提交
82
 * @see The <a href="https://github.com/lijiarui/wechaty-getting-started">Wechaty Starter Project</a>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
83
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
84
export class Wechaty extends EventEmitter implements Sayable {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
85 86
  /**
   * singleton _instance
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
87
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
88
   */
89
  private static _instance: Wechaty
90

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

export default Wechaty