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

add group & contact class, bug fix, and bot enhancements

上级 af1e46f1
const Wechaty = require('../src/wechaty')
const log = require('npmlog')
//log.level = 'verbose'
log.level = 'verbose'
log.level = 'silly'
const welcome = `
| __ __ _ _
......@@ -26,14 +27,19 @@ Please wait... I'm trying to login in...
`
console.log(welcome)
const bot = new Wechaty()
bot.init()
.then(login)
bot.on('message', (m) => {
m
.ready()
.then(msg => log.warn('Bot', 'recv: %s', msg))
.catch(e => log.err('Bot', 'ready: ' + e))
log.info('Bot', 'recv: %s', m)
if (/^ding$/.test(m.get('content'))) {
if (/^ding|ping$/.test(m.get('content'))) {
const r = new Wechaty.Message()
r.set('to', m.get('from'))
r.set('content', 'dong')
......@@ -54,4 +60,3 @@ function login() {
bot.on('login' , () => npm.info('Bot', 'logined'))
bot.on('logout' , () => npm.info('Bot', 'logouted'))
const Wechaty = require('../lib/wechaty')
const EventEmitter = require('events')
var wechaty = new Wechaty()
if (!wechaty.currentUser()) {
// login
}
wechaty.on('login', (e) => {
console.log(e)
})
wechaty.on('logout', (e) => {
console.log(e)
})
wechaty.on('message', (e) => {
console.log(e)
})
wechaty.contact.findAll( (e) => {
console.log(e)
})
wechaty.contact.find( {id: 43143}, (e) => {
console.log(e)
})
wechaty.group.findAll( (e) => {
console.log(e)
})
wechaty.group.find( {id: 43143}, (e) => {
console.log(e)
})
wechaty.message.findAll( {id:333}, (e) => {
console.log(e)
})
wechaty.message.find( {id:333}, (e) => {
console.log(e)
})
......@@ -6,23 +6,48 @@
* https://github.com/zixia/wechaty
*
*/
const log = require('npmlog')
class Contact {
constructor(id) {
if (!Contact.puppet) throw new Error('no puppet attached to Contact');
log.silly('Contact', `constructor(${id})`)
this.id = id
this.obj = {}
Contact.puppet.getContact(id)
this.loading = Contact.puppet.getContact(id)
.then(data => {
log.silly('Contact', `Contact.puppet.getContact(${id}) resolved`)
this.rawObj = data
this.obj = this.parse(this.rawObj)
this.obj = this.parse(data)
return new Promise(r => r())
}).catch(e => {
log.error('Contact', `Contact.puppet.getContact(${id}) rejected: ` + e)
throw new Error('getContact: ' + e)
})
}
ready() {
const timeout = 1 * 1000 // 1 seconds
const sleepTime = 500 // 100 ms
let spentTime = 0
return new Promise((resolve, reject) => {
return readyChecker.apply(this)
function readyChecker() {
log.verbose('Contact', `readyChecker(${spentTime})`)
if (this.obj.id) return resolve(this);
spentTime += sleepTime
if (spentTime > timeout)
return reject('Contact.ready() timeout after ' + timeout + ' ms');
return setTimeout(readyChecker.bind(this), sleepTime)
}
})
}
parse(rawObj) {
return !rawObj ? {} : {
id: rawObj.UserName
......@@ -46,17 +71,16 @@ class Contact {
}
toString() {
return `Contact({id:${this.id})`
return `Contact(${this.id})`
}
getId() { return this.id }
get(prop) { return this.obj[prop] }
send(message) {
}
static find() {
......@@ -66,7 +90,8 @@ class Contact {
}
}
Contact.pool = {}
Contact.init = function () { Contact.pool = {} }
Contact.init()
Contact.load = function (id) {
if (id in Contact.pool) {
return Contact.pool[id]
......
......@@ -6,20 +6,101 @@
* https://github.com/zixia/wechaty
*
*/
const log = require('npmlog')
const Contact = require('./contact')
class Group {
constructor(id) {
this.id = id
if (!Group.puppet) throw new Error('no puppet attached to Group');
log.silly('Group', `constructor(${id})`)
this.id = id
this.obj = {}
this.loading = Group.puppet.getContact(id)
.then(data => {
log.silly('Group', `Group.puppet.getContact(${id}) resolved`)
this.rawObj = data
this.obj = this.parse(data)
}).catch(e => {
log.error('Group', `Group.puppet.getContact(${id}) rejected: ` + e)
throw new Error('getContact: ' + e)
})
}
toString() { return `Group({id=${this.id}})` }
toString() { return this.obj.name ? this.obj.name : this.id }
getId() { return this.id }
ready() {
const timeout = 1 * 1000 // 1 seconds
const sleepTime = 100 // 100 ms
let spentTime = 0
return new Promise((resolve, reject) => {
return readyChecker.apply(this)
function readyChecker() {
log.verbose('Group', `readyChecker(${spentTime})`)
if (this.obj.id) return resolve(this);
spentTime += sleepTime
if (spentTime > timeout)
return reject('Group.ready() timeout after ' + timeout + ' ms');
return setTimeout(readyChecker.bind(this), sleepTime)
}
})
}
parse(rawObj) {
return !rawObj ? {} : {
id: rawObj.UserName
, name: rawObj.NickName
, members: rawObj.MemberList.map(m => {
return {
contact: Contact.load(m.UserName)
, name: m.DisplayName
}
})
}
}
dumpRaw() {
console.error('======= dump raw group =======')
Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
}
dump() {
console.error('======= dump group =======')
Object.keys(this.obj).forEach(k => console.error(`${k}: ${this.obj[k]}`))
}
toString() {
return `Group(${this.id})`
}
getId() { return this.id }
get(prop) { return this.obj[prop] }
static find() {
}
static findAll() {
}
}
Group.init = function () { Group.pool = {} }
Group.init()
Group.load = function (id) {
if (id in Group.pool) {
return Group.pool[id]
}
return Group.pool[id] = new Group(id)
}
Group.attach = function (puppet) { Group.puppet = puppet }
module.exports = Group
......@@ -9,19 +9,22 @@
const Contact = require('./contact')
const Group = require('./group')
const log = require('npmlog')
class Message {
constructor(rawObj) {
this.rawObj = rawObj = rawObj || {}
Message.counter++
// Transform rawObj to local m
this.obj = {
id: rawObj.MsgId
, type: rawObj.MsgType
, from: Contact.load(rawObj.MMActualSender)
, to: Contact.load(rawObj.MMPeerUserName)
, group: rawObj.MMIsChatRoom ? new Group(rawObj.FromUserName) : null
, content: rawObj.MMActualContent
, to: Contact.load(rawObj.ToUserName)
, group: rawObj.MMIsChatRoom ? new Group(rawObj.FromUserName) : null // MMPeerUserName always eq FromUserName ?
, content: rawObj.MMActualContent // Content has @id prefix added by wx
, status: rawObj.Status
, digest: rawObj.MMDigest
......@@ -32,9 +35,20 @@ class Message {
toString() {
const name = this.obj.from.get('name')
const group = this.obj.group
let content = this.obj.content
if (content.length > 20) content = content.substring(0,17) + '...';
return `Message("${name}: ${content}")`
if (group) return `Message(${name}@${group}: ${content})`
else return `Message(${name}: ${content})`
}
ready() {
return new Promise((resolve, reject) => {
this.obj.from.ready() // Contact from
.then(r => this.obj.to.ready()) // Contact to
.then(r => resolve(this))
.catch(e => reject(e))
})
}
get(prop) {
......@@ -58,6 +72,8 @@ class Message {
Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
}
getCount() { return Message.counter }
static find(selector, option) {
return new Message({MsgId: '-1'})
}
......@@ -70,4 +86,6 @@ class Message {
}
}
Message.counter = 0
module.exports = Message
......@@ -73,7 +73,7 @@ class Server extends EventEmitter {
key : require('./ssl-key-cert').key
, cert: require('./ssl-key-cert').cert
}, express).listen(this.port, () => {
log.verbose('Server', `createHttpsServer listening on port ${this.port}!`)
log.verbose('Server', `createHttpsServer port ${this.port}`)
})
}
......@@ -111,7 +111,7 @@ class Server extends EventEmitter {
})
socketServer.sockets.on('connection', (s) => {
log.verbose('Server', 'socket.on connection entried')
log.verbose('Server', 'got connection from browser')
// save to instance: socketClient
this.socketClient = s
......
......@@ -9,6 +9,7 @@
*/
const EventEmitter = require('events')
const log = require('npmlog')
class Puppet extends EventEmitter {
constructor() {
......@@ -32,8 +33,9 @@ class Puppet extends EventEmitter {
logout() { throw new Error('To Be Implementsd') }
alive() { throw new Error('To Be Implementsd') }
getContact() { // for unit testing
return new Promise((rs, rj) => rs({}))
getContact(id) { // for unit testing
log.silly('Puppet', `Interface method getContact(${id})`)
return Promise.resolve({UserName: 'WeChaty', NickName: 'Puppet'})
}
// () { throw new Error('To Be Implemented') }
......
......@@ -33,6 +33,7 @@ class Wechaty extends EventEmitter {
break
}
Contact.attach(this.puppet)
Group.attach(this.puppet)
this.puppet.on('message', (e) => {
this.emit('message', e)
......
const test = require('tape')
const Contact = require('../src/contact')
const Puppet = require('../src/puppet')
const log = require('npmlog')
log.level = 'verbose'
Contact.attach(new Puppet())
test('Contact smoke testing', t => {
const UserName = '@0bb3e4dd746fdbd4a80546aef66f4085'
const NickName = 'Nick Name Test'
// Mock
Contact.puppet.getContact = function (id) {
return new Promise((resolve,reject) => {
if (id!=UserName) return resolve({});
setTimeout(() => {
return resolve({
UserName: UserName
, NickName: NickName
})
}, 200)
})
}
const c = new Contact(UserName)
t.equal(c.getId(), UserName, 'id/UserName right')
c.ready()
.then(r => {
t.equal(c.get('id') , UserName, 'UserName set')
t.equal(c.get('name') , NickName, 'NickName set')
})
.catch(e => t.fail('ready() rejected: ' + e))
.then(t.end) // test end
})
const test = require('tape')
const Message = require('../src/message')
const Group = require('../src/group')
const Puppet = require('../src/puppet')
const log = require('npmlog')
log.level = 'silly'
log.enableColor()
Group.attach(new Puppet())
false && test('Group constructor parser test', t => {
const rawData = JSON.parse('{"MsgId":"1120003476579027592","FromUserName":"@@4aa0ae1e1ebc568b613fa43ce93b478df0339f73340d87083822c2016d2e53d9","ToUserName":"@94e4b0db79ccc844d7bb4a2b1efac3ff","MsgType":1,"Content":"@9ad4ba13fac52c55d323521b67f7cc39:<br/>[Strong]","Status":3,"ImgStatus":1,"CreateTime":1462889712,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"","NickName":"","QQNum":0,"Province":"","City":"","Content":"","Signature":"","Alias":"","Scene":0,"VerifyFlag":0,"AttrStatus":0,"Sex":0,"Ticket":"","OpCode":0},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":1120003476579027600,"MMPeerUserName":"@@4aa0ae1e1ebc568b613fa43ce93b478df0339f73340d87083822c2016d2e53d9","MMDigest":"感恩的心 <img class=\"emoji emoji1f42e\" text=\"_web\" src=\"/zh_CN/htmledition/v2/images/spacer.gif\" />:<img class=\"qqemoji qqemoji79\" text=\"[Strong]_web\" src=\"/zh_CN/htmledition/v2/images/spacer.gif\" />","MMIsSend":false,"MMIsChatRoom":true,"MMUnread":false,"LocalID":"1120003476579027592","ClientMsgId":"1120003476579027592","MMActualContent":"<img class=\"qqemoji qqemoji79\" text=\"[Strong]_web\" src=\"/zh_CN/htmledition/v2/images/spacer.gif\" />","MMActualSender":"@9ad4ba13fac52c55d323521b67f7cc39","MMDigestTime":"22:15","MMDisplayTime":1462889712,"MMTime":"22:15","_h":126,"_index":0,"_offsetTop":0,"$$hashKey":"0QK", "MemberList": [{"Uin":0,"UserName":"@94e4b0db79ccc844d7bb4a2b1efac3ff","NickName":"李卓桓","AttrStatus":37996631,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"北京阿布","KeyWord":"liz"},{"Uin":0,"UserName":"@34887973779b7dd827366a31772cd83df223e6f71d9a79e44fe619aafe2901a4","NickName":"Tiger","AttrStatus":4292711,"PYInitial":"","PYQuanPin":"","RemarkPYInitial":"","RemarkPYQuanPin":"","MemberStatus":0,"DisplayName":"DisplayNameTiger","KeyWord":"","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@34887973779b7dd827366a31772cd83df223e6f71d9a79e44fe619aafe2901a4&skey=@crypt_f9cec94b_8517b7f9fec85f5a78a804c4f45f5536&chatroomid=@7b3dcd218431d79045cda3493c3179ae"}]}')
const EXPECTED = {
id: '179242112323992762'
, from: '@0bb3e4dd746fdbd4a80546aef66f4085'
}
const g = new Group(rawData)
t.equal(m.get('id') , EXPECTED.id , 'id right')
t.equal(m.get('from').getId() , EXPECTED.from , 'from right')
t.end()
})
false && test('Message ready() promise testing', t => {
// must different with other rawData, because Contact class with load() will cache the result. or use Contact.resetPool()
const rawData = JSON.parse('{"RemarkPYQuanPin":"","RemarkPYInitial":"","PYInitial":"BJFRHXS","PYQuanPin":"beijingfeirenhuaxiangsan","Uin":0,"UserName":"@@4aa0ae1e1ebc568b613fa43ce93b478df0339f73340d87083822c2016d2e53d9","NickName":"北京飞人滑翔伞","HeadImgUrl":"/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=649595794&username=@@4aa0ae1e1ebc568b613fa43ce93b478df0339f73340d87083822c2016d2e53d9&skey=","ContactFlag":3,"MemberCount":111,"RemarkName":"","HideInputBarFlag":0,"Sex":0,"Signature":"","VerifyFlag":0,"OwnerUin":2354729644,"StarFriend":0,"AppAccountFlag":0,"Statues":0,"AttrStatus":0,"Province":"","City":"","Alias":"","SnsFlag":0,"UniFriend":0,"DisplayName":"","ChatRoomId":0,"KeyWord":"","EncryChatRoomId":"@7b3dcd218431d79045cda3493c3179ae","MMOrderSymbol":"BEIJINGFEIRENHUAXIANGSAN","MMInChatroom":true,"_index":90,"_h":50,"_offsetTop":4448,"$$hashKey":"01J","MMFromBatchGet":true,"MMFromBatchget":true,"MMBatchgetMember":true,"MMCanCreateChatroom":true}')
const expectedFromUserName = '@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823'
const expectedToUserName = '@b58f91e0c5c9e841e290d862ddb63c14'
const expectedFromNickName = 'From Nick Name Test'
const expectedToNickName = 'To Nick Name Test'
const expectedMsgId = '3009511950433684462'
Contact.init()
// Mock
Contact.puppet.getContact = function (id) {
log.silly('MessageTesting', `mocked getContact(${id})`)
return new Promise((resolve,reject) => {
let obj = {}
switch (id) {
case expectedFromUserName:
obj = {
UserName: expectedFromUserName
, NickName: expectedFromNickName
}
break
case expectedToUserName:
obj = {
UserName: expectedToUserName
, NickName: expectedToNickName
}
break
default:
log.error('MessageTesting', `mocked getContact(${id}) unknown`)
break
}
log.silly('MessageTesting', 'setTimeout mocked getContact')
setTimeout(r => {
log.silly('MessageTesting', 'mocked getContact resolved')
return resolve(obj)
}, 200)
})
}
const m = new Message(rawData)
t.equal(m.get('id'), expectedMsgId, 'id/MsgId right')
m.ready()
.then(r => {
/*
const fromC = m.get('from')
const toC = m.get('to')
fromC.dump()
toC.dump()
*/
t.equal(m.get('from').get('id') , expectedFromUserName, 'contact ready for FromUserName')
t.equal(m.get('from').get('name') , expectedFromNickName, 'contact ready for FromNickName')
t.equal(m.get('to').get('id') , expectedToUserName , 'contact ready for ToUserName')
t.equal(m.get('to').get('name') , expectedToNickName , 'contact ready for ToNickName')
})
.catch(e => t.fail('m.ready() rejected: ' + e))
.then(t.end) // test end
})
false && test('TBW: Message static method', t => {
Contact.attach(new Puppet())
const m = Message.find({
id: 'xxx'
}, {
limit: 1
})
t.ok(m.get('id'), 'Message.find')
const ms = Message.findAll({
from: 'yyy'
}, {
limit: 2
})
t.equal(ms.length, 2, 'Message.findAll with limit 2')
t.end()
})
......@@ -2,6 +2,9 @@ const test = require('tape')
const Message = require('../src/message')
const Contact = require('../src/contact')
const Puppet = require('../src/puppet')
const log = require('npmlog')
log.level = 'verbose'
log.enableColor()
Contact.attach(new Puppet())
......@@ -14,13 +17,78 @@ test('Message constructor parser test', t => {
}
const m = new Message(rawData)
t.equal(m.get('id') , EXPECTED.id, 'id right')
t.equal(m.get('from').getId() , EXPECTED.from, 'from right')
t.equal(m.get('id') , EXPECTED.id , 'id right')
t.equal(m.get('from').getId() , EXPECTED.from , 'from right')
t.end()
})
test('Message ready() promise testing', t => {
// must different with other rawData, because Contact class with load() will cache the result. or use Contact.resetPool()
const rawData = JSON.parse('{"MsgId":"3009511950433684462","FromUserName":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","ToUserName":"@b58f91e0c5c9e841e290d862ddb63c14","MsgType":1,"Content":"哈哈","Status":3,"ImgStatus":1,"CreateTime":1462887888,"VoiceLength":0,"PlayLength":0,"FileName":"","FileSize":"","MediaId":"","Url":"","AppMsgType":0,"StatusNotifyCode":0,"StatusNotifyUserName":"","RecommendInfo":{"UserName":"","NickName":"","QQNum":0,"Province":"","City":"","Content":"","Signature":"","Alias":"","Scene":0,"VerifyFlag":0,"AttrStatus":0,"Sex":0,"Ticket":"","OpCode":0},"ForwardFlag":0,"AppInfo":{"AppID":"","Type":0},"HasProductId":0,"Ticket":"","ImgHeight":0,"ImgWidth":0,"SubMsgType":0,"NewMsgId":3009511950433684500,"MMPeerUserName":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","MMDigest":"哈哈","MMIsSend":false,"MMIsChatRoom":false,"MMUnread":false,"LocalID":"3009511950433684462","ClientMsgId":"3009511950433684462","MMActualContent":"哈哈","MMActualSender":"@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823","MMDigestTime":"21:44","MMDisplayTime":1462887888,"MMTime":"21:44","_h":104,"_index":0,"_offsetTop":0,"$$hashKey":"098"}')
const expectedFromUserName = '@0748ee480711bf20af91c298a0d7dcc77c30a680c1004157386b81cf13474823'
const expectedToUserName = '@b58f91e0c5c9e841e290d862ddb63c14'
const expectedFromNickName = 'From Nick Name Test'
const expectedToNickName = 'To Nick Name Test'
const expectedMsgId = '3009511950433684462'
Contact.init()
// Mock
Contact.puppet.getContact = function (id) {
log.silly('MessageTesting', `mocked getContact(${id})`)
return new Promise((resolve,reject) => {
let obj = {}
switch (id) {
case expectedFromUserName:
obj = {
UserName: expectedFromUserName
, NickName: expectedFromNickName
}
break
case expectedToUserName:
obj = {
UserName: expectedToUserName
, NickName: expectedToNickName
}
break
default:
log.error('MessageTesting', `mocked getContact(${id}) unknown`)
break
}
log.silly('MessageTesting', 'setTimeout mocked getContact')
setTimeout(r => {
log.silly('MessageTesting', 'mocked getContact resolved')
return resolve(obj)
}, 200)
})
}
const m = new Message(rawData)
t.equal(m.get('id'), expectedMsgId, 'id/MsgId right')
m.ready()
.then(r => {
/*
const fromC = m.get('from')
const toC = m.get('to')
fromC.dump()
toC.dump()
*/
t.equal(m.get('from').get('id') , expectedFromUserName, 'contact ready for FromUserName')
t.equal(m.get('from').get('name') , expectedFromNickName, 'contact ready for FromNickName')
t.equal(m.get('to').get('id') , expectedToUserName , 'contact ready for ToUserName')
t.equal(m.get('to').get('name') , expectedToNickName , 'contact ready for ToNickName')
})
.catch(e => t.fail('m.ready() rejected: ' + e))
.then(t.end) // test end
})
test('TBW: Message static method', t => {
Contact.attach(new Puppet())
const m = Message.find({
id: 'xxx'
}, {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册