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

helper function: waitData to retry a promise until get right

上级 c300e011
......@@ -30,18 +30,17 @@ Please wait... I'm trying to login in...
console.log(welcome)
const bot = new Wechaty({head: true})
bot.on('scan', ({url, code}) => {
console.log(`Scan qrcode in url to login: \n${url}`)
console.log(code)
})
bot
.on('login' , () => log.info('Bot', 'logined'))
.on('logout' , () => log.info('Bot', 'logouted'))
.on('scan', ({url, code}) => {
console.log(`Scan qrcode in url to login: ${code}\n${url}`)
})
.on('message', m => {
m.ready()
.then(msg => {
log.info('Bot', 'recv: %s', msg)
logToFile(JSON.stringify(msg.rawObj))
})
.catch(e => log.error('Bot', 'ready: %s' , e))
......@@ -60,3 +59,9 @@ bot.init()
bot.quit()
process.exit(-1)
})
function logToFile(data) {
require('fs').appendFile('message.log', data + '\n\n#############################\n\n', err => {
if (err) { log.error('LogToFile: %s', err) }
})
}
......@@ -39,21 +39,13 @@ bot
.on('message', m => {
co(function* () {
const msg = yield m.ready()
log.info('Bot', 'recv: %s' , msg)
if (m.group()) {
return
if (m.group() && /Wechaty/i.test(m.group().name())) {
log.info('Bot', 'talk: %s' , msg)
talk(m)
} else {
log.info('Bot', 'recv: %s' , msg)
}
const reply = new Wechaty.Message()
.set('to', m.get('from'))
const content = m.get('content')
const {code, text} = yield brain.ask(content, {userid: msg.get('from')})
reply.set('content', text)
yield bot.send(reply)
log.info('Bot', `REPLY: {code:${code}, text:${text}}`)
})
.catch(e => log.error('Bot', 'on message rejected: %s' , e))
})
......@@ -65,3 +57,17 @@ bot.init()
process.exit(-1)
})
function talk(m) {
co(function* () {
const fromId = m.from().id
const content = m.content()
const {code, text} = yield brain.ask(content, {userid: fromId})
const minDelayTime = 5000
const maxDelayTime = 15000
const delayTime = Math.floor(Math.random() * (maxDelayTime - minDelayTime)) + minDelayTime
log.info('Bot', `REPLY(after ${Math.floor(delayTime/1000)}s): {code:${code}, text:${text}}`)
setTimeout(r => { bot.reply(m, text) }, delayTime)
})
}
......@@ -19,26 +19,9 @@ class Contact {
this.obj = {}
}
ready(contactGetter) {
log.silly('Contact', 'ready(' + typeof contactGetter + ')')
if (this.obj.id) {
return Promise.resolve(this)
}
if (!contactGetter) {
log.silly('Contact', 'get contact via ' + Contact.puppet.constructor.name)
contactGetter = Contact.puppet.getContact.bind(Contact.puppet)
}
return contactGetter(this.id)
.then(data => {
log.silly('Contact', `contactGetter(${this.id}) resolved`)
this.rawObj = data
this.obj = this.parse(data)
return this
}).catch(e => {
log.error('Contact', `contactGetter(${this.id}) rejected: `, e)
throw new Error('contactGetter: ' + e)
})
toString() {
var name = this.obj.name ? `${this.obj.name}@${this.id}` : this.id
return `Contact(${name})`
}
parse(rawObj) {
......@@ -56,6 +39,26 @@ class Contact {
}
name() { return this.obj.name }
ready(contactGetter) {
log.silly('Contact', 'ready(' + typeof contactGetter + ')')
if (this.obj.id) { return Promise.resolve(this) }
if (!contactGetter) {
log.silly('Contact', 'get contact via ' + Contact.puppet.constructor.name)
contactGetter = Contact.puppet.getContact.bind(Contact.puppet)
}
return contactGetter(this.id)
.then(data => {
log.silly('Contact', `contactGetter(${this.id}) resolved`)
this.rawObj = data
this.obj = this.parse(data)
return this
}).catch(e => {
log.error('Contact', `contactGetter(${this.id}) rejected: %s`, e)
throw e
})
}
dumpRaw() {
console.error('======= dump raw contact =======')
Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
......@@ -65,10 +68,6 @@ class Contact {
Object.keys(this.obj).forEach(k => console.error(`${k}: ${this.obj[k]}`))
}
toString() {
return `Contact(${this.id})`
}
get(prop) { return this.obj[prop] }
send(message) {
......
......@@ -19,13 +19,15 @@ class Group {
}
}
toString() { return this.obj.name ? this.obj.name : this.id }
toString() {
var name = this.obj.name ? `[${this.obj.name}]${this.id}` : this.id
return `Group(${name})`
}
ready(contactGetter) {
log.silly('Group', `ready(${contactGetter})`)
if (this.obj.id) {
return Promise.resolve(this)
}
if (this.obj.id) { return Promise.resolve(this) }
contactGetter = contactGetter || Group.puppet.getContact.bind(Group.puppet)
return contactGetter(this.id)
.then(data => {
......@@ -39,6 +41,8 @@ class Group {
})
}
name() { return this.obj.name }
parse(rawObj) {
return !rawObj ? {} : {
id: rawObj.UserName
......
......@@ -15,18 +15,11 @@ class Message {
constructor(rawObj) {
Message.counter++
this.logToFile(JSON.stringify(rawObj))
this.rawObj = rawObj = rawObj || {}
this.obj = this.parse(rawObj)
this.id = this.obj.id
}
logToFile(data) {
require('fs').appendFile('message.log', data + '\n\n#############################\n\n', err => {
if (err) { log.error('Message', 'logToFile: ' + err) }
})
}
// Transform rawObj to local m
parse(rawObj) {
return {
......@@ -52,9 +45,9 @@ class Message {
return s
}
getSenderString() {
const name = this.obj.from.get('name')
const name = this.obj.from.get('remark') || this.obj.from.get('name')
const group = this.obj.group
return '<' + name + (group ? `@[${group}]` : '') + '>'
return '<' + name + (group ? `@${group}` : '') + '>'
}
getContentString() {
let content = this.unescapeHtml(this.stripHtml(this.obj.content))
......@@ -74,20 +67,19 @@ class Message {
from() { return this.obj.from }
to() { return this.obj.to }
content() { return this.obj.content }
group() { return this.obj.group }
ready() {
return this.obj.from.ready() // Contact from
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)
.catch(e => { // REJECT
log.error('Message', 'ready() rejected:' + e)
.then(r => this) // ready return this for chain
.catch(e => { // REJECTED
log.error('Message', 'ready() rejected: %s', e)
throw new Error(e)
})
}
group() { return !!(this.obj.group) }
get(prop) {
if (!prop || !(prop in this.obj)) {
const s = '[' + Object.keys(this.obj).join(',') + ']'
......
......@@ -32,7 +32,50 @@ class Bridge {
getLoginQrImgUrl() { return this.proxyWechaty('getLoginQrImgUrl') }
getUserName() { return this.proxyWechaty('getUserName') }
getContact(id) { return this.proxyWechaty('getContact', id) }
getContact(id) {
return this.waitData(r => {
return this.proxyWechaty('getContact', id)
}, 3000)
}
/**
* Call a function repeatly untill it return a value
*
* @param <function> pfunc
* @param <number> timeout
*
*/
waitData(pfunc, timeout) {
log.silly('Bridge', 'waitData()')
const waitTime = 50
let totalTime = 0
return new Promise((resolve, reject) => {
function retry() {
log.silly('Bridge', 'retry()@waitData()')
try {
pfunc().then(data => {
if (data) {
log.silly('Bridge', `waitData(${totalTime}/${timeout}) succ`)
return resolve(data)
} else if (totalTime > timeout) {
log.silly('Bridge', `waitData(${totalTime}/${timeout}) timeout`)
return resolve()
} else {
log.silly('Bridge', `waitData(${totalTime}/${timeout}) retry`)
totalTime += waitTime
return setTimeout(retry, waitTime)
}
throw new Error('should not run to here')
})
} catch (e) {
log.silly('Bridge', `waitData(${totalTime}/${timeout}) exception: %s`, e)
return reject(e)
}
}
return retry()
})
}
send(toUserName, content) { return this.proxyWechaty('send', toUserName, content) }
getInjectio() {
......@@ -54,10 +97,11 @@ class Bridge {
})
.then(r => {
if (true===r) { log.verbose('Bridge', 'Wechaty.init() return: ' + r) }
else { throw new Error('Wechaty.init() return not true') }
else { throw new Error('Wechaty.init() return not true' + r) }
return r
})
} catch (e) {
log.error('Bridge', 'inject() exception: %s', e)
return Promise.reject('inject exception: ' + e)
}
throw new Error('should not run to here')
......
......@@ -46,7 +46,10 @@ class Browser {
initDriver() {
log.verbose('Browser', 'initDriver()')
if (this.head) {
this.driver = new WebDriver.Builder().forBrowser('chrome').build()
this.driver = new WebDriver.Builder()
.setAlertBehavior('ignore')
.forBrowser('chrome')
.build()
} else {
this.driver = this.getPhantomJsDriver()
}
......@@ -59,6 +62,7 @@ class Browser {
const phantomjsExe = require('phantomjs-prebuilt').path
// const phantomjsExe = require('phantomjs2').path
const customPhantom = WebDriver.Capabilities.phantomjs()
.setAlertBehavior('ignore')
.set('phantomjs.binary.path', phantomjsExe)
.set('phantomjs.cli.args', [
'--ignore-ssl-errors=true' // this help socket.io connect with localhost
......@@ -95,10 +99,18 @@ class Browser {
execute(script, ...args) {
//log.verbose('Browser', `Browser.execute(${script})`)
if (!this.driver) {
throw new Error('driver not found')
// throw new Error('driver not found')
const errMsg = 'execute() called without driver'
log.verbose('Browser', errMsg)
return Promise.reject(errMsg)
}
// return promise
return this.driver.executeScript.apply(this.driver, arguments)
try {
return this.driver.executeScript.apply(this.driver, arguments)
} catch (e) {
log.error('Browser', e)
return Promise.reject(e)
}
}
}
......
......@@ -192,9 +192,11 @@ return (function(port) {
return chat.sendMessage(m)
}
function getContact(id) {
return Wechaty.glue.contactFactory
? Wechaty.glue.contactFactory.getContact(id)
: null
if (Wechaty.glue.contactFactory) {
return Wechaty.glue.contactFactory.getContact(id)
}
log('contactFactory not inited')
return null
}
function getUserName() {
return Wechaty.glue.accountFactory
......
......@@ -100,12 +100,10 @@ class Server extends EventEmitter {
this.emit('connection', client)
client.on('disconnect', e => {
log.verbose('Server', 'socket.io disconnected: ' + e)
/**
* 1. Browser reload / 2. Lost connection(Bad network)
*/
log.verbose('Server', 'socket.io disconnect: %s', e)
// 1. Browser reload / 2. Lost connection(Bad network)
this.socketClient = null
this.emit('disconnect', 'server re-emit from socketio')
this.emit('disconnect', e)
})
client.on('error', e => log.error('Server', 'socketio client error: %s', e))
......
......@@ -137,12 +137,21 @@ class PuppetWeb extends Puppet {
}
send(message) {
const userName = message.get('to').id
const userName = message.to().id
const content = message.content()
log.silly('PuppetWeb', `send(${userName}, ${content})`)
log.silly('PuppetWeb', `say(${userName}, ${content})`)
return this.bridge.send(userName, content)
}
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)
}
logout() { return this.bridge.logout() }
getContact(id) { return this.bridge.getContact(id) }
getLoginQrImgUrl() {
......@@ -153,14 +162,6 @@ class PuppetWeb extends Puppet {
return this.bridge.getLoginQrImgUrl()
}
debugLoop() {
// XXX
this.bridge.getLoginStatusCode().then((c) => {
log.verbose('PuppetWeb', `login status code: ${c}`)
setTimeout(this.debugLoop.bind(this), 3000)
})
}
/**
* Interface Methods
*/
......
......@@ -59,8 +59,9 @@ class Wechaty extends EventEmitter {
}
currentUser() { return this.puppet.currentUser() }
send(message) { return this.puppet.send(message) }
quit() { return this.puppet.quit() }
send(message) { return this.puppet.say(message) }
reply(message, reply) { return this.puppet.reply(message, reply) }
ding() {
// TODO: test through the server & browser
......
......@@ -5,7 +5,61 @@ const Browser = require('../src/puppet-web-browser')
const Bridge = require('../src/puppet-web-bridge')
const PORT = 58788
test('Bridge class smoking tests', function(t) {
const log = require('npmlog')
// log.level = 'silly'
test('Bridge functional testing', function(t) {
const browser = new Browser({port: PORT})
t.ok(browser, 'Browser instance created')
const b = new Bridge({browser: browser})
t.ok(b, 'Bridge instance creted')
co(function* () {
const EXPECTED_RETURN = 'Okey'
function delayedFactory(timeout) {
const startTime = Date.now()
return function() {
const nowTime = Date.now()
if (nowTime - startTime > timeout) {
return Promise.resolve(EXPECTED_RETURN)
}
return Promise.resolve()
}
}
yield b.waitData(delayedFactory(100), 10)
.then(r => {
t.notOk(r, 'waitData got none when wait 10ms')
})
.catch(e => {
t.fail(e)
})
yield b.waitData(delayedFactory(100), 100)
.then(r => {
t.equal(r, EXPECTED_RETURN, `waitData got "${EXPECTED_RETURN}" when wait 100ms`)
})
.catch(e => {
t.fail(e)
})
})
.catch(e => { // REJECTED
t.fail(e)
})
.then(r => { // FINALLY
browser.quit()
b.quit()
t.end()
})
.catch(e => { // EXCEPTION
t.fail(e)
})
})
test('Bridge smoke testing', function(t) {
const browser = new Browser({port: PORT})
t.ok(browser, 'Browser instance created')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册