Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

3 (3). 含有上下文的信息处理

DavidMeow edited this page Jan 14, 2023 · 7 revisions

3.8 交互端的信息接收

  • 本页例子将使用全局端讲述, 如果您含有多个端, 请您自己添加自己端的签名等到参数位置, 就如上一页面提到的.

3.8.1 交互端的信息接受规则

  • 交互端不分好友信息 群信息 等类型(同步信息除外), 仅有收到信息这一个事件,
  • 且您无法订阅Client端内的OnFriendMessageRecieve等事件, 防止影响程序正常解析工作.
  • 任何信息发送者将被解析成ContextualSender结构体.
  • 任何接收的信息将会转换为ContextualMessage结构体.
  • 任何接收到的信息会被存到一个集合索引为ContextualSender,元素为Queue<ContextualMessage>的字典内 (Dictionary/HashSet)
  • 任何群号(如果私聊则为-1),和QQ号(一定含有)发送的信息将会被进入Queue<ContextualMessage>这个队列.
  • 任何群号和QQ号一致的发送人只拥有一个队列.

3.8.2 接受信息

Global.G_Client = new ConClient("ws://......");
var c= (Global.G_Client as ConClient);
c.Connect();

c._OnMessageRecieve += (s) =>
{
    Console.WriteLine($"{s.SenderId} {s.PeekPlainMsg()}");// PeekPlainMsg 是一个用于查看队列内第一个信息的方法(但是不会将元素移除)
    s.DelMsgs();// DelMsg 是一个扩展方法, 用于从队列中移除指定数量元素.(*重要,务必添加*)
};
  • 按上述写法,您的控制台内应该会打印出您发送的信息, 并且当执行完后这条信息会从缓存区队列(Dictionary)移除.
  • 关于 重要 的解释, 因为队列在设计时为了含有其他异步函数获取参数执行, 或校验指令树等操作, 并不会将元素出队, 按理论上来讲, 应该由 SE 自动控制元素删除时间, 在此我们建议统一当您完成事务后进行删除, 后续文章中含有异步编程(Action驱动形式)相关, 届时会生成带参数的信息删除方案, 请您务必注意相关条目, 若您以不正当的方式进行操作, 可能导致操作队列产生异常, 可能会使得堆区无缘无故变大, 导致驻留过多内存等问题.

3.8.3 接受信息并产生反馈(3步)

  • 假设我们要做的步骤如下:
sequenceDiagram
    participant bot
    participant user

    user ->> +bot: 命令A ("重复")
    bot ->> +user: 您要重复的话是?
    user ->> +bot: 命令A的B参数 ("这句话")
    bot ->> +user: [返回 字符串 " '这句话' "]
    user ->> +bot: 命令A的C参数 ("再重复一次")
    bot ->> +user: [返回 字符串 "重复第二遍 '这句话',程序结束"]
Loading

3.8.3.1 实例化上下文交互辅助类

using MeowMiraiLib.MultiContext
....//在某一个新的类(例如Command.cs文件的Command类中)

CMsgHelper cmh1 = new(null, //如果您没有使用全局Client,请在这个位置传递您的ConClient实例
    (s, msgs) => //第一个语句的动作
    {
        if (msgs.GetPlainMsgAt(1).Trim() == "重复") // GetPlainMsgAt(num) 是一个扩展方法,可以获取当前的num个信息(队列第一个)
        {
            s.SendMsgBack(new Message[] { new Plain("您要重复的话是?") });
            // SendMsgBack(Message[],Client) 也是一个扩展方法, 用于朝信息发送者的位置快速回信, 如果是群就发群里, 如果是私聊就发送至私聊.
            // 但您也可以使用 SendMessageToFriend / SendMessageToGroup 强制发回群里或者私聊(前提是发送者含有)
            // 或者你也可以使用原版的扩展 使用 Message[]{...}.SendToFriend(123456) *留空Client使用全局,或者传递Client实例.
        }
    },
    (s, msgs) => //第二个语句的动作
    {
        if (msgs.GetPlainMsgAt(1).Trim() == "重复") //检查指令树结构 (必须第一次含有重复)
        {
            s.SendMsgBack(new Message[] { new Plain($"{msgs.GetPlainMsgAt(2)}") });
        }
    },
    (s, msgs) => //第三个语句的动作
    {
        if (msgs.GetPlainMsgAt(1).Trim() == "重复" && msgs.GetPlainMsgAt(3) == "再重复一次") //检查指令树结构 (必须第一次含有重复且这一次说的是再重复一次)
        {
            s.SendMsgBack(new Message[] { new Plain($"重复第二遍: \n{msgs.GetPlainMsgAt(2)}, 程序结束") });
        }
        s.DelMsgs(3); //程序结束,清理驻留
    }
);

/*
 * 程序注意点: 
 * 1. 必须在程序结束的时候清理驻留, 使用delmsg, 您执行了几步就需要删除几个信息. 不要多删也不要少删. 防止后面程序执行错误.
 * 2. 必须每次进入其他Action均 进 行 检 查 指 令 树 结 构, 避免如果多个任务异步接受信息产生信息错位的问题.
 */
  • 按照这种写法您的交互辅助类已经写完了, 现在开始注入.

3.8.3.2 注入并按条件启用上下文交互辅助类

  • 您需要在接受到信息后判定条件,然后转交辅助类进行处理.
  • 程序示例如下:
//...在program.cs下
....
c._OnMessageRecieve += (s) =>
{
    /*
    var proc = true;
    if(s.GroupId > 0) //如果是群聊
    {
        foreach (var i in s.PeekMsgs()) //查看信息类型
        {
            if (i is At && (i as At).target == 你Bot的QQ号) //确定at了本机器人
            {
                proc = true; //可以执行
            }
            else
            {
                proc = false;//不可以执行
            }
        }
    }

    if(proc)
    {   
    */
        var m = s.PeekPlainMsg();//如造成群聊不断回复, 您应该加入at等判断(如上),或者删除最后一个一直回复的操作.
        if (m == "重复")
        {
            Command.cmh1.Invoke(s);//注入并且启动
            return;//执行后退出 (*重要*) (::因为内部我们已经让元素出队,所以要略过下方的删除元素,保持连贯)
        }
        else if (m == "Hello")
        {
            s.SendMsgBack(new Message[] { new Plain("Hello") });
            //如果用户只说Hello的话就回Hello.
        }
        else
        {
            s.SendMsgBack(new Message[] { new Plain(".....?") });
            //其他命令均回复".....?"
        }
        s.DelMsgs();//单回复的执行后删除队列元素 (*重要*)
    /*}*/
};

while (true)
{

}
//阻塞主程序,直到程序被强制中断(按Ctrl+C)

3.8.3.3 测试

image

  • 符合预期,程序写作成功

3.9 FAQ

**1.可实例化多个交互辅助类么 **

A: 可以, 但在接受端需要确定第一个接受的词, 引起正确的Action, _OnMessageRecieve务必注意判定条件和return;

**2.我可以使用上下文交互端写普通命令结构么? **

A: 可以, 其实本端和基础端并没有不同, 您也可以使用本端的 MsgSendBack(Message[],Client) 函数快速返回源地址一个信息, 省时省力. 如果您使用了全局端甚至可以不写Client参数.

**3.新的 快速编写类的功能参数都有什么 **

A: 参见 [AP. 1 (2) 上下文交互端扩展方法一览]