wechatircd injects JavaScript (injector.js
) to wx.qq.com, which uses WebSocket to communicate with an IRC server (wechatircd.py
), thus enable IRC clients connected to the server to send and receive messages from WeChat, set topics, invite/delete members, ...
IRC WebSocket HTTPS
IRC client --- wechatircd.py --------- browser ----- wx.qq.com
injector.user.js
injector.js
Discuss wechatircd by joining #wechatircd on freenode, or the user group on Telegram. Video on using WeChat in WeeChat
yaourt -S wechatircd-git
. It will generate a self-signed CA key/certificate pair/etc/wechatircd/ca.{cert,key}.pem
and a key/certificate pair{cert,key}.pem
(see below).- Import the CA certificate
/etc/wechatircd/ca.cert.pem
to the browser (see below). systemctl start wechatircd
, which runs/usr/bin/wechatircd --http-cert /etc/wechatircd/cert.pem --http-key /etc/wechatircd/key.pem --http-root /usr/share/wechatircd
. You may want to customize/etc/systemd/system/wechatircd.service
.
wechatircd.py
(the server) will listen on 127.0.0.1:6667 (IRC) and 127.0.0.1:9000 (HTTPS + WebSocket over TLS).
If you run the server on another machine, it is recommended to set up IRC over TLS and an IRC connection password with a few more options: --irc-cert /path/to/irc.key --irc-key /path/to/irc.cert --irc-password yourpassword
. As an alternative to the IRC connection password, you may specify --sasl-password yourpassword
and authenticate with SASL PLAIN. You can reuse the HTTPS certificate+key. If you use WeeChat and find it difficult to set up a valid certificate (gnutls checks the hostname), type the following lines in WeeChat:
/set irc.server.wechat.ssl on
/set irc.server.wechat.ssl_verify off
/set irc.server.wechat.password yourpassword
- python >= 3.5
pip install -r requirements.txt
-
openssl req -x509 -newkey rsa:2048 -sha256 -nodes -keyout ca.key.pem -out ca.cert.pem -days 9999 -subj '/CN=127.0.0.1' openssl req -new -newkey rsa:2048 -sha256 -nodes -keyout key.pem -subj '/CN=127.0.0.1' | openssl x509 -req -out cert.pem -CAkey ca.key.pem -CA ca.cert.pem -set_serial 2 -days 9999 -extfile <( printf "subjectAltName = IP:127.0.0.1, DNS:localhost")
- Import the CA certificate
ca.cert.pem
to the browser. ./wechatircd.py --http-cert cert.pem --http-key key.pem
The IP address or the domain name used to serve injector.js
and injector.user.js
should match the subjectAlternativeName
fields. Chrome has removed support for commonName
matching in certificates since version 58. See https://developers.google.com/web/updates/2017/03/chrome-58-deprecations#remove_support_for_commonname_matching_in_certificates for detail.
Chrome/Chromium
- Visit
chrome://settings/certificates
,importca.cert.pem
,click theAuthorities
tab,select the127.0.0.1
certificate, Edit->Trust this certificate for identifying websites. - Install extension Tampermonkey, install https://github.com/MaskRay/wechatircd/raw/master/injector.user.js. It will inject https://127.0.0.1:9000/injector.js to https://wx.qq.com. You need to change
127.0.0.1:9000
if you want wechatircd to listen on another address.
Firefox
- Install extension Greasemonkey,install the userscript.
- Visit https://127.0.0.1:9000/injector.js, Firefox will show "Your connection is not secure", Advanced->Add Exception->Confirm Security Exception
The server serves injector.js
and WebSocket connections on 127.0.0.1:9000 by default, which can be overriden with --http-listen 0.0.0.0 --http-port 9000
.
You can enable HTTPS in two ways:
--http-cert cert.pem --http-key key.pem
to make wechatircd serve HTTPS- Omit
--http-cert --http-key
to make wechatircd serve HTTP, and use Nginx (with HTTPS enabled) as a reverse proxy. In this case, you need to passHost:
to wechatircd (proxy_set_header Host $http_host;
) as it changes the WebSocket URL defined ininjector.js
according toHost:
specified by the browser.
- Run
wechatircd.py
- Visit https://wx.qq.com, the injected JavaScript will create a WebSocket connection to the server
- Open devtools, and run
injector.run()
- Connect to 127.0.0.1:6667 in your IRC client
You will join +wechat
channel automatically and find your contact list there. Some commands are available:
help
eval
, eval a Python expression, such as:eval server.nick2special_user
eval server.name2special_room
status
, show contacts/channelsreload_contact __all__
, reload all contact info in case ofno such nick/channel
in privmsg
The server can only be bound to one wx.qq.com account, however, you may have more than one IRC clients connected to the server.
- Standard IRC channels have names beginning with
#
. - WeChat groups have names beginning with
&
. The channel name is generated from the group title.SpecialChannel#update
- Contacts have modes
+v
(voice, usually displayed with a prefix+
).SpecialChannel#update_detail
- Multi-line messages:
!m line0\nline1
- Multi-line messages:
!html line0<br>line1
nick0: nick1: test
will be converted to@GroupAlias0 @GroupAlias1 test
, whereGroupAlias0
isMy Alias in Group
/Name
in profile/WeChat ID
set by that user. It corresponds toOn-screen names
in the mobile application.- Reply to the message at 12:34:SS:
@1234 !m multi\nline\nreply
, which will be sent as「Re GroupAlias: text」text
- Reply to the message at 12:34:56:
!m @123456 multi\nline\nreply
- Reply to the penultimate message (your own messages are not counted) in this channel/chat:
@2 reply
- Paste detection. Lines will be hold for up to 0.1 seconds before sending, lines in this interval will be packed to a multiline message
--http-url https://127.0.0.1:9000
if you want to shorten media URLs to something likehttps://127.0.0.1:9000/media/0
!m
, @3
, nick:
can be arranged in any order.
For WeeChat, its anti-flood mechanism will prevent two user messages sent to IRC server in the same time. Disable anti-flood to enable paste detection.
/set irc.server.wechat.anti_flood_prio_high 0
server-time
extension from IRC version 3.1, 3.2. wechatircd.py
includes the timestamp (obtained from JavaScript) in messages to tell IRC clients that the message happened at the given time. See http://ircv3.net/irc/. Seehttp://ircv3.net/software/clients.html for Client support of IRCv3.
Configuration for WeeChat:
/set irc.server_default.capabilities "account-notify,away-notify,cap-notify,multi-prefix,server-time,znc.in/server-time-iso,znc.in/self-message"
Supported IRC commands:
/cap
, supported 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
, delete a group member. You must be the group leader to do this. Due to the defect of the Web client, you may not receive notifcations about the change of members./kill $nick [$reason]
, cause the connection of that client to be closed/list
, list groups./mode +m
, no rejoin in--join new
mode./mode -m
to revert./motd
, view latest 5 commits of this repo/names
, update nicks in the channel./part [$channel]
, no longer receive messages from the channel. It just borrows the command/part
and it will not leave the group./query $nick
, open a chat window with$nick
./squit $any
, log out/summon $nick $message
,add a contact./topic topic
, change the topic of a group. Because IRC does not support renaming of a channel, you will leave the channel with the old name and join a channel with the new name./who $channel
, see the member list.
MSGTYPE_TEXT
,text, or invitation of voice/video callMSGTYPE_IMG
,image, displayed as[Image] $url
MSGTYPE_VOICE
,audio, displayed as[Voice] $url
MSGTYPE_VIDEO
,video, displayed as[Video] $url
MSGTYPE_MICROVIDEO
,micro video?,displayed as[MicroVideo] $url
MSGTYPE_APP
,articles from Subscription Accounts, Red Packet, URL, ..., displayed as[App] $title $url
QQ emojis are displayed as <img class="qqemoji qqemoji0" text="[Smile]_web" src="/zh_CN/htmledition/v2/images/spacer.gif">
, [Smile]
in sent messages will be replaced to emoticon.
Emojis are rendered as <img class="emoji emoji1f604" text="_web" src="/zh_CN/htmledition/v2/images/spacer.gif">
. Each emoji will be converted to a single character before delivered to the IRC client. Emojis may overlap as terminal emulators may not know emojis are of width 2,see 终端模拟器下使用双倍宽度多色Emoji字体.
--config
, short option-c
, config file path, see config- HTTP/WebSocket related options
--http-cert cert.pem
, TLS certificate for HTTPS/WebSocket. You may concatenate certificate+key, specify a single PEM file and omit--http-key
. Use HTTP if neither--http-cert
nor--http-key
is specified.--http-key key.pem
, TLS key for HTTPS/WebSocket--http-listen 127.1 ::1
, change HTTPS/WebSocket listen address to127.1
and::1
, overriding--listen
--http-port 9000
, change HTTPS/WebSocket listen port to 9000--http-root .
, the root directory to serveinjector.js
--http-url https://127.0.0.1:9000
, if specified, display media links as https://127.0.0.1:9000/document/$id ; if not,https://wx.qq.com/cgi-bin/...
- Groups that should not join automatically. This feature supplements join mode.
--ignore '&fo[o]' '&bar'
, do not auto join channels whose names(generated from Group Name) partially match regex&fo[o]
or&bar
--ignore-display-name 'fo[o]' bar
, short option-I
, do not auto join channels whose Group Name partially match regexfo[o]
orbar
--ignore-brand
, ignore messages from subscription accounts (MM_USERATTRVERIFYFALG_BIZ_BRAND
)- IRC related options
--irc-cert cert.pem
, TLS certificate for IRC over TLS. You may concatenate certificate+key, specify a single PEM file and omit--irc-key
. Use plain IRC if neither --irc-cert nor --irc-key is specified.--irc-key key.pem
, TLS key for IRC over TLS.--irc-listen 127.1 ::1
, change IRC listen address to127.1
and::1
, overriding--listen
.--irc-nicks ray ray1
, reverved nicks for clients.SpecialUser
will not have these nicks.--irc-password pass
, set the connection password topass
.--irc-port 6667
, IRC server listen port.
- Join mode, short option
-j
--join auto
, default: join the channel upon receiving the first message, no rejoin after issuing/part
and receiving messages later--join all
: join all the channels--join manual
: no automatic join--join new
: likeauto
, but rejoin when new messages arrive even if after/part
--listen 127.0.0.1
, short option-l
, change IRC/HTTP/WebSocket listen address to127.0.0.1
.- Server side log
--logger-ignore '&test0' '&test1'
, list of ignored regex, do not log contacts/groups whose names partially match--logger-mask '/tmp/wechat/$channel/%Y-%m-%d.log'
, format of log filenames--logger-time-format %H:%M
, time format of entries of server side log
--paste-wait 0.1
, lines will be hold for up to 0.1 seconds before sending, lines in this interval will be packed to a multiline message--sasl-password pass
, set the SASL password topass
.--special-channel-prefix
, choices:&
,!
,#
,##
, prefix for SpecialChannel. Quassel does not seem to support channels with prefixes&
,--special-channel-prefix '##'
to make Quassel happy
See wechatircd.service for a template of /etc/systemd/system/wechatircd.service
.
- Create a WebSocket connection to the server and retry on failures.
- Hook
contactFactory#{addContact,deleteContact}
to watch changes to the contacts. CtrlServer#onmessage
, handle commands (text/file messages, invite someone to the group, ...) from the server.CtrlServer#seenLocalID
, prevent the client from receiving messages sent by itself.
.
├── Web HTTP(s)/WebSocket server
├── Server IRC server
├── Channel
│ ├── StandardChannel IRC channels
│ ├── StatusChannel `+wechat`
│ └── SpecialChannel WeChat groups
├── (User)
│ ├── Client IRC clients
│ ├── SpecialUser WeChat users
├── (IRCCommands)
│ ├── UnregisteredCommands available commands: CAP NICK PASS USER QUIT
│ ├── RegisteredCommands
- Replace the mobile application with your IRC client. See WeeChat操作各种聊天软件.
- Bot
- Log. It is difficult to export log from the mobile client https://maskray.me/blog/2014-10-14-wechat-export
If you cannot tolerant scanning QR codes with your phone everyday, see 无需每日扫码的IRC版微信和QQ:wechatircd、webqqircd.
Some special accounts' UserName
do not have the @
prefix: newsapp,fmessage,filehelper,weibo,qqmail,fmessage
。Standard accounts' UserName
start with @
; Groups' UserName
start with @@
。UserName
are different among sessions. Uin
looks like an unique identifier, but most of the time its value is 0.
A group's OwnerUin
is the owners's Uin
,but most of the time Uin
is 0.
My account
angular.element(document.body).scope().account
List of all contacts
angular.element($('#navContact')[0]).scope().allContacts
Delete a member from a group
var injector = angular.element(document).injector()
# 这里获取了chatroomFactory,还可用于获取其他factory、service、controller等
var chatroomFactory = injector.get('chatroomFactory')
# 设置其中的`room`与`userToRemove`
chatroomFactory.delMember(room.UserName, userToRemove.UserName)`
Send a message to the current chat
angular.element('pre:last').scope().editAreaCtn = "Hello,微信";
angular.element('pre:last').scope().sendTextMessage();
If you cannot tolerant scanning QR codes with your phone everyday, you can run the browser and wechatircd on a server.
- Create a new browser user profile with
chromium --user-data-dir=$HOME/.config/chromium-wechatircd
, and do the aforementioned configuration (certificate forinjector.js
, Tampermonkey,injector.user.js
), then close the browser. - Install xvfb (
xorg-server-xvfb
on Arch Linux) xvfb-run -n 99 chromium --user-data-dir=$HOME/.config/chromium-wechatircd https://wx.qq.com
- Wait a few seconds for the QR code.
DISPLAY=:99 import -window root /tmp/a.jpg && $your_image_viewer /tmp/a.jpg
, take a screenshot and scan the QR code with your mobile application.
You can interact with the browser using VNC:
x11vnc -localhost -display :99
- In another terminal,
vncviewer localhost
An alternative is x2go, see 无需每日扫码的IRC版微信和QQ:wechatircd、webqqircd.
On the mobile application, users' On-screen Names
are resolved in this order:
Set Remark and Tag
if setMy Alias in Group
(Group Alias
) if setName
in his/her profileWeChat ID
Contact information is given in APIs batchgetcontact
and webwxsync
. The JSON serialization uses misleading field names.
WeChat friend in contactFactory#addContact
:
.Alias
:Name
in his/her profile.NickName
:WeChat ID
.RemarkName
:Set Remark and Tag
WeChat friend/non-contact in .MemberList
:
.DisplayName
:My Alias in Group
.NickName
:Name
in his/her profile orWeChat ID
JSON for one user may be returned repeatedly and all these fields may be empty. Users' nicks are generated by looking for the first non-empty value from these fields: .RemarkName
, .NickName
, .DisplayName
. You may see xx now known as yy
in your IRC client if a room contact shares multple rooms with you.
You may see these messages in the DevTools console:
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)
Uncaught TypeError: angular.forEach is not a function
injector.js
should be executed after vendor_*.js
and before index_*.js
. However, TamperMonkey cannot finely control the execution time due to the limitation of Chrome.
The WebSocket connection to wechatircd.py
should be closed in this case, let users know they should reload the webpage.
- Log filenames may contain invalid filenames (
:
) on Windows - Stable channel names. This makes server-side log coherent and users will not be distracted by
PART (Change name)
JOIN
messages. Channel names are generated from.NickName
(Group Name) and Group Name may change. I do not know any persistent ID of an account/group becauseUserName
changes in each new session.
- miniircd. Copied a lot of protocol related stuff from miniircd.
- RFC 2810: Internet Relay Chat: Architecture
- RFC 2811: Internet Relay Chat: Channel Management
- RFC 2812: Internet Relay Chat: Client Protocol
- RFC 2813: Internet Relay Chat: Server Protocol