基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人

说明:文章内容全部截选自实验楼项目教程【基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人】.

相信大家平时可能也在各种 QQ 群里遇到过一种叫做 QQ 群机器人的存在,其大多是基于腾讯 SmartQQ 协议实现的,在 github 上有很多关于此的开源项目。

这个项目就用 QQ 机器人,配合图灵机器人的 api,实现一个实现自动回复,自定义回复,满足群里日常聊天互动所需的群聊。

效果图:

《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 image.png
《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 image.png
《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 image.png

开发前准备工作

1. QQRobot 源码

项目地址:https://github.com/zeruniverse/QQRobot

clone or download 到本地。

打开 Xfce 终端(指实验楼在线开发环境):

$ cd Code
$ git clone https://github.com/zeruniverse/QQRobot

2. 图灵机器人

访问图灵机器人官网。注册一个账号。

2.1. 创建机器人

选择 QQ 机器人。

《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 此处输入图片的描述

2.2 设置机器人

《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 此处输入图片的描述

2.3 得到 APIkey

《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 此处输入图片的描述

3. 代码配置

3.1 机器人 API

编辑 Code\QQRobot\ 目录下的 QQbot.py,修改其第34行,加入刚刚申请到的图灵机器人 APIkey。

tulingkey='图灵 API'

3.2 群监控

编辑 Code\QRobot\ 目录下的 groupfollow.txt,将需要机器人监控、回复的群的名字写入,每行一个群名。(注意 : 中文群名格式为 utf-8。)

3.3 额外操作

在实验继续操作之前,需要先用 webqq 登录一下,扫码才有效果,http://web2.qq.com/ ,这一步很重要,之前大家做实验失败很多就是这个原因。群聊的效果不好,建议就试试私聊的效果。

3.4 启动

Code\QQRobot\ 目录下执行命令,:

$ sudo nohup python2 QQBot.py >qbot.log&

如果没有问题,会在当前目录下生成一个名为 v.png 的二维码图片。等待二维码时间可能比较久。通过手机 QQ 扫描该图片,完成登陆。扫描后二维码消失。

$ cat log.log

执行可以输出运行 LOG,查看程序运行过程。

《基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人》 此处输入图片的描述

3.5 功能

机器人会根据指令做出相应动作:

  • 群聊智能回复,在群中通过发送 !ai问题语句,r如:!ai谁最帅?,则机器人向AI平台请求问题的回复并回复到群,带有!ai
    关键字时优先触发此功能。
  • 群聊学习功能,在群中通过发送 !learn {ha}{哈哈}语句,则机器人检测到发言中包含”ha”时将自动回复”哈哈”。!delete {ha}{哈哈}可以删除该内容。学习内容会自动储存在database.群号.save文件!deleteall 可删除该群所有记录。
  • 群聊复读功能,检测到群聊中连续两个回复内容相同,将自动复读该内容1次。

3.6 注意

如果绑定成功后,无法自动回复,查看一下日志文件 log.log,原因可以如下:

  • {"errmsg":"","retcode":103}: 这是登录时验证失败,用你的浏览器登录一遍 webqq,然后再重试,类似于账号被保护的原因。
  • 如果账号被怀疑被盗号(异地登陆),群聊消息会发不出去。表现为程序能收到群聊消息,群聊消息发送返回值为发送成功,但其他群成员无法看到您发出的消息。大约登陆10分钟后您会收到QQ提醒提示账号被盗,要求改密码,同时账号被临时冻结。但是只要按腾讯的要求,修改一下密码,接触冻结之后就可以继续用了。
  • QQ 会临时封禁机器人的临时对话回复和群回复,原理未知,每次封禁约为10分钟。表现为发送消息返回值 retcode 为 0 但其他人无法看到。

ps. 目前 linux 上能够运行的大多数 QQ 机器人都是基于 webQQ 协议实现的,稳定性很差。如果确实需要这个功能,推荐使用 windows 上的一些成熟版本(基于逆向破解的安卓 QQ 协议),稳定性要好得多,而且不容易被腾讯封禁。

QQRobot 分析

1 登录

自2015年10月开始,Web QQ 废除了原先的用户名/密码登录,取而代之的是手机 QQ 扫二维码的登录方式。

目前的协议规定的登录过程如下:

  • 获取二维码
  • 确认二维码已被扫描
  • 获取鉴权参数 ptwebqq
  • 获取鉴权参数 vfwebqq
  • 获取鉴权参数 uin 和 psessionid

登录过程要求获得如下参数,用于其他接口:

  • ptwebqq:保存在 Cookie 中的鉴权信息
  • vfwebqq:类似于 Token 的鉴权信息
  • psessionid:类似于 SessionId 的鉴权信息
  • clientid:设备 id,为固定值53999199
  • uin:登录用户 id(其实就是当前登录的 QQ 号)

