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

5 6
import { StateSwitch }  from 'state-switch'

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

15 16
import { Contact }        from './contact'
import { FriendRequest }  from './friend-request'
M
Mukaiu 已提交
17 18 19 20
import {
  Message,
  MediaMessage,
}                         from './message'
21 22 23 24
import { Puppet }         from './puppet'
import { PuppetWeb }      from './puppet-web/'
import { Room }           from './room'
import { UtilLib }        from './util-lib'
25

26
export interface PuppetSetting {
L
lijiarui 已提交
27 28 29
  head?:    HeadName,
  puppet?:  PuppetName,
  profile?: string,
30
}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
31

32 33 34 35 36 37 38 39 40 41 42
export type WechatyEventName = 'error'
                              | 'friend'
                              | 'heartbeat'
                              | 'login'
                              | 'logout'
                              | 'message'
                              | 'room-join'
                              | 'room-leave'
                              | 'room-topic'
                              | 'scan'
                              | 'EVENT_PARAM_ERROR'
43

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
76 77
  /**
   * the puppet
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
78
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
79
   */
80
  public puppet: Puppet | null
81

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

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

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

118
    setting.head    = setting.head    || Config.head
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
119
    setting.puppet  = setting.puppet  || Config.puppet
120 121
    setting.profile = setting.profile || Config.profile

122
    // setting.port    = setting.port    || Config.port
123

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
124 125
    if (setting.profile) {
      setting.profile  = /\.wechaty\.json$/i.test(setting.profile)
126 127
                        ? setting.profile
                        : setting.profile + '.wechaty.json'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
128
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
129

130
    this.uuid = UtilLib.guid()
131 132
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
133
  /**
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
134
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
135
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
136
  public toString() { return 'Class Wechaty(' + this.setting.puppet + ')'}
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
137

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

157 158
    if (!forceNpm) {
      try {
159
        fs.statSync(dotGitPath).isDirectory()
160 161 162

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

164 165 166 167 168 169 170
        if (ss.status !== 0) {
          throw new Error(ss.error)
        }

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

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

    return this.npmVersion
183
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
184

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

Huan (李卓桓)'s avatar
fix #54  
Huan (李卓桓) 已提交
193 194 195 196 197
    if (!this.puppet || !this.puppet.user) {
      throw new Error('no user')
    }
    return this.puppet.user
  }
198

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

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
221
    if (this.state.current() === 'ready') {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
222
      log.error('Wechaty', 'init() already inited. return and do nothing.')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
223
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
224 225
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
226
    this.state.target('ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
227 228
    this.state.current('ready', false)

229 230 231
    try {
      await this.initPuppet()
    } catch (e) {
Huan (李卓桓)'s avatar
bug fix  
Huan (李卓桓) 已提交
232
      log.error('Wechaty', 'init() exception: %s', e && e.message)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
233
      throw e
234
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235 236

    this.state.current('ready')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
237
    return
238
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
239

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

292
  public on(event: WechatyEventName, listener: Function): this {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
293
    log.verbose('Wechaty', 'addListener(%s, %s)', event, typeof listener)
294

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307
    // 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)
    // })
308

309
    return this
310 311
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
312 313
  /**
   * @todo document me
Huan (李卓桓)'s avatar
jsdoc  
Huan (李卓桓) 已提交
314
   * @private
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
315
   */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
316
  public async initPuppet(): Promise<Puppet> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
317
    let puppet: Puppet
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
318 319 320 321 322

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

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
323
    switch (this.setting.puppet) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
324
      case 'web':
325
        puppet = new PuppetWeb({
L
lijiarui 已提交
326 327
          head:     this.setting.head,
          profile:  this.setting.profile,
328
        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
329
        break
330

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
331
      default:
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
332
        throw new Error('Puppet unsupport(yet?): ' + this.setting.puppet)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
333
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
334

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

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

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

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

363
    await puppet.init()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
364
    return puppet
365 366
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
367 368 369
  /**
   * @todo document me
   */
370
  public async quit(): Promise<void> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
371
    log.verbose('Wechaty', 'quit()')
372 373 374 375 376 377 378

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

381 382
    if (!this.puppet) {
      log.warn('Wechaty', 'quit() without this.puppet')
383
      return
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
384 385
    }

386
    const puppetBeforeDie = this.puppet
387 388
    this.puppet     = null
    Config.puppetInstance(null)
389

390 391 392 393 394
    await puppetBeforeDie.quit()
                        .catch(e => {
                          log.error('Wechaty', 'quit() exception: %s', e.message)
                          throw e
                        })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
395
    this.state.current('standby')
396
    return
397
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
398

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

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

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

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

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

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

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

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

export default Wechaty