Skip to content

控制器模式

控制器模式是 Luolan.QQBot 的核心特色功能。它让你像写 ASP.NET WebAPI 一样编写机器人指令。

基本概念

  • 控制器 — 继承 QQBotController 的类
  • 命令 — 控制器中标记 [Command] 特性的方法
  • 自动路由 — 用户消息自动匹配到对应的命令方法
  • 参数解析 — 自动将字符串参数转换为方法需要的类型

启用控制器

csharp
using Luolan.QQBot;
using Luolan.QQBot.Extensions;

var bot = new QQBotClientBuilder()
    .WithAppId("...")
    .WithClientSecret("...")
    .Build();

// 自动扫描当前程序集中的所有控制器并注册
bot.UseControllers();

await bot.StartAsync();

UseControllers() 做了两件事:

  1. 扫描程序集中所有 QQBotController 的子类
  2. 注册所有 [Command] 方法并绑定消息事件

编写第一个控制器

csharp
using Luolan.QQBot.Controllers;

public class HelloController : QQBotController
{
    // 用户发送: /hello
    [Command("hello")]
    public string Hello()
    {
        return "你好,世界!";
    }
}

就这么简单!用户发送 /hello,机器人回复 你好,世界!

命令定义

命令别名

一个方法可以有多个命令名和别名:

csharp
// 三种写法都可以触发同一个方法
// 用户发送: /help  或  /h  或  /?
[Command("help", "h", "?")]
public string Help()
{
    return "这是帮助信息...";
}

带参数的命令

csharp
// 用户发送: /add 10 20
[Command("add")]
public string Add(int a, int b)
{
    return $"{a} + {b} = {a + b}";
}

SDK 自动将字符串 "10" "20" 转换为 int 类型。

可选参数

csharp
// 用户发送: /greet           → 使用默认值 "世界"
// 用户发送: /greet 小明      → 使用 "小明"
[Command("greet")]
public string Greet(string name = "世界")
{
    return $"你好,{name}!";
}

支持的类型

SDK 内置了丰富的类型转换,以下类型都能自动解析:

类型示例输入说明
stringhello字符串(默认)
int42整数
long9999999999长整数
double3.14双精度浮点
decimal99.99精确小数
booltrue / false / 1 / 0 / yes / no / on / off布尔值
Guid550e8400-e29b-41d4-a716-446655440000GUID
enumInformation / information枚举(大小写不敏感)
int? / 42可空类型

示例

csharp
[Command("toggle")]
public string Toggle(bool enabled)
{
    // 用户发送: /toggle true  或 /toggle yes  或 /toggle 1
    return enabled ? "✅ 已启用" : "❌ 已禁用";
}

[Command("loglevel")]
public string SetLevel(LogLevel level)
{
    // 用户发送: /loglevel Information  或 /loglevel error
    return $"日志级别: {level}";
}

[Command("timeout")]
public string SetTimeout(int? seconds = null)
{
    // 用户发送: /timeout     → null
    // 用户发送: /timeout 30  → 30
    return seconds == null ? "未设置" : $"超时:{seconds}秒";
}

返回值类型

控制器方法可以返回不同类型,SDK 自动处理回复:

返回字符串

最常见的用法,直接回复文本消息。

csharp
[Command("ping")]
public string Ping() => "pong!";

返回图片

csharp
[Command("cat")]
public ImageResult Cat()
{
    return new ImageResult("https://example.com/cat.jpg");
}

// 或者利用隐式转换
[Command("logo")]
public ImageResult Logo()
{
    ImageResult result = "https://example.com/logo.png";
    return result;
}

返回 Markdown

csharp
using Luolan.QQBot.Models;
using Luolan.QQBot.Helpers;

// 方式一:原生内容
[Command("status")]
public MessageMarkdown Status()
{
    return MarkdownBuilder.FromContent(
        "# 系统状态\n" +
        "- CPU: 45%\n" +
        "- 内存: 2.3GB"
    );
}

