系列导航


本文系列源码:点我进入

一. 首先下载或克隆基础模板

话不多说,git的基本使用,不再科普
也可以直接使用我自用的开发模板

二. 按照SDK WIKI进行基础建设

本文使用自用模板进行改造(直接clone base分支即可)

1.修改Native.Core下app.json(文末提供全权限版本json)

"ret": 1,
"apiver": 9,
"name": "手摸手插件DEMO",//插件名字
"version": "1.0.0",//插件版本
"version_id": 1,
"author": "Traceless",//作者
"description": "这里是手摸手插件开发系列教程DEMO",//插件描述

2.修改命名空间


根据SDK WIKI规范,我们将插件取名为Site.Traceless.Demo


解决方案资源管理-Navtive.Core右键-程序集名称修改为site.traceless.demo[注意这边需要全部小写]

3.新建主项目库


根据SDK WIKI规范,我们将主项目库取名为Site.Traceless.Demo.Code


解决方案资源管理-解决方案"Native"右键-添加-新建项目-选择“类库(.Net Framework)”-项目名称Site.Traceless.Demo.Code,框架.NET Framework 4.5-创建

4.引用项目


Native.Core-引用-添加引用-项目-Site.Traceless.Demo.Code
Site.Traceless.Demo.Code-引用-添加引用-项目-Native.Sdk
Site.Traceless.Demo.Code-引用-添加引用-项目-Native.Tool

三. 手动魔改开始

我的分类思路

  • Command【命名空间】:放置功能
  • Event【命名空间】:酷Q事件
  • Model【命名空间】:业务模型
  • Common.cs:公共变量

1.新建公公变量类Common.cs


重命名项目新建自带的Class1.cs为Common.cs
内容如下:
Common.cs

using Native.Sdk.Cqp;
using System.Collections.Generic;

namespace Site.Traceless.Demo.Code
{
    public static class Common
    {
        /// <summary>
        /// 命令映射路由(群聊)
        /// </summary>
        public static Dictionary<string, string> GCommandDic { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// 命令映射路由(私聊)
        /// </summary>
        public static Dictionary<string, string> PCommandDic { get; set; } = new Dictionary<string, string>();

        /// <summary>
        /// 设置
        /// </summary>
        public static Dictionary<string, string> settingDic { get; set; } = new Dictionary<string, string>();

        public static CQApi CqApi
        {
            get; set;
        }
    }
}


2.添加消息解析类


新建文件夹-Model
添加类AnalysisMsg
内容如下:
AnalysisMsg.cs

namespace Site.Traceless.Demo.Code.Model
{
    public class AnalysisMsg
    {
        private static OrderInfoModel _msg = new OrderInfoModel("");

        public string What
        {
            get => _msg.What.Trim() ?? "";
        }

        public string GCommand
        {
            get
            {
                string _gcommand = null;
                Common.GCommandDic.TryGetValue(_msg.What.Trim(), out _gcommand);
                return _gcommand;
            }
        }

        public string PCommand
        {
            get
            {
                string _pcommand = null;
                Common.PCommandDic.TryGetValue(_msg.What.Trim(), out _pcommand);
                return _pcommand;
            }
        }

        public string How
        {
            get => _msg.How.Trim() ?? "";
        }

        public string Who
        {
            get => _msg.Who.Trim() ?? "";
        }

        public int OrderCount
        {
            get => _msg.OrderCount;
        }

        public string OriginStr
        {
            get; set;
        }

        /// <summary>
        /// 解析消息结构
        /// </summary>
        /// <param name="msg"></param>
        public AnalysisMsg(string msg)
        {
            if (!string.IsNullOrEmpty(msg))
            {
                string str = new System.Text.RegularExpressions.Regex("[\\s]+").Replace(msg, " ");
                _msg = new OrderInfoModel(str);
            }
            else
            {
                _msg = new OrderInfoModel("");
            }

            OriginStr = msg;
        }