1.1 获取二维码

请求方式: Get

url:https://ssl.ptlogin2.qq.com/ptqrshow?appid=501004106&e=0&l=M&s=5&d=72&v=4&t=0.1

1.2 获取二维码扫描状态

请求方式:Get

url:

https://ssl.ptlogin2.qq.com/ptqrlogin?webqq_type=10&remember_uin=1&login2qq=1&aid=501004106 &u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10 &ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert &action=0-0-157510&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10143&login_sig=&pt_randsalt=0

referer:

https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&no_verifyimg=1 &s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert &strong_login=1&login_state=10&t=20131024001

返回内容:

  • 扫描前(未失效):
ptuiCB('66','0','','0','二维码未失效。(3203423232)','');
  • 扫描后,认证前:
ptuiCB('66','0','','0','二维码认证中。(3203423232)','');
  • 认证后:
ptuiCB('66','0','','0','http://ptlogin4.web2.qq.com/check_sig?xxxxxx','');

这个请求可以直接轮训请求,直到认证成功后,将返回的地址保存下来用作下次请求。

1.3 获取 ptwebqq

请求方式:Get

url:

刚才获得的地址

referer:

http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1

这个请求成功后的 Http 状态码是302,之后需要将 Cookie 中的 ptwebqq 保存下来。

1.4 获取 vfwebqq

请求方式:Get

url:

http://s.web2.qq.com/api/getvfwebqq?ptwebqq=#{ptwebqq}&clientid=53999199&psessionid=&t=0.1

referer:

http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1

url 中需要填入上一步获取到的 ptwebqq,请求成功后会返回一个 JSON,将 result.vfwebqq 保存下来。

1.5 获取psessionid和uin

请求方式:Post

url:

http://d1.web2.qq.com/channel/login2

referer:

http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2

表单数据只有一个,Key 为 r,Value 是一个 JSON,内容为:

{
  "ptwebqq": "#{ptwebqq}",
  "clientid": 53999199,
  "psessionid": "",
  "status": "online",
}

其中真正的动态参数只有 ptwebqq,请求成功后会返回一个 JSON,将 result.uin 和 result.psessionid 保存下来。

需要注意的是,这里也返回了一个 result.vfwebqq,但是这个值是无用的。

登录流程就是完成这五个参数的获取:

  • ptwebqq:在流程三中通过从 Cookie 中获得
  • vfwebqq:在流程四中通过返回的 JSON中获得(在流程五中也会返回一个,不要使用那个)
  • uin:在流程五中通过返回的 JSON 中获得(其实就是 qq 号)
  • psessionid:在流程五中通过返回的 JSON 中获得
  • clientid:固定值为 53999199

登录成功后一般可以维持 2 天左右,并且如今 Web QQ 是不会出现顶号情况的,但是多终端登录后在接收消息等接口可能会出现冲突的情况。

收发消息

1 轮训消息

请求方式:Post

url:

http://d1.web2.qq.com/channel/poll2

referer:

http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2

请求参数只有一个 r,值是一个 JSON,内容为:

{
    "ptwebqq": "#{ptwebqq}",
    "clientid": 53999199,
    "psessionid": "#{psessionid}",
    "key": ""
}

ptwebqq 和 psessionid 都是登录后获得的参数。

请求成功后返回的内容为:

{
    "result": [
        {
            "poll_type": "message",
            "value": {
                "content": [
                    [
                        "font",
                        {
                            "color": "000000",
                            "name": "微软雅黑",
                            "size": 10,
                            "style": [
                                0,
                                0,
                                0
                            ]
                        }
                    ],
                    "好啊"
                ],
                "from_uin": 3785096088,
                "msg_id": 25477,
                "msg_type": 0,
                "time": 1450686775,
                "to_uin": 931996776
            }
        }
    ],
    "retcode": 0
}

poll_type 为 message 表示这是个好友消息。from_uin 是用户的编号,可以用于发消息,但不是 qq 号。to_uin 是接受者的编号,同时也是 qq 号。time 为消息的发送时间,content[0] 为字体,后面为消息的内容。

如果为群消息,返回内容为:

{
    "result": [
        {
            "poll_type": "group_message",
            "value": {
                "content": [
                    [
                        "font",
                        {
                            "color": "000000",
                            "name": "微软雅黑",
                            "size": 10,
                            "style": [
                                0,
                                0,
                                0
                            ]
                        }
                    ],
                    "好啊",
                ],
                "from_uin": 2323421101,
                "group_code": 2323421101,
                "msg_id": 50873,
                "msg_type": 0,
                "send_uin": 3680220215,
                "time": 1450687625,
                "to_uin": 931996776
            }
        }
    ],
    "retcode": 0
}