// 方式二:模板(需要先在QQ开放平台申请模板)
[Command("notice")]
public MessageMarkdown Notice()
{
    return new MarkdownBuilder()
        .UseTemplate("your_custom_template_id")
        .AddParam("title", "通知标题")
        .Build();
}

返回异步结果

csharp
[Command("fetch")]
public async Task<string> FetchData()
{
    await Task.Delay(1000); // 模拟网络请求
    return "数据获取完成!";
}

上下文访问

在任何控制器方法中,可以直接访问以下属性:

csharp
public class InfoController : QQBotController
{
    [Command("info")]
    public string Info()
    {
        // Client — 当前机器人客户端实例
        var botId = Client.CurrentUser?.Id;

        // Message — 当前触发命令的消息对象
        var msgContent = Message.Content;
        var channelId = Message.ChannelId;
        var guildId = Message.GuildId;
        var groupOpenId = Message.GroupOpenId;

        // User — 发送消息的用户(等同于 Message.Author)
        var userName = User?.Username;
        var userId = User?.Id;

        // RawContent — 去除命令名后的原始文本
        // RawArguments — 解析后的参数数组
        var args = RawArguments;

        return $"用户: {userName}\n命令: {RawContent}\n参数: [{string.Join(", ", args)}]";
    }

    // 在方法内发送额外消息
    [Command("notify")]
    public async Task<string> Notify(string message)
    {
        await ReplyAsync($"正在广播: {message}");
        return "广播完成!";
    }
}

引号参数

SDK 的 CommandParser 支持引号包裹的参数,用于处理包含空格的参数:

用户发送: /say "hello world, how are you?" 小明
       → message = "hello world, how are you?"
       → target = "小明"
csharp
[Command("say")]
public string Say(string message, string? target = null)
{
    return target != null
        ? $"对 {target} 说: {message}"
        : message;
}

params 参数

如果需要接收不确定数量的参数,使用 params string[]

csharp
[Command("tags")]
public string Tags(params string[] tags)
{
    return $"标签: [{string.Join("], [", tags)}]";
}
// 用户发送: /tags C# .NET QQBot SDK
// 输出: 标签: [C#], [.NET], [QQBot], [SDK]

命令注册细节

  • 命令是不区分大小写的(/HELLO/hello 相同)
  • 如果用户 @机器人后紧接命令(如 @Bot /hello),会自动跳过 @ 部分
  • 命令名和参数之间用空格分隔
  • 一个类可以有多个 [Command] 方法
  • 一个程序集可以有多个控制器类

完整示例

csharp
using Luolan.QQBot.Controllers;
using Luolan.QQBot.Helpers;
using Luolan.QQBot.Models;

public class MyRobotController : QQBotController
{
    // 基础文本
    [Command("hello")]
    public string Hello(string? name = null)
        => $"你好,{name ?? User?.Username ?? "世界"}!";

    // 整数计算
    [Command("add")]
    public string Add(int a, int b)
        => $"{a} + {b} = {a + b}";

    // 布尔开关
    [Command("switch")]
    public string Switch(bool on)
        => on ? "开了" : "关了";

    // 枚举
    [Command("level")]
    public string Level(LogLevel level)
        => $"级别: {level}";

    // 可空参数
    [Command("delay")]
    public string Delay(int? ms = null)
        => $"延迟: {ms ?? 1000}ms";

    // 图片
    [Command("img")]
    public ImageResult Image(string url)
        => new ImageResult(url);

    // Markdown
    [Command("md")]
    public MessageMarkdown Markdown()
        => MarkdownBuilder.FromContent("**加粗** *斜体*");

    // 异步
    [Command("async")]
    public async Task<string> DoAsync()
    {
        await Task.Delay(500);
        return "异步完成!";
    }

    //上下文
    [Command("whoami")]
    public string WhoAmI()
        => $"你是 {User?.Username}, 来自 {Message.GuildId}";
}

下一步

基于 MIT 协议发布