        ~AnalysisMsg()
        {
        }
    }
}

添加类OrderInfoModel
内容如下:

OrderInfoModel.cs

using System;

namespace Site.Traceless.Demo.Code.Model
{
    public class OrderInfoModel
    {
        public int OrderCount { get; private set; } = 0;
        public string What { get; private set; } = "";
        public string Who { get; private set; } = "";
        public string How { get; private set; } = "";
        public string OriginStr { get; private set; }

        public OrderInfoModel(string msg)
        {
            if (string.IsNullOrEmpty(msg)) return;
            try
            {
                OriginStr = msg;
                string[] temp = msg.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (temp.Length == 0)
                {
                    What = msg;
                    OrderCount = 0;
                }
                else if (temp.Length == 1)
                {
                    What = temp[0];
                    OrderCount = 1; ;
                }
                else if (temp.Length == 2)
                {
                    What = temp[0];
                    Who = temp[1];
                    OrderCount = 2;
                }
                else if (temp.Length > 2)
                {
                    What = temp[0];
                    Who = temp[1];
                    How = msg.TrimStart().Substring(What.Length + Who.Length + 2);
                    OrderCount = 3;
                }
            }
            catch (Exception ex)
            {
                OrderCount = 0;
                What = ex + "";
            }
        }
    }
}


3.添加业务功能入口


这里会涉及到群、私聊消息(讨论组消息本文不讨论,方法类似,自行拓展)
新建文件夹-Command
添加群功能入口GroupApp
内容如下:
GroupApp.cs

using Site.Traceless.Demo.Code.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Native.Sdk.Cqp.EventArgs;

namespace Site.Traceless.Demo.Code.Command
{
    public class GroupApp
    {
        public static void funcOne(CQGroupMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(415206409,$"[这里是群方法1]", $"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }
        public static void funcTwo(CQGroupMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(415206409, $"[这里是群方法2]\n", $"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }
    }
}


添加私聊功能入口FriendApp
内容如下:
FriendApp.cs

using Native.Sdk.Cqp.EventArgs;
using Site.Traceless.Demo.Code.Model;

namespace Site.Traceless.Demo.Code.Command
{
    public class FriendApp
    {
        public static void funcOne(CQPrivateMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(e.FromQQ,$"[这里是私聊方法1]",$"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }
        public static void funcTwo(CQPrivateMessageEventArgs e, AnalysisMsg msg)
        {
            e.CQApi.SendPrivateMessage(e.FromQQ,$"[这里是私聊方法2]", $"参数数 {msg.OrderCount}\n", $"触发指令(第一参数 what) {msg.What}\n", $"目标(第二参数 who) {msg.Who}\n", $"怎么做(第三参数 how) {msg.How}\n", $"原始信息 {msg.OriginStr}\n", e.ToString());
        }
    }
}


4.添加酷Q事件


新建文件夹-Event
按照这个链接,添加所有事件类,记得更改Site.Traceless.Demo为你的插件命名空间
可以使用ctrl+shift+H进行批量替换
除Event_AppEnable、Event_GroupMsg、Event_PrivateMsg其它文件中的方法内容都可以清空
Event_AppEnable在第5步会添加内容
Event_GroupMsg、Event_PrivateMsg的内容比较核心,此处放出【后期随着需求增加可能会有改动】
Event_PrivateMsg.cs

using Native.Sdk.Cqp.EventArgs;
using Native.Sdk.Cqp.Interface;
using Site.Traceless.Demo.Code.Command;
using Site.Traceless.Demo.Code.Model;
using System;

namespace Site.Traceless.Demo.Code.Event
{
    public class Event_PrivateMsg : IPrivateMessage
    {
        public void PrivateMessage(object sender, CQPrivateMessageEventArgs e)
        {
            AnalysisMsg nowModel = new AnalysisMsg(e.Message.Text);//解析消息
            if (String.IsNullOrEmpty(nowModel.PCommand))
            {//没有私聊命令在其中,没有其它业务,直接返回
                e.Handler = false;
                return;
            }
            //触发私聊业务,通过反射创建FriendApp实例,并调用对应方法
            var papp = Activator.CreateInstance(typeof(FriendApp)) as FriendApp;
            var method = papp.GetType().GetMethod(nowModel.PCommand);
            object result = method.Invoke(null, new object[] { e, nowModel });

            e.Handler = false;
        }
    }
}


Event_GroupMsg.cs

using Native.Sdk.Cqp.EventArgs;
using Native.Sdk.Cqp.Interface;
using Site.Traceless.Demo.Code.Command;
using Site.Traceless.Demo.Code.Model;
using System;

namespace Site.Traceless.Demo.Code.Event
{
    public class Event_GroupMsg : IGroupMessage
    {
        public void GroupMessage(object sender, CQGroupMessageEventArgs e)
        {
            AnalysisMsg nowModel = new AnalysisMsg(e.Message.Text);
            if (String.IsNullOrEmpty(nowModel.GCommand))
            {//没有私聊命令在其中,没有其它业务,直接返回
                e.Handler = false;
                return;
            }
            //触发群聊业务,通过反射创建GroupApp实例,并调用对应方法
            var gapp = Activator.CreateInstance(typeof(GroupApp)) as GroupApp;
            var method = gapp.GetType().GetMethod(nowModel.GCommand);
            object result = method.Invoke(null, new object[] { e, nowModel });

            e.Handler = false;
        }
    }
}


4.添加酷Q事件注入

此处代码里的中文字一个都不能错!


打开Native.Core-CQMain.cs
内容如下:
CQMain.cs

using Native.Sdk.Cqp.Interface;
using Site.Traceless.Demo.Code.Event;
using Unity;

namespace Native.Core
{
    /// <summary>
    /// 酷Q应用主入口类
    /// </summary>
    public class CQMain
    {
        /// <summary>
        /// 在应用被加载时将调用此方法进行事件注册, 请在此方法里向 <see cref="IUnityContainer"/> 容器中注册需要使用的事件
        /// </summary>
        /// <param name="container">用于注册的 IOC 容器</param>
        public static void Register(IUnityContainer container)
        {
            container.RegisterType<IGroupMessage, Event_GroupMsg>("群消息处理");
            container.RegisterType<IPrivateMessage, Event_PrivateMsg>("私聊消息处理");
            container.RegisterType<IDiscussMessage, Event_DiscussMsg>("讨论组消息处理");
            container.RegisterType<IGroupUpload, Event_GroupUpload>("群文件上传事件处理");
            container.RegisterType<IGroupManageChange, Event_GroupManageChange>("群管理变动事件处理");
            container.RegisterType<IGroupMemberDecrease, Event_GroupMemberDecrease>("群成员减少事件处理");
            container.RegisterType<IGroupMemberIncrease, Event_GroupMemberIncrease>("群成员增加事件处理");
            container.RegisterType<IGroupBanSpeak, Event_GroupBanSpeak>("群禁言事件处理");
            container.RegisterType<IFriendAdd, Event_FriendAdd>("好友已添加事件处理");
            container.RegisterType<IFriendAddRequest, Event_FriendAddRequest>("好友添加请求处理");
            container.RegisterType<IGroupAddRequest, Event_GroupAddRequest>("群添加请求处理");
            container.RegisterType<ICQStartup, Event_CQStartup>("酷Q启动事件");
            container.RegisterType<ICQExit, Event_CQExit>("酷Q关闭事件");
            container.RegisterType<IAppEnable, Event_AppEnable>("应用已被启用");
            container.RegisterType<IAppDisable, Event_AppDisable>("应用将被停用");
        }
    }
}


5.初始化App启动方法


打开-Site.Traceless.Demo.Code.Event-Event_AppEnable
内容如下:
Event_AppEnable.cs

using System.Linq;
using System.Text;
using Native.Sdk.Cqp.Interface;
using Native.Sdk.Cqp.EventArgs;
using System.IO;
using Native.Tool.IniConfig.Linq;

namespace Site.Traceless.Demo.Code.Event
{
    public class Event_AppEnable : IAppEnable
    {
        public void AppEnable(object sender, CQAppEnableEventArgs e)
        {
            Common.CqApi = e.CQApi;//将API存入,方便业务处调用API
            string commandPath = Common.CqApi.AppDirectory + "command.ini";//命令路由文件路径
            IniObject iObject;
            if (!File.Exists(commandPath))
            {//如果不存在文件,初始化
                iObject = new IniObject
                {
                    new IniSection("gcommands")
                    {
                        { "功能1","funcOne"},
                        { "功能2","funcTwo"}
                    },
                    new IniSection("pcommands")
                    {
                        { "功能1","funcOne"},
                        { "功能2","funcTwo"}
                    }
                };
                iObject.Save(commandPath);
            };
            iObject = IniObject.Load(commandPath, Encoding.Default);//默认编码读取ini路由文件
            IniSection pCommand = iObject["pcommands"];//读取并加载私聊路由
            Common.PCommandDic = pCommand.ToDictionary(p => p.Key, p => p.Value.ToString());
            IniSection gCommand = iObject["gcommands"];//读取并加载群聊路由
            Common.GCommandDic = gCommand.ToDictionary(p => p.Key, p => p.Value.ToString());

            commandPath = Common.CqApi.AppDirectory + "setting.ini";//设置文件,可以存放插件中一些配置量,比如主人QQ
            if (!File.Exists(commandPath))
            {
                iObject = new IniObject
                {
                    new IniSection("setting")
                    {
                        { "master",415206409}
                    }
                };
                iObject.Save(commandPath);
            };
            iObject = IniObject.Load(commandPath, Encoding.Default);
            IniSection settings = iObject["setting"];
            Common.settingDic = settings.ToDictionary(p => p.Key, p => p.Value.ToString());
        }
    }
}


最后!!右键-Native.Core-Export-CQExport.tt-运行自定义工具!这步很重要!

四. 流程解析

以上配置可以实现,群聊关键字“功能1”触发GroupApp中funcOne方法。
流程如下:

  1. 群聊事件获取到原始信息 “功能1 XXXX XXXX”【这边的XXXX也可以没有】
  2. 从Common.GCommandDic字典中查找功能1,对应的方法名“funcOne”
  3. 利用反射,调用GroupApp中,方法名为funcOne的方法。
  4. 功能逻辑运行,执行完毕

可以看到,我们的路由是在插件启动时读取并加载的,运行过一次插件完成初始化以后,可以到 酷Q目录\data\app\你的appid 下,找到command.ini文件,进行修改,再重新启动插件,即可完成新功能的添加老功能的临时禁用多个关键字触发同一个功能的操作,而不需要对群聊事件进行if-else或switch的书写

关于调试

点我查看官方WIKI

好了!按照自己的想法去实现自己的机器人吧!

这里提供一个全事件、全权限版本的app.json,请根据自己的需要删减

篇幅过长,点击查看

// [重要] appid 规则见 https://cqp.im/v9/appid
{
  "ret": 1,
  "apiver": 9,
  "name": "这里改成你的插件名字",
  "version": "1.0.0",//版本号涉及到在酷Q论坛发布,认真填写
  "version_id": 1,
  "author": "这里是你的名字",
  "description": "这里改成你的插件描述",
  "event": [//下面的这些Event,没用的可以去掉,自用插件无所谓,需要发布的插件还是要认真选择
    {
      "id": 1,
      "type": 21,
      "name": "私聊消息处理",
      "function": "_eventPrivateMsg",
      "priority": 30000
    },
    {
      "id": 2,
      "type": 2,
      "name": "群消息处理",
      "function": "_eventGroupMsg",
      "priority": 30000
    },
    {
      "id": 3,
      "type": 4,
      "name": "讨论组消息处理",
      "function": "_eventDiscussMsg",
      "priority": 30000
    },
    {
      "id": 4,
      "type": 11,
      "name": "群文件上传事件处理",
      "function": "_eventGroupUpload",
      "priority": 30000
    },
    {
      "id": 5,
      "type": 101,
      "name": "群管理变动事件处理",
      "function": "_eventSystem_GroupAdmin",
      "priority": 30000
    },
    {
      "id": 6,
      "type": 102,
      "name": "群成员减少事件处理",
      "function": "_eventSystem_GroupMemberDecrease",
      "priority": 30000
    },
    {
      "id": 7,
      "type": 103,
      "name": "群成员增加事件处理",
      "function": "_eventSystem_GroupMemberIncrease",
      "priority": 30000
    },
    {
      "id": 8,
      "type": 104,
      "name": "群禁言事件处理",
      "function": "_eventSystem_GroupBan",
      "priority": 30000
    },
    {
      "id": 10,
      "type": 201,
      "name": "好友已添加事件处理",
      "function": "_eventFriend_Add",
      "priority": 30000
    },
    {
      "id": 11,
      "type": 301,
      "name": "好友添加请求处理",
      "function": "_eventRequest_AddFriend",
      "priority": 30000
    },
    {
      "id": 12,
      "type": 302,
      "name": "群添加请求处理",
      "function": "_eventRequest_AddGroup",
      "priority": 30000
    },
    {
      "id": 1001,
      "type": 1001,
      "name": "酷Q启动事件",
      "priority": 30000,
      "function": "_eventStartup"
    },
    {
      "id": 1002,
      "type": 1002,
      "name": "酷Q关闭事件",
      "priority": 30000,
      "function": "_eventExit"
    },
    {
      "id": 1003,
      "type": 1003,
      "name": "应用已被启用",
      "priority": 30000,
      "function": "_eventEnable"
    },
    {
      "id": 1004,
      "type": 1004,
      "name": "应用将被停用",
      "priority": 30000,
      "function": "_eventDisable"
    }
  ],
  "menu": [ // 设置菜单(发布前请删除无用菜单,如果无需设置菜单请全部删除)
    {
      "name": "设置A", //菜单名称
      "function": "_menuA" //菜单对应函数
    },
    {
      "name": "设置B",
      "function": "_menuB"
    }
  ],
  "status": [ // 悬浮窗状态(见 com.example.status 样例)
    {
      "id": 1, // 悬浮窗ID
      "name": "运行时间", // 名称
      "title": "UPTIME", // 英文名称
      "function": "_statusUptime", // 数据更新对应函数
      "period": "1000" // 更新间隔(单位ms(毫秒),目前仅支持1000ms(1秒))
    }
  ],
  "auth": [ // 应用权限(发布前请删除无用权限)
    20,  //[敏感]取Cookies    getCookies / getCsrfToken
    30,  //接收语音           getRecord
    101, //发送群消息         sendGroupMsg
    103, //发送讨论组消息     sendDiscussMsg
    106, //发送私聊消息       sendPrivateMsg
    110, //[敏感]发送赞       sendLike
    120, //置群员移除         setGroupKick
    121, //置群员禁言         setGroupBan
    122, //置群管理员         setGroupAdmin
    123, //置全群禁言         setGroupWholeBan
    124, //置匿名群员禁言     setGroupAnonymousBan
    125, //置群匿名设置       setGroupAnonymous
    126, //置群成员名片       setGroupCard
    127, //[敏感]置群退出     setGroupLeave
    128, //置群成员专属头衔   setGroupSpecialTitle
    130, //取群成员信息       getGroupMemberInfo
    131, //取陌生人信息       getStrangerInfo
    132, //取群信息           getGroupInfo
    140, //置讨论组退出       setDiscussLeave
    150, //置好友添加请求     setFriendAddRequest
    151, //置群添加请求       setGroupAddRequest
    160, //取群成员列表       getGroupMemberList
    161, //取群列表           getGroupList
    162, //取好友列表         getFriendList
    180 //撤回消息           deleteMsg
  ]
}

下集预告

  1. 如何通过接口请求一键生成业务模型(IDE 需要是Visual Studio)
  2. 增加HTTP帮助类实现对HTTP接口的POST/GET调用
  3. 【针对特殊接口】利用JSONObject解析所需要的值


本文作者:Author:     文章标题:【手摸手系列】教你开发QQ机器人 - 基础框架搭建
本文地址:https://traceless.site/index.php/archives/21/     
版权说明:若无注明,本文皆为“Traceless”原创,转载请保留文章出处。
Last modification:March 18th, 2020 at 05:14 pm
如果觉得我的文章对你有用,请随意赞赏