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

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

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

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

效果图:

基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人_第1张图片
image.png
image.png
基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人_第2张图片
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 机器人。

此处输入图片的描述

2.2 设置机器人

此处输入图片的描述

2.3 得到 APIkey

此处输入图片的描述

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,查看程序运行过程。

此处输入图片的描述

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 协议的了解,如果感兴趣的话,可以根据该框架,开发一下其他有意思的插件。

你可能感兴趣的:(基于 QQRobot 和图灵机器人实现的 QQ 群聊机器人)