2

参考

前言

现在WebQQ已经凉了,网上那些基于WebQQ协议的框架也随之而去。
现在想基于QQ做些自动化的可以考虑IOTQQ,这是一款基于MacQQ逆向的QQ机器人框架。

服务器部署

其实IOTQQ项目主页的步骤介绍已经很清晰,这里只说一些注意点吧:

  • 可以使用docker实现单台服务器启动多个IOTQQ实例,参见文章开头的参考。
  • 现在腾讯对这类基于逆向破解协议的野生机器人封杀的还是很厉害的,安全起见,建议注册新QQ号进行测试。
  • 3.0.1以上的新版IOTQQ不再需要通过公网和Github交互,运行在本地也不再需要内网穿透,部署方式大大简化了。
  • 部署在服务器建议尽量用固定的一台服务器IP,否则说不定腾讯会因为异地登录封杀你的QQ号。
  • iotbot3.0.x的Gitter Token是跟QQ号绑定的,不能随意更换登录QQ,解绑请联系IOTQQ作者。
  • 经过本人一段时间的验证,IOTQQ运行于CentOS坑有点多,建议使用Ubuntu。
  • 64位系统应该下载iotbot_3.x.x_linux_arm64.tar.gz这个包。
  • 用下面的命令后台启动IOTQQ:

    nohup /path/to/iotbot >> /path/to/iotbot/log.txt 2>&1 &
  • 首次启动iotbot会拉取部分脚本并有详细输出,当出现Everything is ok!说明服务就绪,即可开始登录流程
  • 初始化成功以后请求http://IP:PORT/v1/Login/GetQRcode获取登录二维码,手机QQ扫码登陆即可。

WEB_API的调用

  • IOTQQ提供Web API供外部调用,只要知道接口地址,可以用任何http工具或者编程语言调用
  • IOTQQ项目中有个WebAPI.json,可以直接导入Chrome插件RestletClient,既可以直接调用测试,也可以作为文档参考,很方便
  • 这个插件在Chrome商店里也能搜索到,新版本叫Talend API Tester
  • 把WebAPI.json导入之前,可以找个文本编辑器修改一下,主要是把接口地址和QQ号批量替换一下,这样导入后可以直接用
  • 下面是使用request调用Web API的例子:

    const rp = require('request-promise').defaults({ json: true, gzip: true })
    async function callApi (name, params) {
      const url = `${WEB_API}/LuaApiCaller?qq=${LOGIN_QQ}&funcname=${name}&timeout=10`
      if (params) return rp.post(url, { body: params })
      return rp.get(url)
    }

WebSocket接口的使用

  • WebAPI是用来发消息、发指令的,对应的收消息、收事件则需要使用IOTQQ的websocket接口
  • IOTQQ的websocket是基于socket.io实现的,不能直接用ws://...方式访问,因此网上的一些websocket测试工具都无法使用,只能用兼容socket.io的客户端访问
  • nodejs开发需要引入socket.io-client
  • 简单代码如下:

    const io = require('socket.io-client')
    const socket = io(WS_API, { transports: ['websocket'] })
    socket.on('connect', e => {
      console.log('WS已连接')
      socket.emit('GetWebConn', '' + LOGIN_QQ, (data) => console.log(data))
    })
    socket.on('disconnect', e => console.log('WS已断开', e))
    socket.on('OnGroupMsgs', async data => {
      console.log('>>OnGroupMsgs', JSON.stringify(data, null, 2))
    })
    socket.on('OnFriendMsgs', async data => {
      console.log('>>OnFriendMsgs', JSON.stringify(data, null, 2))
      const { FromUin, MsgType, Content } = data.CurrentPacket.Data
      if (MsgType !== 'TextMsg') return
      const reply = Content.replace(/你/g, '我').replace(/(?:么?|么\?|吗?|吗\?|?|\?)?$/, '!')
      const params = { toUser: FromUin, sendToType: 1, sendMsgType: 'TextMsg', content: reply, groupid: 0, atUser: 0, replayInfo: null }
      const resp = await callApi('SendMsg', params)
      console.log('callApi.result', resp)
    })
    socket.on('OnEvents', async data => {
      console.log('>>OnEvents', JSON.stringify(data, null, 2))
    })

    比较关键的有两点:

    1. 调用io函数创建连接时需要提供{ transports: ['websocket'] }参数
    2. 连接上之后,需要先向服务器发送GetWebConn事件,否则收不到事件
  • 上面代码会打印输出收到的所有群消息、好友消息和事件,对于好友消息,会自动回复
  • 从IOTQQ v2版开始,已经简化了ws部分,现在架构很清晰,只有上面代码里的OnGroupMsgs, OnFriendMsgs, OnEvents三种事件,其它功能都需通过调用web api实现

服务安全性考虑

  • 大家应该注意到了,IOTQQ的Web API是赤裸裸的暴露在公网上的,这意味着,如果有人知道了你的API地址和当前登录的QQ号,他就可以用你的QQ来群发消息了,因此如果不只是玩玩的话,我们需要慎重考虑安全性问题
  • 可以修改IOTQQ/CoreConf.conf里的Port配置项为127.0.0.1:xxx来仅允许本地访问,然后用nginx反向代理来控制外网访问权限
  • 所有的Web api,加上Basic Auth验证,即需要客户端请求中包括Authorization请求头,可以自行设定用户名密码
  • 综上,在nginx.conf中加上下面配置即可:

    location /v1/Github/WebHook {
      proxy_pass http://localhost:8888;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location /v1/ {
      proxy_http_version 1.1;
      if ($http_authorization != "Basic aW90cXE6MTIzNDU2Nzg=") {
        return 401;
      }
      proxy_pass http://localhost:8888;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location / {
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_http_version 1.1;
      proxy_pass http://localhost:8888;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    说明: if ($http_authorization != "Basic aW90cXE6MTIzNDU2Nzg=") 这一段进行用户名密码的比对,其中Basic后面的字符串的算法为BASE64(<用户名> + ':' + <密码>)

  • 经过以上配置后,对于node.js端的Web API请求,只要给request的options参数中添加如下字段即可正常交互:auth: { user: <用户名>, pass: <密码> }
  • websocket接口可以同样通过nginx反向代理实现权限验证,见本文的续篇

rockswang
1.4k 声望154 粉丝

To play is Human