铮铮卡穆 发表于 2016-9-28 17:25

酷Q机器人开发笔记(C++)

本帖最后由 铮铮卡穆 于 2016-10-9 20:11 编辑

声明
本人做C开发的,C++略懂,最近觉得QQ群机器人有点意思,所以找到酷Q的平台玩一玩。
说实话,C++的SDK太不完善,函数没有说明,主要靠参数名来猜。论坛里基本都是易语言的,搞了一个发现实在是看不懂,不会写,但是注释相对完善,就当一个文档来看了,主要还是C++来做开发。
PS:两个开发者群都申请了,但是没人答复,不知道怎么回事,只好来论坛上边摸索边分享了。

前期准备
按照[官方文档],下载 VC++ SDK,开启开发者模式。SDK是用VC++2010编写的,实测用VS2015也可以。编译出dll之后,和json文件一起copy到酷Q的app文件夹,酷Q重启,正常就可以加载到新的插件了。
这里踩过的坑:

[*]一定要开启开发者模式,否则编译的dll库不会被载入;
[*] 程序里的appid、dll文件名、json文件名一定要一致,否则载入失败。
[*] 每次编译、更新dll之后都要重启酷Q,否则加载的还是原来的库,新增功能不生效。





蓝色幻想 发表于 2016-9-28 17:31

感谢分享经验,帮助更多开发者!

铮铮卡穆 发表于 2016-9-28 17:31

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-10-12 20:22 编辑

基本功能验证
消息回放
先放一段消息回放的代码,后面会用得到。
另外后文写的关于日志的部分,由于代码是私人的,我没有权力公开,所以就不要照抄了,作为参考代码来看看。

