wechaty.ts 12.0 KB
Newer Older
1 2
import { EventEmitter } from 'events'
import * as fs          from 'fs'
3
import * as path        from 'path'
4

5
import {
6 7 8 9 10 11
  Config,
  HeadName,
  PuppetName,
  Sayable,
  log,
}                         from './config'
12

13 14 15 16 17 18 19 20
import { Contact }        from './contact'
import { FriendRequest }  from './friend-request'
import { Message }        from './message'
import { Puppet }         from './puppet'
import { PuppetWeb }      from './puppet-web/'
import { Room }           from './room'
import { StateMonitor }   from './state-monitor'
import { UtilLib }        from './util-lib'
21

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
22
export type PuppetSetting = {
23
  head?:    HeadName
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
24
  puppet?:  PuppetName
25
  profile?: string
26
}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
27

28 29 30 31 32 33 34 35 36 37 38
export type WechatyEventName = 'error'
                              | 'friend'
                              | 'heartbeat'
                              | 'login'
                              | 'logout'
                              | 'message'
                              | 'room-join'
                              | 'room-leave'
                              | 'room-topic'
                              | 'scan'
                              | 'EVENT_PARAM_ERROR'
39

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40 41 42 43 44 45 46 47 48 49
/**
 *
 * Wechaty: Wechat for ChatBots.
 * Connect ChatBots
 *
 * Class Wechaty
 *
 * Licenst: ISC
 * https://github.com/zixia/wechaty
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62
 *
 * **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 (李卓桓) 已提交
63
 * @see The <a href="https://github.com/lijiarui/wechaty-getting-started">Wechaty Starter Project</a>
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
64
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
65
export class Wechaty extends EventEmitter implements Sayable {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
66 67
  /**
   * singleton _instance
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
68
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
69
   */
70
  private static _instance: Wechaty
71

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72 73
  /**
   * the puppet
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
74
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
75
   */
76
  public puppet: Puppet | null
77

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
78 79
  /**
   * the state
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
80
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
81
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
82
  private state = new StateMonitor<'standby', 'ready'>('Wechaty', 'standby')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
83 84
  /**
   * the npmVersion
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
85
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
86
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
87
  private npmVersion: string = require('../package.json').version
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
88 89
  /**
   * the uuid
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
90
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
91
   */
