Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
.Veneno.
wechaty
提交
860e85ec
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,发现更多精彩内容 >>
提交
860e85ec
编写于
6月 02, 2018
作者:
Huan (李卓桓)
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Copy/Paste from Wechaty4u run-core.js (#69)
上级
0d89e41a
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
770 addition
and
142 deletion
+770
-142
src/puppet-config.ts
src/puppet-config.ts
+2
-2
src/puppet-mock/puppet-mock.ts
src/puppet-mock/puppet-mock.ts
+9
-0
src/puppet-puppeteer/event.ts
src/puppet-puppeteer/event.ts
+2
-1
src/puppet-puppeteer/firer.ts
src/puppet-puppeteer/firer.ts
+49
-49
src/puppet-puppeteer/puppet-puppeteer.ts
src/puppet-puppeteer/puppet-puppeteer.ts
+52
-1
src/puppet-wechat4u/README.md
src/puppet-wechat4u/README.md
+19
-0
src/puppet-wechat4u/index.ts
src/puppet-wechat4u/index.ts
+3
-3
src/puppet-wechat4u/puppet-wechat4u.ts
src/puppet-wechat4u/puppet-wechat4u.ts
+616
-83
src/puppet/puppet.spec.ts
src/puppet/puppet.spec.ts
+4
-1
src/puppet/puppet.ts
src/puppet/puppet.ts
+14
-2
未找到文件。
src/puppet-config.ts
浏览文件 @
860e85ec
import
{
PuppetPuppeteer
}
from
'
./puppet-puppeteer/
'
import
{
PuppetMock
}
from
'
./puppet-mock/
'
// import { PuppetPadchat } from './puppet-padchat'
//
import PuppetWechat4u from './puppet-wechat4u/'
import
PuppetWechat4u
from
'
./puppet-wechat4u/
'
/**
* Wechaty Official Puppet Plugins List
...
...
@@ -10,7 +10,7 @@ export const PUPPET_DICT = {
mock
:
PuppetMock
,
puppeteer
:
PuppetPuppeteer
,
// padchat: PuppetPadchat,
//
wechat4u: PuppetWechat4u,
wechat4u
:
PuppetWechat4u
,
}
export
type
PuppetName
=
keyof
typeof
PUPPET_DICT
...
...
src/puppet-mock/puppet-mock.ts
浏览文件 @
860e85ec
...
...
@@ -30,6 +30,8 @@ import {
ContactType
,
ContactPayload
,
FriendRequestPayload
,
RoomPayload
,
// RoomQueryFilter,
}
from
'
../puppet/
'
...
...
@@ -319,6 +321,13 @@ export class PuppetMock extends Puppet {
* FriendRequest
*
*/
public
async
friendRequestRawPayload
(
id
:
string
)
:
Promise
<
any
>
{
return
{
id
}
as
any
}
public
async
friendRequestRawPayloadParser
(
rawPayload
:
any
)
:
Promise
<
FriendRequestPayload
>
{
return
rawPayload
}
public
async
friendRequestSend
(
contactId
:
string
,
hello
:
string
,
...
...
src/puppet-puppeteer/event.ts
浏览文件 @
860e85ec
...
...
@@ -195,7 +195,8 @@ async function onMessage(
switch
(
rawPayload
.
MsgType
)
{
case
WebMessageType
.
VERIFYMSG
:
firer
.
checkFriendRequest
(
rawPayload
)
this
.
emit
(
'
friend
'
,
rawPayload
.
MsgId
)
// firer.checkFriendRequest(rawPayload)
break
case
WebMessageType
.
SYS
:
...
...
src/puppet-puppeteer/firer.ts
浏览文件 @
860e85ec
...
...
@@ -19,24 +19,24 @@
/* tslint:disable:no-var-requires */
// const retryPromise = require('retry-promise').default
import
*
as
cuid
from
'
cuid
'
//
import * as cuid from 'cuid'
import
{
log
,
}
from
'
../config
'
import
{
WebRecomendInfo
,
//
WebRecomendInfo,
WebMessageRawPayload
,
}
from
'
./web-schemas
'
import
PuppetPuppeteer
from
'
./puppet-puppeteer
'
import
{
// FriendRequestPayload,
FriendRequestType
,
FriendRequestPayloadReceive
,
FriendRequestPayloadConfirm
,
}
from
'
../puppet/
'
//
import {
//
// FriendRequestPayload,
//
FriendRequestType,
//
FriendRequestPayloadReceive,
//
FriendRequestPayloadConfirm,
//
} from '../puppet/'
const
REGEX_CONFIG
=
{
friendConfirm
:
[
...
...
@@ -101,37 +101,37 @@ export class Firer {
//
}
public
async
checkFriendRequest
(
rawPayload
:
WebMessageRawPayload
,
):
Promise
<
void
>
{
if
(
!
rawPayload
.
RecommendInfo
)
{
throw
new
Error
(
'
no RecommendInfo
'
)
}
const
recommendInfo
:
WebRecomendInfo
=
rawPayload
.
RecommendInfo
log
.
verbose
(
'
PuppetPuppeteerFirer
'
,
'
fireFriendRequest(%s)
'
,
recommendInfo
)
if
(
!
recommendInfo
)
{
throw
new
Error
(
'
no recommendInfo
'
)
}
const
contactId
=
recommendInfo
.
UserName
const
hello
=
recommendInfo
.
Content
const
ticket
=
recommendInfo
.
Ticket
const
type
=
FriendRequestType
.
Receive
const
id
=
cuid
()
const
payloadReceive
:
FriendRequestPayloadReceive
=
{
id
,
contactId
,
hello
,
ticket
,
type
,
}
this
.
puppet
.
cacheFriendRequestPayload
.
set
(
id
,
payloadReceive
)
this
.
puppet
.
emit
(
'
friend
'
,
id
)
}
//
public async checkFriendRequest(
//
rawPayload : WebMessageRawPayload,
//
): Promise<void> {
//
if (!rawPayload.RecommendInfo) {
//
throw new Error('no RecommendInfo')
//
}
//
const recommendInfo: WebRecomendInfo = rawPayload.RecommendInfo
//
log.verbose('PuppetPuppeteerFirer', 'fireFriendRequest(%s)', recommendInfo)
//
if (!recommendInfo) {
//
throw new Error('no recommendInfo')
//
}
//
const contactId = recommendInfo.UserName
//
const hello = recommendInfo.Content
//
const ticket = recommendInfo.Ticket
//
const type = FriendRequestType.Receive
//
const id = cuid()
//
const payloadReceive: FriendRequestPayloadReceive = {
//
id,
//
contactId,
//
hello,
//
ticket,
//
type,
//
}
//
this.puppet.cacheFriendRequestPayload.set(id, payloadReceive)
//
this.puppet.emit('friend', id)
//
}
public
async
checkFriendConfirm
(
rawPayload
:
WebMessageRawPayload
,
...
...
@@ -143,20 +143,20 @@ export class Firer {
return
}
const
contactId
=
rawPayload
.
FromUserName
const
type
=
FriendRequestType
.
Confirm
//
const contactId = rawPayload.FromUserName
//
const type = FriendRequestType.Confirm
const
id
=
cuid
()
//
const id = cuid()
const
payloadConfirm
:
FriendRequestPayloadConfirm
=
{
id
,
contactId
,
type
,
}
//
const payloadConfirm: FriendRequestPayloadConfirm = {
//
id,
//
contactId,
//
type,
//
}
this
.
puppet
.
cacheFriendRequestPayload
.
set
(
id
,
payloadConfirm
)
//
this.puppet.cacheFriendRequestPayload.set(id, payloadConfirm)
this
.
puppet
.
emit
(
'
friend
'
,
i
d
)
this
.
puppet
.
emit
(
'
friend
'
,
rawPayload
.
MsgI
d
)
}
public
async
checkRoomJoin
(
...
...
src/puppet-puppeteer/puppet-puppeteer.ts
浏览文件 @
860e85ec
...
...
@@ -62,6 +62,7 @@ import {
WebMessageRawPayload
,
WebMediaType
,
WebMessageType
,
WebRecomendInfo
,
// WebRoomRawMember,
WebRoomRawPayload
,
}
from
'
./web-schemas
'
...
...
@@ -71,6 +72,11 @@ import {
// ContactQueryFilter,
ContactType
,
FriendRequestPayload
,
FriendRequestPayloadReceive
,
FriendRequestPayloadConfirm
,
FriendRequestType
,
MessagePayload
,
MessageType
,
...
...
@@ -1110,6 +1116,51 @@ export class PuppetPuppeteer extends Puppet {
* FriendRequest
*
*/
public
async
friendRequestRawPayload
(
id
:
string
):
Promise
<
WebMessageRawPayload
>
{
log
.
warn
(
'
PuppetPuppeteer
'
,
'
friendRequestRawPayload(%s)
'
,
id
)
const
rawPayload
=
await
this
.
bridge
.
getMessage
(
id
)
if
(
!
rawPayload
)
{
throw
new
Error
(
'
no rawPayload
'
)
}
return
rawPayload
}
public
async
friendRequestRawPayloadParser
(
rawPayload
:
WebMessageRawPayload
):
Promise
<
FriendRequestPayload
>
{
log
.
warn
(
'
PuppetPuppeteer
'
,
'
friendRequestRawPayloadParser(%s)
'
,
rawPayload
)
switch
(
rawPayload
.
MsgType
)
{
case
WebMessageType
.
VERIFYMSG
:
if
(
!
rawPayload
.
RecommendInfo
)
{
throw
new
Error
(
'
no RecommendInfo
'
)
}
const
recommendInfo
:
WebRecomendInfo
=
rawPayload
.
RecommendInfo
if
(
!
recommendInfo
)
{
throw
new
Error
(
'
no recommendInfo
'
)
}
const
payloadReceive
:
FriendRequestPayloadReceive
=
{
id
:
rawPayload
.
MsgId
,
contactId
:
recommendInfo
.
UserName
,
hello
:
recommendInfo
.
Content
,
ticket
:
recommendInfo
.
Ticket
,
type
:
FriendRequestType
.
Receive
,
}
return
payloadReceive
case
WebMessageType
.
SYS
:
const
payloadConfirm
:
FriendRequestPayloadConfirm
=
{
id
:
rawPayload
.
MsgId
,
contactId
:
rawPayload
.
FromUserName
,
type
:
FriendRequestType
.
Confirm
,
}
return
payloadConfirm
default
:
throw
new
Error
(
'
not supported friend request message raw payload
'
)
}
}
public
async
friendRequestSend
(
contactId
:
string
,
hello
:
string
,
...
...
@@ -1306,7 +1357,7 @@ export class PuppetPuppeteer extends Puppet {
private
filename
(
rawPayload
:
WebMessageRawPayload
,
):
null
|
string
{
):
string
{
log
.
verbose
(
'
PuppetPuppeteer
'
,
'
filename()
'
)
let
filename
=
rawPayload
.
FileName
||
rawPayload
.
MediaId
||
rawPayload
.
MsgId
...
...
src/puppet-wechat4u/README.md
0 → 100644
浏览文件 @
860e85ec
# WECHATY-PUPPET-WECHAT4U
## NOTES
```
js
/**
* 发送撤回消息请求
*/
bot
.
sendMsg
(
'
测试撤回
'
,
ToUserName
)
.
then
(
res
=>
{
// 需要取得待撤回消息的MsgID
return
bot
.
revokeMsg
(
res
.
MsgID
,
ToUserName
)
})
.
catch
(
err
=>
{
console
.
log
(
err
)
})
```
src/puppet-wechat4u/index.ts
浏览文件 @
860e85ec
...
...
@@ -16,10 +16,10 @@
* limitations under the License.
*
*/
import
{
Puppet
Mock
}
from
'
./puppet-mock
'
import
{
Puppet
Wechat4u
}
from
'
./puppet-wechat4u
'
export
{
Puppet
Mock
,
Puppet
Wechat4u
,
}
export
default
Puppet
Mock
export
default
Puppet
Wechat4u
src/puppet-wechat4u/puppet-wechat4u.ts
浏览文件 @
860e85ec
...
...
@@ -16,7 +16,9 @@
* limitations under the License.
*
*/
import
*
as
path
from
'
path
'
// import * as path from 'path'
import
*
as
LRU
from
'
lru-cache
'
import
{
FileBox
,
...
...
@@ -29,10 +31,14 @@ import {
MessagePayload
,
// ContactQueryFilter,
ContactGender
,
//
ContactGender,
ContactType
,
ContactPayload
,
FriendRequestPayload
,
FriendRequestPayloadReceive
,
FriendRequestType
,
RoomPayload
,
// RoomQueryFilter,
}
from
'
../puppet/
'
...
...
@@ -46,39 +52,75 @@ import {
import
{
log
,
}
from
'
../config
'
import
{
Wechat4uBridge
}
from
'
./bridge
'
;
import
{
WebAppMsgType
,
WebContactRawPayload
,
// WebMessageMediaPayload,
WebMessageRawPayload
,
// WebMediaType,
WebMessageType
,
// WebRoomRawMember,
WebRecomendInfo
,
WebRoomRawPayload
,
}
from
'
../puppet-puppeteer/web-schemas
'
export
type
PuppetFoodType
=
'
scan
'
|
'
ding
'
export
type
ScanFoodType
=
'
scan
'
|
'
login
'
|
'
logout
'
export
interface
Wechat4uContactRawPayload
{
name
:
string
,
}
//
export interface Wechat4uContactRawPayload {
//
name : string,
//
}
export
interface
Wechat4u
MessageRawPayload
{
id
:
string
,
from
:
string
,
to
:
string
,
text
:
string
}
// export interface Web
MessageRawPayload {
//
id : string,
//
from : string,
//
to : string,
//
text : string
//
}
export
interface
Wechat4uRoomRawPayload
{
topic
:
string
,
memberList
:
string
[],
ownerId
:
string
,
}
//
export interface Wechat4uRoomRawPayload {
//
topic : string,
//
memberList : string[],
//
ownerId : string,
//
}
// MemoryCard Slot Name
const
SYNC_DATA_SLOT
=
'
wechat4u-sync-data
'
export
class
PuppetWechat4u
extends
Puppet
{
/**
* Wecaht4u
*
* Code from:
* https://github.com/nodeWechat/wechat4u/blob/46931e78bcb56899b8d2a42a37b919e7feaebbef/run-core.js
*
*/
private
wechat4u
:
any
private
scanQrCode
?:
string
public
readonly
cacheWechat4uMessageRawPayload
:
LRU
.
Cache
<
string
,
WebMessageRawPayload
>
public
readonly
cacheWechat4uFriendRequestRawPayload
:
LRU
.
Cache
<
string
,
WebMessageRawPayload
>
constructor
(
public
options
:
PuppetOptions
,
)
{
super
(
options
)
const
lruOptions
:
LRU
.
Options
=
{
max
:
1000
,
// length: function (n) { return n * 2},
dispose
:
function
(
key
:
string
,
val
:
Object
)
{
log
.
silly
(
'
Puppet
'
,
'
constructor() lruOptions.dispose(%s, %s)
'
,
key
,
JSON
.
stringify
(
val
))
},
maxAge
:
1000
*
60
*
60
,
}
this
.
cacheWechat4uMessageRawPayload
=
new
LRU
<
string
,
WebMessageRawPayload
>
(
lruOptions
)
this
.
cacheWechat4uFriendRequestRawPayload
=
new
LRU
<
string
,
WebMessageRawPayload
>
(
lruOptions
)
}
public
async
start
():
Promise
<
void
>
{
...
...
@@ -93,29 +135,99 @@ export class PuppetWechat4u extends Puppet {
this
.
wechat4u
=
new
Wechat4u
()
}
if
(
this
.
wechat4u
.
PROP
.
uin
)
{
// 存在登录数据时,可以随时调用restart进行重启
this
.
wechat4u
.
restart
()
}
else
{
this
.
wechat4u
.
start
()
}
this
.
initHookEvents
(
this
.
wechat4u
)
// await some tasks...
this
.
state
.
on
(
true
)
this
.
id
=
'
logined_user_id
'
// const user = this.Contact.load(this.id)
this
.
emit
(
'
login
'
,
this
.
id
)
const
MOCK_MSG_ID
=
'
mockid
'
this
.
cacheMessagePayload
.
set
(
MOCK_MSG_ID
,
{
id
:
MOCK_MSG_ID
,
type
:
MessageType
.
Text
,
text
:
'
mock text
'
,
timestamp
:
Date
.
now
(),
fromId
:
'
xxx
'
,
toId
:
'
xxx
'
,
}
private
initHookEvents
(
wechat4u
:
any
)
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
initHookEvents()
'
)
/**
* uuid事件,参数为uuid,根据uuid生成二维码
*/
this
.
wechat4u
.
on
(
'
uuid
'
,
(
uuid
:
string
)
=>
{
this
.
scanQrCode
=
'
https://login.weixin.qq.com/l/
'
+
uuid
this
.
emit
(
'
scan
'
,
this
.
scanQrCode
,
200
)
})
/**
* 登录用户头像事件,手机扫描后可以得到登录用户头像的Data URL
*/
wechat4u
.
on
(
'
user-avatar
'
,
(
avatarDataUrl
:
string
)
=>
{
this
.
emit
(
'
scan
'
,
this
.
scanQrCode
||
''
,
408
,
avatarDataUrl
)
})
/**
* 登录成功事件
*/
wechat4u
.
on
(
'
login
'
,
async
()
=>
{
// FIXME: where's the logined user id?
const
userId
=
this
.
wechat4u
.
user
.
UserName
if
(
!
userId
)
{
this
.
emit
(
'
error
'
,
'
login event can not found selfId
'
)
return
}
this
.
login
(
userId
)
// 保存数据,将数据序列化之后保存到任意位置
await
this
.
options
.
memory
.
set
(
SYNC_DATA_SLOT
,
wechat4u
.
botData
)
})
/**
* 登出成功事件
*/
wechat4u
.
on
(
'
logout
'
,
async
()
=>
{
this
.
logout
()
// 清除数据
await
this
.
options
.
memory
.
delete
(
SYNC_DATA_SLOT
)
})
/**
* 联系人更新事件,参数为被更新的联系人列表
*/
wechat4u
.
on
(
'
contacts-updated
'
,
(
contacts
:
any
[])
=>
{
// TODO: save them for the future usage
console
.
log
(
contacts
)
console
.
log
(
'
联系人数量:
'
,
Object
.
keys
(
wechat4u
.
contacts
).
length
)
})
/**
* 错误事件,参数一般为Error对象
*/
wechat4u
.
on
(
'
error
'
,
(
err
:
Error
)
=>
{
this
.
emit
(
'
error
'
,
err
&&
err
.
message
)
})
/**
* 如何处理会话消息
*/
wechat4u
.
on
(
'
message
'
,
(
msg
:
WebMessageRawPayload
)
=>
{
this
.
cacheWechat4uMessageRawPayload
.
set
(
msg
.
MsgId
,
msg
)
setInterval
(()
=>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
`start() setInterval() pretending received a new message:
${
MOCK_MSG_ID
}
`
)
this
.
emit
(
'
message
'
,
MOCK_MSG_ID
)
},
3000
)
this
.
emit
(
'
message
'
,
msg
.
MsgId
)
if
(
msg
.
MsgType
===
wechat4u
.
CONF
.
MSGTYPE_VERIFYMSG
)
{
this
.
cacheWechat4uFriendRequestRawPayload
.
set
(
msg
.
MsgId
,
msg
)
this
.
emit
(
'
friend
'
,
msg
.
MsgId
)
}
/**
* 获取消息时间
*/
// console.log(`----------${msg.getDisplayTime()}----------`)
/**
* 获取消息发送者的显示名
*/
// console.log(wechat4u.contacts[msg.FromUserName].getDisplayName())
})
wechat4u
.
on
(
'
logout
'
,
()
=>
{
this
.
logout
()
})
}
public
async
stop
():
Promise
<
void
>
{
...
...
@@ -128,7 +240,9 @@ export class PuppetWechat4u extends Puppet {
}
this
.
state
.
off
(
'
pending
'
)
// await some tasks...
await
this
.
wechat4u
.
stop
()
this
.
state
.
off
(
true
)
}
...
...
@@ -157,41 +271,104 @@ export class PuppetWechat4u extends Puppet {
log
.
verbose
(
'
PuppetWechat4u
'
,
'
contactAlias(%s, %s)
'
,
contactId
,
alias
)
if
(
typeof
alias
===
'
undefined
'
)
{
return
'
mock alias
'
const
payload
=
await
this
.
contactPayload
(
contactId
)
return
payload
.
alias
}
return
await
this
.
wechat4u
.
updateRemarkName
(
contactId
,
alias
)
}
public
async
contactList
():
Promise
<
string
[]
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
contactList()
'
)
return
[]
const
idList
=
this
.
wechat4u
.
contacts
.
filter
((
contact
:
any
)
=>
!
contact
.
isRoomContact
())
.
map
(
(
rawPayload
:
WebContactRawPayload
)
=>
rawPayload
.
UserName
,
)
return
idList
}
public
async
contactAvatar
(
contactId
:
string
):
Promise
<
FileBox
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
contactAvatar(%s)
'
,
contactId
)
const
WECHATY_ICON_PNG
=
path
.
resolve
(
'
../../docs/images/wechaty-icon.png
'
)
return
FileBox
.
fromLocal
(
WECHATY_ICON_PNG
)
const
rawPayload
=
await
this
.
contactRawPayload
(
contactId
)
const
res
=
await
this
.
wechat4u
.
getHeadImg
(
rawPayload
.
HeadImgUrl
)
/**
* 如何获取联系人头像
*/
return
FileBox
.
fromStream
(
res
.
data
,
`
${
contactId
}
.jpg`
,
// FIXME
)
}
public
async
contactRawPayload
(
id
:
string
):
Promise
<
We
chat4u
ContactRawPayload
>
{
public
async
contactRawPayload
(
id
:
string
):
Promise
<
We
b
ContactRawPayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
contactRawPayload(%s)
'
,
id
)
const
rawPayload
:
Wechat4uContactRawPayload
=
{
name
:
'
mock name
'
,
const
rawPayload
:
WebContactRawPayload
=
this
.
wechat4u
.
contacts
[
id
]
if
(
!
rawPayload
)
{
throw
new
Error
(
'
no rawPayload
'
)
}
return
rawPayload
}
public
async
contactRawPayloadParser
(
rawPayload
:
Wechat4uContactRawPayload
):
Promise
<
ContactPayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
contactRawPayloadParser(%s)
'
,
rawPayload
)
public
async
contactRawPayloadParser
(
rawPayload
:
WebContactRawPayload
,
):
Promise
<
ContactPayload
>
{
log
.
silly
(
'
PuppetPuppeteer
'
,
'
contactParseRawPayload(Object.keys(payload).length=%d)
'
,
Object
.
keys
(
rawPayload
).
length
,
)
if
(
!
Object
.
keys
(
rawPayload
).
length
)
{
log
.
error
(
'
PuppetPuppeteer
'
,
'
contactParseRawPayload(Object.keys(payload).length=%d)
'
,
Object
.
keys
(
rawPayload
).
length
,
)
log
.
error
(
'
PuppetPuppeteer
'
,
'
contactParseRawPayload() got empty rawPayload!
'
)
throw
new
Error
(
'
empty raw payload
'
)
// return {
// gender: Gender.Unknown,
// type: Contact.Type.Unknown,
// }
}
const
payload
:
ContactPayload
=
{
id
:
'
id
'
,
gender
:
ContactGender
.
Unknown
,
type
:
ContactType
.
Unknown
,
// this.id = rawPayload.UserName // MMActualSender??? MMPeerUserName??? `getUserContact(message.MMActualSender,message.MMPeerUserName).HeadImgUrl`
// uin: rawPayload.Uin, // stable id: 4763975 || getCookie("wxuin")
return
{
id
:
rawPayload
.
UserName
,
weixin
:
rawPayload
.
Alias
,
// Wechat ID
name
:
rawPayload
.
NickName
||
''
,
alias
:
rawPayload
.
RemarkName
,
gender
:
rawPayload
.
Sex
,
province
:
rawPayload
.
Province
,
city
:
rawPayload
.
City
,
signature
:
rawPayload
.
Signature
,
address
:
rawPayload
.
Alias
,
// XXX: need a stable address for user
star
:
!!
rawPayload
.
StarFriend
,
friend
:
rawPayload
.
stranger
===
undefined
?
undefined
:
!
rawPayload
.
stranger
,
// assign by injectio.js
avatar
:
rawPayload
.
HeadImgUrl
,
/**
* @see 1. https://github.com/Chatie/webwx-app-tracker/blob/7c59d35c6ea0cff38426a4c5c912a086c4c512b2/formatted/webwxApp.js#L3243
* @see 2. https://github.com/Urinx/WeixinBot/blob/master/README.md
* @ignore
*/
// tslint:disable-next-line
type
:
(
!!
rawPayload
.
UserName
&&
!
rawPayload
.
UserName
.
startsWith
(
'
@@
'
)
&&
!!
(
rawPayload
.
VerifyFlag
&
8
))
?
ContactType
.
Official
:
ContactType
.
Personal
,
/**
* @see 1. https://github.com/Chatie/webwx-app-tracker/blob/7c59d35c6ea0cff38426a4c5c912a086c4c512b2/formatted/webwxApp.js#L3246
* @ignore
*/
// special: specialContactList.indexOf(rawPayload.UserName) > -1 || /@qqim$/.test(rawPayload.UserName),
}
return
payload
}
/**
...
...
@@ -200,33 +377,149 @@ export class PuppetWechat4u extends Puppet {
*
*/
public
async
messageFile
(
id
:
string
):
Promise
<
FileBox
>
{
return
FileBox
.
fromBase64
(
'
cRH9qeL3XyVnaXJkppBuH20tf5JlcG9uFX1lL2IvdHRRRS9kMMQxOPLKNYIzQQ==
'
,
'
mock-file
'
+
id
+
'
.txt
'
,
)
log
.
verbose
(
'
PuppetWechat4u
'
,
'
messageFile(%s)
'
,
id
)
const
rawPayload
=
await
this
.
messageRawPayload
(
id
)
/**
* 判断消息类型
*/
switch
(
rawPayload
.
MsgType
)
{
case
this
.
wechat4u
.
CONF
.
MSGTYPE_TEXT
:
/**
* 文本消息
*/
throw
new
Error
(
'
msg type is text
'
)
case
this
.
wechat4u
.
CONF
.
MSGTYPE_EMOTICON
:
/**
* 表情消息
*/
case
this
.
wechat4u
.
CONF
.
MSGTYPE_IMAGE
:
/**
* 图片消息
*/
// console.log('图片消息,保存到本地')
return
FileBox
.
fromStream
(
(
await
this
.
wechat4u
.
getMsgImg
(
rawPayload
.
MsgId
)).
data
,
this
.
filename
(
rawPayload
),
)
case
this
.
wechat4u
.
CONF
.
MSGTYPE_VOICE
:
/**
* 语音消息
*/
// console.log('语音消息,保存到本地')
return
FileBox
.
fromStream
(
(
await
this
.
wechat4u
.
getVoice
(
rawPayload
.
MsgId
)).
data
,
this
.
filename
(
rawPayload
),
)
case
this
.
wechat4u
.
CONF
.
MSGTYPE_VIDEO
:
case
this
.
wechat4u
.
CONF
.
MSGTYPE_MICROVIDEO
:
/**
* 视频消息
*/
// console.log('视频消息,保存到本地')
return
FileBox
.
fromStream
(
(
await
this
.
wechat4u
.
getVideo
(
rawPayload
.
MsgId
)).
data
,
this
.
filename
(
rawPayload
),
)
case
this
.
wechat4u
.
CONF
.
MSGTYPE_APP
:
if
(
rawPayload
.
AppMsgType
===
6
)
{
/**
* 文件消息
*/
// console.log('文件消息,保存到本地')
return
FileBox
.
fromStream
(
(
await
this
.
wechat4u
.
getDoc
(
rawPayload
.
FromUserName
,
rawPayload
.
MediaId
,
rawPayload
.
FileName
)).
data
,
this
.
filename
(
rawPayload
),
)
}
break
default
:
break
}
throw
new
Error
(
'
unsupported message. id:
'
+
id
)
}
public
async
messageRawPayload
(
id
:
string
):
Promise
<
We
chat4u
MessageRawPayload
>
{
public
async
messageRawPayload
(
id
:
string
):
Promise
<
We
b
MessageRawPayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
messageRawPayload(%s)
'
,
id
)
const
rawPayload
:
Wechat4uMessageRawPayload
=
{
id
:
'
id
'
,
from
:
'
from_id
'
,
text
:
'
mock message text
'
,
t
o
:
'
to_id
'
,
const
rawPayload
=
this
.
cacheWechat4uMessageRawPayload
.
get
(
id
)
if
(
!
rawPayload
)
{
t
hrow
new
Error
(
'
id not found
'
)
}
return
rawPayload
}
public
async
messageRawPayloadParser
(
rawPayload
:
Wechat4uMessageRawPayload
):
Promise
<
MessagePayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
messagePayload(%s)
'
,
rawPayload
)
const
payload
:
MessagePayload
=
{
id
:
rawPayload
.
id
,
timestamp
:
Date
.
now
(),
fromId
:
'
xxx
'
,
text
:
'
mock message text
'
,
toId
:
this
.
selfId
(),
type
:
MessageType
.
Text
,
public
async
messageRawPayloadParser
(
rawPayload
:
WebMessageRawPayload
,
):
Promise
<
MessagePayload
>
{
log
.
verbose
(
'
PuppetPuppeteer
'
,
'
messageRawPayloadParser(%s) @ %s
'
,
rawPayload
,
this
)
const
id
=
rawPayload
.
MsgId
const
fromId
=
rawPayload
.
MMActualSender
// MMPeerUserName
const
text
:
string
=
rawPayload
.
MMActualContent
// Content has @id prefix added by wx
const
timestamp
:
number
=
rawPayload
.
MMDisplayTime
// Javascript timestamp of milliseconds
const
filename
:
undefined
|
string
=
this
.
filename
(
rawPayload
)
||
undefined
let
roomId
:
undefined
|
string
let
toId
:
undefined
|
string
// FIXME: has there any better method to know the room ID?
if
(
rawPayload
.
MMIsChatRoom
)
{
if
(
/^@@/
.
test
(
rawPayload
.
FromUserName
))
{
roomId
=
rawPayload
.
FromUserName
// MMPeerUserName always eq FromUserName ?
}
else
if
(
/^@@/
.
test
(
rawPayload
.
ToUserName
))
{
roomId
=
rawPayload
.
ToUserName
}
else
{
throw
new
Error
(
'
parse found a room message, but neither FromUserName nor ToUserName is a room(/^@@/)
'
)
}
// console.log('rawPayload.FromUserName: ', rawPayload.FromUserName)
// console.log('rawPayload.ToUserName: ', rawPayload.ToUserName)
// console.log('rawPayload.MMPeerUserName: ', rawPayload.MMPeerUserName)
}
if
(
rawPayload
.
ToUserName
)
{
if
(
!
/^@@/
.
test
(
rawPayload
.
ToUserName
))
{
// if a message in room without any specific receiver, then it will set to be `undefined`
toId
=
rawPayload
.
ToUserName
}
}
const
type
:
MessageType
=
this
.
messageTypeFromWeb
(
rawPayload
.
MsgType
)
const
payloadBase
=
{
id
,
type
,
fromId
,
filename
,
text
,
timestamp
,
}
let
payload
:
MessagePayload
if
(
toId
)
{
payload
=
{
...
payloadBase
,
toId
,
roomId
,
}
}
else
if
(
roomId
)
{
payload
=
{
...
payloadBase
,
toId
,
roomId
,
}
}
else
{
throw
new
Error
(
'
neither roomId nor toId
'
)
}
return
payload
}
...
...
@@ -235,6 +528,17 @@ export class PuppetWechat4u extends Puppet {
text
:
string
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
messageSend(%s, %s)
'
,
receiver
,
text
)
const
id
=
receiver
.
contactId
||
receiver
.
roomId
if
(
!
id
)
{
throw
new
Error
(
'
no id
'
)
}
/**
* 发送文本消息,可以包含emoji(😒)和QQ表情([坏笑])
*/
await
this
.
wechat4u
.
sendMsg
(
text
,
id
)
}
public
async
messageSendFile
(
...
...
@@ -242,6 +546,34 @@ export class PuppetWechat4u extends Puppet {
file
:
FileBox
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
messageSend(%s, %s)
'
,
receiver
,
file
)
const
id
=
receiver
.
roomId
||
receiver
.
contactId
if
(
!
id
)
{
throw
new
Error
(
'
no id
'
)
}
/**
* 通过表情MD5发送表情
*/
// wechat4u.sendMsg({
// emoticonMd5: '00c801cdf69127550d93ca52c3f853ff'
// }, ToUserName)
// .catch(err => {
// bot.emit('error', err)
// })
/**
* 以下通过上传文件发送图片,视频,附件等
* 通用方法为入下
* file为多种类型
* filename必填,主要为了判断文件类型
*/
await
this
.
wechat4u
.
sendMsg
({
file
:
await
file
.
toStream
(),
filename
:
file
.
name
,
},
id
)
}
public
async
messageForward
(
...
...
@@ -252,6 +584,22 @@ export class PuppetWechat4u extends Puppet {
receiver
,
messageId
,
)
const
rawPayload
=
await
this
.
messageRawPayload
(
messageId
)
if
(
!
rawPayload
)
{
throw
new
Error
(
'
no rawPayload
'
)
}
const
id
=
receiver
.
contactId
||
receiver
.
roomId
if
(
!
id
)
{
throw
new
Error
(
'
no id
'
)
}
/**
* 如何直接转发消息
*/
await
this
.
wechat4u
.
forwardMsg
(
rawPayload
,
id
)
}
/**
...
...
@@ -261,36 +609,53 @@ export class PuppetWechat4u extends Puppet {
*/
public
async
roomRawPayload
(
id
:
string
,
):
Promise
<
We
chat4u
RoomRawPayload
>
{
):
Promise
<
We
b
RoomRawPayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
roomRawPayload(%s)
'
,
id
)
const
rawPayload
:
We
chat4uRoomRawPayload
=
{
ownerId
:
'
mock_room_owner_id
'
,
topic
:
'
mock topic
'
,
memberList
:
[],
const
rawPayload
:
We
bRoomRawPayload
=
this
.
wechat4u
.
contacts
[
id
]
if
(
!
rawPayload
)
{
throw
new
Error
(
'
no rawPayload
'
)
}
return
rawPayload
}
public
async
roomRawPayloadParser
(
rawPayload
:
We
chat4u
RoomRawPayload
,
rawPayload
:
We
b
RoomRawPayload
,
):
Promise
<
RoomPayload
>
{
log
.
verbose
(
'
Puppet
Wechat4u
'
,
'
roomRawPayloadParser(%s)
'
,
rawPayload
)
log
.
verbose
(
'
Puppet
Puppeteer
'
,
'
roomRawPayloadParser(%s)
'
,
rawPayload
)
const
payload
:
RoomPayload
=
{
id
:
'
id
'
,
topic
:
'
mock topic
'
,
memberIdList
:
[],
aliasDict
:
{},
const
id
=
rawPayload
.
UserName
const
rawMemberList
=
rawPayload
.
MemberList
||
[]
const
memberIdList
=
rawMemberList
.
map
(
rawMember
=>
rawMember
.
UserName
)
const
aliasDict
=
{}
as
{
[
id
:
string
]:
string
|
undefined
}
if
(
Array
.
isArray
(
rawPayload
.
MemberList
))
{
rawPayload
.
MemberList
.
forEach
(
rawMember
=>
{
aliasDict
[
rawMember
.
UserName
]
=
rawMember
.
DisplayName
})
}
return
payload
const
roomPayload
:
RoomPayload
=
{
id
,
topic
:
rawPayload
.
NickName
||
''
,
memberIdList
,
aliasDict
,
}
return
roomPayload
}
public
async
roomList
():
Promise
<
string
[]
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
roomList()
'
)
return
[]
const
idList
=
this
.
wechat4u
.
contacts
.
filter
((
contact
:
any
)
=>
contact
.
isRoomContact
())
.
map
(
(
rawPayload
:
WebContactRawPayload
)
=>
rawPayload
.
UserName
,
)
return
idList
}
public
async
roomDel
(
...
...
@@ -298,6 +663,10 @@ export class PuppetWechat4u extends Puppet {
contactId
:
string
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
roomDel(%s, %s)
'
,
roomId
,
contactId
)
const
type
=
'
delmember
'
this
.
wechat4u
.
updateChatroom
(
roomId
,
[
contactId
],
type
)
}
public
async
roomAdd
(
...
...
@@ -305,6 +674,10 @@ export class PuppetWechat4u extends Puppet {
contactId
:
string
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
roomAdd(%s, %s)
'
,
roomId
,
contactId
)
// https://github.com/nodeWechat/wechat4u/tree/46931e78bcb56899b8d2a42a37b919e7feaebbef#botupdatechatroomchatroomusername-memberlist-fun
const
type
=
'
addmember
'
// invitemember ???
this
.
wechat4u
.
updateChatroom
(
roomId
,
[
contactId
],
type
)
}
public
async
roomTopic
(
...
...
@@ -325,7 +698,8 @@ export class PuppetWechat4u extends Puppet {
):
Promise
<
string
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
roomCreate(%s, %s)
'
,
contactIdList
,
topic
)
return
'
mock_room_id
'
const
roomId
=
await
this
.
wechat4u
.
createChatroom
(
topic
,
contactIdList
)
return
roomId
}
public
async
roomQuit
(
roomId
:
string
):
Promise
<
void
>
{
...
...
@@ -342,6 +716,8 @@ export class PuppetWechat4u extends Puppet {
hello
:
string
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
friendRequestSend(%s, %s)
'
,
contactId
,
hello
)
await
this
.
wechat4u
.
addFriend
(
contactId
,
hello
)
}
public
async
friendRequestAccept
(
...
...
@@ -349,12 +725,169 @@ export class PuppetWechat4u extends Puppet {
ticket
:
string
,
):
Promise
<
void
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
friendRequestAccept(%s, %s)
'
,
contactId
,
ticket
)
await
this
.
wechat4u
.
verifyUser
(
contactId
,
ticket
)
}
public
async
friendRequestRawPayload
(
id
:
string
)
:
Promise
<
any
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
friendRequestRawPayload(%s)
'
,
id
)
const
rawPayload
=
this
.
cacheWechat4uFriendRequestRawPayload
.
get
(
id
)
if
(
!
rawPayload
)
{
throw
new
Error
(
'
no rawPayload
'
)
}
return
rawPayload
}
public
async
friendRequestRawPayloadParser
(
rawPayload
:
any
)
:
Promise
<
FriendRequestPayload
>
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
friendRequestRawPayloadParser(%s)
'
,
rawPayload
)
const
recommendInfo
:
WebRecomendInfo
=
rawPayload
.
RecommendInfo
if
(
!
recommendInfo
)
{
throw
new
Error
(
'
no recommendInfo
'
)
}
const
contactId
=
recommendInfo
.
UserName
const
hello
=
recommendInfo
.
Content
const
ticket
=
recommendInfo
.
Ticket
const
type
=
FriendRequestType
.
Receive
const
id
=
rawPayload
.
MsgId
const
payloadReceive
:
FriendRequestPayloadReceive
=
{
id
,
contactId
,
hello
,
ticket
,
type
,
}
return
payloadReceive
}
public
ding
(
data
?:
any
):
Promise
<
string
>
{
return
data
}
private
filename
(
rawPayload
:
WebMessageRawPayload
,
):
string
{
log
.
verbose
(
'
PuppetWechat4u
'
,
'
filename()
'
)
let
filename
=
rawPayload
.
FileName
||
rawPayload
.
MediaId
||
rawPayload
.
MsgId
const
re
=
/
\.[
a-z0-9
]{1,7}
$/i
if
(
!
re
.
test
(
filename
))
{
if
(
rawPayload
.
MMAppMsgFileExt
)
{
filename
+=
'
.
'
+
rawPayload
.
MMAppMsgFileExt
}
else
{
filename
+=
this
.
extname
(
rawPayload
)
}
}
log
.
silly
(
'
PuppetWechat4u
'
,
'
filename()=%s, build from rawPayload
'
,
filename
)
return
filename
}
private
extname
(
rawPayload
:
WebMessageRawPayload
,
):
string
{
let
ext
:
string
// const type = this.type()
switch
(
rawPayload
.
MsgType
)
{
case
WebMessageType
.
EMOTICON
:
ext
=
'
.gif
'
break
case
WebMessageType
.
IMAGE
:
ext
=
'
.jpg
'
break
case
WebMessageType
.
VIDEO
:
case
WebMessageType
.
MICROVIDEO
:
ext
=
'
.mp4
'
break
case
WebMessageType
.
VOICE
:
ext
=
'
.mp3
'
break
case
WebMessageType
.
APP
:
switch
(
rawPayload
.
AppMsgType
)
{
case
WebAppMsgType
.
URL
:
ext
=
'
.url
'
// XXX
break
default
:
ext
=
'
.
'
+
rawPayload
.
MsgType
break
}
break
case
WebMessageType
.
TEXT
:
if
(
rawPayload
.
SubMsgType
===
WebMessageType
.
LOCATION
)
{
ext
=
'
.jpg
'
}
ext
=
'
.
'
+
rawPayload
.
MsgType
break
default
:
log
.
silly
(
'
PuppeteerMessage
'
,
`ext() got unknown type:
${
rawPayload
.
MsgType
}
`
)
ext
=
'
.
'
+
rawPayload
.
MsgType
}
return
ext
}
private
messageTypeFromWeb
(
webMsgType
:
WebMessageType
):
MessageType
{
switch
(
webMsgType
)
{
case
WebMessageType
.
TEXT
:
return
MessageType
.
Text
case
WebMessageType
.
EMOTICON
:
case
WebMessageType
.
IMAGE
:
return
MessageType
.
Image
case
WebMessageType
.
VOICE
:
return
MessageType
.
Audio
case
WebMessageType
.
MICROVIDEO
:
case
WebMessageType
.
VIDEO
:
return
MessageType
.
Video
case
WebMessageType
.
TEXT
:
return
MessageType
.
Text
/**
* Treat those Types as TEXT
*
* FriendRequest is a SYS message
* FIXME: should we use better message type at here???
*/
case
WebMessageType
.
SYS
:
case
WebMessageType
.
APP
:
return
MessageType
.
Text
// VERIFYMSG = 37,
// POSSIBLEFRIEND_MSG = 40,
// SHARECARD = 42,
// LOCATION = 48,
// VOIPMSG = 50,
// STATUSNOTIFY = 51,
// VOIPNOTIFY = 52,
// VOIPINVITE = 53,
// SYSNOTICE = 9999,
// RECALLED = 10002,
default
:
log
.
warn
(
'
Wechat4uPuppeteer
'
,
'
messageTypeFromWeb(%d) un-supported WebMsgType, treat as TEXT
'
,
webMsgType
)
return
MessageType
.
Text
}
}
}
export
default
PuppetWechat4u
src/puppet/puppet.spec.ts
浏览文件 @
860e85ec
...
...
@@ -18,7 +18,7 @@ import {
// ContactPayloadFilterFactory,
}
from
'
../puppet/schemas/contact
'
import
{
//
FriendRequestPayload,
FriendRequestPayload
,
}
from
'
../puppet/schemas/friend-request
'
import
{
MessagePayload
,
...
...
@@ -62,6 +62,9 @@ class PuppetTest extends Puppet {
* FriendRequest
*
*/
public
async
friendRequestRawPayload
(
id
:
string
)
:
Promise
<
any
>
{
return
{
id
}
as
any
}
public
async
friendRequestRawPayloadParser
(
rawPayload
:
any
)
:
Promise
<
FriendRequestPayload
>
{
return
rawPayload
}
public
async
friendRequestSend
(
contactId
:
string
,
hello
?:
string
)
:
Promise
<
void
>
{
return
{
contactId
,
hello
}
as
any
}
public
async
friendRequestAccept
(
contactId
:
string
,
ticket
:
string
)
:
Promise
<
void
>
{
return
{
contactId
,
ticket
}
as
any
}
...
...
src/puppet/puppet.ts
浏览文件 @
860e85ec
...
...
@@ -71,7 +71,11 @@ import {
let
PUPPET_COUNTER
=
0
/**
* Abstract Puppet Class
*
* Puppet Base Class
*
* See: https://github.com/Chatie/wechaty/wiki/Puppet
*
*/
export
abstract
class
Puppet
extends
EventEmitter
implements
Sayable
{
...
...
@@ -492,6 +496,9 @@ export abstract class Puppet extends EventEmitter implements Sayable {
public
abstract
async
friendRequestSend
(
contactId
:
string
,
hello
?:
string
)
:
Promise
<
void
>
public
abstract
async
friendRequestAccept
(
contactId
:
string
,
ticket
:
string
)
:
Promise
<
void
>
public
abstract
async
friendRequestRawPayload
(
id
:
string
)
:
Promise
<
any
>
public
abstract
async
friendRequestRawPayloadParser
(
rawPayload
:
any
)
:
Promise
<
FriendRequestPayload
>
public
async
friendRequestPayload
(
id
:
string
,
noCache
=
false
,
...
...
@@ -516,7 +523,12 @@ export abstract class Puppet extends EventEmitter implements Sayable {
log
.
silly
(
'
Puppet
'
,
'
friendRequestPayload() cache MISS
'
)
throw
new
Error
(
'
no payload
'
)
const
rawPayload
=
await
this
.
friendRequestRawPayload
(
id
)
const
payload
=
await
this
.
friendRequestRawPayloadParser
(
rawPayload
)
this
.
cacheFriendRequestPayload
.
set
(
id
,
payload
)
return
payload
}
/**
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录