V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MaskRay
V2EX  ›  编程

webqqircd (webqq -> irc server, 类似的还有 wechatircd)

  •  2
     
  •   MaskRay · 2016-04-11 23:20:56 +08:00 · 2800 次点击
    这是一个创建于 2935 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://maskray.me/blog/2016-04-11-webqqircd

    webqqircd 类似于 bitlbee ,在 WebQQ(SmartQQ)和 IRC 间建起桥梁,可以使用 IRC 客户端收发消息。大部分代码来自wechatircd,为适配 QQ 做了一些修改,去除了 wechatircd 中的 token ,因此只支持单客户端。

    原理

    修改 WebQQ 用的 JS ,通过 WebSocket 把信息发送到服务端,服务端兼做 IRC 服务端,把 IRC 客户端的命令通过 WebSocket 传送到网页版 JS 执行。未实现 IRC 客户端,因此无法把 QQ 群的消息转发到另一个 IRC 服务器(打通两个群的 bot)。

    安装

    需要 Python 3.5 或以上,支持async/await语法 pip install -r requirements.txt安装依赖

    Arch Linux 可以安装https://aur.archlinux.org/packages/webqqircd-git,会自动在/etc/webqqircd/下生成自签名证书(见下文),导入浏览器即可。

    运行

    HTTPS 、 WebSocket over TLS

    推荐使用 TLS 。

    • openssl req -newkey rsa:2048 -nodes -keyout a.key -x509 -out a.crt -subj '/CN=127.0.0.1' -dates 9999创建密钥与证书。
    • Chrome 访问chrome://settings/certificates,导入 a.crt ,在 Authorities 标签页选择该证书, Edit->Trust this certificate for identifying websites.
    • Chrome 安装 Switcheroo Redirector 扩展,把http://pub.idqqimg.com/smartqq/js/mq.js重定向至https://127.0.0.1:9002/mq.js。若 js 更新,该路径会变化。
    • ./webqqircd.py --tls-cert a.crt --tls-key a.key,会监听 127.1:6668 的 IRC 和 127.1:9002 的 HTTPS 与 WebSocket over TLS

    HTTP 、 WebSocket

    如果嫌 X.509 太麻烦的话可以不用 TLS ,但 Chrome 会在 console 里给出警告。

    IRC 客户端

    • IRC 客户端连接 127.1:6668(weechat 的话使用/server add qq 127.1/6668),会自动加入+status channel
    • 登录http://w.qq.com
    • 回到 IRC 客户端,可以看到 QQ 朋友加入了+status channel ,在这个 channel 发信并不会群发,只是为了方便查看有哪些朋友。
    • QQ 朋友的 nick 优先选取备注名(RemarkName),其次为DisplayName(原始 JS 根据昵称等自动填写的一个名字)

    +status channel 可以执行一些命令:

    • help,帮助
    • status,已获取的 QQ 朋友、群列表
    • eval $password $expr: 如果运行时带上了--password $password选项,这里可以 eval ,方便调试,比如eval $password client.uin2qq_user

    若服务端或客户端重启,刷新 WebQQ 。

    IRC 命令

    webqqircd 是个简单的 IRC 服务器,可以执行通常的 IRC 命令,可以对其他客户端私聊。

    以下命令会有特殊作用:

    • 程序默认选项为--join auto,收到某个 QQ 群的第一条消息后会自动加入对应的 channel ,即开始接收该 QQ 群的消息。
    • /join [channel]表示开始接收该 QQ 群的消息
    • /list,列出所有 QQ 群
    • /names,更新当前群成员列表
    • /part [channel]的 IRC 原义为离开 channel ,转换为 QQ 代表在当前 IRC 会话中不再接收该 QQ 群的消息。不用担心, webqqircd 并没有主动退出群的功能
    • /query nick打开与$nick的私聊窗口,与之私聊即为在 QQ 上和他 /她 /它对话
    • /who channel,查看群成员列表

    JS 改动

    原始文件mq.js在 Chrome DevTools 里格式化后得到orig/mq.pretty.js,可以用diff -u orig/mq.pretty.js mq.js查看改动。

    修改的地方都有//@标注,结合 diff ,方便 WebQQ 更新后重新应用这些修改。增加的代码中大多数地方都用try catch保护,出错则consoleerr(ex.stack)

    目前的改动如下:

    mq.js开头

    创建到服务端的 WebSocket 连接,若onerror则自动重连。监听onmessage,收到的消息为服务端发来的控制命令:send_text_message等。

    定期把通讯录发送到服务端

    获取所有联系人(朋友、订阅号、群),deliveredContact记录投递到服务端的联系人,deliveredContact记录同处一群的非直接联系人。

    每隔一段时间把未投递过的联系人发送到服务端。

    收到 QQ 服务器消息messageProcess

    原有代码会更新未读标记数及声音提醒,现在改为若成功发送到服务端则不再提醒,以免浏览器的这个标签页造成干扰。

    Python 服务端代码

    当前只有一个文件webqqircd.py,从 miniircd 抄了很多代码,后来自己又搬了好多 RFC 上的用不到的东西……

    .
    ├── Web                      HTTP(s)/WebSocket server
    ├── Server                   IRC server
    ├── Channel
    │   ├── StandardChannel      `#`开头的 IRC channel
    │   ├── StatusChannel        `+status`,查看控制当前 QQ 会话
    │   └── WeChatRoom           QQ 群对应的 channel ,仅该客户端可见
    ├── (User)
    │   ├── Client               IRC 客户端连接
    │   ├── WeChatUser           QQ 用户对应的 user ,仅该客户端可见
    ├── (IRCCommands)
    │   ├── UnregisteredCommands 注册前可用命令: NICK USER QUIT
    │   ├── RegisteredCommands   注册后可用命令
    

    我的配置

    https://wiki.archlinux.org/index.php/Systemd/User

    ~/.config/systemd/user/webqqircd.service:

    [Unit]
    Description=webqqircd
    Documentation=https://github.com/MaskRay/webqqircd
    After=network.target
    
    [Service]
    WorkingDirectory=%h/projects/webqqircd
    ExecStart=/home/ray/projects/webqqircd/webqqircd.py --tls-key a.key --tls-cert a.crt --password a --ignore 不想自动加入的群名 0 不想自动加入的群名 1
    
    [Install]
    WantedBy=multi-user.target
    

    WeeChat:

    /server add qq 127.1/6668 -autoconnect
    
    2 条回复    2016-04-12 10:04:58 +08:00
    oott123
        1
    oott123  
       2016-04-12 07:47:25 +08:00
    听起来是依赖 Chrome 插件的?_(:з」∠)_不知道有没有想过做 headless 呢…
    MaskRay
        2
    MaskRay  
    OP
       2016-04-12 10:04:58 +08:00
    @oott123 因为要扫码登录……登录时留个 head 方便,既然有了就常驻好了……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5871 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 84ms · UTC 02:25 · PVG 10:25 · LAX 19:25 · JFK 22:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.