92
  public uuid:        string
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
93

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
94
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
95
   * get the singleton instance of Wechaty
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
96
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
97
  public static instance(setting?: PuppetSetting) {
98
    if (setting && this._instance) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
99
      throw new Error('there has already a instance. no params will be allowed any more')
100 101 102 103 104 105 106
    }
    if (!this._instance) {
      this._instance = new Wechaty(setting)
    }
    return this._instance
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
107
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
108
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
109
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
110
  private constructor(private setting: PuppetSetting = {}) {
111
    super()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
112 113
    log.verbose('Wechaty', 'contructor()')

114
    setting.head    = setting.head    || Config.head
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
115
    setting.puppet  = setting.puppet  || Config.puppet
116 117
    setting.profile = setting.profile || Config.profile

118
    // setting.port    = setting.port    || Config.port
119

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
120 121
    if (setting.profile) {
      setting.profile  = /\.wechaty\.json$/i.test(setting.profile)
122 123
                        ? setting.profile
                        : setting.profile + '.wechaty.json'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
125

126
    this.uuid = UtilLib.guid()
127 128
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
129
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
130
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
131
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
132
  public toString() { return 'Class Wechaty(' + this.setting.puppet + ')'}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
133

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
134 135
  /**
   * Return version of Wechaty
Huan (李卓桓)'s avatar
doc  
Huan (李卓桓) 已提交
136
   *
Huan (李卓桓)'s avatar
dodc  
Huan (李卓桓) 已提交
137 138
   * @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 (李卓桓) 已提交
139
   * @returns {string}                  - the version number
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
140 141
   * @example
   *  console.log(Wechaty.instance().version())
142
   *  // '#git[af39df]'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
143
   *  console.log(Wechaty.instance().version(true))
144
   *  // '0.7.9'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145 146
   */
  public version(forceNpm = false): string {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
147
    // TODO: use  git rev-parse HEAD  ?
148
    const dotGitPath  = path.join(__dirname, '..', '.git') // only for ts-node, not for dist
149 150
    const gitLogCmd   = 'git'
    const gitLogArgs  = ['log', '--oneline', '-1']
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
151

152 153
    if (!forceNpm) {
      try {
154
        fs.statSync(dotGitPath).isDirectory()
155 156 157

        const ss = require('child_process')
                    .spawnSync(gitLogCmd, gitLogArgs, { cwd:  __dirname })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
158

159 160 161 162 163 164 165
        if (ss.status !== 0) {
          throw new Error(ss.error)
        }

        const revision = ss.stdout
                          .toString()
                          .trim()
166
        return `#git[${revision}]`
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
167

168
      } catch (e) { /* fall safe */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
169 170 171 172
        /**
         *  1. .git not exist
         *  2. git log fail
         */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
173
        log.silly('Wechaty', 'version() form development environment is not availble: %s', e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
174
      }
175 176 177
    }

    return this.npmVersion
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)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
228
      throw e
229
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
230 231

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

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

287
  public on(event: WechatyEventName, listener: Function): 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({
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
321
            head:     this.setting.head
322 323
          , profile:  this.setting.profile
        })
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 331 332 333 334 335 336 337 338 339 340 341 342 343
    const eventList: WechatyEventName[] = [
        'error'
      , 'friend'
      , 'heartbeat'
      , 'login'
      , 'logout'
      , 'message'
      , 'room-join'
      , 'room-leave'
      , 'room-topic'
      , 'scan'
    ]

    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 356

    // set puppet instance to Wechaty Static variable, for using by Contact/Room/Message/FriendRequest etc.
    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()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
367
    this.state.current('standby', false)
368

369 370
    if (!this.puppet) {
      log.warn('Wechaty', 'quit() without this.puppet')
371
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
372 373
    }

374
    const puppetBeforeDie = this.puppet
375 376
    this.puppet     = null
    Config.puppetInstance(null)
377

378 379 380 381 382
    await puppetBeforeDie.quit()
                        .catch(e => {
                          log.error('Wechaty', 'quit() exception: %s', e.message)
                          throw e
                        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
383
    this.state.current('standby')
384
    return
385
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
386

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
387 388 389
  /**
   * @todo document me
   */
390
  public async logout(): Promise<void>  {
391 392 393
    if (!this.puppet) {
      throw new Error('no puppet')
    }
394 395 396 397 398 399
    await this.puppet.logout()
                    .catch(e => {
                      log.error('Wechaty', 'logout() exception: %s', e.message)
                      throw e
                    })
    return
400
  }
401

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
402
  /**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
403
   * get current user
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
404
   * @returns {Contact} current logined user
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
405
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
406
  public self(): Contact {
407
    if (!this.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
408
      throw new Error('Wechaty.self() no puppet')
409
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
410
    return this.puppet.self()
411
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
412

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
413 414 415
  /**
   * @todo document me
   */
416
  public async send(message: Message): Promise<void> {
417 418 419
    if (!this.puppet) {
      throw new Error('no puppet')
    }
420
    await this.puppet.send(message)
421 422 423 424
                      .catch(e => {
                        log.error('Wechaty', 'send() exception: %s', e.message)
                        throw e
                      })
425
    return
426
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
427

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
428 429 430
  /**
   * @todo document me
   */
431
  public async say(content: string): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
432 433
    log.verbose('Wechaty', 'say(%s)', content)

434 435 436
    if (!this.puppet) {
      throw new Error('no puppet')
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
437
    this.puppet.say(content)
438
    return
439 440
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
441 442 443
  /**
   * @todo document me
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
444 445
  public async sleep(millisecond: number): Promise<void> {
    await new Promise(resolve => {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
446 447 448 449
      setTimeout(resolve, millisecond)
    })
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
450 451
  /**
   * @todo document me
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
452
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
453
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
454
  public ding() {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
455 456 457 458
    if (!this.puppet) {
      return Promise.reject(new Error('wechaty cant ding coz no puppet'))
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
459
    return this.puppet.ding() // should return 'dong'
460 461 462 463
                      .catch(e => {
                        log.error('Wechaty', 'ding() exception: %s', e.message)
                        throw e
                      })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
464
  }
465
}