提交 754a6a9c 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

lots of stability enhancement, and part of class re-structed

上级 d3c1eb67
......@@ -5,9 +5,7 @@ node_js:
- "6"
os:
- linux
- centos
- macosx
- windows
dist: trusty
before_install:
- export DISPLAY=:99.0
......
......@@ -26,26 +26,18 @@ So a tireless bot working for me 24x7 on wechat, moniting/filtering the most imp
# Examples
Wechaty is super easy to use: 10 lines of javascript is enough for your first wechat robot.
## 1. Basic: 10 lines
The following 10 lines of code implement a bot who reply "roger" for every message received:
## 1. Basic: 9 lines
The following 9 lines of code implement a bot who reply "roger" for every message received:
```javascript
const Wechaty = require('wechaty')
const bot = new Wechaty()
bot.init()
.on('scan', ({url, code}) => {
console.log(`Scan qrcode in url to login: ${code}\n${url}`)
})
.on('message', m => {
console.log('RECV: ' + m.get('content')) // 1. print received message
const reply = new Wechaty.Message() // 2. create reply message
.set('to', m.get('from')) // 1) set receipt
.set('content', 'roger.') // 2) set content
bot.send(reply) // 3. do reply!
.then(() => console.log('REPLY: roger.')) // 4. print reply message
bot.init().on('scan', ({url, code}) => {
console.log(`Use Wechat to scan qrcode in url to login: ${code}\n${url}`)
}).on('message', m => {
bot.send(m.reply('roger')) // 1. reply
.then(() => console.log(`RECV: ${m}, REPLY: "roger"`)) // 2. log message
.catch(e => console.error(e)) // 3. catch exception
})
```
......@@ -133,9 +125,9 @@ bot.on('scan', ({code, url}) => {
1. url: {String} the qrcode image url
2. code: {Number} the scan status code. some known status of the code list here is:
1. 0 initial
2. 408 wait for scan
3. 201 scaned, wait for confirm
4. 200 login confirmed
1. 200 login confirmed
1. 201 scaned, wait for confirm
1. 408 wait for scan
`scan` event will be emit when it will detect a new code status change.
......@@ -215,6 +207,14 @@ message.ready()
})
```
### Message.reply()
Create a new Message instance that reply the original one. which means: the `to` field of the reply message is the `from` of origin message.
```javascript
const replyMessage = message.reply('roger!')
bot.send(replyMessage)
```
## Class Contact
### Contact.get(prop)
......@@ -324,7 +324,7 @@ Know more about TAP: [Why I use Tape Instead of Mocha & So Should You](https://m
- [ ] Message
- [ ] Send/Reply image message
- [ ] Session save/load
Everybody is welcome to issue your needs.
# Known Issues & Support
......
const log = require('npmlog')
log.level = 'verbose'
// log.level = 'silly'
log.level = 'silly'
const Wechaty = require('../src/wechaty')
......@@ -39,16 +39,14 @@ bot
.on('message', m => {
m.ready()
.then(msg => {
log.info('Bot', 'recv: %s', msg)
logToFile(JSON.stringify(msg.rawObj))
log.info('Bot', 'recv: %s', msg.toStringEx())
// logToFile(JSON.stringify(msg.rawObj))
})
.catch(e => log.error('Bot', 'ready: %s' , e))
if (/^(ding|ping|bing)$/i.test(m.get('content'))) {
const reply = new Wechaty.Message()
reply.set('to', m.group() ? m.get('group') : m.get('from'))
reply.set('content', 'dong')
bot.send(reply)
const replyMsg = m.reply('dong')
bot.send(replyMsg)
.then(() => { log.warn('Bot', 'REPLY: dong') })
}
})
......
const Wechaty = require('..')
const bot = new Wechaty({head: true})
const bot = new Wechaty()
bot
bot.init()
.on('scan', ({url, code}) => {
console.log(`Scan qrcode in url to login: ${code}\n${url}`)
console.log(`Use Wechat to scan qrcode in url to login: ${code}\n${url}`)
})
.on('message', m => {
console.log('RECV: ' + m.get('content')) // 1. print received message
const reply = new Wechaty.Message() // 2. create reply message
.set('to', m.get('from')) // 1) set receipt
.set('content', 'roger.') // 2) set content
bot.send(reply) // 3. do reply!
.then(() => console.log('REPLY: roger.')) // 4. print reply message
.catch(e => console.error(e))
bot.send(m.reply('roger')) // 1. reply
.then(() => console.log(`RECV: ${m}, REPLY: "roger"`)) // 2. log message
.catch(e => console.error(e)) // 3. catch exception
})
bot.init()
.catch(e => console.error(e))
......@@ -13,11 +13,13 @@ class Contact {
log.silly('Contact', `constructor(${id})`)
if (!Contact.puppet) { throw new Error('no puppet attached to Contact') }
this.id = id
this.obj = {}
if (id && typeof id !== 'string') { throw new Error('id must be string if provided. we got: ' + typeof id) }
this.id = id
this.obj = {}
}
toString() { return `Contact(${this.obj.name}[${this.id}])` }
toString() { return this.id }
toStringEx() { return `Contact(${this.obj.name}[${this.id}])` }
parse(rawObj) {
return !rawObj ? {} : {
......@@ -31,15 +33,24 @@ class Contact {
, city: rawObj.City
, signature: rawObj.Signature
, address: rawObj.Alias // XXX: need a stable address for user
, star: !!rawObj.StarFriend
, stranger: !!rawObj.stranger // assign by injectio.js
}
}
name() { return this.obj.name }
name() { return this.obj.name }
remark() { return this.obj.remark }
ready(contactGetter) {
log.silly('Contact', 'ready(' + typeof contactGetter + ')')
if (this.obj.id) { return Promise.resolve(this) }
if (!this.id) {
log.warn('Contact', 'ready() call on an un-inited contact')
return Promise.resolve(this)
} else if (this.obj.id) {
return Promise.resolve(this)
}
if (!contactGetter) {
log.silly('Contact', 'get contact via ' + Contact.puppet.constructor.name)
......@@ -68,16 +79,17 @@ class Contact {
get(prop) { return this.obj[prop] }
send(message) {
const msg = new Contact.puppet.Message() // create a empty message
msg.set('from', this)
msg.set('content', message)
return Contact.puppet.send(message)
}
// KISS: don't cross reference
// send(message) {
// const msg = new Contact.puppet.Message() // create a empty message
// msg.set('from', this)
// msg.set('content', message)
// return Contact.puppet.send(message)
// }
stranger() { return this.obj.stranger }
star() { return this.obj.star }
static find() { }
static findAll() { }
}
......
......@@ -11,18 +11,25 @@ const Contact = require('./contact')
class Group {
constructor(id) {
log.silly('Group', `constructor(${id})`)
this.id = id
this.obj = {}
log.silly('Group', `constructor(${id})`)
if (!Group.puppet) {
throw new Error('no puppet attached to Group')
}
}
toString() { return `Group(${this.obj.name}[${this.id}])` }
toString() { return this.id }
toStringEx() { return `Group(${this.obj.name}[${this.id}])` }
ready(contactGetter) {
log.silly('Group', `ready(${contactGetter})`)
if (this.obj.id) { return Promise.resolve(this) }
if (!this.id) {
log.warn('Group', 'ready() on a un-inited group')
return Promise.resolve(this)
} else if (this.obj.id) {
return Promise.resolve(this)
}
contactGetter = contactGetter || Group.puppet.getContact.bind(Group.puppet)
return contactGetter(this.id)
......
......@@ -25,28 +25,30 @@ class Message {
return {
id: rawObj.MsgId
, type: rawObj.MsgType
// , from: rawObj.MMActualSender
// , to: rawObj.ToUserName
// , group: !!(rawObj.MMIsChatRoom) // MMPeerUserName always eq FromUserName ?
, from: rawObj.MMActualSender
, to: rawObj.ToUserName
, group: rawObj.MMIsChatRoom ? rawObj.FromUserName : null // MMPeerUserName always eq FromUserName ?
, content: rawObj.MMActualContent // Content has @id prefix added by wx
, status: rawObj.Status
, digest: rawObj.MMDigest
, date: rawObj.MMDisplayTime // Javascript timestamp of milliseconds
, from: Contact.load(rawObj.MMActualSender)
, to: Contact.load(rawObj.ToUserName)
, group: rawObj.MMIsChatRoom ? Group.load(rawObj.FromUserName) : null
, date: new Date(rawObj.MMDisplayTime*1000)
// , from: Contact.load(rawObj.MMActualSender)
// , to: Contact.load(rawObj.ToUserName)
// , group: rawObj.MMIsChatRoom ? Group.load(rawObj.FromUserName) : null
// , date: new Date(rawObj.MMDisplayTime*1000)
}
}
toString() {
toString() { return this.obj.content }
toStringEx() {
var s = `${this.constructor.name}#${Message.counter}`
s += '(' + this.getSenderString()
s += ':' + this.getContentString() + ')'
return s
}
getSenderString() {
const name = this.obj.from.get('remark') || this.obj.from.get('name')
const group = this.obj.group
const name = Contact.load(this.obj.from).toStringEx()
const group = this.obj.group ? Contact.load(this.obj.group).toStringEx() : null
return '<' + name + (group ? `@${group}` : '') + '>'
}
getContentString() {
......@@ -69,24 +71,36 @@ class Message {
content() { return this.obj.content }
group() { return this.obj.group }
reply(content) {
const from = this.from()
reply(replyContent) {
return new Message()
.set('to', from)
.set('content', content)
.set('to' , this.group() ? this.group() : this.from())
.set('content', replyContent)
}
ready() {
log.silly('Message', 'ready()')
return this.obj.from.ready() // Contact from
.then(r => this.obj.to.ready()) // Contact to
.then(r => this.obj.group && this.obj.group.ready()) // Group member list
.then(r => this) // ready return this for chain
const f = Contact.load(this.obj.from)
const t = Contact.load(this.obj.to)
const g = this.obj.group ? Contact.load(this.obj.group) : null
return f.ready() // Contact from
.then(r => t.ready()) // Contact to
.then(r => g && g.ready()) // Group member list
.then(r => this) // return this for chain
.catch(e => { // REJECTED
log.error('Message', 'ready() rejected: %s', e)
throw new Error(e)
throw e
})
// return this.obj.from.ready() // Contact from
// .then(r => this.obj.to.ready()) // Contact to
// .then(r => this.obj.group && this.obj.group.ready()) // Group member list
// .then(r => this) // return this for chain
// .catch(e => { // REJECTED
// log.error('Message', 'ready() rejected: %s', e)
// throw new Error(e)
// })
}
get(prop) {
......
......@@ -53,30 +53,19 @@ class Bridge {
const timeout = max * (backoff * max) / 2
return retryPromise({ max: max, backoff: backoff }, function (attempt) {
log.verbose('Bridge', 'getContact() retryPromise: attampt %s/%s time for timeout %s'
log.silly('Bridge', 'getContact() retryPromise: attampt %s/%s time for timeout %s'
, attempt, max, timeout)
/**
* This promise is MUST have here,
* the reason is as the following NOTICE explained
*/
return new Promise((resolve, reject) => {
this.proxyWechaty('getContact', id)
.then(r => {
if (r) {
resolve(r)
}
/**
* NOTICE: the promise that this.proxyWechaty returned will be resolved but be `undefined`
* which should be treat as `rejected`
*/
return reject('got empty')
}).catch(e => {
reject(e)
})
return this.proxyWechaty('getContact', id)
.then(r => {
if (!r) {
throw ('got empty return')
}
return r
})
}.bind(this))
.catch(e => {
log.error('Bridge', 'getContact() retryPromise FAIL: %s', e)
log.error('Bridge', 'getContact() retryPromise finally FAIL: %s', e)
throw e
})
/////////////////////////////////
......@@ -98,12 +87,12 @@ class Bridge {
try {
return this.execute(injectio, this.port)
.then(r => {
log.verbose('Bridge', 'injected. initing...')
log.verbose('Bridge', `injected, got [${r}]. now initing...`)
return this.proxyWechaty('init')
})
.then(r => {
if (true===r) { log.verbose('Bridge', 'Wechaty.init() return: ' + r) }
else { throw new Error('Wechaty.init() return not true: ' + r) }
if (true!==r) { throw new Error('Wechaty.init() failed: ' + r) }
log.verbose('Bridge', 'Wechaty.init() successful')
return r
})
} catch (e) {
......@@ -142,8 +131,10 @@ class Bridge {
module.exports = Bridge
/*
*
/**
*
* some handy browser javascript snips
*
ac = Wechaty.glue.contactFactory.getAllContacts();
Object.keys(ac).filter(function(k) { return /李/.test(ac[k].NickName) }).map(function(k) { var c = ac[k]; return {NickName: c.NickName, Alias: c.Alias, Uin: c.Uin, MMInChatRoom: c.MMInChatRoom} })
......@@ -151,5 +142,5 @@ Object.keys(window._chatContent).filter(function (k) { return window._chatConten
.web_wechat_tab_add
.web_wechat_tab_launch-chat
*
*/
\ No newline at end of file
*
*/
\ No newline at end of file
......@@ -24,7 +24,7 @@ class Browser {
init() {
log.verbose('Browser', 'init()')
return this.initDriver()
.then(r => {
log.verbose('Browser', 'initDriver() done')
......@@ -38,8 +38,8 @@ class Browser {
open() {
const WX_URL = 'https://wx.qq.com'
log.verbose('Browser', `open() at ${WX_URL}`)
log.verbose('Browser', `open()ing at ${WX_URL}`)
return this.driver.get(WX_URL)
}
......@@ -93,50 +93,45 @@ class Browser {
return Promise.resolve('no driver session')
}
log.verbose('Browser', 'driver.quit')
this.driver.close() // http://stackoverflow.com/a/32341885/1123955
this.driver.quit()
this.driver = null
return this.clean()
return this.driver.close() // http://stackoverflow.com/a/32341885/1123955
.then(r => this.driver.quit())
.then(r => this.driver = null)
.then(r => this.clean())
}
clean() {
const max = 5
const max = 15
const backoff = 100
// max = (2*totalTime/backoff) ^ (1/2)
// timeout = 11250 for {max: 15, backoff: 100}
// timeout = 45000 for {max: 30, backoff: 100}
const timeout = max * (backoff * max) / 2
return retryPromise({ max: max, backoff: backoff }, resolveAfterClean.bind(this))
.catch(e => {
log.error('Browser', 'waitClean() retryPromise failed: %s', e)
throw e
})
////////////////////////////////////////////////
function resolveAfterClean(attempt) {
log.verbose('Browser', 'clean() retryPromise: attampt %s time for timeout %s'
return retryPromise({ max: max, backoff: backoff }, attempt => {
log.silly('Browser', 'clean() retryPromise: attampt %s time for timeout %s'
, attempt, timeout)
return this.numProcess()
.then(n => {
if (n > 0) throw new Error('reject because there has driver process not exited')
log.verbose('Browser', 'waitClean hit')
return n
})
}
}
numProcess() {
return new Promise((resolve, reject) => {
require('ps-tree')(process.pid, (err, children) => {
if (err) {
return reject(err)
}
const num = children.filter(child => /phantomjs/i.test(child.COMMAND)).length
return resolve(num)
return new Promise((resolve, reject) => {
require('ps-tree')(process.pid, (err, children) => {
if (err) {
return reject(err)
}
const num = children.filter(child => /phantomjs/i.test(child.COMMAND)).length
if (num==0) {
return resolve('clean')
} else {
return reject('dirty')
}
})
})
})
.catch(e => {
log.error('Browser', 'retryPromise failed: %s', e)
throw e
})
}
execute(script, ...args) {
//log.verbose('Browser', `Browser.execute(${script})`)
if (!this.driver) {
......
......@@ -3,7 +3,7 @@
* Wechaty - Wechat for Bot, and human who talk to bot.
*
* Class PuppetWebInjectio
*
*
* Inject this js code to browser,
* in order to interactive with wechat web program.
*
......@@ -14,30 +14,17 @@
/*global angular*/
if (typeof Wechaty !== 'undefined') {
return 'Wechaty already injected?'
}
return (function(port) {
port = port || 8788
/**
* Log to console
* http://stackoverflow.com/a/7089553/1123955
*/
function clog(s) {
var d = new Date()
s = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ' <Wechaty> ' + s
var i = document.createElement('iframe')
i.style.display = 'none'
document.body.appendChild(i)
i.contentWindow.console.log(s)
i.parentNode.removeChild(i)
if (typeof Wechaty !== 'undefined') {
return 'Wechaty already injected?'
}
var Wechaty = {
glue: {} // will be initialized by glueAngular() function
glue: {
} // will be initialized by glueAngular() function
// glue funcs
, getLoginStatusCode: function() { return Wechaty.glue.loginScope.code }
, getLoginQrImgUrl: function() { return Wechaty.glue.loginScope.qrcodeUrl }
......@@ -45,7 +32,9 @@ return (function(port) {
// variable
, vars: {
login: false
logined: false
, inited: false
, socket: null
, eventsBuf: []
, scanCode: null
......@@ -65,6 +54,28 @@ return (function(port) {
, getUserName: getUserName
}
window.Wechaty = Wechaty
if (isWxLogin()) {
login('page refresh')
}
/**
* 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('Wechaty')
} else {
return 'Wechaty'
}
return 'Should not run to here'
/////////////////////////////////////////////////////////////////////////////
/**
*
* Functions that Glued with AngularJS
......@@ -79,6 +90,11 @@ return (function(port) {
)
}
function init() {
if (Wechaty.vars.inited === true) {
log('Wechaty.init() called twice: already inited')
return
}
if (!isReady()) {
clog('angular not ready. wait 500ms...')
setTimeout(init, 500)
......@@ -92,8 +108,15 @@ return (function(port) {
checkScan()
heartBeat()
clog('inited!. ;-D')
return true
return Wechaty.vars.inited = true
}
function heartBeat() {
Wechaty.emit('ding', 'heartbeat in browser')
setTimeout(heartBeat, 15000)
}
function glueAngular() {
......@@ -160,16 +183,17 @@ return (function(port) {
return
}
function isLogin() { return !!Wechaty.vars.login }
function isLogin() { return !!Wechaty.vars.logined }
function login(data) {
clog('login()')
Wechaty.vars.login = true
Wechaty.vars.logined = true
Wechaty.emit('login', data)
}
function logout(data) {
clog('logout()')
Wechaty.vars.login = false
Wechaty.vars.logined = false
Wechaty.emit('logout', data)
checkScan()
}
function quit() {
clog('quit()')
......@@ -231,13 +255,12 @@ return (function(port) {
}
// Wechaty.emit, will save event & data when there's no socket io connection to prevent event lost
function emit(event, data) {
if (event) {
Wechaty.vars.eventsBuf.push([event, data])
}
if (!Wechaty.vars.socket) {
clog('Wechaty.vars.socket not ready')
if (event) {
Wechaty.vars.eventsBuf.push([event, data])
}
setTimeout(emit, 1000) // resent eventsBuf after 1000ms
return
return setTimeout(emit, 1000) // resent eventsBuf after 1000ms
}
if (Wechaty.vars.eventsBuf.length) {
clog('Wechaty.vars.eventsBuf has ' + Wechaty.vars.eventsBuf.length + ' unsend events')
......@@ -247,9 +270,9 @@ return (function(port) {
}
clog('Wechaty.vars.eventsBuf all sent')
}
if (event) {
Wechaty.vars.socket.emit(event, data)
}
// if (event) {
// Wechaty.vars.socket.emit(event, data)
// }
}
function connectSocket() {
clog('connectSocket()')
......@@ -287,16 +310,20 @@ return (function(port) {
// })
}
window.Wechaty = Wechaty
if (isWxLogin()) {
login('page refresh')
}
var callback = arguments[arguments.length - 1]
if (typeof callback === 'function') {
return callback('Wechaty')
}
/**
* Log to console
* http://stackoverflow.com/a/7089553/1123955
*/
function clog(s) {
var d = new Date()
s = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ' <Wechaty> ' + s
return 'Wechaty'
var i = document.createElement('iframe')
i.style.display = 'none'
document.body.appendChild(i)
i.contentWindow.console.log(s)
i.parentNode.removeChild(i)
}
}.apply(window, arguments))
......@@ -4,7 +4,7 @@
* Web Server for puppet
*
* Class PuppetWebServer
*
*
* Licenst: ISC
* https://github.com/zixia/wechaty
*
......@@ -111,7 +111,8 @@ class Server extends EventEmitter {
this.emit('disconnect', e)
})
client.on('error', e => log.error('Server', 'socketio client error: %s', e))
client.on('error' , e => log.error('Server', 'socketio client error: %s', e))
client.on('ding' , e => log.silly('Server', 'got ding: %s', e))
// Events from Wechaty@Broswer --to--> Server
;[
......
......@@ -3,7 +3,7 @@
* wechaty: Wechat for Bot. and for human who talk to bot/robot
*
* Class PuppetWeb
*
*
* use to control wechat web.
*
* Licenst: ISC
......@@ -16,6 +16,7 @@
* Class PuppetWeb
*
***************************************/
const util = require('util')
const log = require('npmlog')
const co = require('co')
......@@ -35,14 +36,14 @@ class PuppetWeb extends Puppet {
this.port = options.port || 8788 // W(87) X(88), ascii char code ;-]
this.head = options.head
this.user = null // <Contact> currentUser
this.user = null // <Contact>
}
toString() { return `Class PuppetWeb({browser:${this.browser},port:${this.port}})` }
init() {
log.verbose('PuppetWeb', 'init()')
return this.initAttach()
.then(r => {
log.verbose('PuppetWeb', 'initAttach done: %s', r)
......@@ -50,7 +51,7 @@ class PuppetWeb extends Puppet {
})
.then(r => {
log.verbose('PuppetWeb', 'initBrowser done: %s', r)
return this.initBridge()
return this.initBridge()
})
.then(r => {
log.verbose('PuppetWeb', 'initBridge done: %s', r)
......@@ -66,10 +67,10 @@ class PuppetWeb extends Puppet {
})
.then(r => { // Finally
log.verbose('PuppetWeb', 'all initXXX done.')
return true
return this // for Chaining
})
}
initAttach() {
log.verbose('PuppetWeb', 'initAttach()')
Contact.attach(this)
......@@ -97,7 +98,7 @@ class PuppetWeb extends Puppet {
server.on('logout', this.onServerLogout.bind(this))
server.on('message', this.onServerMessage.bind(this))
server.on('unload', this.onServerUnload.bind(this))
server.on('connection', this.onServerConnection.bind(this))
server.on('disconnect', this.onServerDisconnect.bind(this))
server.on('log', this.onServerLog.bind(this))
......@@ -117,7 +118,7 @@ class PuppetWeb extends Puppet {
}
onServerConnection(data) {
log.verbose('PuppetWeb', 'onServerConnection: %s', data)
log.verbose('PuppetWeb', 'onServerConnection: %s', data.constructor.name)
}
onServerDisconnect(data) {
log.verbose('PuppetWeb', 'onServerDisconnect: %s', data)
......@@ -127,9 +128,9 @@ class PuppetWeb extends Puppet {
onServerLog(data) {
log.verbose('PuppetWeb', 'onServerLog: %s', data)
}
onServerLogin(data) {
co.call(this, function* () {
co.call(this, function* () {
// co.call to make `this` context work inside generator.
// See also: https://github.com/tj/co/issues/274
const userName = yield this.bridge.getUserName()
......@@ -166,14 +167,19 @@ class PuppetWeb extends Puppet {
*/
log.verbose('PuppetWeb', 'server received unload event')
this.emit('logout', data) // XXX: should emit event[logout] from browser
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')
if (!this.browser || !this.bridge) {
log.warn('PuppetWeb', 'bridge gone, should be quiting now')
return
}
return this.browser.quit()
.then(r => log.verbose('PuppetWeb', 'browser.quit()ed:' + r))
.then(r => this.browser.init())
.then(r => log.verbose('PuppetWeb', 'browser.re-init()ed:' + r))
.then(r => this.bridge.init())
.then(r => log.verbose('PuppetWeb', 'bridge.re-init()ed:' + r))
.catch(e => log.error('PuppetWeb', 'onServerUnload() err: ' + e))
}
send(message) {
......@@ -209,9 +215,9 @@ class PuppetWeb extends Puppet {
quit() {
log.verbose('PuppetWeb', 'quit()')
let p = Promise.resolve(true)
if (this.bridge) {
p.then(this.bridge.quit.bind(this.bridge))
if (this.bridge) {
p.then(this.bridge.quit.bind(this.bridge))
this.bridge = null
} else {
log.warn('PuppetWeb', 'quit() without bridge')
......@@ -230,7 +236,7 @@ class PuppetWeb extends Puppet {
} else {
log.warn('PuppetWeb', 'quit() without server')
}
return p // return Promise
}
}
......
......@@ -18,12 +18,6 @@ class Puppet extends EventEmitter {
super()
}
/**
* Get current logined user
* @return <Contact>
*/
currentUser() { throw new Error('To Be Implemented') }
/**
* let puppet send message
*
......@@ -33,32 +27,14 @@ class Puppet extends EventEmitter {
send(message) { throw new Error('To Be Implemented') }
logout() { throw new Error('To Be Implementsd') }
alive() { throw new Error('To Be Implementsd') }
ding() { throw new Error('To Be Implementsd') }
getContact(id) { // for unit testing
log.silly('Puppet', `Interface method getContact(${id})`)
log.verbose('Puppet', `Interface method getContact(${id})`)
return Promise.resolve({UserName: 'WeChaty', NickName: 'Puppet'})
}
// () { throw new Error('To Be Implemented') }
/**
*
* Events .on(...)
*
* login -
* logout -
*
*
*/
debug(cb) {
// List of all events
[ 'message' // event data should carry a instance of Message
, 'login'
, 'logout'
].map(e => { this.on(e, cb) })
}
}
Object.assign(Puppet, {
......
......@@ -3,7 +3,7 @@
* wechaty: Wechat for Bot. and for human who talk to bot/robot
*
* Class Wechaty
*
*
* Licenst: ISC
* https://github.com/zixia/wechaty
*
......@@ -24,7 +24,7 @@ class Wechaty extends EventEmitter {
super()
this.options = options || {}
this.options.puppet = this.options.puppet || 'web'
this.VERSION = require('../package.json').version
}
toString() { return 'Class Wechaty(' + this.puppet + ')'}
......@@ -32,7 +32,11 @@ class Wechaty extends EventEmitter {
log.info('Wechaty', 'init() with version: %s', this.VERSION)
this.initPuppet()
this.initEventHook()
return this.puppet.init()
.then(r => {
return this // for chaining
})
}
initPuppet() {
switch (this.options.puppet) {
......@@ -61,12 +65,21 @@ class Wechaty extends EventEmitter {
this.puppet.on('logout', (e) => {
this.emit('logout', e)
})
/**
* TODO: support more events:
* 1. error
* 2. send
* 3. reply
* 4. quit
* 5. ...
*/
return Promise.resolve()
}
currentUser() { return this.puppet.currentUser() }
quit() { return this.puppet.quit() }
quit() { return this.puppet.quit() }
send(message) { return this.puppet.send(message) }
reply(message, reply) { return this.puppet.reply(message, reply) }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册