const char* buildSendBackMsg(const char* msg)
{
    static const char *back = "你刚才说:";
    char *sendback;
    if (NULL == msg)return NULL;
    sendback = (char*)malloc(strlen(msg) + +strlen(back) + 1);
    if (sendback)
    {
      sprintf(sendback, "%s%s", back, msg);
      return sendback;
    }
    else
      return NULL;
}
void releaseSendBackMsg(const char* msg)
{
    free((void*)msg);
}接收和发送私聊消息
__eventPrivateMsg 接收到私聊信息的回调函数
subType 子类型 11/来自好友 1/来自在线状态 2/来自群 3/来自讨论组
sendTime 时间戳
fromQQ 哪个QQ发过来的
msg 消息内容
font 字面意思字体
CQ_sendPrivateMsg 发送私聊消息
AuthCode 校验码,在Initialize初始化的全局变量ac
QQID 要发给哪个QQ号
msg 发送的内容<blockquote>CQEVENT(int32_t, __eventPrivateMsg, 24)(int32_t subType, int32_t sendTime, int64_t fromQQ, const char *msg, int32_t font) {上面这一块有问题,无法贴上来完整的代码,总被改掉

实测效果
* 日志和返回都正常,使用格式化输出请注意int64_t类型的数据应该使用%lld才能正确输出,否则会导致后面的格式错乱,后文不会再提;
* LOG_INFO是我自己的日志封装接口,不是SDK提供的,我不对外提供;
* 不是好友的QQ,从QQ群里点开机器人的,subtype是来自群;
* 不是好友的QQ,从讨论组里点开机器人的,subtype是来自讨论组;
* 来自在线状态不知道是什么;
* 消息内容为标准表情,日志里会使用 这样的编码来标记,不认识的表情用 来标记;
* 消息内容为emoji表情,日志里会使用 这样的编码来标记,关于CQ编码后面再详细研究。


铮铮卡穆 发表于 2016-9-28 17:36

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-10-9 19:54 编辑

接收和发送群消息
__eventGroupMsg 接收到群信息的回调函数
subType 子类型,实测全是1
sendTime 时间戳
fromGroup 哪个群发来的消息
fromQQ 哪个QQ发过来的
fromAnonymous 匿名者的标识
msg 消息内容
font 字体
CQ_sendGroupMsg 发送群消息
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 要发给哪个QQ群号
msg 发送的内容
CQEVENT(int32_t, __eventGroupMsg, 36)(int32_t subType, int32_t sendTime, int64_t fromGroup, int64_t fromQQ, const char *fromAnonymous, const char *msg, int32_t font) {

    const char* send = NULL;

    LOG_INFO("fromGroup:[%lld], fromQQ:[%lld], fromAnonymous:[%s], msg:%s", fromGroup, fromQQ, fromAnonymous, msg);

    send = buildSendBackMsg(msg);
    CQ_sendGroupMsg(ac, fromGroup, send);
    releaseSendBackMsg(send);

    return EVENT_BLOCK;
}
实测效果

[*]* 非匿名用户发消息,fromAnonymous为空字符串;
* 匿名用户发消息,fromQQ为80000000,msg里面会包含匿名的名字(如:[誉王]),fromAnonymous为一串base64编码,是匿名者的标识,后面会介绍利用这个标识对指定的匿名用户禁言;


铮铮卡穆 发表于 2016-9-28 17:38

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-10-9 19:55 编辑

接收和发送讨论组消息
__eventDiscussMsg 接收到讨论组信息的回调函数
subType 子类型
sendTime 时间戳
fromDiscuss 哪个讨论组发来的消息
fromQQ 哪个QQ发过来的
msg 消息内容
font 字体
CQ_sendDiscussMsg 发送讨论组消息
AuthCode 校验码,在Initialize初始化的全局变量ac
discussid 要发给哪个讨论组号
msg 发送的内容
<blockquote>CQEVENT(int32_t, __eventDiscussMsg, 32)(int32_t subType, int32_t sendTime, int64_t fromDiscuss, int64_t fromQQ, const char *msg, int32_t font) {


实测效果
* 日志和返回都正常



︶悠扬、Le逍遥^ 发表于 2016-9-28 17:40

本帖最后由 ︶悠扬、Le逍遥^ 于 2016-9-28 17:41 编辑

感谢分享
msg乱码可能是正则的问题

铮铮卡穆 发表于 2016-9-28 17:41

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-10-9 19:57 编辑

群事件
__eventSystem_GroupAdmin 管理员变动
subType 子类型,1/被取消管理员 2/被设置管理员
fromGroup 群号
beingOperateQQ 被操作者QQ号
__eventSystem_GroupMemberDecrease 群成员减少
subType 子类型,1/群员离开 2/群员被踢 3/自己(即登录号)被踢
sendTime 时间戳
fromGroup 群号
fromQQ 操作者QQ号(管理员,仅subType为2、3时存在)
beingOperateQQ 被操作者QQ号
__eventSystem_GroupMemberIncrease 群成员增加
subType 子类型,1/管理员已同意 2/管理员邀请
sendTime 时间戳
fromGroup 群号
fromQQ 操作者QQ号(管理员)
beingOperateQQ 被操作者QQ号

<blockquote>CQEVENT(int32_t, __eventSystem_GroupAdmin, 24)(int32_t subType, int32_t sendTime, int64_t fromGroup, int64_t beingOperateQQ) {


实测效果
* 1/被取消管理员 2/被设置管理员 1/群员离开 2/群员被踢 1/管理员已同意 2/管理员邀请 六个功能的subtype验证正常,**3/自己(即登录号)被踢**没有出现,被踢时表现为**2/群员被踢**;


铮铮卡穆 发表于 2016-9-28 17:43

RE: 酷Q机器人开发笔记

︶悠扬、Le逍遥^ 发表于 2016-9-28 17:40
感谢分享
msg乱码可能是正则的问题

直接原样返回就是乱码,这也跟正则有关系?

︶悠扬、Le逍遥^ 发表于 2016-9-28 17:45

RE: 酷Q机器人开发笔记

你的json里有没有这东西……,
            "regex":{
                "key":["qq","action"],
                "expression":["^(?<action>\\S{1,4}?)\\s*(?<qq>\\d{5,10})\\s*?$",
                         "^(?<action>\\S{1,4}?)\\s*\\\\s*?$"]
            }易语言是要解析下的,不知道C怎么样

铮铮卡穆 发表于 2016-9-28 17:48

RE: 酷Q机器人开发笔记

︶悠扬、Le逍遥^ 发表于 2016-9-28 17:45
你的json里有没有这东西……易语言是要解析下的,不知道C怎么样

默认的json配置我没动,没看到有类似的代码。

371655313 发表于 2016-9-29 13:35

哥们,我也是C++的,能加个好友吗,想跟你请教个问题?加我Q

铮铮卡穆 发表于 2016-9-29 16:40

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-9-29 16:41 编辑

371655313 发表于 2016-9-29 13:35
哥们,我也是C++的,能加个好友吗,想跟你请教个问题?加我Q
我自己也没弄明白呢……你的名字就是QQ号?官方的两个QQ群都没有批准我,现在只能自己摸索。

371655313 发表于 2016-9-29 16:42

RE: 酷Q机器人开发笔记

铮铮卡穆 发表于 2016-9-29 16:40
我自己也没弄明白呢……你的名字就是QQ号?官方的两个QQ群都没有批准我,现在只能自己摸索。
...

对的,昵称就是QQ号码,加我一下一起研究下

铮铮卡穆 发表于 2016-9-29 20:18

7楼提到的错位问题已经验证了,前文所有的实例,应该把%ld改成%lld,就可以正常显示了。
既然是错位,那就是说数据本身没有问题,是格式化输出出的问题。

铮铮卡穆 发表于 2016-10-9 19:58

RE: 酷Q机器人开发笔记

前面的楼层已经更新过了,后面会继续补充新内容

铮铮卡穆 发表于 2016-10-9 20:00

RE: 酷Q机器人开发笔记

请求处理
__eventFriend_Add 好友事件-好友已添加
subType 子类型
sendTime 时间戳
fromQQ 哪个QQ发过来的

__eventRequest_AddFriend 请求-好友添加
subType 子类型
sendTime 时间戳
fromQQ 哪个QQ发过来的
msg 验证消息内容
responseFlag 反馈标识,处理请求用

CQ_setFriendAddRequest 处理好友添加请求
AuthCode 校验码,在Initialize初始化的全局变量ac
responseFlag 反馈标识,__eventRequest_AddFriend带过来的参数
responseoperation 处理方法,同意或拒绝,REQUEST_ALLOW/REQUEST_DENY
remark 添加后的好友备注

__eventRequest_AddGroup 请求-群添加
subType 子类型,1/他人申请入群 2/自己(即登录号)受邀入群
sendTime 时间戳
fromGroup 哪个群
fromQQ 哪个QQ
msg 验证消息内容
responseFlag 反馈标识,处理请求用

CQ_setGroupAddRequestV2 处理群添加请求
AuthCode 校验码,在Initialize初始化的全局变量ac
responseFlag 反馈标识,__eventRequest_AddGroup带过来的参数
requesttype 根据请求类型的子类型区分,群添加或群邀请,REQUEST_GROUPADD/REQUEST_GROUPINVITE
responseoperation 处理方法,同意或拒绝,REQUEST_ALLOW/REQUEST_DENY
reason 操作理由,可以为空

CQEVENT(int32_t, __eventFriend_Add, 16)(int32_t subType, int32_t sendTime, int64_t fromQQ) {
      
      LOG_INFO("好友已添加, fromQQ[%lld]",fromQQ);

      CQ_sendPrivateMsg(ac, fromQQ, "你好,新朋友!");

      return EVENT_BLOCK;
}

CQEVENT(int32_t, __eventRequest_AddFriend, 24)(int32_t subType, int32_t sendTime, int64_t fromQQ, const char *msg, const char *responseFlag) {

      LOG_INFO("请求好友添加, fromQQ[%lld], msg:%s, responseFlag:%s", fromQQ, msg, responseFlag);
      if(fromQQ == info.controllerQQ)
                CQ_setFriendAddRequest(ac, responseFlag, REQUEST_ALLOW, "");
      else
                CQ_setFriendAddRequest(ac, responseFlag, REQUEST_DENY, "");

      return EVENT_BLOCK;
}

CQEVENT(int32_t, __eventRequest_AddGroup, 32)(int32_t subType, int32_t sendTime, int64_t fromGroup, int64_t fromQQ, const char *msg, const char *responseFlag) {
      
      static const char* sub[] = { "NULL","他人申请入群","自己受邀入群" };
      char* str;
      LOG_INFO("subType:[%d,%s], fromGroup:[%lld], fromQQ:[%lld], msg:%s, responseFlag:%s", subType, sub, fromGroup, fromQQ, msg, responseFlag);
      str = (char*)malloc(100);
      if (str)
      {
                if (1 == subType)
                {
                        sprintf(str, "%lld 申请入群。", fromQQ);
                        CQ_setGroupAddRequestV2(ac, responseFlag, REQUEST_GROUPADD, REQUEST_ALLOW, "");//批准入群
                }
                else if (2 == subType)
                {
                        if (fromQQ == info.controllerQQ)//如果邀请自己的是控制者则同意,否则拒绝。
                              CQ_setGroupAddRequestV2(ac, responseFlag, REQUEST_GROUPINVITE, REQUEST_ALLOW, "");
                        else
                              CQ_setGroupAddRequestV2(ac, responseFlag, REQUEST_GROUPINVITE, REQUEST_DENY, "");
                        sprintf(str, "%lld 邀请我入群。", fromQQ);
                }
                CQ_sendGroupMsg(ac, fromGroup, str);
                free(str);
      }
      else
                CQ_sendGroupMsg(ac, fromGroup, "内存不足!");

      return EVENT_BLOCK;
}

实测效果
* 普通QQ登录,添加别人好友,换CoolQ登录,被添加的QQ同意好友添加,__eventFriend_Add没有响应,没有日志,不生效;
* 请求-好友添加、同意添加好友、拒绝添加好友,正常生效;
* 请求-群添加、同意申请入群、同意受邀入群、拒绝受邀入群,正常生效,拒绝申请入群未测试,相信会正常生效;










铮铮卡穆 发表于 2016-10-9 20:04

RE: 酷Q机器人开发笔记

群组操作
CQ_setDiscussLeave 退出讨论组
实测效果:正常使用。如果在退出讨论组之前有在讨论组里说话,有可能会先提示退出讨论组,后显示发出的消息

CQ_setGroupKick 踢人
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
rejectaddrequest 是否禁止他再次申请,禁止之后将无法再次收到他的入群请求。0不禁止,1禁止。
实测效果:正常有效。

CQ_setGroupBan 单人禁言
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
duration 禁言时长,秒数,为0则取消禁言。
实测效果:正常有效。禁言会在群里收到来自1000000号的系统消息。

CQ_setGroupAnonymousBan 指定匿名用户禁言
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
anomymous 匿名用户标识,也就是__eventGroupMsg中的fromAnonymous
duration 禁言时长,秒数,为0则取消禁言。
实测效果:正常有效。禁言会在群里收到来自1000000号的系统消息。

CQ_setGroupWholeBan 全员禁言
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
enableban 0取消禁言,1全员禁言
实测效果:正常有效。禁言会在群里收到来自1000000号的系统消息。

CQ_setGroupAnonymous 设置匿名
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
enableban 0禁止匿名,1允许匿名
实测效果:**没有效果**。其他管理员设置匿名会在群里收到来自1000000号的系统消息。

CQ_setGroupCard 设置群名片
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
newcard 群名片的名字,置NULL则取消名片
实测效果:正常有效。

CQ_setGroupLeave 退出群
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
isdismiss 是否解散群
实测效果:退出别人的群正常有效,没有测试解散自己的群。

CQ_setGroupSpecialTitle 设置专属头衔
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
newspecialtitle 要设置的专属头衔
duration 专属头衔生效时长,秒数,为-1则永久生效。
实测效果:需要群主权限,管理员权限不够,正常有效。

CQ_setGroupAdmin 设置群管理员
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
setadmin 0取消管理员,1设置管理员
实测效果:需要群主权限,正常有效。




铮铮卡穆 发表于 2016-10-9 20:05

RE: 酷Q机器人开发笔记

本帖最后由 铮铮卡穆 于 2016-10-11 19:12 编辑

获取信息
CQ_getStrangerInfo 获取陌生人信息
AuthCode 校验码,在Initialize初始化的全局变量ac
QQID 操作的QQ号
nocache 1使用缓存,0使用缓存,使用缓存的话,用户更新信息后,这里可能还返回更新之前的信息
实测效果:返回一串base64编码的信息,以后再分析内容,未测试使用缓存。
CQ_getGroupMemberInfoV2 获取群成员信息
AuthCode 校验码,在Initialize初始化的全局变量ac
groupid 操作的群号
QQID 操作的QQ号
nocache 1不使用缓存,0使用缓存,使用缓存的话,用户更新信息后,这里可能还返回更新之前的信息
实测效果:返回一串base64编码的信息,以后再分析内容,未测试使用缓存。
CQ_getAppDirectory 获取插件路径
实测效果:返回绝对路径,CoolQ路径下的\app\com.coxxs.start\

CQ_getLoginQQ 获取当前登录的QQ号
实测效果:返回QQ号

CQ_getLoginNick 获取当前登录的QQ昵称
实测效果:返回QQ昵称


铮铮卡穆 发表于 2016-10-9 20:08

RE: 酷Q机器人开发笔记

其他
CQ_sendLike 点赞
实测效果:没发现有什么作用,不知道干嘛的。

CQ_addLog 日志
priority 日志等级,在cqp.h头文件里,CQLOG_DEBUG等8个等级
category 日志类型,自定义字符串
content 日志内容,自定义字符串
实测效果:我只测试了CQLOG_INFO和CQLOG_WARNING两个,CQLOG_INFO没啥反应,没找到日志打到哪里去了,CQLOG_WARNING会在右下角弹出警告。没弄明白日志系统是怎么回事,所以我用的自己的日志模块。

CQ_getCsrfToken
未测试
CQ_getCookies
未测试
CQ_setFatal 抛出致命错误
AuthCode 校验码,在Initialize初始化的全局变量ac
errorinfo 错误信息,自定义内容,显示在错误对话框中
实测效果:CoolQ抛出异常终止的异常,内容为errorinfo,点击确定之后CoolQ进程退出,无法继续服务,要重新启动CoolQ。如果没有点击确定,仍然可以继续使用。




铮铮卡穆 发表于 2016-10-12 19:59

RE: 酷Q机器人开发笔记(C++)

本帖最后由 铮铮卡穆 于 2016-10-12 20:02 编辑

信息解析
这里解析的都是上面提到的base64编码的信息,首先要经过base64解码,下面都不再提,分析的数据都是解码之后的。相关代码可以参考https://cqp.cc/t/26287
陌生人信息
即**CQ_getStrangerInfo**返回的信息,已经添加过好友的也一样。
前8个字节,即一个Int64_t长度,QQ号;
接下来2个字节,即一个short长度,昵称长度;
接下来昵称长度个字节,昵称文本;
接下来4个字节,即一个int长度,性别,0男1女;
接下来4个字节,即一个int长度,年龄,QQ里不能直接修改年龄,以出生年为准;
结构参考:
struct CQ_TYPE_QQ
{
      int64_t         QQID;      //QQ号
      std::string    nick;      //昵称
      int               sex;      //性别
      int                  age;      //年龄
};群成员信息
即**CQ_getGroupMemberInfoV2**返回的信息
前8个字节,即一个Int64_t长度,QQ群号;
接下来8个字节,即一个Int64_t长度,QQ号;
接下来2个字节,即一个short长度,昵称长度;
接下来昵称长度个字节,昵称文本;
接下来2个字节,即一个short长度,群名片长度;
接下来群名片长度个字节,群名片文本;
接下来4个字节,即一个int长度,性别,0男1女;
接下来4个字节,即一个int长度,年龄,QQ里不能直接修改年龄,以出生年为准;
接下来2个字节,即一个short长度,地区长度;
接下来地区长度个字节,地区文本;
接下来4个字节,即一个int长度,入群时间戳;
接下来4个字节,即一个int长度,最后发言时间戳;
接下来2个字节,即一个short长度,群等级长度;
接下来群等级长度个字节,群等级文本;
接下来4个字节,即一个int长度,管理权限,1成员,2管理员,3群主;
接下来4个字节,即一个int长度,0,不知道是什么,可能是不良记录成员;
接下来2个字节,即一个short长度,专属头衔长度;
接下来专属头衔长度长度个字节,专属头衔长度文本;
接下来4个字节,即一个int长度,专属头衔过期时间戳;
接下来4个字节,即一个int长度,允许修改名片,1允许,猜测0是不允许;
结构参考:

struct CQ_Type_GroupMember
{
      int64_t            GroupID;                     // 群号
      int64_t            QQID;                           // QQ号
      std::string      nick;                               // QQ昵称
      std::string      card;                               // 群名片
      int                  sex;                              // 性别 0/男 1/女
      int                  age;                               // 年龄
      std::string      area;                               // 地区
      int                   jointime;                     // 入群时间
      int                  lastsent ;                         // 上次发言时间
      std::string      level_name;                     // 头衔名字
      int                  permission;                  // 权限等级 1/成员 2/管理员 3/群主
      bool               unfriendly;                     // 不良成员记录
      std::string      title;                                  // 自定义头衔
      int                  titleExpiretime;                   // 头衔过期时间
      bool               cardcanchange;               // 管理员是否能协助改名
};匿名信息
即**__eventGroupMsg**的**fromAnonymous**参数
前8个字节,即一个Int64_t长度,匿名标识号;
接下来2个字节,即一个short长度,匿名名称长度;
接下来匿名名称长度个字节,匿名名称文本;
接下来2个字节,即一个short长度,token长度,目前看都是40字节;
接下来token长度个字节,token内容;



页: [1]23
查看完整版本: 酷Q机器人开发笔记(C++)