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

runable v0.0.2

上级 2b0e3b8c
......@@ -5,8 +5,8 @@
"main": "index.js",
"scripts": {
"test": "tape tests/*.js",
"build": "src -d lib",
"lint": "eslint lib"
"build": "src -d src",
"lint": "eslint src"
},
"repository": {
"type": "git",
......
......@@ -2,8 +2,10 @@
class Contact {
constructor(id) {
this.id = id
}
toString() {
return `Class Contact({id:${this.id})`
}
getId() { return this.id }
......
/* jshint node:true, unused:true */
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
//const EventEmitter = require('events')
const Contact = require('./contact')
const Group = require('./group')
class Message {
constructor(rawObj) {
this.rawObj = rawObj
this.rawObj = rawObj = rawObj || {}
// Transform rawObj to local m
this.m = {
id: rawObj.MsgId
, 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
, status: rawObj.Status
, digest: rawObj.MMDigest
, to: rawObj.MMPeerUserName
, actual_content: rawObj.MMActualContent
, group: rawObj.MMIsChatRoom ? rawObj.FromUserName : null
, 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) {
if (!prop || !(prop in this.m)) {
const s = '[' + Object.keys(this.m).join(',') + ']'
......@@ -28,13 +41,17 @@ class Message {
return this.m[prop]
}
set(prop, value) {
this.m[prop] = value
}
dump() {
console.log('======= dump message =======')
Object.keys(this.m).forEach(k => console.log(`${k}: ${this.m[k]}`))
console.error('======= dump message =======')
Object.keys(this.m).forEach(k => console.error(`${k}: ${this.m[k]}`))
}
dumpRaw() {
console.log('======= dump raw message =======')
Object.keys(this.rawObj).forEach(k => console.log(`${k}: ${this.rawObj[k]}`))
console.error('======= dump raw message =======')
Object.keys(this.rawObj).forEach(k => console.error(`${k}: ${this.rawObj[k]}`))
}
static find(selector, option) {
......
/****************************************
*
* Class Browser
*
header cookie
BaseRequest
Uin
Sid
Skey
DeviceId
***************************************/
const fs = require('fs')
const path = require('path')
const WebDriver = require('selenium-webdriver')
class Browser {
constructor(options) {
options = options || {}
this.browser = options.browser || 'phantomjs'
this.port = options.port || 8788 // 'W' 'X' Ascii Code
}
toString() { return `Class Wechaty.Puppet.Browser(${this.browser}, ${this.port})` }
init() {
return this.open()
.then(this.inject.bind(this))
}
open() {
const WX_URL = 'https://wx.qq.com'
console.error(`browser init ${this.browser}:${this.port}`)
this.driver = new WebDriver.Builder().forBrowser(this.browser).build()
return this.driver.get(WX_URL)
}
getInjectio() {
return fs.readFileSync(
path.join(path.dirname(__filename), 'puppet-web-injectio.js')
, 'utf8'
)
}
inject() {
const injectio = this.getInjectio()
console.error('injecting')
return this.execute(injectio, this.port)
.then(() => {
console.error('injected / call Wechaty.init()')
return this.execute('return Wechaty.init()')
}).then((data) => {
console.error('Wechaty.init() return: ' + data)
return new Promise((resolve, reject) => resolve(data))
})
}
quit() {
console.error('Browser.quit')
if (!this.driver) {
console.error('no need to quite because no driver')
return new Promise((resolve, reject) => resolve('no driver'))
}
console.error('Browser.driver.quit')
this.execute('return (typeof Wechaty)!=="undefined" && Wechaty.quit()').then(() => {
this.driver.quit(true)
delete this.driver
return new Promise((resolve, reject) => resolve())
})
}
execute(script, ...args) {
// console.error(`Browser.execute(${script})`)
if (!this.driver)
throw new Error('driver not found')
// a promise
return this.driver.executeScript.apply(this.driver, arguments)
}
}
module.exports = Browser
/****************************************
*
* Class Browser
*
header cookie
BaseRequest
Uin
Sid
Skey
DeviceId
***************************************/
const fs = require('fs')
const path = require('path')
const WebDriver = require('selenium-webdriver')
const log = require('npmlog')
//log.disableColor()
class Browser {
constructor(options) {
options = options || {}
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() {
return this.open()
.then(this.inject.bind(this))
}
open() {
const WX_URL = 'https://wx.qq.com'
log.verbose('Browser', `init ${this.browser}:${this.port}`)
this.driver = new WebDriver.Builder().forBrowser(this.browser).build()
/*
this.driver = new WebDriver.Builder()//.forBrowser(this.browser).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()
*/
return this.driver.get(WX_URL)
}
getInjectio() {
return fs.readFileSync(
path.join(path.dirname(__filename), 'puppet-web-injectio.js')
, 'utf8'
)
}
inject() {
const injectio = this.getInjectio()
log.verbose('Browser', 'injecting')
return this.execute(injectio, this.port)
.then(() => {
log.verbose('Browser', 'injected / try Wechaty.init()')
return this.execute('return (typeof Wechaty)==="undefined" ? false : Wechaty.init()')
}).then((data) => {
log.verbose('Browser', 'Wechaty.init() return: ' + data)
return new Promise((resolve, reject) => resolve(data))
})
}
quit() {
log.verbose('Browser', 'Browser.quit')
if (!this.driver) {
log.verbose('Browser', 'no need to quite because no driver')
return new Promise((resolve, reject) => resolve('no driver'))
}
log.verbose('Browser', 'Browser.driver.quit')
return this.execute('return (typeof Wechaty)!=="undefined" && Wechaty.quit()').then(() => {
this.driver.quit()
this.driver = null
return new Promise((resolve, reject) => resolve())
})
}
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 @@
408: 未确认
*/
if (typeof Wechaty!=='undefined') return 'Wechaty already injected?';
;return (function (port) {
port = port || 8788
......@@ -38,7 +40,7 @@
}
var Wechaty = {
glue: {} // see 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 }
......@@ -52,7 +54,7 @@
, init: init
, send: send
, clog: clog // Console log
, slog: slog // Socket IO log
, slog: slog // log throw Socket IO
, ding: ding
, quit: quit
}
......@@ -110,22 +112,21 @@
function slog(data) { return Wechaty.socket && Wechaty.socket.emit('log', data) }
function ding() { return 'dong' }
function send(ToUserName, Content) {
var c = Wechaty.glue.chatFactory
var m = c.createMessage({
var chat = Wechaty.glue.chatFactory
var conf = Wechaty.glue.confFactory
var m = chat.createMessage({
ToUserName: ToUserName
, Content: Content
, MsgType: conf.MSGTYPE_TEXT
})
c.appendMessage(m)
return c.sendMessage(m)
chat.appendMessage(m)
return chat.sendMessage(m)
}
function hookMessage() {
var rootScope = Wechaty.glue.rootScope
rootScope.$on("message:add:success", function (event, data) {
Wechaty.socket.emit('message', data)
.catch(function (e) {
clog('socket.emit(message, data) fail:')
clog(e)
})
if (Wechaty.socket) Wechaty.socket.emit('message', data);
else clog('Wechaty.socket not ready');
})
}
function hookUnload() {
......@@ -151,28 +152,22 @@
// Wechaty global variable: socket
var socket = Wechaty.socket = io.connect('https://127.0.0.1:' + port)
socket.on('connect', function() {
clog('on connect entried')
// new message
Wechaty.glue.rootScope.$on("message:add:success", function (event, data) {
socket.emit('message', data)
})
// 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)
// })
// 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')
})
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
......
const Browser = require('./puppet-web-browser')
const co = require('co')
/****************************************
*
* Class Server
*
***************************************/
const fs = require('fs')
const io = require('socket.io')
const path = require('path')
const https = require('https')
const bodyParser = require('body-parser')
const Express = require('express')
const EventEmitter = require('events')
class Server extends EventEmitter {
constructor(port) {
super()
this.port = port || 8788 // W(87) X(88), ascii char code ;-]
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)
this.socketio = this.createSocketIo(this.server)
this.browser = this.createBrowser()
return new Promise((resolve, reject) => {
this.browser.init()
.then(() => {
console.error('browser init finished with port: ' + this.port + '.')
resolve() // after init success
})
})
}
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.
*/
this.on('unload', () => {
console.error('server received unload event')
this.browser.inject()
.then(() => console.error('re-injected'))
})
return b
}
/*
* Ssl Key & Cert files.
* No need to re-config & Hardcoded here,
* because there will only be visit from 127.0.0.1
*/
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
// openssl rsa -in key.pem -out newkey.pem && mv newkey.pem key.pem
return {
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-----
`
, 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-----
`
}
}
/**
*
* Https Server
*
*/
createHttpsServer(express) {
const key = this.getSsl().key
const cert = this.getSsl().cert
return https.createServer({
key: key
, cert: cert
}, express).listen(this.port, () => {
console.error(`createHttpsServer listening on port ${this.port}!`)
})
}
/**
*
* Express Middleware
*
*/
createExpress() {
const app = new Express()
app.use(bodyParser.json())
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")
next()
})
app.get('/ding', function (req, res) {
console.error(new Date() + ' GET /ding')
res.send('dong')
})
// app.post('/', function (req, res) {
// console.log('post ' + new Date())
// console.log(req.body)
// res.send(req.body)
// })
return app
}
/**
*
* Socket IO
*
*/
createSocketIo(server) {
const socketServer = io.listen(server, {
log: true
})
socketServer.sockets.on('connection', (s) => {
console.log('socket.on connection entried')
// save to instance: socketClient
this.socketClient = s
s.on('disconnect', function() {
console.error('socket.io disconnected')
/**
* Possible conditions:
* 1. Browser reload
* 2. Lost connection(Bad network
* 3.
*/
this.socketClient = null
})
// Events from Wechaty@Broswer --to--> Server
const events = [
'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
const Browser = require('./puppet-web-browser')
/****************************************
*
* Class Server
*
***************************************/
const fs = require('fs')
const io = require('socket.io')
const path = require('path')
const https = require('https')
const bodyParser = require('body-parser')
const Express = require('express')
const EventEmitter = require('events')
const log = require('npmlog')
//const co = require('co')
class Server extends EventEmitter {
constructor(options) {
super()
options = options || {}
this.port = options.port || 8788 // W(87) X(88), ascii char code ;-]
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)
this.socketio = this.createSocketIo(this.server)
this.browser = this.createBrowser()
return new Promise((resolve, reject) => {
this.browser.init()
.then(() => {
log.verbose('Server',`browser init finished with port: ${this.port}`)
resolve() // after init success
})
})
}
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.
*/
this.on('unload', () => {
log.verbose('Server', 'server received unload event')
this.browser.inject()
.then(() => log.verbose('Server', 're-injected'))
})
return b
}
/**
*
* Https Server
*
*/
createHttpsServer(express) {
return https.createServer({
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}!`)
})
}
/**
*
* Express Middleware
*
*/
createExpress() {
const app = new Express()
app.use(bodyParser.json())
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")
next()
})
app.get('/ding', function (req, res) {
log.verbose('Server', '%s GET /ding', new Date())
res.end('dong')
})
return app
}
/**
*
* Socket IO
*
*/
createSocketIo(server) {
const socketServer = io.listen(server, {
log: true
})
socketServer.sockets.on('connection', (s) => {
log.verbose('Server', 'socket.on connection entried')
// save to instance: socketClient
this.socketClient = s
s.on('disconnect', function() {
log.verbose('Server', 'socket.io disconnected')
/**
* Possible conditions:
* 1. Browser reload
* 2. Lost connection(Bad network
* 3.
*/
this.socketClient = null
})
// Events from Wechaty@Broswer --to--> Server
;[
'message'
, 'login'
, 'logout'
, 'unload'
].map(e => {
s.on(e, data => {
log.verbose('Server', `recv event[${e}] from browser`)
this.emit(e, data)
})
})
s.on('error', e => log.error('Server', 'socket error: %s', e))
/**
* 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, ...args) {
//const args = Array.prototype.slice.call(arguments, 1)
const argsJson = JSON.stringify(args)
const wechatyScript = `return (Wechaty && Wechaty.${wechatyFunc}.apply(undefined, JSON.parse('${argsJson}')))`
log.verbose('Server', 'proxyWechaty: ' + wechatyScript)
return this.browserExecute(wechatyScript)
}
}
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.
*
* Licenst: MIT
* https://github.com/zixia/wechaty-lib
* Licenst: ISC
* https://github.com/zixia/wechaty
*
*/
......@@ -17,43 +17,59 @@
*
***************************************/
const Puppet = require('./puppet')
const Message = require('./message')
const WebServer = require('./puppet-web-server')
class PuppetWeb extends Puppet {
constructor(port) {
constructor(options) {
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() {
this.server = new WebServer(this.port)
this.server = new WebServer({port: this.port})
const EVENTS_IN = [
'message'
, 'login'
;[ // events. ';' for seprate from the last code line
'login'
, 'logout'
]
EVENTS_IN.map( event =>
].map( event =>
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) {
if (!this.browser) throw new Error('browser not exist!');
const ToUserName = message.get('to')
const Content = message.get('content')
const toContact = message.get('to')
const content = message.get('content')
const script = `return Wechaty.send('${ToUserName}', '${Content}')`
return this.browser.execte(script)
return this.server.proxyWechaty('send', toContact.getId(), content)
}
logout() {
if (!this.browser) throw new Error('browser not exist!');
return this.browser.execte('return Wechaty.logout()')
return this.server.proxyWechaty('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 {
* Public Methods
*
*/
getLoginQrImgUrl() { return this.server.Wechaty_getLoginQrImgUrl() }
getLoginStatusCode() { return this.server.Wechaty_getLoginStatusCode() }
getLoginQrImgUrl() {
return this.server.proxyWechaty('getLoginQrImgUrl')
}
getLoginStatusCode() {
return this.server.proxyWechaty('getLoginStatusCode')
}
}
module.exports = PuppetWeb
/**
* Wechat for Bot. and for human who can talk with bot/robot
*
* Interface for puppet
*
*/
* Wechat for Bot. and for human who can talk with bot/robot
*
* Interface for puppet
*
* Licenst: ISC
* https://github.com/zixia/wechaty
*
*/
const EventEmitter = require('events')
......@@ -13,32 +16,33 @@ class Puppet extends EventEmitter {
}
/**
* Get current logined user
* @return <Contact>
*/
* Get current logined user
* @return <Contact>
*/
currentUser() { throw new Error('To Be Implemented') }
/**
* let puppet send message
*
* @param <Message> message - the message to be sent
* @return <Promise>
*/
* let puppet send message
*
* @param <Message> message - the message to be sent
* @return <Promise>
*/
send(message) { throw new Error('To Be Implemented') }
logout() { throw new Error('To Be Implementsd') }
alive() { throw new Error('To Be Implementsd') }
// () { throw new Error('To Be Implemented') }
/**
*
* Events .on(...)
*
* login -
* logout -
*
*
*/
*
* Events .on(...)
*
* login -
* logout -
*
*
*/
debug(cb) {
// List of all events
[ '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')
class Wechaty extends EventEmitter {
// cookie,Uin, Sid,SKey
constructor() {
constructor(puppet) {
super()
this.puppet = new Puppet.Web()
/*
this.contact = new Contact(this.puppet)
this.group = new Group(this.puppet)
this.message = new Message(this.puppet)
*/
puppet = puppet || 'web'
switch(puppet) {
case 'web':
this.puppet = new Puppet.Web()
break
default:
throw new Error('Puppet unknown: ' + puppet)
break
}
this.puppet.on('message', (e) => {
this.emit('message', e)
......
......@@ -10,8 +10,8 @@ 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') , 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()
})
......
......@@ -4,7 +4,7 @@ const Browser = require('../src/puppet-web-browser')
test('Browser class smoking tests', function (t) {
//t.plan(5)
const PORT = 58788
const b = new Browser('chrome', PORT)
const b = new Browser({browser: 'chrome', port: PORT})
t.ok(b, 'Browser instance created')
b.open()
......
const https = require('https')
const test = require('tape')
const log = require('npmlog')
const test = require('tape')
const Server = require('../src/puppet-web-server')
test('Server basic tests', function (t) {
//t.plan(9)
const PORT = 58788
const s = new Server(PORT)
const s = new Server({port: PORT})
t.equal(typeof s , 'object', 'Server instance created')
const express = s.createExpress()
t.equal(typeof express, 'function', 'create express')
delete express
const server = s.createHttpsServer(express, PORT)
const server = s.createHttpsServer(express)
t.equal(typeof server, 'object', 'create server')
server.on('close', () => t.ok(true, 'HttpsServer quited'))
server.close(() => t.ok(true, 'HttpsServer closed'))
......@@ -33,9 +33,9 @@ test('Server basic tests', function (t) {
s.quit() + t.end()
})
test('Server smoking tests', function (t) {
test('Server smoke testing', function (t) {
const PORT = 58788
const server = new Server(PORT)
const server = new Server({port: PORT})
server.init()
.then(() => {
......@@ -59,6 +59,7 @@ test('Server smoking tests', function (t) {
t.equal(retBrowser, 'dong', 'ding browser got dong')
t.equal(retProxy, 'dong', 'ding proxy got dong')
// XXX sometimes object? 201605
t.equal(typeof retStatus, 'number', 'status is number')
t.end() + server.quit()
......@@ -74,7 +75,7 @@ test('Server smoking tests', function (t) {
return new Promise((resolve, reject) => {
https.get(options, res => {
res.on('data', chunk => {
console.log('https on data got: ' + chunk.toString())
log.info('TestingServer', 'https on data got: ' + chunk.toString())
resolve(chunk.toString())
})
}).on('error', e => reject('https get error:' + e))
......@@ -89,20 +90,24 @@ test('Server smoking tests', function (t) {
setTimeout(testDing, waitTime)
function testDing() {
//log.info('TestingServer', server.socketio)
if (!server.socketClient) {
totalTime += waitTime
if (totalTime > maxTime)
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)
return
}
//log.info('TestingServer', server.socketClient)
server.socketClient.on ('dong', data => {
console.log('socket on dong got: ' + data)
log.info('TestingServer', 'socket on dong got: ' + data)
resolve(data)
})
server.socketClient.emit('ding')
server.socketClient.emit('ding')
server.socketClient.emit('ding')
}
})
}
......@@ -116,7 +121,7 @@ test('Server smoking tests', function (t) {
}
function getLoginStatusCode() {
return server.Wechaty_getLoginStatusCode()
return server.proxyWechaty('getLoginStatusCode')
}
}).catch((e) => {
......
......@@ -9,8 +9,13 @@ const WebBrowser = require('../src/puppet-web-browser')
test('WebDriver smoke testing', function(t) {
const driver = new WebDriver.Builder()
//.withCapabilities(WebDriver.Capabilities.chrome()).build()
.withCapabilities(WebDriver.Capabilities.phantomjs()).build()
.withCapabilities(WebDriver.Capabilities.chrome()).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')
// 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.
先完成此消息的编辑!
想要评论请 注册