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