內容可能過時,最新文檔參見:https://github.com/MaskRay/wechatircd。
wechatircd
wechatircd在wx.qq.com裏注入JavaScript,用WebSocket與IRC
server(wechatircd.py)通信,使得IRC客戶端可以收發微信朋友、羣消息、設置羣名、邀請刪除成員等。
1 | IRC WebSocket HTTPS |
安裝
需要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
3set 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
- 訪問
chrome://settings/certificates,導入cert.pem,在Authorities標籤頁選擇該證書,Edit->Trust this certificate for identifying websites. - 安裝Tampermonkey擴展,點擊https://github.com/MaskRay/wechatircd/raw/master/injector.user.js安裝userscript,效果是在https://wx.qq.com頁面注入https://127.0.0.1:9000/injector.js
Firefox
- 訪問https://127.0.0.1:9000/injector.js,報告Your connection is not secure,Advanced->Add Exception->Confirm Security Exception
- 安裝Greasemonkey擴展,安裝userscript

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),會自動加入+wechatchannel
在+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 sendwhich 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]跟URLMSGTYPE_VOICE,語音,顯示[Image]跟URLMSGTYPE_VIDEO,視頻,顯示[Video]跟URLMSGTYPE_MICROVIDEO,微視頻?,顯示[MicroVideo]跟URLMSGTYPE_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等。
Hook contactFactory#addContact以記錄聯繫人列表的變更
Python服務端代碼
當前只有一個文件wechatircd.py,從miniircd抄了很多代碼,後來自己又搬了好多RFC上的用不到的東西……
1 | . |
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
5var 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
2angular.element('pre:last').scope().editAreaCtn = "Hello,微信";
angular.element('pre:last').scope().sendTextMessage();
已知問題
1 | Uncaught TypeError: angular.extend is not a function |
1 | Uncaught TypeError: angular.forEach is not a function |
injector.js得在vendor_*.js後index_*.js前執行。但TamperMonkey無法精細控制script的運行時機。