其中 poll_type 会变成 group_message,group_code 和 from_uin 都为群的编号,可以用于发群消息,但不是群号。send_uin 为发信息的用户的编号。其他的字段和上面的相同。

如果是讨论组消息, poll_type 会变为 discu_message,did 为讨论组的编号,其他的字段都和群消息相同。

{
    "result": [
        {
            "poll_type": "discu_message",
            "value": {
                "content": [
                    [
                        "font",
                        {
                            "color": "000000",
                            "name": "微软雅黑",
                            "size": 10,
                            "style": [
                                0,
                                0,
                                0
                            ]
                        }
                    ],
                    "好啊",
                ],
                "from_uin": 2322423201,
                "did": 2322423201,
                "msg_id": 50873,
                "msg_type": 0,
                "send_uin": 3680220215,
                "time": 1450687625,
                "to_uin": 931996776
            }
        }
    ],
    "retcode": 0
}

注意:

  • 服务端收到这个请求后,如果没有新消息,会一直保持住链接,所以遇到 ReadTimeout 异常是正常的
  • Web QQ 无法接受图片、@别人、自定义表情等消息,消息内容只有默认表情和文字 如果消息内容为表情,content[1] 的内容就不是 String 类型了,而是一个 JSONArray 类型,里面有表情的编号,所以 content 的长度有可能大于 2,代表着消息的内容为文字和表情的混排,content[1] 开始的每一位都是分割后的文字或表情
  • 这个请求有时候会返回retcode的值为103,此时需要登录 Smart QQ,确认能收到消息后点击设置-退出登录,就会恢复正常了
  • 在这里接受到的 uin、group_code 等并不是固定的,而是会改变的,所以不要长时间保存这些信息,

2 发送消息给好友

请求方式:Post

url:

http://d1.web2.qq.com/channel/send_buddy_msg2

referer:

http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2

请求参数只有一个 r,值是一个 JSON,内容为:

{
    "to": #{user_id},
    "content": [
        "#{msg}",
        [
            "font",
            {
                "name": "宋体",
                "size": 10,
                "style": [
                    0,
                    0,
                    0
                ],
                "color": "000000"
            }
        ]
    ].to_string(),
    "face": 522,
    "clientid": 53999199,
    "msg_id": 65890001,
    "psessionid": "#{psessionid}",
}

psessionid 是登录后获取的参数,msg 是需要发送的内容,to是用户编号,msg_id 只要是一个比较大的数字即可。

如果发送成功,会返回如下数据:

{
    "errCode": 0,
    "msg": "send ok"
}

3 发送群消息

请求方式:Post

url:

http://d1.web2.qq.com/channel/send_qun_msg2

referer:

http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2

请求参数和上面几乎一样,只是将 to 替换成了 group_uin:

{
    "group_uin": #{group_uin},
    "content": [
        "#{msg}",
        [
            "font",
            {
                "name": "宋体",
                "size": 10,
                "style": [
                    0,
                    0,
                    0
                ],
                "color": "000000"
            }
        ]
    ].to_string(),
    "face": 522,
    "clientid": 53999199,
    "msg_id": 65890001,
    "psessionid": "#{psessionid}",
}

4 发送讨论组消息

请求方式:Post

url:

http://d1.web2.qq.com/channel/send_discu_msg2

referer:

http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2

格式也是一样的,只是替换为了 did:

{
    "did": #{discuss_id},
    "content": [
        "#{msg}",
        [
            "font",
            {
                "name": "宋体",
                "size": 10,
                "style": [
                    0,
                    0,
                    0
                ],
                "color": "000000"
            }
        ]
    ].to_string(),
    "face": 522,
    "clientid": 53999199,
    "msg_id": 65890001,
    "psessionid": "#{psessionid}",
}

由于篇幅有限,还有以下内容没能分享:

  • 好友相关
    • 获取好友列表
    • 获取好友在线状态
    • 通过uin获得QQ号
    • 获取好友详细信息
  • 群和讨论组相关
    • 获取群列表
    • 获取群详细信息
    • 获取讨论组列表
    • 获取讨论组详细信息
  • 其他
    • 获取最近会话列表
    • 获取当前登录用户信息

如果想要查看完整内容呢,点击【基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人】,即可查看完整教程内容了~

这个项目教程主要是教你部署图灵机器人与 QQ 机器人开源框架的结合,大家在部署过程中,可以增进对 SmartQQ 协议的了解,如果感兴趣的话,可以根据该框架,开发一下其他有意思的插件。

    原文作者:实验楼
    原文地址: https://www.jianshu.com/p/9e18b46bfc65
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