Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
.Veneno.
wechaty
提交
f26cb42d
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,发现更多精彩内容 >>
提交
f26cb42d
编写于
11月 12, 2016
作者:
Huan (李卓桓)
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
#4
Add more support code for saving media file, and prepare for sending attachments
上级
3e0b8ac3
变更
10
显示空白变更内容
内联
并排
Showing
10 changed file
with
345 addition
and
151 deletion
+345
-151
example/media-file-bot.ts
example/media-file-bot.ts
+38
-21
index.ts
index.ts
+2
-2
src/message-media.ts
src/message-media.ts
+101
-35
src/message.ts
src/message.ts
+67
-23
src/puppet-web/bridge.ts
src/puppet-web/bridge.ts
+21
-7
src/puppet-web/event.ts
src/puppet-web/event.ts
+21
-12
src/puppet-web/puppet-web.ts
src/puppet-web/puppet-web.ts
+25
-28
src/puppet-web/wechaty-bro.js
src/puppet-web/wechaty-bro.js
+7
-0
src/util-lib.spec.ts
src/util-lib.spec.ts
+1
-1
src/util-lib.ts
src/util-lib.ts
+62
-22
未找到文件。
example/media-file-bot.ts
浏览文件 @
f26cb42d
...
...
@@ -10,13 +10,14 @@
/* tslint:disable:variable-name */
const
QrcodeTerminal
=
require
(
'
qrcode-terminal
'
)
import
*
as
util
from
'
util
'
// import { inspect } from 'util'
import
{
createWriteStream
,
writeFileSync
}
from
'
fs
'
import
{
Config
// , Message
,
MessageType
,
Wechaty
Config
,
Message
,
MsgType
,
Wechaty
,
}
from
'
../
'
const
bot
=
Wechaty
.
instance
({
profile
:
Config
.
DEFAULT_PROFILE
})
...
...
@@ -28,28 +29,44 @@ bot
}
console
.
log
(
`
${
url
}
\n[
${
code
}
] Scan QR Code in above url to login: `
)
})
.
on
(
'
login
'
,
user
=>
console
.
log
(
`
${
user
}
logined`
))
.
on
(
'
message
'
,
m
=>
{
console
.
log
(
`RECV:
${
m
}
`
)
console
.
log
(
util
.
inspect
(
m
))
// console.log(inspect(m))
saveRawObj
(
m
.
rawObj
)
if
(
m
.
type
()
===
MessageType
.
IMAGE
||
m
.
type
()
===
MessageType
.
EMOTICON
||
m
.
type
()
===
MessageType
.
VIDEO
||
m
.
type
()
===
MessageType
.
VOICE
||
m
.
type
()
===
MessageType
.
MICROVIDEO
if
(
m
.
type
()
===
MsgType
.
IMAGE
||
m
.
type
()
===
MsgType
.
EMOTICON
||
m
.
type
()
===
MsgType
.
VIDEO
||
m
.
type
()
===
MsgType
.
VOICE
||
m
.
type
()
===
MsgType
.
MICROVIDEO
||
m
.
type
()
===
MsgType
.
APP
||
(
m
.
type
()
===
MsgType
.
TEXT
&&
m
.
typeSub
()
===
MsgType
.
LOCATION
)
// LOCATION
)
{
const
filename
=
m
.
id
+
m
.
ext
()
saveMediaFile
(
m
)
}
})
.
init
()
.
catch
(
e
=>
console
.
error
(
'
bot.init() error:
'
+
e
))
function
saveMediaFile
(
message
:
Message
)
{
const
filename
=
message
.
filename
()
console
.
log
(
'
IMAGE local filename:
'
+
filename
)
const
fileStream
=
require
(
'
fs
'
).
createWriteStream
(
filename
)
const
fileStream
=
createWriteStream
(
filename
)
m
.
readyStream
()
.
then
(
stream
=>
stream
.
pipe
(
fileStream
))
console
.
log
(
'
start to readyStream()
'
)
message
.
readyStream
()
.
then
(
stream
=>
{
stream
.
pipe
(
fileStream
)
.
on
(
'
close
'
,
()
=>
{
console
.
log
(
'
finish readyStream()
'
)
})
})
.
catch
(
e
=>
console
.
log
(
'
stream error:
'
+
e
))
}
}
})
.
init
()
.
catch
(
e
=>
console
.
error
(
'
bot.init() error:
'
+
e
))
function
saveRawObj
(
o
)
{
writeFileSync
(
'
rawObj.log
'
,
JSON
.
stringify
(
o
,
null
,
'
'
)
+
'
\n\n\n
'
,
{
flag
:
'
a
'
})
}
index.ts
浏览文件 @
f26cb42d
...
...
@@ -8,7 +8,7 @@ import { FriendRequest } from './src/friend-request'
import
{
IoClient
}
from
'
./src/io-client
'
import
{
Message
,
M
essage
Type
,
M
sg
Type
}
from
'
./src/message
'
import
{
Puppet
}
from
'
./src/puppet
'
import
{
PuppetWeb
}
from
'
./src/puppet-web/
'
...
...
@@ -24,7 +24,7 @@ export {
,
FriendRequest
,
IoClient
,
Message
,
M
essage
Type
,
M
sg
Type
,
Puppet
,
PuppetWeb
,
Room
...
...
src/message-media.ts
浏览文件 @
f26cb42d
...
...
@@ -7,10 +7,14 @@
*
*/
import
{
Config
,
log
Config
,
log
,
}
from
'
./config
'
import
{
Message
}
from
'
./message
'
import
{
AppMsgType
,
Message
,
MsgType
,
}
from
'
./message
'
import
{
UtilLib
}
from
'
./util-lib
'
import
{
PuppetWeb
}
from
'
./puppet-web/puppet-web
'
import
{
Bridge
}
from
'
./puppet-web/bridge
'
...
...
@@ -31,27 +35,67 @@ export class MediaMessage extends Message {
try
{
await
super
.
ready
()
let
url
:
string
let
url
:
string
|
null
=
null
switch
(
this
.
type
())
{
case
M
essage
.
TYPE
[
'
EMOTICON
'
]
:
case
M
sgType
.
EMOTICON
:
url
=
await
this
.
bridge
.
getMsgEmoticon
(
this
.
id
)
break
case
M
essage
.
TYPE
[
'
IMAGE
'
]
:
case
M
sgType
.
IMAGE
:
url
=
await
this
.
bridge
.
getMsgImg
(
this
.
id
)
break
case
M
essage
.
TYPE
[
'
VIDEO
'
]
:
case
M
essage
.
TYPE
[
'
MICROVIDEO
'
]
:
case
M
sgType
.
VIDEO
:
case
M
sgType
.
MICROVIDEO
:
url
=
await
this
.
bridge
.
getMsgVideo
(
this
.
id
)
break
case
M
essage
.
TYPE
[
'
VOICE
'
]
:
case
M
sgType
.
VOICE
:
url
=
await
this
.
bridge
.
getMsgVoice
(
this
.
id
)
break
case
MsgType
.
APP
:
if
(
!
this
.
rawObj
)
{
throw
new
Error
(
'
no rawObj
'
)
}
switch
(
this
.
typeApp
())
{
case
AppMsgType
.
ATTACH
:
if
(
!
this
.
rawObj
.
MMAppMsgDownloadUrl
)
{
throw
new
Error
(
'
no MMAppMsgDownloadUrl
'
)
}
// had set in Message
// url = this.rawObj.MMAppMsgDownloadUrl
break
case
AppMsgType
.
URL
:
if
(
!
this
.
rawObj
.
Url
)
{
throw
new
Error
(
'
no Url
'
)
}
// had set in Message
// url = this.rawObj.Url
break
default
:
const
e
=
new
Error
(
'
ready() unsupported typeApp():
'
+
this
.
typeApp
())
log
.
warn
(
'
MediaMessage
'
,
e
.
message
)
throw
e
}
break
case
MsgType
.
TEXT
:
if
(
this
.
typeSub
()
===
MsgType
.
LOCATION
)
{
url
=
await
this
.
bridge
.
getMsgPublicLinkImg
(
this
.
id
)
}
break
default
:
throw
new
Error
(
'
not support message type for MediaMessage
'
)
}
this
.
obj
.
url
=
url
// return this // IMPORTANT!
if
(
!
url
)
{
if
(
!
this
.
obj
.
url
)
{
throw
new
Error
(
'
no obj.url
'
)
}
url
=
this
.
obj
.
url
}
this
.
obj
.
url
=
url
}
catch
(
e
)
{
log
.
warn
(
'
MediaMessage
'
,
'
ready() exception: %s
'
,
e
.
message
)
...
...
@@ -59,24 +103,50 @@ export class MediaMessage extends Message {
}
}
p
ublic
ext
():
string
{
p
rivate
ext
():
string
{
switch
(
this
.
type
())
{
case
M
essage
.
TYPE
[
'
EMOTICON
'
]
:
return
'
.
gif
'
case
M
sgType
.
EMOTICON
:
return
'
gif
'
case
M
essage
.
TYPE
[
'
IMAGE
'
]
:
return
'
.
jpg
'
case
M
sgType
.
IMAGE
:
return
'
jpg
'
case
M
essage
.
TYPE
[
'
VIDEO
'
]
:
case
M
essage
.
TYPE
[
'
MICROVIDEO
'
]
:
return
'
.
mp4
'
case
M
sgType
.
VIDEO
:
case
M
sgType
.
MICROVIDEO
:
return
'
mp4
'
case
M
essage
.
TYPE
[
'
VOICE
'
]
:
return
'
.
mp3
'
case
M
sgType
.
VOICE
:
return
'
mp3
'
default
:
case
MsgType
.
APP
:
switch
(
this
.
typeApp
())
{
case
AppMsgType
.
URL
:
return
'
url
'
// XXX
}
break
case
MsgType
.
TEXT
:
if
(
this
.
typeSub
()
===
MsgType
.
LOCATION
)
{
return
'
jpg
'
}
break
}
throw
new
Error
(
'
not support type:
'
+
this
.
type
())
}
public
filename
():
string
{
if
(
!
this
.
rawObj
)
{
throw
new
Error
(
'
no rawObj
'
)
}
let
filename
=
this
.
rawObj
.
FileName
||
this
.
rawObj
.
MediaId
||
this
.
rawObj
.
MsgId
const
re
=
/
\.[
a-z0-9
]{1,7}
$/i
if
(
!
re
.
test
(
filename
))
{
const
ext
=
this
.
rawObj
.
MMAppMsgFileExt
||
this
.
ext
()
filename
+=
'
.
'
+
ext
}
return
filename
}
// private getMsgImg(id: string): Promise<string> {
...
...
@@ -87,22 +157,18 @@ export class MediaMessage extends Message {
// })
// }
public
readyStream
():
Promise
<
NodeJS
.
ReadableStream
>
{
return
this
.
ready
()
.
then
(()
=>
{
public
async
readyStream
():
Promise
<
NodeJS
.
ReadableStream
>
{
try
{
await
this
.
ready
()
// FIXME: decoupling needed
return
(
Config
.
puppetInstance
()
as
PuppetWeb
)
.
browser
.
readCookie
()
})
.
then
(
cookies
=>
{
const
cookies
=
await
(
Config
.
puppetInstance
()
as
PuppetWeb
).
browser
.
readCookie
()
if
(
!
this
.
obj
.
url
)
{
throw
new
Error
(
'
no url
'
)
}
return
UtilLib
.
downloadStream
(
this
.
obj
.
url
,
cookies
)
})
.
catch
(
e
=>
{
log
.
warn
(
'
MediaMessage
'
,
'
stream() exception: %s
'
,
e
.
message
)
return
UtilLib
.
urlStream
(
this
.
obj
.
url
,
cookies
)
}
catch
(
e
)
{
log
.
warn
(
'
MediaMessage
'
,
'
stream() exception: %s
'
,
e
.
stack
)
throw
e
}
)
}
}
}
src/message.ts
浏览文件 @
f26cb42d
...
...
@@ -36,12 +36,18 @@ export type MessageRawObj = {
MMAppMsgDesc
:
string
// class="desc" ng-bind="message.MMAppMsgDesc"
/**
* Attachment
*
* MsgType == MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_ATTACH
*/
MMAppMsgFileExt
:
string
// doc, docx ...
FileName
:
string
MMAppMsgFileSize
:
number
MMAppMsgDownloadUrl
:
string
// <a download ng-if="message.MMFileStatus == CONF.MM_SEND_FILE_STATUS_SUCCESS
FileName
:
string
// FileName: '钢甲互联项目BP1108.pdf',
FileSize
:
number
// FileSize: '2845701',
MediaId
:
string
// MediaId: '@crypt_b1a45e3f_c21dceb3ac01349...
MMAppMsgFileExt
:
string
// doc, docx ... 'undefined'?
MMAppMsgFileSize
:
string
// '2.7MB',
MMAppMsgDownloadUrl
:
string
// 'https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmedia?sender=@4f549c2dafd5ad731afa4d857bf03c10&mediaid=@crypt_b1a45e3f
// <a download ng-if="message.MMFileStatus == CONF.MM_SEND_FILE_STATUS_SUCCESS
// && (massage.MMStatus == CONF.MSG_SEND_STATUS_SUCC || massage.MMStatus === undefined)
// " href="{{message.MMAppMsgDownloadUrl}}">下载</a>
MMUploadProgress
:
number
// < 100
...
...
@@ -63,10 +69,10 @@ export type MessageRawObj = {
* MsgType == CONF.MSGTYPE_VOICE : ng-style="{'width':40 + 7*message.VoiceLength/1000}
*/
MsgType
:
number
AppMsgType
:
number
// message.MsgType == CONF.MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_URL
AppMsgType
:
AppMsgType
// message.MsgType == CONF.MSGTYPE_APP && message.AppMsgType == CONF.APPMSGTYPE_URL
// message.MsgType == CONF.MSGTYPE_TEXT && message.SubMsgType != CONF.MSGTYPE_LOCATION
SubMsgType
:
number
// "msgType":"{{message.MsgType}}","subType":{{message.SubMsgType||0}},"msgId":"{{message.MsgId}}"
SubMsgType
:
MsgType
// "msgType":"{{message.MsgType}}","subType":{{message.SubMsgType||0}},"msgId":"{{message.MsgId}}"
/**
* Status-es
...
...
@@ -82,6 +88,8 @@ export type MessageRawObj = {
*/
MMLocationUrl
:
string
// ng-if="message.MsgType == CONF.MSGTYPE_TEXT && message.SubMsgType == CONF.MSGTYPE_LOCATION"
// <a href="{{message.MMLocationUrl}}" target="_blank">
// 'http://apis.map.qq.com/uri/v1/geocoder?coord=40.075041,116.338994'
MMLocationDesc
:
string
// MMLocationDesc: '北京市昌平区回龙观龙腾苑(五区)内(龙腾街南)',
/**
* MsgType == CONF.MSGTYPE_EMOTICON
...
...
@@ -133,7 +141,27 @@ export type MessageTypeMap = {
// , MessageTypeValue: MessageTypeName
}
export
const
enum
MessageType
{
export
const
enum
AppMsgType
{
TEXT
=
1
,
IMG
=
2
,
AUDIO
=
3
,
VIDEO
=
4
,
URL
=
5
,
ATTACH
=
6
,
OPEN
=
7
,
EMOJI
=
8
,
VOICE_REMIND
=
9
,
SCAN_GOOD
=
10
,
GOOD
=
13
,
EMOTION
=
15
,
CARD_TICKET
=
16
,
REALTIME_SHARE_LOCATION
=
17
,
TRANSFERS
=
2
e3
,
RED_ENVELOPES
=
2001
,
READER_TYPE
=
100001
,
}
export
enum
MsgType
{
TEXT
=
1
,
IMAGE
=
3
,
VOICE
=
34
,
...
...
@@ -151,7 +179,7 @@ export const enum MessageType {
MICROVIDEO
=
62
,
SYSNOTICE
=
9999
,
SYS
=
10000
,
RECALLED
=
10002
RECALLED
=
10002
,
}
export
class
Message
implements
Sayable
{
...
...
@@ -187,8 +215,8 @@ export class Message implements Sayable {
throw
Error
(
'
abstract method
'
)
}
public
ext
():
string
{
throw
Error
(
'
abstract method
'
)
public
filename
():
string
{
throw
Error
(
'
not a media message
'
)
}
constructor
(
public
rawObj
?:
MessageRawObj
)
{
...
...
@@ -207,14 +235,15 @@ export class Message implements Sayable {
// Transform rawObj to local m
private
parse
(
rawObj
):
MessageObj
{
const
obj
:
MessageObj
=
{
id
:
rawObj
.
MsgId
,
type
:
rawObj
.
MsgType
,
from
:
rawObj
.
MMActualSender
// MMPeerUserName
,
to
:
rawObj
.
ToUserName
,
content
:
rawObj
.
MMActualContent
// Content has @id prefix added by wx
,
status
:
rawObj
.
Status
,
digest
:
rawObj
.
MMDigest
,
date
:
rawObj
.
MMDisplayTime
// Javascript timestamp of milliseconds
id
:
rawObj
.
MsgId
,
type
:
rawObj
.
MsgType
,
from
:
rawObj
.
MMActualSender
,
// MMPeerUserName
to
:
rawObj
.
ToUserName
,
content
:
rawObj
.
MMActualContent
,
// Content has @id prefix added by wx
status
:
rawObj
.
Status
,
digest
:
rawObj
.
MMDigest
,
date
:
rawObj
.
MMDisplayTime
,
// Javascript timestamp of milliseconds
url
:
rawObj
.
Url
||
rawObj
.
MMAppMsgDownloadUrl
||
rawObj
.
MMLocationUrl
}
// FIXME: has ther any better method to know the room ID?
...
...
@@ -230,6 +259,7 @@ export class Message implements Sayable {
// } else {
// obj.room = undefined
}
return
obj
}
public
toString
()
{
...
...
@@ -334,8 +364,22 @@ export class Message implements Sayable {
return
this
.
obj
.
content
}
public
type
():
MessageType
{
return
this
.
obj
.
type
as
MessageType
public
type
():
MsgType
{
return
this
.
obj
.
type
as
MsgType
}
public
typeSub
():
MsgType
{
if
(
!
this
.
rawObj
)
{
throw
new
Error
(
'
no rawObj
'
)
}
return
this
.
rawObj
.
SubMsgType
}
public
typeApp
():
AppMsgType
{
if
(
!
this
.
rawObj
)
{
throw
new
Error
(
'
no rawObj
'
)
}
return
this
.
rawObj
.
AppMsgType
}
public
typeEx
()
{
return
Message
.
TYPE
[
this
.
obj
.
type
]
}
...
...
src/puppet-web/bridge.ts
浏览文件 @
f26cb42d
...
...
@@ -121,12 +121,15 @@ export class Bridge {
})
}
public
getUserName
():
Promise
<
string
>
{
return
this
.
proxyWechaty
(
'
getUserName
'
)
.
catch
(
e
=>
{
public
async
getUserName
():
Promise
<
string
>
{
log
.
verbose
(
'
PuppetWebBridge
'
,
'
getUserName()
'
)
try
{
return
await
this
.
proxyWechaty
(
'
getUserName
'
)
}
catch
(
e
)
{
log
.
error
(
'
PuppetWebBridge
'
,
'
getUserName() exception: %s
'
,
e
.
message
)
throw
e
})
}
}
public
async
contactRemark
(
contactId
:
string
,
remark
:
string
):
Promise
<
boolean
>
{
...
...
@@ -294,6 +297,17 @@ export class Bridge {
}
}
public
async
getMsgPublicLinkImg
(
id
):
Promise
<
string
>
{
log
.
verbose
(
'
PuppetWebBridge
'
,
'
getMsgPublicLinkImg(%s)
'
,
id
)
try
{
return
await
this
.
proxyWechaty
(
'
getMsgPublicLinkImg
'
,
id
)
}
catch
(
e
)
{
log
.
silly
(
'
PuppetWebBridge
'
,
'
proxyWechaty(getMsgPublicLinkImg, %d) exception: %s
'
,
id
,
e
.
message
)
throw
e
}
}
public
getContact
(
id
:
string
):
Promise
<
string
>
{
if
(
id
!==
id
)
{
// NaN
const
err
=
new
Error
(
'
NaN! where does it come from?
'
)
...
...
@@ -365,7 +379,7 @@ export class Bridge {
throw
new
Error
(
'
there is no WechatyBro in browser(yet)
'
)
}
}
catch
(
e
)
{
log
.
error
(
'
PuppetWebBridge
'
,
'
proxyWechaty() noWechaty exception: %s
'
,
e
.
message
)
log
.
error
(
'
PuppetWebBridge
'
,
'
proxyWechaty() noWechaty exception: %s
'
,
e
.
stack
)
throw
e
}
...
...
src/puppet-web/event.ts
浏览文件 @
f26cb42d
...
...
@@ -16,14 +16,15 @@
*
*/
import
{
WatchdogFood
,
ScanInfo
,
log
WatchdogFood
,
ScanInfo
,
log
,
}
from
'
../config
'
import
{
Contact
}
from
'
../contact
'
import
{
Message
,
MediaMessage
Message
,
MediaMessage
,
MsgType
,
}
from
'
../message
'
import
{
Firer
}
from
'
./firer
'
...
...
@@ -351,11 +352,11 @@ async function onServerMessage(this: PuppetWeb, data): Promise<void> {
*/
switch
(
m
.
type
())
{
// data.MsgType
case
M
essage
.
TYPE
[
'
VERIFYMSG
'
]
:
case
M
sgType
.
VERIFYMSG
:
Firer
.
checkFriendRequest
.
call
(
this
,
m
)
break
case
M
essage
.
TYPE
[
'
SYS
'
]
:
case
M
sgType
.
SYS
:
if
(
m
.
room
())
{
Firer
.
checkRoomJoin
.
call
(
this
,
m
)
Firer
.
checkRoomLeave
.
call
(
this
,
m
)
...
...
@@ -373,14 +374,22 @@ async function onServerMessage(this: PuppetWeb, data): Promise<void> {
console
.
log
(
m
.
type
())
switch
(
m
.
type
())
{
case
Message
.
TYPE
[
'
EMOTICON
'
]:
case
Message
.
TYPE
[
'
IMAGE
'
]:
case
Message
.
TYPE
[
'
VIDEO
'
]:
case
Message
.
TYPE
[
'
VOICE
'
]:
case
Message
.
TYPE
[
'
MICROVIDEO
'
]:
case
MsgType
.
EMOTICON
:
case
MsgType
.
IMAGE
:
case
MsgType
.
VIDEO
:
case
MsgType
.
VOICE
:
case
MsgType
.
MICROVIDEO
:
case
MsgType
.
APP
:
log
.
verbose
(
'
PuppetWebEvent
'
,
'
onServerMessage() EMOTICON/IMAGE/VIDEO/VOICE/MICROVIDEO message
'
)
m
=
new
MediaMessage
(
data
)
break
case
MsgType
.
TEXT
:
if
(
m
.
typeSub
()
===
MsgType
.
LOCATION
)
{
log
.
verbose
(
'
PuppetWebEvent
'
,
'
onServerMessage() (TEXT&LOCATION) message
'
)
m
=
new
MediaMessage
(
data
)
}
break
}
// To Be Deleted: set self...
...
...
src/puppet-web/puppet-web.ts
浏览文件 @
f26cb42d
...
...
@@ -315,11 +315,12 @@ export class PuppetWeb extends Puppet {
,
room
?
room
.
topic
()
:
(
to
as
Contact
).
name
()
,
content
)
try
{
await
this
.
bridge
.
send
(
destination
.
id
,
content
)
.
catch
(
e
=>
{
}
catch
(
e
)
{
log
.
error
(
'
PuppetWeb
'
,
'
send() exception: %s
'
,
e
.
message
)
throw
e
})
}
return
}
...
...
@@ -349,36 +350,32 @@ export class PuppetWeb extends Puppet {
* logout from browser, then server will emit `logout` event
*/
public
async
logout
():
Promise
<
void
>
{
try
{
await
this
.
bridge
.
logout
()
.
catch
(
e
=>
{
}
catch
(
e
)
{
log
.
error
(
'
PuppetWeb
'
,
'
logout() exception: %s
'
,
e
.
message
)
throw
e
})
return
}
public
getContact
(
id
:
string
):
Promise
<
any
>
{
if
(
!
this
.
bridge
)
{
throw
new
Error
(
'
PuppetWeb has no bridge for getContact()
'
)
}
return
this
.
bridge
.
getContact
(
id
)
.
catch
(
e
=>
{
public
async
getContact
(
id
:
string
):
Promise
<
any
>
{
try
{
return
await
this
.
bridge
.
getContact
(
id
)
}
catch
(
e
)
{
log
.
error
(
'
PuppetWeb
'
,
'
getContact(%d) exception: %s
'
,
id
,
e
.
message
)
throw
e
})
}
}
public
logined
():
boolean
{
return
!!
(
this
.
user
)
}
public
ding
(
data
?:
any
):
Promise
<
string
>
{
if
(
!
this
.
bridge
)
{
return
Promise
.
reject
(
new
Error
(
'
ding fail: no bridge(yet)!
'
))
}
return
this
.
bridge
.
ding
(
data
)
.
catch
(
e
=>
{
public
async
ding
(
data
?:
any
):
Promise
<
string
>
{
try
{
return
await
this
.
bridge
.
ding
(
data
)
}
catch
(
e
)
{
log
.
warn
(
'
PuppetWeb
'
,
'
ding(%s) rejected: %s
'
,
data
,
e
.
message
)
throw
e
})
}
}
public
async
contactRemark
(
contact
:
Contact
,
remark
:
string
):
Promise
<
boolean
>
{
...
...
src/puppet-web/wechaty-bro.js
浏览文件 @
f26cb42d
...
...
@@ -435,6 +435,12 @@
return
location
+
path
}
function
getMsgPublicLinkImg
(
id
)
{
var
location
=
window
.
location
.
href
.
replace
(
/
\/
$/
,
''
)
var
path
=
'
/cgi-bin/mmwebwx-bin/webwxgetpubliclinkimg?url=xxx&msgid=
'
+
id
+
'
&pictype=location
'
return
location
+
path
}
function
send
(
ToUserName
,
Content
)
{
var
chatFactory
=
WechatyBro
.
glue
.
chatFactory
var
confFactory
=
WechatyBro
.
glue
.
confFactory
...
...
@@ -766,6 +772,7 @@
,
getMsgEmoticon
,
getMsgVideo
,
getMsgVoice
,
getMsgPublicLinkImg
// for Wechaty Contact Class
,
contactFindAsync
...
...
src/util-lib.spec.ts
浏览文件 @
f26cb42d
...
...
@@ -110,7 +110,7 @@ test('downloadStream() for media', t => {
})
server
.
listen
(
8000
)
UtilLib
.
download
Stream
(
'
http://127.0.0.1:8000/ding
'
,
[{
name
:
'
life
'
,
value
:
42
}])
UtilLib
.
url
Stream
(
'
http://127.0.0.1:8000/ding
'
,
[{
name
:
'
life
'
,
value
:
42
}])
.
then
(
s
=>
{
s
.
on
(
'
data
'
,
(
chunk
)
=>
{
// console.log(`BODY: ${chunk}`)
...
...
src/util-lib.ts
浏览文件 @
f26cb42d
...
...
@@ -5,7 +5,9 @@
* https://github.com/wechaty/wechaty
*
*/
import
*
as
https
from
'
https
'
import
*
as
http
from
'
http
'
import
*
as
url
from
'
url
'
import
{
log
}
from
'
./config
'
...
...
@@ -96,46 +98,84 @@ export class UtilLib {
)
}
public
static
downloadStream
(
url
:
string
,
cookies
:
any
[]):
Promise
<
NodeJS
.
ReadableStream
>
{
public
static
urlStream
(
href
:
string
,
cookies
:
any
[]):
Promise
<
NodeJS
.
ReadableStream
>
{
// const myurl = 'http://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg?&MsgID=3080011908135131569&skey=%40crypt_c117402d_53a58f8fbb21978167a3fc7d3be7f8c9'
url
=
url
.
replace
(
/^https/i
,
'
http
'
)
// use http for better performance
const
options
=
require
(
'
url
'
).
parse
(
url
)
// url = url.replace(/^https/i, 'http') // use http for better performance
console
.
log
(
href
)
const
u
=
url
.
parse
(
href
)
const
protocol
:
'
https:
'
|
'
http:
'
=
u
.
protocol
as
any
console
.
log
(
u
)
let
options
// let request
if
(
protocol
===
'
https:
'
)
{
// request = https.request.bind(https)
options
=
u
as
https
.
RequestOptions
options
.
agent
=
https
.
globalAgent
}
else
if
(
protocol
===
'
http:
'
)
{
// request = http.request.bind(http)
options
=
u
as
http
.
RequestOptions
options
.
agent
=
http
.
globalAgent
}
else
{
throw
new
Error
(
'
protocol unknown:
'
+
protocol
)
}
options
.
headers
=
{
// Accept: 'image/webp,image/*,*/*;q=0.8'
Accept
:
'
*/*
'
,
Referer
:
'
https://wx.qq.com/
'
,
Range
:
'
bytes=0-
'
'
User-Agent
'
:
'
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
'
,
// Accept: 'image/webp,image/*,*/*;q=0.8',
Accept
:
'
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
'
,
// MsgType.IMAGE | VIDEO
// Accept: '*/*',
Host
:
options
.
hostname
,
// 'wx.qq.com', // MsgType.VIDEO | IMAGE
Referer
:
protocol
+
'
//wx.qq.com/
'
,
// 'Upgrade-Insecure-Requests': 1, // MsgType.VIDEO | IMAGE
Range
:
'
bytes=0-
'
,
,
'
User-Agent
'
:
'
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
'
// , 'Accept-Encoding': 'gzip, deflate, sdch'
,
'
Accept-Encoding
'
:
'
identity;q=1, *;q=0
'
,
'
Accept-Language
'
:
'
zh-CN,zh;q=0.8
'
// 'Accept-Encoding': 'gzip, deflate, sdch',
'
Accept-Encoding
'
:
'
gzip, deflate, sdch, br
'
,
// MsgType.IMAGE | VIDEO
// 'Accept-Encoding': 'identity;q=1, *;q=0',
'
Accept-Language
'
:
'
zh-CN,zh;q=0.8
'
,
// MsgType.IMAGE | VIDEO
// 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.6,en-US;q=0.4,en;q=0.2',
}
options
.
agent
=
http
.
globalAgent
/**
* 'pgv_pvi=6639183872; pgv_si=s8359147520;
* webwxuvid=747895d9dac5a25dd3a78175a5e931d879e026cacaf3ac06de0bd5f0714 ... ;
* mm_lang=zh_CN; MM_WX_NOTIFY_STATE=1; MM_WX_SOUND_STATE=1; wxloadtime=1465928826_expired;
* wxpluginkey=1465901102; wxuin=1211516682; wxsid=zMT7Gb24aTQzB1rA;
* webwx_data_ticket=gSeBbuhX+0kFdkXbgeQwr6Ck'
* pgv_pvi=6639183872; pgv_si=s8359147520; webwx_data_ticket=gSeBbuhX+0kFdkXbgeQwr6Ck
*/
options
.
headers
.
Cookie
=
cookies
.
map
(
c
=>
`
${
c
[
'
name
'
]}
=
${
c
[
'
value
'
]}
`
).
join
(
'
;
'
)
options
.
headers
[
'
Cookie
'
]
=
cookies
.
map
(
c
=>
`
${
c
[
'
name
'
]}
=
${
c
[
'
value
'
]}
`
).
join
(
'
;
'
)
// log.verbose('Util', 'Cookie: %s', options.headers.Cookie)
console
.
log
(
options
)
return
new
Promise
((
resolve
,
reject
)
=>
{
const
req
=
http
.
request
(
options
,
(
res
)
=>
{
/*
const req = request(options, (res) => {
// console.log(`STATUS: ${res.statusCode}`);
// console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
// res.setEncoding('utf8');
resolve(res)
})
*/
let
req
if
(
protocol
===
'
https:
'
)
{
req
=
https
.
request
(
options
,
resolve
)
}
else
{
req
=
http
.
request
(
options
,
resolve
)
}
req
.
on
(
'
error
'
,
(
e
)
=>
{
log
.
warn
(
'
WebUtil
'
,
`downloadStream() problem with request:
${
e
.
message
}
`
)
reject
(
e
)
})
req
.
end
()
req
.
end
(()
=>
{
log
.
verbose
(
'
UtilLib
'
,
'
urlStream() req.end() request sent
'
)
})
})
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录