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

runable v0.0.2

上级 2b0e3b8c
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "tape tests/*.js", "test": "tape tests/*.js",
"build": "src -d lib", "build": "src -d src",
"lint": "eslint lib" "lint": "eslint src"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
class Contact { class Contact {
constructor(id) { constructor(id) {
this.id = id this.id = id
}
toString() {
return `Class Contact({id:${this.id})`
} }
getId() { return this.id } getId() { return this.id }
......
/* jshint node:true, unused:true */
class Group { class Group {
constructor() { constructor(id) {
this.id = id
} }
find() { toString() { return `Class Group({id=${this.id}})` }
getId() { return this.id }
static find() {
} }
findAll() { static findAll() {
} }
} }
module.exports = Group module.exports = Group
//const EventEmitter = require('events') //const EventEmitter = require('events')
const Contact = require('./contact')
const Group = require('./group')
class Message { class Message {
constructor(rawObj) { constructor(rawObj) {
this.rawObj = rawObj this.rawObj = rawObj = rawObj || {}
// Transform rawObj to local m // Transform rawObj to local m
this.m = { this.m = {
id: rawObj.MsgId id: rawObj.MsgId
, type: rawObj.MsgType , type: rawObj.MsgType
, from: rawObj.MMActualSender , from: new Contact(rawObj.MMActualSender)
, to: new Contact(rawObj.MMPeerUserName)
, group: rawObj.MMIsChatRoom ? new Group(rawObj.FromUserName) : null
, content: rawObj.MMActualContent , content: rawObj.MMActualContent
, status: rawObj.Status , status: rawObj.Status
, digest: rawObj.MMDigest , digest: rawObj.MMDigest
, to: rawObj.MMPeerUserName
, actual_content: rawObj.MMActualContent , actual_content: rawObj.MMActualContent
, group: rawObj.MMIsChatRoom ? rawObj.FromUserName : null
, date: new Date(rawObj.MMDisplayTime*1000) , date: new Date(rawObj.MMDisplayTime*1000)
} }
} }
toString() {
const id = this.m.id
// Contact
const from = this.m.from.getId()
const to = this.m.to.getId()
//return `Class Message({id:${id}, from:${from}, to:${to})`
const content = this.m.content
if (content.length > 20) content = content.substring(0,17) + '...';
return `Class Message("${content}")`
}
get(prop) { get(prop) {
if (!prop || !(prop in this.m)) { if (!prop || !(prop in this.m)) {
const s = '[' + Object.keys(this.m).join(',') + ']' const s = '[' + Object.keys(this.m).join(',') + ']'
...@@ -28,13 +41,17 @@ class Message { ...@@ -28,13 +41,17 @@ class Message {
return this.m[prop] return this.m[prop]
} }
set(prop, value) {
this.m[prop] = value
}
dump() { dump() {
console.log('======= dump message =======') console.error('======= dump message =======')
Object.keys(this.m).forEach(k => console.log(`${k}: ${this.m[k]}`)) Object.keys(this.m).forEach(k => console.error(`${k}: ${this.m[k]}`))
} }
dumpRaw() { dumpRaw() {
console.log('======= dump raw message =======') console.error('======= dump raw message =======')
Object.keys(this.rawObj).forEach(k => console.log(`${k}: ${this.rawObj[k]}`)) Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
} }
static find(selector, option) { static find(selector, option) {
......
/**************************************** /****************************************
* *
* Class Browser * Class Browser
* *
header cookie header cookie
BaseRequest BaseRequest
Uin Uin
Sid Sid
Skey Skey
DeviceId DeviceId
***************************************/ ***************************************/
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const WebDriver = require('selenium-webdriver') const WebDriver = require('selenium-webdriver')
const log = require('npmlog')
class Browser { //log.disableColor()
constructor(options) {
options = options || {} class Browser {
constructor(options) {
this.browser = options.browser || 'phantomjs' options = options || {}
this.port = options.port || 8788 // 'W' 'X' Ascii Code
} this.browser = options.browser || 'chrome' //'phantomjs'
this.port = options.port || 8788 // 'W' 'X' Ascii Code
toString() { return `Class Wechaty.Puppet.Browser(${this.browser}, ${this.port})` } }
init() { toString() { return `Class Wechaty.Puppet.Browser(${this.browser}, ${this.port})` }
return this.open()
.then(this.inject.bind(this)) init() {
} return this.open()
.then(this.inject.bind(this))
open() { }
const WX_URL = 'https://wx.qq.com'
open() {
console.error(`browser init ${this.browser}:${this.port}`) const WX_URL = 'https://wx.qq.com'
this.driver = new WebDriver.Builder().forBrowser(this.browser).build()
log.verbose('Browser', `init ${this.browser}:${this.port}`)
return this.driver.get(WX_URL) this.driver = new WebDriver.Builder().forBrowser(this.browser).build()
}
/*
getInjectio() { this.driver = new WebDriver.Builder()//.forBrowser(this.browser).build()
return fs.readFileSync( .withCapabilities(
path.join(path.dirname(__filename), 'puppet-web-injectio.js') WebDriver.Capabilities.phantomjs()
, 'utf8' .set('phantomjs.binary.path', 'D:\\cygwin64\\home\\zixia\\git\\wechaty\\node_modules\\phantomjs-prebuilt\\lib\\phantom\\bin\\phantomjs.exe')
) ).build()
} */
inject() {
const injectio = this.getInjectio() return this.driver.get(WX_URL)
console.error('injecting') }
return this.execute(injectio, this.port)
.then(() => { getInjectio() {
console.error('injected / call Wechaty.init()') return fs.readFileSync(
return this.execute('return Wechaty.init()') path.join(path.dirname(__filename), 'puppet-web-injectio.js')
}).then((data) => { , 'utf8'
console.error('Wechaty.init() return: ' + data) )
return new Promise((resolve, reject) => resolve(data)) }
}) inject() {
} const injectio = this.getInjectio()
log.verbose('Browser', 'injecting')
quit() { return this.execute(injectio, this.port)
console.error('Browser.quit') .then(() => {
if (!this.driver) { log.verbose('Browser', 'injected / try Wechaty.init()')
console.error('no need to quite because no driver') return this.execute('return (typeof Wechaty)==="undefined" ? false : Wechaty.init()')
return new Promise((resolve, reject) => resolve('no driver')) }).then((data) => {
} log.verbose('Browser', 'Wechaty.init() return: ' + data)
console.error('Browser.driver.quit') return new Promise((resolve, reject) => resolve(data))
this.execute('return (typeof Wechaty)!=="undefined" && Wechaty.quit()').then(() => { })
this.driver.quit(true) }
delete this.driver
return new Promise((resolve, reject) => resolve()) quit() {
}) log.verbose('Browser', 'Browser.quit')
} if (!this.driver) {
log.verbose('Browser', 'no need to quite because no driver')
execute(script, ...args) { return new Promise((resolve, reject) => resolve('no driver'))
// console.error(`Browser.execute(${script})`) }
if (!this.driver) log.verbose('Browser', 'Browser.driver.quit')
throw new Error('driver not found') return this.execute('return (typeof Wechaty)!=="undefined" && Wechaty.quit()').then(() => {
// a promise this.driver.quit()
return this.driver.executeScript.apply(this.driver, arguments) this.driver = null
} return new Promise((resolve, reject) => resolve())
} })
}
module.exports = Browser
execute(script, ...args) {
//log.verbose('Browser', `Browser.execute(${script})`)
if (!this.driver)
throw new Error('driver not found')
// return promise
return this.driver.executeScript.apply(this.driver, arguments)
}
}
module.exports = Browser
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
408: 未确认 408: 未确认
*/ */
if (typeof Wechaty!=='undefined') return 'Wechaty already injected?';
;return (function (port) { ;return (function (port) {
port = port || 8788 port = port || 8788
...@@ -38,7 +40,7 @@ ...@@ -38,7 +40,7 @@
} }
var Wechaty = { var Wechaty = {
glue: {} // see glueAngular() function glue: {} // will be initialized by glueAngular() function
// glue funcs // glue funcs
, getLoginStatusCode: function () { return Wechaty.glue.loginScope.code } , getLoginStatusCode: function () { return Wechaty.glue.loginScope.code }
, getLoginQrImgUrl: function () { return Wechaty.glue.loginScope.qrcodeUrl } , getLoginQrImgUrl: function () { return Wechaty.glue.loginScope.qrcodeUrl }
...@@ -52,7 +54,7 @@ ...@@ -52,7 +54,7 @@
, init: init , init: init
, send: send , send: send
, clog: clog // Console log , clog: clog // Console log
, slog: slog // Socket IO log , slog: slog // log throw Socket IO
, ding: ding , ding: ding
, quit: quit , quit: quit
} }
...@@ -110,22 +112,21 @@ ...@@ -110,22 +112,21 @@
function slog(data) { return Wechaty.socket && Wechaty.socket.emit('log', data) } function slog(data) { return Wechaty.socket && Wechaty.socket.emit('log', data) }
function ding() { return 'dong' } function ding() { return 'dong' }
function send(ToUserName, Content) { function send(ToUserName, Content) {
var c = Wechaty.glue.chatFactory var chat = Wechaty.glue.chatFactory
var m = c.createMessage({ var conf = Wechaty.glue.confFactory
var m = chat.createMessage({
ToUserName: ToUserName ToUserName: ToUserName
, Content: Content , Content: Content
, MsgType: conf.MSGTYPE_TEXT
}) })
c.appendMessage(m) chat.appendMessage(m)
return c.sendMessage(m) return chat.sendMessage(m)
} }
function hookMessage() { function hookMessage() {
var rootScope = Wechaty.glue.rootScope var rootScope = Wechaty.glue.rootScope
rootScope.$on("message:add:success", function (event, data) { rootScope.$on("message:add:success", function (event, data) {
Wechaty.socket.emit('message', data) if (Wechaty.socket) Wechaty.socket.emit('message', data);
.catch(function (e) { else clog('Wechaty.socket not ready');
clog('socket.emit(message, data) fail:')
clog(e)
})
}) })
} }
function hookUnload() { function hookUnload() {
...@@ -151,28 +152,22 @@ ...@@ -151,28 +152,22 @@
// Wechaty global variable: socket // Wechaty global variable: socket
var socket = Wechaty.socket = io.connect('https://127.0.0.1:' + port) var socket = Wechaty.socket = io.connect('https://127.0.0.1:' + port)
socket.on('connect', function() { // ding -> dong. for test & live check purpose
clog('on connect entried') // ping/pong are reserved by socket.io https://github.com/socketio/socket.io/issues/2414
// new message socket.on('ding', function (e) {
Wechaty.glue.rootScope.$on("message:add:success", function (event, data) { clog('received socket io event: ding. emit dong...')
socket.emit('message', data) socket.emit('dong', 'dong')
})
// ding -> dong. for test & live check purpose
// ping/pong are reserved by socket.io https://github.com/socketio/socket.io/issues/2414
socket.on('ding', function (e) {
clog('received socket io event: ding. emit dong...')
socket.emit('dong', 'dong')
})
// re-connect XXX will socketio library auto re-connect by itself???
// socket.on('disconnect', function(e) {
// clog('event: socket disconnect')
// // Reconnect...
// setTimeout(function () {
// clog('starting initSocket after disconnect')
// initSocket()
// }, 1000)
// })
}) })
socket.on('connect' , function(e) { clog('connected to server:' + e) })
socket.on('disconnect', function(e) { clog('socket disconnect:' + e) })
// // Reconnect...
// setTimeout(function () {
// clog('starting initSocket after disconnect')
// initSocket()
// }, 1000)
// })
} }
window.Wechaty = Wechaty window.Wechaty = Wechaty
......
const Browser = require('./puppet-web-browser') const Browser = require('./puppet-web-browser')
const co = require('co')
/****************************************
/**************************************** *
* * Class Server
* Class Server *
* ***************************************/
***************************************/
const fs = require('fs')
const fs = require('fs') const io = require('socket.io')
const io = require('socket.io') const path = require('path')
const path = require('path') const https = require('https')
const https = require('https') const bodyParser = require('body-parser')
const bodyParser = require('body-parser')
const Express = require('express')
const Express = require('express') const EventEmitter = require('events')
const EventEmitter = require('events') const log = require('npmlog')
//const co = require('co')
class Server extends EventEmitter {
constructor(port) { class Server extends EventEmitter {
super() constructor(options) {
super()
this.port = port || 8788 // W(87) X(88), ascii char code ;-] options = options || {}
this.logined = false this.port = options.port || 8788 // W(87) X(88), ascii char code ;-]
this.on('login' , () => this.logined = true ) this.logined = false
this.on('logout', () => this.logined = false )
this.on('login' , () => this.logined = true )
} this.on('logout', () => this.logined = false )
init() { }
this.express = this.createExpress()
this.server = this.createHttpsServer(this.express, this.port) init() {
this.socketio = this.createSocketIo(this.server) this.express = this.createExpress()
this.server = this.createHttpsServer(this.express, this.port)
this.browser = this.createBrowser() this.socketio = this.createSocketIo(this.server)
return new Promise((resolve, reject) => { this.browser = this.createBrowser()
this.browser.init()
.then(() => { return new Promise((resolve, reject) => {
console.error('browser init finished with port: ' + this.port + '.') this.browser.init()
resolve() // after init success .then(() => {
}) log.verbose('Server',`browser init finished with port: ${this.port}`)
}) resolve() // after init success
} })
})
createBrowser() { }
const b = new Browser({port: this.port})
createBrowser() {
/** const b = new Browser({port: this.port})
* `unload` event is sent from js@browser to webserver via socketio
* after received `unload`, we re-inject the Wechaty js code into browser. /**
*/ * `unload` event is sent from js@browser to webserver via socketio
this.on('unload', () => { * after received `unload`, we re-inject the Wechaty js code into browser.
console.error('server received unload event') */
this.browser.inject() this.on('unload', () => {
.then(() => console.error('re-injected')) log.verbose('Server', 'server received unload event')
}) this.browser.inject()
.then(() => log.verbose('Server', 're-injected'))
return b })
}
return b
/* }
* Ssl Key & Cert files.
* No need to re-config & Hardcoded here, /**
* because there will only be visit from 127.0.0.1 *
*/ * Https Server
getSsl() { *
// http://blog.mgechev.com/2014/02/19/create-https-tls-ssl-application-with-express-nodejs/ */
// openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 createHttpsServer(express) {
// openssl rsa -in key.pem -out newkey.pem && mv newkey.pem key.pem return https.createServer({
return { key : require('./ssl-key-cert').key
key: ` , cert: require('./ssl-key-cert').cert
-----BEGIN RSA PRIVATE KEY----- }, express).listen(this.port, () => {
MIIEowIBAAKCAQEAt1c10tCbJG5ydWPjBV5c4gA3f1/8ubhNnYj98dtFPR8a4VPk log.verbose('Server', `createHttpsServer listening on port ${this.port}!`)
ORCyst157tLq5uPgmlZLedAm08VFiDUwn8iGQI/RegQSuRjxaS2MccfP9jpzDazy })
eMBi0mLg25z/4v9y/8N9nLSqbHrPrye+hzpSkSkyQ/zf/P85ZCdTGwCnFX6WlBQX }
I/3o5wWWRv4DaZTaLhChHjAa6+HJdYFvDvI6QUxggj3Vq64HsJv2xGvG7pZWjxXp
FS0Mg8MQbHME1J92OwPNaqsNUY3JfPkaeYmfQi5Qy53ULGLxVgV0eyIFf6s4NSCr /**
FI4PjJXyBWYCAlxVIUm1WdylIAl4MVvGADA1mQIDAQABAoIBAEOFUsU5HmnkYzLo *
fotTnVF+UvIOH70mKy+BbETOREmmUvf5NWvuwmEtP+K8utYdxnIQpetOxX3ogRsQ * Express Middleware
u7+c0hSk4rjVFzAkB4R8yeR9ehFspUK8FvBxqfNhhv5aa8Ll4SxgirpTrxAUirgv *
IvQafp4HVgPD9ZnvROulr+2Z5+7596qif4F1HrrxN6tl+cGNZFIZ7vk7uGsF8k4G */
LQ8xik8QABcTE4pKpRtNlesRpojSGI8cnu/z8MgDIPMHu6wgdz+OR1rZwNMuREZi createExpress() {
zejf5gg82B1KxeFNEmtMI1GM+whKVkPBxwASJTOaiN2Oh7SdSO5SHxFv2bAxjJ6z const app = new Express()
SC4mwqECgYEA7YSIINzOTCkUGGcJrg14P0m1KxuoPFSoAXk61F6kwW1FQVPmfk4i
n/MO8+2/CSAZiFEFNTUvWj5xM955wDVgSY8Z7l/aYxn11gGzV0XypsK77edTRrfp app.use(bodyParser.json())
6AlvIepclSX5ocmhizHe4mrm8KaZ014qMtO3RUaoDA9h7zqQOK5OyxcCgYEAxZty app.use(function(req, res, next) {
Dy7IxOBk3QfGdVqto/oDX2fQ6PTAIYwuOAh6rrQDkSXShPWI3bUJegylQeVOYG40 res.header("Access-Control-Allow-Origin", "*")
3ti/fd/247OkFwPsPODNisWQTsdX5Kr4KWjmfTSpSDm1AkDvnuPk/tcyFhijxZ48 res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
6Q0ZL529oy7cwel3p3uzDIFbAMEdATKIRp9css8CgYAkJNfmUFOgaVvifsONVgVn next()
dBr6rWHDlIpgdwdJzAE8Yhl44ICh1dgVCRLMcfBxPg5EnTeyqh5DmF73qrJSWo0F })
hJ5IlRORoyCy6V1WOZG8aMPaZypYB6KzqcPcoGJoW/gJ87n+iZ9GS0hLdL7R2HGJ
fIhWJXNrKmgX1Iyf436gDwKBgQC57ekEICEIHZrJ3eb9xLRc9YD249fNWXzuE9fp app.get('/ding', function (req, res) {
IRFOEFLK36uVLvH4qb6g+AUGW5vDX+6fP5Ht/i1vUjey8B33qg275OhDN42busKF log.verbose('Server', '%s GET /ding', new Date())
NA6rAEHHk4Sc+jx8ZDGzFwgpgkWWS61EGu73vpQQVqegTOwoyltOCOh3bTy9Q661 res.end('dong')
xHyUQQKBgA1vFFy09wJmQNCoo2kLvghkyPHrBXQlzoRW3cafw+vwoYZFukxst0dd })
2mQ3CyRJ7buoDdj0cFVlScB7KSQtIvLMtAn6tkL6ecooep346OSoLlsQd/F5ODep
bBdRj0Orj7xQDeIicM7ASTYPAivh9NTu4yJL0r/YOX3OvXxaBSGf return app
-----END RSA PRIVATE KEY----- }
`
, cert: ` /**
-----BEGIN CERTIFICATE----- *
MIIDXTCCAkWgAwIBAgIJAKMz7h5gRwqgMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV * Socket IO
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX *
aWRnaXRzIFB0eSBMdGQwHhcNMTYwNTAxMTUyNzM5WhcNMTcwNTAxMTUyNzM5WjBF */
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 createSocketIo(server) {
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB const socketServer = io.listen(server, {
CgKCAQEAt1c10tCbJG5ydWPjBV5c4gA3f1/8ubhNnYj98dtFPR8a4VPkORCyst15 log: true
7tLq5uPgmlZLedAm08VFiDUwn8iGQI/RegQSuRjxaS2MccfP9jpzDazyeMBi0mLg })
25z/4v9y/8N9nLSqbHrPrye+hzpSkSkyQ/zf/P85ZCdTGwCnFX6WlBQXI/3o5wWW
Rv4DaZTaLhChHjAa6+HJdYFvDvI6QUxggj3Vq64HsJv2xGvG7pZWjxXpFS0Mg8MQ socketServer.sockets.on('connection', (s) => {
bHME1J92OwPNaqsNUY3JfPkaeYmfQi5Qy53ULGLxVgV0eyIFf6s4NSCrFI4PjJXy log.verbose('Server', 'socket.on connection entried')
BWYCAlxVIUm1WdylIAl4MVvGADA1mQIDAQABo1AwTjAdBgNVHQ4EFgQU/Sed0ljf // save to instance: socketClient
HEpQsmReiphJnSsPTFowHwYDVR0jBBgwFoAU/Sed0ljfHEpQsmReiphJnSsPTFow this.socketClient = s
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAXjqJfXSEwVktRmeB+tW0
F837NEfzyedP82gSCCC+pbs+4E6DbDANupxP8vqIfqTe03YScZHVR/ha/f/WPLpc s.on('disconnect', function() {
HvDuyOSXrms9x0OHxsH70Ajx5/JBWyBbtFdox6yCEoeydOXl+MQDXgnGGv8VFXdN log.verbose('Server', 'socket.io disconnected')
dd2RP6/Ovx88hYGWcwf4RekTrbsM40n7BkkCCEedZPy7ouRmAs2FXpd+cm3zD9jt /**
Bas7b0wEOA7H2HejkbFOUierE40Kzh72vDD7M6DqUZFSvClY0O0+EYefB5TiRsN0 * Possible conditions:
g+Xdc4Ag/St5eqgrp95KOlVeepSlb35LAD1Cc91LddTXCYS7+dc4ndQYpgrLU0ru * 1. Browser reload
Sw== * 2. Lost connection(Bad network
-----END CERTIFICATE----- * 3.
` */
} this.socketClient = null
} })
/**
* // Events from Wechaty@Broswer --to--> Server
* Https Server ;[
* 'message'
*/ , 'login'
createHttpsServer(express) { , 'logout'
const key = this.getSsl().key , 'unload'
const cert = this.getSsl().cert ].map(e => {
return https.createServer({ s.on(e, data => {
key: key log.verbose('Server', `recv event[${e}] from browser`)
, cert: cert this.emit(e, data)
}, express).listen(this.port, () => { })
console.error(`createHttpsServer listening on port ${this.port}!`) })
})
} s.on('error', e => log.error('Server', 'socket error: %s', e))
/**
/** * prevent lost event: buffer new event received when socket disconnected
* while (buff.length) {
* Express Middleware let e = buff.shift()
* socket.emit(e.event, e.data)
*/ }
createExpress() { */
const app = new Express() })
app.use(bodyParser.json()) return socketServer
app.use(function(req, res, next) { }
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept") isLogined() {
next() return this.logined
}) }
app.get('/ding', function (req, res) { quit() {
console.error(new Date() + ' GET /ding') if (this.browser) {
res.send('dong') this.browser.quit()
}) delete this.browser
}
// app.post('/', function (req, res) { if (this.socketServer) {
// console.log('post ' + new Date()) socketServer.httpsServer.close()
// console.log(req.body) socketServer.close()
// res.send(req.body) delete this.socketServer
// }) }
if (this.socketClient) {
return app this.socketClient.disconnect()
} delete this.socketClient
}
/** if (this.server) {
* this.server.close()
* Socket IO delete this.server
* }
*/ }
createSocketIo(server) {
const socketServer = io.listen(server, { /**
log: true *
}) * Proxy Call to Wechaty in Browser
*
socketServer.sockets.on('connection', (s) => { */
console.log('socket.on connection entried') browserExecute(script) {
// save to instance: socketClient if (!this.browser)
this.socketClient = s throw new Error('no browser!')
return this.browser.execute(script)
s.on('disconnect', function() { }
console.error('socket.io disconnected')
/** proxyWechaty(wechatyFunc, ...args) {
* Possible conditions: //const args = Array.prototype.slice.call(arguments, 1)
* 1. Browser reload const argsJson = JSON.stringify(args)
* 2. Lost connection(Bad network const wechatyScript = `return (Wechaty && Wechaty.${wechatyFunc}.apply(undefined, JSON.parse('${argsJson}')))`
* 3.
*/ log.verbose('Server', 'proxyWechaty: ' + wechatyScript)
this.socketClient = null return this.browserExecute(wechatyScript)
}) }
}
// Events from Wechaty@Broswer --to--> Server
const events = [ module.exports = Server
'message'
, 'login'
, 'logout'
, 'unload'
]
events.map(e => {
s.on(e, data => {
console.log(`recv event[${e}] from browser`)
this.emit(e, data)
})
})
/**
* prevent lost event: buffer new event received when socket disconnected
while (buff.length) {
let e = buff.shift()
socket.emit(e.event, e.data)
}
*/
})
return socketServer
}
isLogined() {
return this.logined
}
quit() {
if (this.browser) {
this.browser.quit()
delete this.browser
}
if (this.socketServer) {
socketServer.httpsServer.close()
socketServer.close()
delete this.socketServer
}
if (this.socketClient) {
this.socketClient.disconnect()
delete this.socketClient
}
if (this.server) {
this.server.close()
delete this.server
}
}
/**
*
* Proxy Call to Wechaty in Browser
*
*/
browserExecute(script) {
if (!this.browser)
throw new Error('no browser!')
return this.browser.execute(script)
}
proxyWechaty(wechatyFunc) {
const args = Array.prototype.slice.call(arguments, 1)
const argsJson = JSON.stringify(args)
const wechatyScript = `return (Wechaty && Wechaty.${wechatyFunc}.apply(undefined, JSON.parse('${argsJson}')))`
console.error('proxyWechaty: ' + wechatyScript)
return this.browserExecute(wechatyScript)
}
Wechaty_getLoginStatusCode() { return this.proxyWechaty('getLoginStatusCode') }
Wechaty_getLoginQrImgUrl() { return this.proxyWechaty('getLoginQrImgUrl') }
// Wechaty_CARPEDIEM() { return this.proxyWechaty('call') }
debugLoop() {
this.Wechaty_getLoginStatusCode().then((c) => {
console.error(`login status code: ${c}`)
setTimeout(this.debugLoop.bind(this), 3000)
})
}
}
module.exports = Server
/** /**
* *
* wechaty-lib: Wechat for Bot. and for human who can talk with bot/robot * wechaty: Wechat for Bot. and for human who talk to bot/robot
* *
* Web Soul of Puppet * Web Puppet
* use to control wechat web. * use to control wechat web.
* *
* Licenst: MIT * Licenst: ISC
* https://github.com/zixia/wechaty-lib * https://github.com/zixia/wechaty
* *
*/ */
...@@ -17,43 +17,59 @@ ...@@ -17,43 +17,59 @@
* *
***************************************/ ***************************************/
const Puppet = require('./puppet') const Puppet = require('./puppet')
const Message = require('./message')
const WebServer = require('./puppet-web-server') const WebServer = require('./puppet-web-server')
class PuppetWeb extends Puppet { class PuppetWeb extends Puppet {
constructor(port) { constructor(options) {
super() super()
this.port = port || 8788 // W(87) X(88), ascii char code ;-] options = options || {}
this.port = options.port || 8788 // W(87) X(88), ascii char code ;-]
} }
toString() { return `Class PuppetWeb({port:${this.port}})` }
init() { init() {
this.server = new WebServer(this.port) this.server = new WebServer({port: this.port})
const EVENTS_IN = [ ;[ // events. ';' for seprate from the last code line
'message' 'login'
, 'login'
, 'logout' , 'logout'
] ].map( event =>
EVENTS_IN.map( event =>
this.server.on(event, data => this.emit(event, data) ) this.server.on(event, data => this.emit(event, data) )
) )
return this.server.init(this.port) this.server.on('message', data => {
const m = new Message(data)
this.emit('message', m)
})
return this.server.init()
} }
send(message) { send(message) {
if (!this.browser) throw new Error('browser not exist!'); const toContact = message.get('to')
const content = message.get('content')
const ToUserName = message.get('to')
const Content = message.get('content')
const script = `return Wechaty.send('${ToUserName}', '${Content}')` return this.server.proxyWechaty('send', toContact.getId(), content)
return this.browser.execte(script)
} }
logout() { logout() {
if (!this.browser) throw new Error('browser not exist!'); return this.server.proxyWechaty('logout')
return this.browser.execte('return Wechaty.logout()') }
getLoginStatusCode() {
log.verbose('PuppetWeb', 'getLoginStatusCode')
return this.server.proxyWechaty('getLoginStatusCode')
}
getLoginQrImgUrl() { return this.server.proxyWechaty('getLoginQrImgUrl') }
debugLoop() {
this.getLoginStatusCode().then((c) => {
log.verbose('PuppetWeb', `login status code: ${c}`)
setTimeout(this.debugLoop.bind(this), 3000)
})
} }
/* /*
...@@ -80,8 +96,12 @@ class PuppetWeb extends Puppet { ...@@ -80,8 +96,12 @@ class PuppetWeb extends Puppet {
* Public Methods * Public Methods
* *
*/ */
getLoginQrImgUrl() { return this.server.Wechaty_getLoginQrImgUrl() } getLoginQrImgUrl() {
getLoginStatusCode() { return this.server.Wechaty_getLoginStatusCode() } return this.server.proxyWechaty('getLoginQrImgUrl')
}
getLoginStatusCode() {
return this.server.proxyWechaty('getLoginStatusCode')
}
} }
module.exports = PuppetWeb module.exports = PuppetWeb
/** /**
* Wechat for Bot. and for human who can talk with bot/robot * Wechat for Bot. and for human who can talk with bot/robot
* *
* Interface for puppet * Interface for puppet
* *
*/ * Licenst: ISC
* https://github.com/zixia/wechaty
*
*/
const EventEmitter = require('events') const EventEmitter = require('events')
...@@ -13,32 +16,33 @@ class Puppet extends EventEmitter { ...@@ -13,32 +16,33 @@ class Puppet extends EventEmitter {
} }
/** /**
* Get current logined user * Get current logined user
* @return <Contact> * @return <Contact>
*/ */
currentUser() { throw new Error('To Be Implemented') } currentUser() { throw new Error('To Be Implemented') }
/** /**
* let puppet send message * let puppet send message
* *
* @param <Message> message - the message to be sent * @param <Message> message - the message to be sent
* @return <Promise> * @return <Promise>
*/ */
send(message) { throw new Error('To Be Implemented') } send(message) { throw new Error('To Be Implemented') }
logout() { throw new Error('To Be Implementsd') } logout() { throw new Error('To Be Implementsd') }
alive() { throw new Error('To Be Implementsd') } alive() { throw new Error('To Be Implementsd') }
// () { throw new Error('To Be Implemented') } // () { throw new Error('To Be Implemented') }
/** /**
* *
* Events .on(...) * Events .on(...)
* *
* login - * login -
* logout - * logout -
* *
* *
*/ */
debug(cb) { debug(cb) {
// List of all events // List of all events
[ 'message' // event data should carry a instance of Message [ 'message' // event data should carry a instance of Message
......
/**
* Ssl Key & Cert files.
* No need to re-config & Hardcoded here,
* because there will only be visit from 127.0.0.1
*
* http://blog.mgechev.com/2014/02/19/create-https-tls-ssl-application-with-express-nodejs/
* openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
* openssl rsa -in key.pem -out newkey.pem && mv newkey.pem key.pem
*/
const key = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAt1c10tCbJG5ydWPjBV5c4gA3f1/8ubhNnYj98dtFPR8a4VPk
ORCyst157tLq5uPgmlZLedAm08VFiDUwn8iGQI/RegQSuRjxaS2MccfP9jpzDazy
eMBi0mLg25z/4v9y/8N9nLSqbHrPrye+hzpSkSkyQ/zf/P85ZCdTGwCnFX6WlBQX
I/3o5wWWRv4DaZTaLhChHjAa6+HJdYFvDvI6QUxggj3Vq64HsJv2xGvG7pZWjxXp
FS0Mg8MQbHME1J92OwPNaqsNUY3JfPkaeYmfQi5Qy53ULGLxVgV0eyIFf6s4NSCr
FI4PjJXyBWYCAlxVIUm1WdylIAl4MVvGADA1mQIDAQABAoIBAEOFUsU5HmnkYzLo
fotTnVF+UvIOH70mKy+BbETOREmmUvf5NWvuwmEtP+K8utYdxnIQpetOxX3ogRsQ
u7+c0hSk4rjVFzAkB4R8yeR9ehFspUK8FvBxqfNhhv5aa8Ll4SxgirpTrxAUirgv
IvQafp4HVgPD9ZnvROulr+2Z5+7596qif4F1HrrxN6tl+cGNZFIZ7vk7uGsF8k4G
LQ8xik8QABcTE4pKpRtNlesRpojSGI8cnu/z8MgDIPMHu6wgdz+OR1rZwNMuREZi
zejf5gg82B1KxeFNEmtMI1GM+whKVkPBxwASJTOaiN2Oh7SdSO5SHxFv2bAxjJ6z
SC4mwqECgYEA7YSIINzOTCkUGGcJrg14P0m1KxuoPFSoAXk61F6kwW1FQVPmfk4i
n/MO8+2/CSAZiFEFNTUvWj5xM955wDVgSY8Z7l/aYxn11gGzV0XypsK77edTRrfp
6AlvIepclSX5ocmhizHe4mrm8KaZ014qMtO3RUaoDA9h7zqQOK5OyxcCgYEAxZty
Dy7IxOBk3QfGdVqto/oDX2fQ6PTAIYwuOAh6rrQDkSXShPWI3bUJegylQeVOYG40
3ti/fd/247OkFwPsPODNisWQTsdX5Kr4KWjmfTSpSDm1AkDvnuPk/tcyFhijxZ48
6Q0ZL529oy7cwel3p3uzDIFbAMEdATKIRp9css8CgYAkJNfmUFOgaVvifsONVgVn
dBr6rWHDlIpgdwdJzAE8Yhl44ICh1dgVCRLMcfBxPg5EnTeyqh5DmF73qrJSWo0F
hJ5IlRORoyCy6V1WOZG8aMPaZypYB6KzqcPcoGJoW/gJ87n+iZ9GS0hLdL7R2HGJ
fIhWJXNrKmgX1Iyf436gDwKBgQC57ekEICEIHZrJ3eb9xLRc9YD249fNWXzuE9fp
IRFOEFLK36uVLvH4qb6g+AUGW5vDX+6fP5Ht/i1vUjey8B33qg275OhDN42busKF
NA6rAEHHk4Sc+jx8ZDGzFwgpgkWWS61EGu73vpQQVqegTOwoyltOCOh3bTy9Q661
xHyUQQKBgA1vFFy09wJmQNCoo2kLvghkyPHrBXQlzoRW3cafw+vwoYZFukxst0dd
2mQ3CyRJ7buoDdj0cFVlScB7KSQtIvLMtAn6tkL6ecooep346OSoLlsQd/F5ODep
bBdRj0Orj7xQDeIicM7ASTYPAivh9NTu4yJL0r/YOX3OvXxaBSGf
-----END RSA PRIVATE KEY-----
`
const cert = `
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKMz7h5gRwqgMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTYwNTAxMTUyNzM5WhcNMTcwNTAxMTUyNzM5WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAt1c10tCbJG5ydWPjBV5c4gA3f1/8ubhNnYj98dtFPR8a4VPkORCyst15
7tLq5uPgmlZLedAm08VFiDUwn8iGQI/RegQSuRjxaS2MccfP9jpzDazyeMBi0mLg
25z/4v9y/8N9nLSqbHrPrye+hzpSkSkyQ/zf/P85ZCdTGwCnFX6WlBQXI/3o5wWW
Rv4DaZTaLhChHjAa6+HJdYFvDvI6QUxggj3Vq64HsJv2xGvG7pZWjxXpFS0Mg8MQ
bHME1J92OwPNaqsNUY3JfPkaeYmfQi5Qy53ULGLxVgV0eyIFf6s4NSCrFI4PjJXy
BWYCAlxVIUm1WdylIAl4MVvGADA1mQIDAQABo1AwTjAdBgNVHQ4EFgQU/Sed0ljf
HEpQsmReiphJnSsPTFowHwYDVR0jBBgwFoAU/Sed0ljfHEpQsmReiphJnSsPTFow
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAXjqJfXSEwVktRmeB+tW0
F837NEfzyedP82gSCCC+pbs+4E6DbDANupxP8vqIfqTe03YScZHVR/ha/f/WPLpc
HvDuyOSXrms9x0OHxsH70Ajx5/JBWyBbtFdox6yCEoeydOXl+MQDXgnGGv8VFXdN
dd2RP6/Ovx88hYGWcwf4RekTrbsM40n7BkkCCEedZPy7ouRmAs2FXpd+cm3zD9jt
Bas7b0wEOA7H2HejkbFOUierE40Kzh72vDD7M6DqUZFSvClY0O0+EYefB5TiRsN0
g+Xdc4Ag/St5eqgrp95KOlVeepSlb35LAD1Cc91LddTXCYS7+dc4ndQYpgrLU0ru
Sw==
-----END CERTIFICATE-----
`
module.exports = {
cert: cert
, key: key
}
...@@ -11,14 +11,18 @@ const Group = require('./group') ...@@ -11,14 +11,18 @@ const Group = require('./group')
class Wechaty extends EventEmitter { class Wechaty extends EventEmitter {
// cookie,Uin, Sid,SKey // cookie,Uin, Sid,SKey
constructor() { constructor(puppet) {
super() super()
this.puppet = new Puppet.Web() puppet = puppet || 'web'
/*
this.contact = new Contact(this.puppet) switch(puppet) {
this.group = new Group(this.puppet) case 'web':
this.message = new Message(this.puppet) this.puppet = new Puppet.Web()
*/ break
default:
throw new Error('Puppet unknown: ' + puppet)
break
}
this.puppet.on('message', (e) => { this.puppet.on('message', (e) => {
this.emit('message', e) this.emit('message', e)
......
...@@ -10,8 +10,8 @@ test('Message constructor parser test', t => { ...@@ -10,8 +10,8 @@ test('Message constructor parser test', t => {
} }
const m = new Message(rawData) const m = new Message(rawData)
t.equal(m.get('id') , EXPECTED.id, 'id right') t.equal(m.get('id') , EXPECTED.id, 'id right')
t.equal(m.get('from') , EXPECTED.from, 'from right') t.equal(m.get('from').getId() , EXPECTED.from, 'from right')
t.end() t.end()
}) })
......
...@@ -4,7 +4,7 @@ const Browser = require('../src/puppet-web-browser') ...@@ -4,7 +4,7 @@ const Browser = require('../src/puppet-web-browser')
test('Browser class smoking tests', function (t) { test('Browser class smoking tests', function (t) {
//t.plan(5) //t.plan(5)
const PORT = 58788 const PORT = 58788
const b = new Browser('chrome', PORT) const b = new Browser({browser: 'chrome', port: PORT})
t.ok(b, 'Browser instance created') t.ok(b, 'Browser instance created')
b.open() b.open()
......
const https = require('https') const https = require('https')
const log = require('npmlog')
const test = require('tape') const test = require('tape')
const Server = require('../src/puppet-web-server') const Server = require('../src/puppet-web-server')
test('Server basic tests', function (t) { test('Server basic tests', function (t) {
//t.plan(9) //t.plan(9)
const PORT = 58788 const PORT = 58788
const s = new Server(PORT) const s = new Server({port: PORT})
t.equal(typeof s , 'object', 'Server instance created') t.equal(typeof s , 'object', 'Server instance created')
const express = s.createExpress() const express = s.createExpress()
t.equal(typeof express, 'function', 'create express') t.equal(typeof express, 'function', 'create express')
delete express delete express
const server = s.createHttpsServer(express, PORT) const server = s.createHttpsServer(express)
t.equal(typeof server, 'object', 'create server') t.equal(typeof server, 'object', 'create server')
server.on('close', () => t.ok(true, 'HttpsServer quited')) server.on('close', () => t.ok(true, 'HttpsServer quited'))
server.close(() => t.ok(true, 'HttpsServer closed')) server.close(() => t.ok(true, 'HttpsServer closed'))
...@@ -33,9 +33,9 @@ test('Server basic tests', function (t) { ...@@ -33,9 +33,9 @@ test('Server basic tests', function (t) {
s.quit() + t.end() s.quit() + t.end()
}) })
test('Server smoking tests', function (t) { test('Server smoke testing', function (t) {
const PORT = 58788 const PORT = 58788
const server = new Server(PORT) const server = new Server({port: PORT})
server.init() server.init()
.then(() => { .then(() => {
...@@ -59,6 +59,7 @@ test('Server smoking tests', function (t) { ...@@ -59,6 +59,7 @@ test('Server smoking tests', function (t) {
t.equal(retBrowser, 'dong', 'ding browser got dong') t.equal(retBrowser, 'dong', 'ding browser got dong')
t.equal(retProxy, 'dong', 'ding proxy got dong') t.equal(retProxy, 'dong', 'ding proxy got dong')
// XXX sometimes object? 201605
t.equal(typeof retStatus, 'number', 'status is number') t.equal(typeof retStatus, 'number', 'status is number')
t.end() + server.quit() t.end() + server.quit()
...@@ -74,7 +75,7 @@ test('Server smoking tests', function (t) { ...@@ -74,7 +75,7 @@ test('Server smoking tests', function (t) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
https.get(options, res => { https.get(options, res => {
res.on('data', chunk => { res.on('data', chunk => {
console.log('https on data got: ' + chunk.toString()) log.info('TestingServer', 'https on data got: ' + chunk.toString())
resolve(chunk.toString()) resolve(chunk.toString())
}) })
}).on('error', e => reject('https get error:' + e)) }).on('error', e => reject('https get error:' + e))
...@@ -89,20 +90,24 @@ test('Server smoking tests', function (t) { ...@@ -89,20 +90,24 @@ test('Server smoking tests', function (t) {
setTimeout(testDing, waitTime) setTimeout(testDing, waitTime)
function testDing() { function testDing() {
//log.info('TestingServer', server.socketio)
if (!server.socketClient) { if (!server.socketClient) {
totalTime += waitTime totalTime += waitTime
if (totalTime > maxTime) if (totalTime > maxTime)
return reject('timeout after ' + totalTime + 'ms'); return reject('timeout after ' + totalTime + 'ms');
console.error('waiting socketClient to connect for ' + totalTime + '/' + maxTime + ' ms...') log.info('TestingServer', 'waiting socketClient to connect for ' + totalTime + '/' + maxTime + ' ms...')
setTimeout(testDing, waitTime) setTimeout(testDing, waitTime)
return return
} }
//log.info('TestingServer', server.socketClient)
server.socketClient.on ('dong', data => { server.socketClient.on ('dong', data => {
console.log('socket on dong got: ' + data) log.info('TestingServer', 'socket on dong got: ' + data)
resolve(data) resolve(data)
}) })
server.socketClient.emit('ding') server.socketClient.emit('ding')
server.socketClient.emit('ding')
server.socketClient.emit('ding')
} }
}) })
} }
...@@ -116,7 +121,7 @@ test('Server smoking tests', function (t) { ...@@ -116,7 +121,7 @@ test('Server smoking tests', function (t) {
} }
function getLoginStatusCode() { function getLoginStatusCode() {
return server.Wechaty_getLoginStatusCode() return server.proxyWechaty('getLoginStatusCode')
} }
}).catch((e) => { }).catch((e) => {
......
...@@ -9,8 +9,13 @@ const WebBrowser = require('../src/puppet-web-browser') ...@@ -9,8 +9,13 @@ const WebBrowser = require('../src/puppet-web-browser')
test('WebDriver smoke testing', function(t) { test('WebDriver smoke testing', function(t) {
const driver = new WebDriver.Builder() const driver = new WebDriver.Builder()
//.withCapabilities(WebDriver.Capabilities.chrome()).build() .withCapabilities(WebDriver.Capabilities.chrome()).build()
.withCapabilities(WebDriver.Capabilities.phantomjs()).build() /*
.withCapabilities(
WebDriver.Capabilities.phantomjs()
//.set('phantomjs.binary.path', 'D:\\cygwin64\\home\\zixia\\git\\wechaty\\node_modules\\phantomjs-prebuilt\\lib\\phantom\\bin\\phantomjs.exe')
).build()
*/
// .set('webdriver.load.strategy', 'unstable') // .set('webdriver.load.strategy', 'unstable')
// https://stackoverflow.com/questions/37071807/how-to-executescript-before-page-load-by-webdriver-in-selenium // https://stackoverflow.com/questions/37071807/how-to-executescript-before-page-load-by-webdriver-in-selenium
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册