内容可能过时,最新文档参见:https://github.com/MaskRay/wechatircd 。
wechatircd
wechatircd在wx.qq.com里注入JavaScript,用WebSocket与IRC
server(wechatircd.py
)通信,使得IRC客户端可以收发微信朋友、群消息、设置群名、邀请删除成员等。
1 2 3 4 IRC WebSocket HTTPS IRC client --- wechatircd.py --------- browser ----- wx.qq.com injector.user.js injector.js
安装
需要Python 3.5或以上,支持async/await
语法
pip install -r requirements.txt
安装依赖
Arch Linux
yaourt -S wechatircd-git
。会在/etc/wechatircd/
下生成自签名证书。
把/etc/wechatircd/cert.pem
导入到浏览器(见下文)
systemctl start wechatircd
会运行/usr/bin/wechatircd --http-cert /etc/wechatircd/cert.pem --http-key /etc/wechatircd/key.pem --http-root /usr/share/wechatircd
IRC服务器默认监听127.0.0.1:6667 (IRC)和127.0.0.1:9000 (HTTPS +
WebSocket over TLS)。
如果你在非本机运行,建议配置IRC over TLS,设置IRC connection
password:/usr/bin/wechatircd --http-cert /etc/wechatircd/cert.pem --http-key /etc/wechatircd/key.pem --http-root /usr/share/wechatircd --irc-cert /path/to/irc.key --irc-key /path/to/irc.cert --irc-password yourpassword
可以把HTTPS私钥证书用作IRC over
TLS私钥证书。使用WeeChat的话,如果觉得让WeeChat信任证书比较麻烦(gnutls会检查hostname),可以用:
1 2 3 set irc.server.wechat.ssl on` set irc.server.wechat.ssl_verify off set irc.server.wechat.password yourpassword`
其他发行版
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -out cert.pem -subj '/CN=127.0.0.1' -days 9999
创建密钥与证书。
把cert.pem
导入浏览器,见下文
./wechatircd.py --http-cert cert.pem --http-key key.pem
导入自签名证书到浏览器
Chrome/Chromium
Firefox
HTTPS、WebSocket over
TLS默认用9000端口,使用其他端口需要修改userscript,启动wechatircd.py
时用--web-port 10000
指定其他端口。
使用
运行wechatircd.py
访问https://wx.qq.com ,userscript注入的JavaScript会向服务器发起WebSocket连接
IRC客户端连接127.1:6667(weechat的话使用/server add wechat 127.1/6667
),会自动加入+wechat
channel
在+wechat
发信并不会群发,只是为了方便查看有哪些朋友。
微信朋友的nick优先选取备注名(RemarkName
),其次为DisplayName
(原始JS根据暱称等自动填写的一个名字)
在+wechat
channel可以执行一些命令:
help
,帮助
status [pattern]
,已获取的微信朋友、群列表,支持
pattern 参数用来筛选满足 pattern
的结果,目前仅支持子串查询。如要查询所有群,由于群由 &
开头,所以可以执行 status &
。
eval $password $expr
:
如果运行时带上了--password $password
选项,这里可以eval,方便调试,比如eval $password client.wechat_users
服务器选项
Join mode,短选项-j
--join auto
,默认:收到某个群第一条消息后自动加入,如果执行过/part
命令了,则之后收到消息不会重新加入
--join all
:加入所有channel
--join manual
:不自动加入
--join new
:类似于auto
,但执行/part
命令后,之后收到消息仍自动加入
指定不自动加入的群名,用于补充join mode
--ignore 'fo[o]' bar
,channel名部分匹配正则表达式fo[o]
或bar
--ignore-topic 'fo[o]' bar
,
群标题部分匹配正则表达式fo[o]
或bar
HTTP/WebSocket相关选项
--http-cert cert.pem
,HTTPS/WebSocketTLS的证书。你可以把证书和私钥合并为一个文件,省略--http-key
选项。如果--http-cert
和--http-key
均未指定,使用不加密的HTTP
--http-key key.pem
,HTTPS/WebSocket的私钥
--http-listen 127.1 ::1
,HTTPS/WebSocket监听地址设置为127.1
和::1
,overriding
--listen
--http-port 9000
,HTTPS/WebSocket监听端口设置为9000
--http-root .
,
存放injector.js
的根目录
-l 127.0.0.1
,IRC/HTTP/WebSocket监听地址设置为127.0.0.1
IRC相关选项
--irc-cert cert.pem
,IRC over
TLS的证书。你可以把证书和私钥合并为一个文件,省略--irc-key
选项。如果--irc-cert
和--irc-key
均未指定,使用不加密的IRC
--irc-key key.pem
,IRC over TLS的私钥
--irc-listen 127.1 ::1
,IRC over
TLS监听地址设置为127.1
和::1
,overriding
--listen
--irc-nicks ray ray1
,给客户端保留的nick。SpecialUser
不会占用这些名字
--irc-password pass
,IRC connection
password设置为pass
--irc-port 6667
,IRC监听端口
服务端日志
--logger-ignore '&test0' '&test1'
,不记录部分匹配指定正则表达式的朋友/群日志
--logger-mask '/tmp/wechat/$channel/%Y-%m-%d.log'
,日志文件名格式
--logger-time-format %H:%M
,日志单条消息的时间格式
IRC命令
标准IRC channel名以#
开头
WeChat群名以&
开头。SpecialChannel#update
联系人带有mode +v
(voice,
通常显示为前缀+
)。SpecialChannel#update_detail
多行消息:!m line0\nline1
多行消息:!html line0<br>line1
nick0: nick1: test
会被转换成@GroupAlias0 @GroupAlias1 test
,GroupAlias0
是nick0
在频道里的Group
Alias,如果没设置的话就是Alias。GroupAlias0
是移动端用户看到的名字
回复12:34:SS的消息:@1234 !m multi\nline\nreply
,会发送「Re GroupAlias: text」text
回复12:34:56的消息:!m @123456 multi\nline\nreply
回复朋友/群的倒数第二条消息:@2 reply
若客户端启用IRC 3.1
3.2的server-time
扩展,wechatircd.py
会在发送的消息中包含
网页版获取的时间戳。客户端显示消息时时间就会和服务器收到的消息的时刻一致。参见http://ircv3.net/irc/ 。参见http://ircv3.net/software/clients.html 查看IRCv3的客户端支持情况。
WeeChat配置方式: 1 /set irc.server_default.capabilities "account-notify,away-notify,cap-notify,multi-prefix,server-time,znc.in/server-time-iso,znc.in/self-message"
支持的IRC命令:
/cap
,列出支持的capabilities
/dcc send $nick/$channel $filename
, send image or
file。This feature borrows the command /dcc send
which is
well supported in IRC clients. See https://en.wikipedia.org/wiki/Direct_Client-to-Client#DCC_SEND .
/invite $nick [$channel]
, invite a contact to the
group.
/kick $nick
,删除群成员,群主才有效。由于网页版限制,可能收不到群成员变更的消息
/list
,列出所有群
/mode +m
,
--join new
模式下防止自动重新join。用/mode -m
撤销
/names
, 更新当前群成员列表
/part $channel
的IRC原义为离开channel,这里表示当前IRC会话中不再接收该群的消息。不用担心,telegramircd并没有主动退出群的功能
/query $nick
,打开和$nick
聊天的窗口
/summon $nick $message
,发送添加朋友请求,$message
为备注消息
/topic topic
修改群标题。因为IRC不支持channel改名,实现为离开原channel并加入新channel
/who $channel
,查看群的成员列表
显示
MSGTYPE_TEXT
,文本或是视频/语音聊天请求,显示文本
MSGTYPE_IMG
,图片,显示[Image]
跟URL
MSGTYPE_VOICE
,语音,显示[Image]
跟URL
MSGTYPE_VIDEO
,视频,显示[Video]
跟URL
MSGTYPE_MICROVIDEO
,微视频?,显示[MicroVideo]
跟URL
MSGTYPE_APP
,订阅号新文章、各种应用分享送红包、URL分享等属于此类,还有子分类APPMSGTYPE_*
,显示[App]
跟title跟URL
QQ表情会显示成<img class="qqemoji qqemoji0" text="[Smile]_web" src="/zh_CN/htmledition/v2/images/spacer.gif">
样,发送时用[Smile]
即可(相当于在网页版文本输入框插入文本后点击发送)。
Emoji在网页上呈现时为<img class="emoji emoji1f604" text="_web" src="/zh_CN/htmledition/v2/images/spacer.gif">
,传送至IRC时转换成单个emoji字符。若使用终端IRC客户端,会因为emoji字符宽度为1导致重叠,参见终端模拟器下使用双倍宽度多色Emoji字体 。
JS改动
修改的地方都有//@
标注,结合diff,方便微信网页版JS更新后重新应用这些修改。增加的代码中大多数地方都用try catch
保护,出错则consoleerr(ex.stack)
。原始JS把console
对象抹掉了……consoleerr
是我保存的一个副本。
目前的改动如下:
webwxapp.js
开头
创建到服务端的WebSocket连接,若onerror
则自动重连。监听onmessage
,收到的消息为服务端发来的控制命令:send_text_message
、add_member
等。
Python服务端代码
当前只有一个文件wechatircd.py
,从miniircd抄了很多代码,后来自己又搬了好多RFC上的用不到的东西……
1 2 3 4 5 6 7 8 9 10 11 12 13 . ├── Web HTTP(s)/WebSocket server ├── Server IRC server ├── Channel │ ├── StandardChannel `#`开头的IRC channel │ ├── StatusChannel `+wechat`,查看控制当前微信会话 │ └── SpecialChannel 微信群对应的channel,仅该客户端可见 ├── (User) │ ├── Client IRC客户端连接 │ ├── SpecialUser 微信用户对应的user,仅该客户端可见 ├── (IRCCommands) │ ├── UnregisteredCommands 注册前可用命令:NICK USER QUIT │ ├── RegisteredCommands 注册后可用命令
FAQ
使用这个方法的理由
原本想研究微信网页版登录、收发消息的协议,自行实现客户端。参考过https://github.com/0x5e/wechat-deleted-friends ,仿制了https://gist.github.com/MaskRay/3b5b3fcbccfcba3b8f29 ,可以登录。但根据minify后JS把相关部分重写非常困难,错误处理很麻烦,所以就让网页版JS自己来传递信息。
用途
可以使用强大的IRC客户端,方便记录日志(微信日志导出太麻烦https://maskray.me/blog/2014-10-14-wechat-export ),可以写bot。
微信数据获取及控制
少量特殊账户的UserName
不带@
前缀:newsapp,fmessage,filehelper,weibo,qqmail,fmessage
等的;一般账户(公众号、服务号、直接联系人、群友)的UserName
以@
开头;微信群的UserName
以@@
开头。不同session
UserName
会变化。Uin
应该是唯一id,但微信网页版API多数时候都返回0,隐藏了真实值。
群的OwnerUin
字段是群主的Uin
,但大群用户的Uin
通常都为0,因此难以对应。
自己的帐号 1 angular.element (document .body ).scope ().account
所有联系人列表 1 angular.element ($('#navContact' )[0 ]).scope ().allContacts
删除群中成员 1 2 3 4 5 var injector = angular.element (document ).injector ()# 这里获取了chatroomFactory,还可用于获取其他factory、service、controller等 var chatroomFactory = injector.get ('chatroomFactory' )# 设置其中的`room` 与`userToRemove` chatroomFactory.delMember (room.UserName , userToRemove.UserName )`
名称中包含xxx
的最近联系人列表中的群 1 angular.element ($('span:contains("xxx")' )).scope ().chatContact
当前窗口发送消息 1 2 angular.element ('pre:last' ).scope ().editAreaCtn = "Hello,微信" ; angular.element ('pre:last' ).scope ().sendTextMessage ();
已知问题
1 2 3 4 5 6 7 8 9 10 11 Uncaught TypeError: angular.extend is not a function at Object.setUserInfo (index_0c7087d.js:4) at index_0c7087d.js:2 at c (vendor_2de5d3a.js:11) at vendor_2de5d3a.js:11 at c.$eval (vendor_2de5d3a.js:11) at c.$digest (vendor_2de5d3a.js:11) at c.$apply (vendor_2de5d3a.js:11) at l (vendor_2de5d3a.js:11) at m (vendor_2de5d3a.js:11) at XMLHttpRequest.C.onreadystatechange (vendor_2de5d3a.js:11)
1 Uncaught TypeError: angular.forEach is not a function
injector.js
得在vendor_*.js
后index_*.js
前执行。但TamperMonkey无法精细控制script的运行时机。
参考