wechaty-bro.js 17.1 KB
Newer Older
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1
/**
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
2 3 4
 *
 * Wechaty - Wechat for Bot, and human who talk to bot.
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
5
 * Class PuppetWebInjectio
6
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
7
 * Inject this js code to browser,
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
8 9
 * in order to interactive with wechat web program.
 *
10 11
 * Licenst: ISC
 * https://github.com/wechaty/wechaty
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
12
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * ATTENTION:
 *
 * JAVASCRIPT IN THIS FILE
 * IS RUN INSIDE
 *
 *    BROWSER
 *
 * INSTEAD OF
 *
 *    NODE
 *
 * read more about this in puppet-web-bridge.js
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
27
 */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
28 29 30

/*global angular*/

31
(function(port) {
32

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33
  function init() {
34 35 36 37 38 39
    if (!angularIsReady()) {
      retObj.code = 503 // 503 SERVICE UNAVAILABLE https://httpstatuses.com/503
      retObj.message = 'init() without a ready angular env'
      return retObj
    }

40
    if (!initClog(false)) { // make console.log work (wxapp disabled the console.log)
41
      retObj.code = 503 // 503 Service Unavailable http://www.restapitutorial.com/httpstatuscodes.html
42 43 44 45
      retObj.message = 'initClog fail'
      return retObj
    }

46 47
    if (WechatyBro.vars.initStatus === true) {
      log('WechatyBro.init() called twice: already inited')
48
      retObj.code = 304 // 304 Not Modified https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
49 50
      retObj.message = 'init() already inited before. returned with do nothing'
      return retObj
51 52
    }

53 54 55 56
    clog('init on port:' + port)

    if (MMCgiLogined()) {
      login('page refresh')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
57
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
58

59
    glueToAngular()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
60
    connectSocket()
61 62 63
    hookEvents()

    checkScan()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
64

65
    heartBeat(true)
66

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
67
    clog('inited!. ;-D')
68
    WechatyBro.vars.initStatus = true
69 70

    retObj.code = 200
71
    retObj.message = 'WechatyBro Init Succ on port: ' + port
72
    return retObj
73 74
  }

75 76 77 78
  /**
  * Log to console
  * http://stackoverflow.com/a/7089553/1123955
  */
79 80 81 82 83
  function initClog(enabled) {
    if (!enabled) {
      return true
    }

84
    if (WechatyBro.vars.iframe) {
85 86 87 88
      log('initClog() again? there is already a iframe')
      return true
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
89
    if (!document.body) { // Javascript Error Null is not an Object
90
      // log('initClog() not ready because document.body not ready')
91 92 93
      return false
    }

94
    var i = document.createElement('iframe')
95
    if (!i) {
96
      // log('initClog() not ready because document.createElement fail')
97
      return false
98 99
    }

100 101 102
    // slog('initClog got iframe element')
    i.style.display = 'none'
    document.body.appendChild(i)
103 104
    WechatyBro.vars.iframe = i
    // if (!WechatyBro.vars.iframe) {
105 106 107 108
    //   throw new Error('iframe gone after appendChild, WTF???')
    // }
    // slog('initClog done')
    return true
109 110 111
  }

  function clog(s) {
112
    if (!WechatyBro.vars.iframe) {
113 114 115 116
      // throw new Error('clog() iframe not found when be invocked')
      return
    }

117
    var d = new Date()
118
    s = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ' <WechatyBro> ' + s
119

120
    WechatyBro.vars.iframe.contentWindow.console.log(s)
121 122
  }

123
  function slog(msg)  { WechatyBro.emit('log', msg) }
124 125
  function log(s)     { clog(s); slog(s) }

126
  /**
127
   * WechatyBro.emit, will save event & data when there's no socket io connection to prevent event lost
128 129 130
   * NOTICE: only clog available here, because slog & log will call emit, death loop
   */
  function emit(event, data) {
131
    var eventsBuf = WechatyBro.vars.eventsBuf
132
    if (!eventsBuf.map) {
133
      throw new Error('WechatyBro.vars.eventsBuf must be a Array')
134 135
    }
    if (event) {
136
      eventsBuf.unshift([event, data])
137
    }
138
    var socket = WechatyBro.vars.socket
139
    if (!socket) {
140
      clog('WechatyBro.vars.socket not ready')
141 142 143 144
      return setTimeout(emit, 1000) // resent eventsBuf after 1000ms
    }
    var bufLen = eventsBuf.length
    if (bufLen) {
145
      if (bufLen > 1) { clog('WechatyBro.vars.eventsBuf has ' + bufLen + ' unsend events') }
146 147 148 149 150 151

      while (eventsBuf.length) {
        var eventData = eventsBuf.pop()
        if (eventData && eventData.map && eventData.length===2) {
          clog('emiting ' + eventData[0])
          socket.emit(eventData[0], eventData[1])
152
        } else { clog('WechatyBro.emit() got invalid eventData: ' + eventData[0] + ', ' + eventData[1] + ', length: ' + eventData.length) }
153 154
      }

155
      if (bufLen > 1) { clog('WechatyBro.vars.eventsBuf[' + bufLen + '] all sent') }
156 157 158
    }
  }

159 160 161 162 163 164
  /**
  *
  * Functions that Glued with AngularJS
  *
  */
  function MMCgiLogined() { return !!(window.MMCgi && window.MMCgi.isLogin) }
165

166
  function angularIsReady() {
167 168
    // don't log inside, because we has not yet init clog here.
    return !!(
169 170 171
      (typeof angular) !== 'undefined'
      && angular.element
      && angular.element('body')
172
      && angular.element(document).injector()
173 174 175
    )
  }

176
  function heartBeat(firstTime) {
177
    var TIMEOUT = 15000 // 15s
178 179
    if (firstTime && WechatyBro.vars.heartBeatTimmer) {
      WechatyBro.log('heartBeat timer exist when 1st time is true? return for do nothing')
180 181
      return
    }
182 183
    WechatyBro.emit('ding', 'heartbeat@browser')
    WechatyBro.vars.heartBeatTimmer = setTimeout(heartBeat, TIMEOUT)
184
    return TIMEOUT
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
185 186
  }

187
  function glueToAngular() {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
188
    var injector  = angular.element(document).injector()
189 190 191
    if (!injector) {
      throw new Error('glueToAngular cant get injector(right now)')
    }
192 193

    var accountFactory  = injector.get('accountFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
194
    var appFactory      = injector.get('appFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
195
    var chatroomFactory = injector.get('chatroomFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
196
    var chatFactory     = injector.get('chatFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
197
    var contactFactory  = injector.get('contactFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
198
    var confFactory     = injector.get('confFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
199
    var loginFactory    = injector.get('loginFactory')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
200 201

    var http            = injector.get('$http')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
202
    var state           = injector.get('$state')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203
    var mmHttp          = injector.get('mmHttp')
204 205

    var appScope    = angular.element('[ng-controller="appController"]').scope()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
206
    var rootScope   = injector.get('$rootScope')
207
    var loginScope  = angular.element('[ng-controller="loginController"]').scope()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
209 210 211 212
/*
    // method 1
    appFactory.syncOrig = appFactory.sync
    appFactory.syncCheckOrig = appFactory.syncCheck
213 214
    appFactory.sync = function() { WechatyBro.log('appFactory.sync() !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); return appFactory.syncOrig(arguments) }
    appFactory.syncCheck = function() { WechatyBro.log('appFactory.syncCheck() !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); return appFactory.syncCheckOrig(arguments) }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
215 216 217

    // method 2
    $.ajaxOrig = $.ajax
218
    $.ajax = function() { WechatyBro.log('$.ajax() !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); return $.ajaxOrig(arguments) }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
219 220 221 222 223 224 225 226 227 228

    $.ajax({
      url: "https://wx.qq.com/zh_CN/htmledition/v2/images/webwxgeticon.jpg"
      , type: "GET"
    }).done(function (response) {
      alert("success");
    })

    // method 3 - mmHttp
    mmHttp.getOrig = mmHttp.get
229
    mmHttp.get = function() { WechatyBro.log('mmHttp.get() !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); return mmHttp.getOrig(arguments) }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
230 231
*/

232 233 234 235 236 237 238 239 240 241 242
    /**
     * generate $scope with a contoller (as it is not assigned in html staticly)
     * https://github.com/angular/angular.js/blob/a4e60cb6970d8b6fa9e0af4b9f881ee3ba7fdc99/test/ng/controllerSpec.js#L24
     */
    var contentChatScope  = rootScope.$new()
    injector.get('$controller')('contentChatController', {$scope: contentChatScope })
    /*
    s =

    */

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
243
    // get all we need from wx in browser(angularjs)
244
    WechatyBro.glue = {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
245 246
      injector:       injector
      , http:         http
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
247
      , state
248 249

      , accountFactory: accountFactory
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
250
      , chatroomFactory
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
251 252 253
      , chatFactory:    chatFactory
      , confFactory:    confFactory
      , contactFactory: contactFactory
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
254
      , loginFactory:   loginFactory
255 256 257

      , rootScope:    rootScope
      , appScope:     appScope
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
258
      , loginScope:   loginScope
259 260

      , contentChatScope: contentChatScope
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
261
    }
262 263

    return true
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
264 265
  }

266
  function checkScan() {
267
    clog('checkScan()')
268
    if (isLogin()) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
269 270
      log('checkScan() - already login, no more check, and return(only)') //but I will emit a login event')
      // login('checkScan found already login')
271 272
      return
    }
273
    if (!WechatyBro.glue.loginScope) {
274 275 276 277 278
      log('checkScan() - loginScope disappeared, no more check')
      login('loginScope disappeared')
      return
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
279 280
    // loginScope.code:
    // 0:   显示二维码
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
281
    // 408: 未确认(显示二维码后30秒触发)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
282 283
    // 201: 扫描,未确认
    // 200: 登录成功
284 285 286
    var code  = +WechatyBro.glue.loginScope.code
    var url   =  WechatyBro.glue.loginScope.qrcodeUrl
    if (url && code !== WechatyBro.vars.scanCode) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
287

288 289
      log('checkScan() - code change detected: from '
        + WechatyBro.vars.scanCode
290 291 292
        + ' to '
        + code
      )
293
      WechatyBro.emit('scan', {
294 295 296
        code:   code
        , url:  url
      })
297
      WechatyBro.vars.scanCode = code
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
298
    }
299 300 301 302 303 304

    if (code === 200) {
      login('scan code 200')
    } else {
      setTimeout(checkScan, 1000)
    }
305 306 307
    return
  }

308
  function isLogin() { return !!WechatyBro.vars.loginStatus }
309
  function login(data) {
310
    log('login(' + data + ')')
311 312
    if (!WechatyBro.vars.loginStatus) {
      WechatyBro.vars.loginStatus = true
313
    }
314
    WechatyBro.emit('login', data)
315 316
  }
  function logout(data) {
317
    log('logout(' + data + ')')
318 319 320
    WechatyBro.vars.loginStatus = false
    // WechatyBro.emit('logout', data)
    WechatyBro.glue.loginFactory.loginout()
321
    checkScan()
322 323
  }
  function quit() {
324 325
    log('quit()')
    logout('quit()')
326 327 328
    if (WechatyBro.vars.socket) {
      WechatyBro.vars.socket.close()
      WechatyBro.vars.socket = null
329
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
330
  }
331

332
  function ding(data) { log('recv ding'); return data || 'dong' }
333
  function hookEvents() {
334 335
    var rootScope = WechatyBro.glue.rootScope
    var appScope = WechatyBro.glue.appScope
336 337 338 339 340
    if (!rootScope || !appScope) {
      log('hookEvents() no rootScope')
      return false
    }
    rootScope.$on('message:add:success', function(event, data) {
341 342 343
      if (!isLogin()) { // in case of we missed the pageInit event
        login('by event[message:add:success]')
      }
344
      WechatyBro.emit('message', data)
345
    })
346
    rootScope.$on('root:pageInit:success'), function (event, data) {
347
      login('by event[root:pageInit:success]')
348
    }
349 350 351 352
    // newLoginPage seems not stand for a user login action
    // appScope.$on("newLoginPage", function(event, data) {
    //   login('by event[newLoginPage]')
    // })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
353
    window.addEventListener('unload', function(e) {
354
      // XXX only 1 event can be emitted here???
355 356 357 358 359
      WechatyBro.emit('unload', String(e))
      // WechatyBro.slog('emit unload')
      // WechatyBro.emit('logout', e)
      // WechatyBro.slog('emit logout')
      // WechatyBro.slog('emit logout&unload over')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
360
    })
361
    return true
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
362
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
363
  function connectSocket() {
364
    log('connectSocket()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
365
    if (typeof io !== 'function') {
366
      log('connectSocket: io not found. loading lib...')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
367 368 369
      // http://stackoverflow.com/a/3248500/1123955
      var script = document.createElement('script')
      script.onload = function() {
370 371
        log('socket io lib loaded.')
        connectSocket()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
372
      }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
373
      script.src = '//cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js'
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
374
      document.getElementsByTagName('head')[0].appendChild(script)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
375 376
      return // wait to be called via script.onload()
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
377

378 379
    /*global io*/ // WechatyBro global variable: socket
    var socket  = WechatyBro.vars.socket = io.connect('wss://127.0.0.1:' + port/*, {transports: ['websocket']}*/)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
380

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
381 382
    // ding -> dong. for test & live check purpose
    // ping/pong are reserved by socket.io https://github.com/socketio/socket.io/issues/2414
383 384 385
    socket.on('ding', function(data) {
      log('received socket io event: ding(' + data + '). emit dong...')
      socket.emit('dong', data)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
386
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
387 388

    socket.on('connect'   , function(e) { clog('connected to server:' + e) })
389
    socket.on('disconnect', function(e) { clog('socket disconnect:'   + e) })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
390
  }
391

392
  /**
393 394 395 396 397
   *
   * Help Functions which Proxy to WXAPP AngularJS Scope & Factory
   *
   */
  function getMsgImg(id) {
398
    var contentChatScope = WechatyBro.glue.contentChatScope
399 400
    if (!contentChatScope) {
      throw new Error('getMsgImg() contentChatScope not found')
401
    }
402 403 404
    var location = window.location.href.replace(/\/$/, '')
    var path = contentChatScope.getMsgImg(id)
    return location + path
405
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
406

407
  function send(ToUserName, Content) {
408 409
    var chatFactory = WechatyBro.glue.chatFactory
    var confFactory = WechatyBro.glue.confFactory
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
410

411 412 413 414 415 416 417 418 419 420 421
    if (!chatFactory || !confFactory) {
      log('send() chatFactory or confFactory not exist.')
      return false
    }
    var m = chatFactory.createMessage({
      ToUserName: ToUserName
      , Content: Content
      , MsgType: confFactory.MSGTYPE_TEXT
    })
    chatFactory.appendMessage(m)
    return chatFactory.sendMessage(m)
422
  }
423
  function getContact(id) {
424
    var contactFactory = WechatyBro.glue.contactFactory
425 426 427 428 429
    if (!contactFactory) {
      log('contactFactory not inited')
      return null
    }
    var c = contactFactory.getContact(id)
430 431 432 433 434 435 436 437 438 439 440 441
    var contactWithoutFunction = {}

    if (c) {
      if (c.isContact) {
        c.stranger = !(c.isContact())
      }

      Object.keys(c).forEach(function(k) {
        if (typeof c[k] !== 'function') {
          contactWithoutFunction[k] = c[k]
        }
      })
442
    }
443 444

    return contactWithoutFunction
445
  }
446

447
  function getUserName() {
448
    var accountFactory = WechatyBro.glue.accountFactory
449
    return accountFactory
450 451
            ? accountFactory.getUserName()
            : null
452 453
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
  function roomFind(filterFunction) {
    var contactFactory = WechatyBro.glue.contactFactory

    var match
    if (!filterFunction) {
      match = function() { return true }
    } else {
      match = eval(filterFunction)
    }
    // log(match.toString())
    return contactFactory.getAllChatroomContact()
                          .filter(r => match(r.NickName))
                          .map(r => r.UserName)
  }

  function roomDelMember(ChatRoomName, UserName) {
    const chatroomFactory = WechatyBro.glue.chatroomFactory
    return chatroomFactory.delMember(ChatRoomName, UserName)
  }

  function roomAddMember(ChatRoomName, UserName) {
    const chatroomFactory = WechatyBro.glue.chatroomFactory
    // XXX
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
477 478
    // log(ChatRoomName)
    // log(UserName)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
479 480 481
    return chatroomFactory.addMember(ChatRoomName, UserName)
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
482 483 484 485 486
  function roomModTopic(ChatRoomName, topic) {
    const chatroomFactory = WechatyBro.glue.chatroomFactory
    return chatroomFactory.modTopic(ChatRoomName, topic)
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
  function roomCreate(UserNameList) {
    const UserNameListArg = UserNameList.map(n => { return { UserName: n } })

    const chatroomFactory = WechatyBro.glue.chatroomFactory
    chatroomFactory.create(UserNameListArg)
                    .then(r => {
                      if (r.BaseResponse && 0 == r.BaseResponse.Ret || -2013 == e.BaseResponse.Ret) {
                        // be careful: key name is userName, not UserName! 20161001
                        WechatyBro.glue.state.go('chat', { userName: r.ChatRoomName })
                      }
                    })
                    .catch(e => {
                      // TBD
                      console.log(e)
                    })
    return 'no callback (yet)'
  }
  /////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////

  port = port || 8788

  /*
   * WechatyBro injectio must return this object.
   * PuppetWebBridge need this to decide if injection is successful.
   */
  var retObj = {
    code: 200 // 2XX ok, 4XX/5XX error. HTTP like
    , message: 'any message'
    , port: port
  }

  if (typeof this.WechatyBro !== 'undefined') {
    retObj.code = 201
    retObj.message = 'WechatyBro already injected?'
    return retObj
  }

  var WechatyBro = {
    glue: {
      // will be initialized by glueToAngular() function
    }

    // glue funcs
    // , getLoginStatusCode: function() { return WechatyBro.glue.loginScope.code }
    // , getLoginQrImgUrl:   function() { return WechatyBro.glue.loginScope.qrcodeUrl }
    , angularIsReady:    angularIsReady

    // variable
    , vars: {
      loginStatus:      false
      , initStatus:     false

      , socket:     null
      , eventsBuf:  []
      , scanCode:   null
      , heartBeatTimmer:   null
    }

    // funcs
    , init: init  // initialize WechatyBro @ Browser
    , send: send  // send message to wechat user
    , clog: clog  // log to Console
    , slog: slog  // log to SocketIO
    , log:  log   // log to both Console & SocketIO
    , ding: ding  // simple return 'dong'
    , quit: quit  // quit wechat
    , emit: emit  // send event to server
    , logout: logout // logout current logined user

    , getContact: getContact
    , getUserName: getUserName
    , getMsgImg: getMsgImg

    , roomFind
    , roomCreate
    , roomAddMember
    , roomDelMember
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
566
    , roomModTopic
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591

    // test purpose
    , isLogin: isLogin
    , initClog: initClog
  }

  this.WechatyBro = WechatyBro
  retObj.code = 200
  retObj.message = 'WechatyBro Inject Succ'

  /**
   * Two return mode of WebDriver (should be one of them at a time)
   * 1. a callback. return a value by call callback with args
   * 2. direct return
   */
  var callback = arguments[arguments.length - 1]
  if (typeof callback === 'function') {
    return callback(retObj)
  }
  return retObj

  // retObj.code = 500
  // retObj.message = 'SHOULD NOT RUN TO HERE'
  // return retObj

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
592
}.apply(window, arguments))