Learner0x5a's Studio.

基于酷Q开发的QQ聊天机器人

Word count: 1.5kReading time: 6 min
2020/12/14 Share

基于酷Q开发的QQ聊天机器人

接入腾讯AI开放平台需要计算资源较好以及网络条件较好,不然延迟太大,不能支持高频率聊天或者高并发聊天。

安装酷Q

酷Q机器人是一款比较成熟的QQ机器人,建议开一个QQ小号玩~
安装完直接打开,登录小号,配置应用,可以先走一遍互动式教程,非常容易上手。
酷Q的图灵机器人需要另外申请,最可恨的是实名制之后每天也就100次的调用次数(即聊天回复累积多于100条时就GG),限制太死。

插件开发: C++ SDK

C++ SDK的配置

首先下载/克隆项目,安装Visual Studio 2019
直接用VS 2019打开 awesome-bot 文件夹,VS 将会自动进行 CMake 配置,产生的 build 目录在 out
中,点击菜单栏的「生成」-「全部生成」即可构建,产生的 app.dll 和 app_dev.exe 文件在 out/build/config/ 中。

编译插件

用VS 2019的x86-Release模式生成即可

集成到酷Q

打开酷Q的开发模式
然后将awesome-bot中的app.json和awesome-bot/build中的app.dll一同放进酷Q目录的dev/com.example.demo目录(需手动创建)中
再重启酷Q或重载应用即可在应用管理中看到demo应用,启用即可。

SDK自带了一个demo,私聊是一个复读机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
on_private_message([](const PrivateMessageEvent &e) {  
try {
auto msgid = send_private_message(e.user_id, e.message); // 直接复读消息
logging::info_success("私聊", "私聊消息复读完成, 消息 Id: " + to_string(msgid));
send_message(e.target,
MessageSegment::face(111) + "这是通过 message 模块构造的消息~"); // 使用 message 模块构造消息
} catch (ApiError &e) {
logging::warning("私聊", "私聊消息复读失败, 错误码: " + to_string(e.code));
}
});
```

## 接入腾讯AI开放平台

复读机有点太蠢了,考虑用一下现成的AI
腾讯AI开放平台有一个[智能闲聊](https://ai.qq.com/product/nlpchat.shtml)的功能,考虑用一下
调用智能闲聊的API需要HTTP通信和json解析

### [jsoncpp](https://github.com/open-source-parsers/jsoncpp)的安装

最新版的安装方法如下(需要cmake):

```bash
cd json-master
cmake.exe CMakeList.txt
```

---|---

得到VS工程文件`ALL_BUILD.vcxproj`,打开编译即可。
但这里遇到一个问题,酷Q插件要求必须是x86的PE,但是最新版jsoncpp似乎不支持x86了。
下载jsoncpp 0.5.0版,打开解决方案jsoncpp-src-0.5.0/makefiles/vs71/jsoncpp.sln(vs71即VS2003,高版本VS默认转换即可)
编译release版时,注意修改生成静态库文件的工程的属性:菜单 - 项目 - 属性 - 配置属性 - C/C++ - 输出文件 - 汇编程序输出 改为
无列表

### jsoncpp的使用

VS中新建一个win32控制台工程,项目属性配置如下:

* C/C++ - 附加包含目录 设为jsoncpp源码的include目录,例如jsoncpp-src-0.5.0/include/json,或者直接将这个文件夹复制到cpp文件目录下
* C/C++ - 代码生成 - 运行库和jsoncpp编译时保持一致
* 链接器 - 常规 - 附加库目录 添加jsoncpp工程编译出的静态库所在的目录
* 链接器 - 输入 - 附加依赖项 添加jsoncpp工程编译出的静态库的文件名

编写源代码时,引用方法如下:

```C++
#include <iostream>
#include <fstream>
#include "json/json.h"
using namespace std;

int main()
{
Json::Value value1;

// 给字段赋值,key必须为string型
// 类似STL的map,访问一个不存在的字段时会自动新建一个字段
value1["key"] = "value";
```


### HTTP通信

C++的HTTP通信比较麻烦,可以借助libcurl实现 -> Visual Studio
2017/2019的libcurl[编译](https://stackoverflow.com/questions/53861300/how-do-you-properly-install-libcurl-for-use-in-visual-studio-2017/54680718#54680718)

* 这里碰到一个bug,jsoncpp和libcurl分别编译都能通过,但是放到同一个项目里面的时候不兼容,MT/MD的问题,后来干脆都不用
* 根据腾讯AI开放平台的[通信协议](https://ai.qq.com/doc/auth.shtml#3-%E6%9C%80%E7%BB%88%E8%AF%B7%E6%B1%82%E6%95%B0%E6%8D%AE)
* 计算完sign之后把参数字典做url编码(即实现一下PHP的`http_build_query()`函数),写到文件`CQ_question`用Python处理通信,C++和Python用文件交互即可


```python
def get_content():
url = "https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat"
f = open("CQ_question","r",encoding="utf-8")
question = f.readlines()[0]
f.close()
r = requests.post(url,data=question)
return r.json()["data"]["answer"]

f1 = open("CQ_answer","w",encoding="utf-8")
f1.write(answer)
f1.close()

计算sign的过程中用到了md5url编码
C++的字典用map实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    map<string, string> paras;  
paras["app_id"] = "xxxxxxx";
paras["time_stamp"] = to_string(time(0));
paras["nonce_str"] = randstr();
paras["session"] = "10000";
paras["question"] = "111";

map<string, string> getSign(map<string,string> paras, string appkey) {
string before_md5 = "";
Encoder encoder;
for (auto iter = paras.begin(); iter != paras.end();iter++) {
cout << iter->first << ":" << iter->second << endl;
before_md5 += iter->first + "=" + encoder.UTF8UrlEncode(iter->second) + "&";
}

before_md5 += "app_key=" + appkey;
cout << before_md5 << endl;
MD5 sign;
sign.update(before_md5);
string upper_sign = sign.toString();
transform(upper_sign.begin(), upper_sign.end(), upper_sign.begin(), ::toupper);
paras["sign"] = upper_sign;
cout << upper_sign << endl;
return paras;
}
string appkey = "xxxxxx";
paras = getSign(paras,appkey);

Encoder encoder;
string http_query = "";
for (auto iter = paras.begin(); iter != paras.end(); iter++) {
cout << iter->first << ":" << iter->second << endl;
http_query += iter->first + "=" + encoder.UTF8UrlEncode(iter->second) + "&";
}
http_query.pop_back();
ofile.open("CQ_question", ios::out);
ofile << "question" << endl;
ofile.close();
system("python CQ.py");
ifstream ifs;
string buf="";
_sleep(1000);
do {
ifs.open("CQ_answer", ios::in);

getline(ifs, buf);
if (buf.size() > 1) {
logging::info_success("腾讯AI", "收到消息: " + buf);
break;
}
} while (true);

send_message(e.target, MessageSegment::face(111) + buf); // 使用 message 模块构造消息

参考Python版本

CATALOG
  1. 1. 基于酷Q开发的QQ聊天机器人
    1. 1.1. 安装酷Q
    2. 1.2. 插件开发: C++ SDK
      1. 1.2.1. C++ SDK的配置
      2. 1.2.2. 编译插件
      3. 1.2.3. 集成到酷Q
    3. 1.3. 参考Python版本