puppet-web.js 6.5 KB
Newer Older
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
1 2
/**
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
3
 * wechaty: Wechat for Bot. and for human who talk to bot/robot
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
4
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
5 6
 * Class PuppetWeb
 * 
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
7 8
 * use to control wechat web.
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
9 10
 * Licenst: ISC
 * https://github.com/zixia/wechaty
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
11 12 13
 *
 */

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
14
/**************************************
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
15 16 17
 *
 * Class PuppetWeb
 *
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
18
 ***************************************/
19
const log = require('npmlog')
20
const co  = require('co')
21

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
22
const Puppet = require('./puppet')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
23
const Message = require('./message')
24 25
const Contact = require('./contact')
const Group   = require('./group')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
26

27 28
const Server  = require('./puppet-web-server')
const Browser = require('./puppet-web-browser')
29
const Bridge  = require('./puppet-web-bridge')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
30 31

class PuppetWeb extends Puppet {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
32
  constructor(options) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33
    super()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
34
    options = options || {}
35 36
    this.port = options.port || 8788 // W(87) X(88), ascii char code ;-]
    this.head = options.head
37

38
    this.user = null  // <Contact> currentUser
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
39
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
40

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
41
  toString() { return `Class PuppetWeb({browser:${this.browser},port:${this.port}})` }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
42

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
43
  init() {
44
    log.verbose('PuppetWeb', 'init()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
45
    
46
    return this.initAttach()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    .then(r => {
      log.verbose('PuppetWeb', 'initAttach done: %s', r)
      return this.initBrowser()
    })
    .then(r => {
      log.verbose('PuppetWeb', 'initBrowser done: %s', r)
      return this.initBridge() 
    })
    .then(r => {
      log.verbose('PuppetWeb', 'initBridge done: %s', r)
      return this.initServer()
    })
    .then(r => {
      log.verbose('PuppetWeb', 'initServer done: %s', r)
      return r
    })
    .catch(e => {                 // Reject
64 65
      log.error('PuppetWeb', e)
      throw e
66
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
67 68 69 70
    .then(r => {                  // Finally
      log.verbose('PuppetWeb', 'all initXXX done.')
      return true
    })
71
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
72
  
73 74 75 76
  initAttach() {
    log.verbose('PuppetWeb', 'initAttach()')
    Contact.attach(this)
    Group.attach(this)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
77
    return Promise.resolve(true)
78 79 80 81 82 83 84 85 86 87 88
  }
  initBrowser() {
    log.verbose('PuppetWeb', 'initBrowser')
    this.browser  = new Browser({ head: this.head })
    return this.browser.init()
  }
  initBridge() {
    log.verbose('PuppetWeb', 'initBridge()')
    this.bridge = new Bridge({
      browser:  this.browser
      , port:   this.port
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
89
    })
90 91
    return this.bridge.init()
  }
92
  initServer() {
93 94 95 96 97 98 99
    log.verbose('PuppetWeb', 'initServer()')
    const server = new Server({port: this.port})

    server.on('login',   this.onServerLogin.bind(this))
    server.on('logout',  this.onServerLogout.bind(this))
    server.on('message', this.onServerMessage.bind(this))
    server.on('unload',  this.onServerUnload.bind(this))
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
100 101 102 103
    
    server.on('connection', this.onServerConnection.bind(this))
    server.on('disconnect', this.onServerDisconnect.bind(this))
    server.on('log', this.onServerLog.bind(this))
104

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
105
    ;[  // Public events to end user
106 107 108 109 110 111 112
      , 'scan'
      , 'dong'
    ].map(e => {
      server.on(e, data => {
        log.verbose('PuppetWeb', 'Server event[%s]: %s', e, data)
        this.emit(e, data)
      })
113
    })
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
114

115
    this.server = server
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
116
    return this.server.init()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
117 118
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
119 120 121 122 123 124 125 126 127 128 129 130
  onServerConnection(data) {
    log.verbose('PuppetWeb', 'onServerConnection: %s', data)
  }
  onServerDisconnect(data) {
    log.verbose('PuppetWeb', 'onServerDisconnect: %s', data)
    log.verbose('PuppetWeb', 'onServerDisconnect: unloaded? call onServerUnload to try to fix connection')
    this.onServerUnload(data)
  }
  onServerLog(data) {
    log.verbose('PuppetWeb', 'onServerLog: %s', data)
  }
  
131
  onServerLogin(data) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
132 133 134
    co.call(this, function* () { 
      // co.call to make `this` context work inside generator.
      // See also: https://github.com/tj/co/issues/274
135 136 137 138 139 140 141 142 143 144
      const userName = yield this.bridge.getUserName()
      if (!userName) {
        log.silly('PuppetWeb', 'onServerLogin: browser not full loaded, retry later.')
        setTimeout(this.onServerLogin.bind(this), 100)
        return
      }
      log.silly('PuppetWeb', 'userName: %s', userName)
      this.user = yield Contact.load(userName).ready()
      log.verbose('PuppetWeb', `user ${this.user.name()} logined`)
      this.emit('login', this.user)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
145
    })
146
    .catch(e => log.error('PuppetWeb', 'onServerLogin rejected: %s', e))
147
  }
148 149 150 151 152
  onServerLogout(data) {
    this.user = null
    this.emit('logout', data)
  }
  onServerMessage(data) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
153
    const m = new Message(data)
154
    if (!this.user) {
155 156 157
      log.warn('PuppetWeb', 'onServerMessage() without this.user')
    } else if (this.user.id==m.from().id) {
      log.silly('PuppetWeb', 'onServerMessage skip msg send by self')
158 159
      return
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
160 161
    this.emit('message', m)
  }
162 163 164 165 166 167 168
  onServerUnload(data) {
    /**
     * `unload` event is sent from js@browser to webserver via socketio
     * after received `unload`, we re-inject the Wechaty js code into browser.
     */
    log.verbose('PuppetWeb', 'server received unload event')
    this.emit('logout', data) // XXX: should emit event[logout] from browser
Huan (李卓桓)'s avatar
v0.0.9  
Huan (李卓桓) 已提交
169 170 171 172 173 174 175 176
    
    if (this.bridge) {
      this.bridge.inject()
      .then(r  => log.verbose('PuppetWeb', 're-injected:' + r))
      .catch(e => log.error('PuppetWeb', 'inject err: ' + e))
    } else {
      log.verbose('PuppetWeb', 'bridge gone, should be quiting now')
    }
177 178
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
179
  send(message) {
180
    const userName    = message.to().id
181
    const content     = message.content()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
182

Huan (李卓桓)'s avatar
v0.0.9  
Huan (李卓桓) 已提交
183
    log.silly('PuppetWeb', `send(${userName}, ${content})`)
184
    return this.bridge.send(userName, content)
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
185
  }
186 187 188 189 190 191 192 193 194
  reply(recvMsg, replyMsg) {
    var contact = recvMsg.group()
    if (!contact) { contact = recvMsg.from() }
    const contactId = contact.id

    log.silly('PuppetWeb', `reply(${contact}, ${replyMsg})`)
    return this.bridge.send(contactId, replyMsg)
  }

195 196 197 198 199 200 201 202
  logout()        { return this.bridge.logout() }
  getContact(id)  { return this.bridge.getContact(id) }
  getLoginQrImgUrl() {
    if (!this.bridge) {
      log.error('PuppetWeb', 'bridge not found')
      return
    }
    return this.bridge.getLoginQrImgUrl()
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
203 204
  }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
205 206 207
  /**
   *  Interface Methods
   */
208 209
  quit() {
    log.verbose('PuppetWeb', 'quit()')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
210 211
    let p = Promise.resolve(true)
    
Huan (李卓桓)'s avatar
v0.0.9  
Huan (李卓桓) 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    if (this.bridge)  { 
      p.then(this.bridge.quit.bind(this.bridge)) 
      this.bridge = null
    } else {
      log.warn('PuppetWeb', 'quit() without bridge')
    }

    if (this.browser) {
      p.then(this.browser.quit.bind(this.browser))
      this.browser = null
    } else {
      log.warn('PuppetWeb', 'quit() without browser')
    }

    if (this.server) {
      p.then(this.server.quit.bind(this.server))
      this.server = null
    } else {
      log.warn('PuppetWeb', 'quit() without server')
    }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
232 233
    
    return p // return Promise
234
  }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235
  
236
  isLogined()           { return !!(this.user) }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
237 238
}

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
239
module.exports = PuppetWeb