Simple Robot v4.6.0 Help

QGBot

作为一个QQ频道的 Bot 库,有一个用于描述机器人的 Bot 想必肯定是很正常的。

API?

在 API 模块 (simbot-component-qq-guild-api) 中,你可能找不到太多有关 Bot 的身影。 毕竟 API 模块仅是针对QQ频道中的 API 的封装与实现,本身是不包括对 Bot 的描述的。

标准库 Bot

在标准库模块 (simbot-component-qq-guild-stdlib) 中, 你可以发现一个类型 love.forte.simbot.qguild.Bot ,它便是对一个QQ频道机器人的描述, 可以用来订阅并处理事件等。

创建 Bot

使用工厂类 BotFactory.create(..) 可创建一个尚未启动的 Bot 实例。

使用 Ticket

// 准备 bot 的必要信息 val botId = "xxxx" val botSecret = "" // secret 如果用不到可使用空字符串 val botToken = "xxxx" // 用于注册 bot 的 “票据” 信息。 val ticket = Bot.Ticket(botId, botSecret, botToken) // 构建一个 Bot,并可选地进行一些配置。 val bot = BotFactory.create(ticket) { // 各种配置... // 比如切换服务地址为沙箱频道的服务地址 useSandboxServerUrl() // 指定需要订阅的事件的 intents,默认会订阅: // - 频道相关事件 // - 频道成员相关事件 // - 公域消息相关事件 intents // = xxx // 自定义一个 shard,默认是 Shard.FULL shard = Shard.FULL // 其他各种配置... }

直接作为参数

也可以直接将这三个必要信息作为参数使用。

// 准备 bot 的必要信息 val botId = "xxxx" val botSecret = "" // secret 如果用不到可使用空字符串 val botToken = "xxxx" // 构建一个 Bot,并可选地进行一些配置。 val bot = BotFactory.create(botId, botSecret, botToken) { ... }

直接提供配置类

作为 DSL 的配置类也可以独立构建后直接提供。

// 准备 bot 的必要信息 val botId = "xxxx" val botSecret = "" // secret 如果用不到可使用空字符串 val botToken = "xxxx" // 构建配置类 val configuration = ConfigurableBotConfiguration() // config... // 构建一个 Bot,并提供配置。 val bot = BotFactory.create(botId, botSecret, botToken, configuration)
// 准备 bot 的必要信息 var botId = "xxxx"; var botSecret = ""; // secret 如果用不到可使用空字符串 var botToken = "xxxx"; // 用于注册 bot 的 “票据” 信息。 var ticket = new Bot.Ticket(botId, botSecret, botToken); // 构建一个 Bot,并可选地进行一些配置。 var bot = BotFactory.create(ticket, config -> { // 各种配置... // 比如切换服务地址为沙箱频道的服务地址 config.useSandboxServerUrl(); // 指定需要订阅的事件的 intents,默认会订阅: // - 频道相关事件 // - 频道成员相关事件 // - 公域消息相关事件 // config.setIntentsValue(...); // 自定义一个 shard,默认是 Shard.FULL config.setShard(Shard.FULL); // 其他各种配置... });

直接作为参数

也可以直接将这三个必要信息作为参数使用。

// 准备 bot 的必要信息 var botId = "xxxx"; var botSecret = ""; // secret 如果用不到可使用空字符串 var botToken = "xxxx"; // 构建一个 Bot,并可选地进行一些配置。 var bot = BotFactory.create(botId, botSecret, botToken, config -> { ... });

直接提供配置类

作为 DSL 的配置类也可以独立构建后直接提供。

// 准备 bot 的必要信息 var botId = "xxxx"; var botSecret = ""; // secret 如果用不到可使用空字符串 var botToken = "xxxx"; var configuration = new ConfigurableBotConfiguration(); // 构建一个 Bot,并可选地进行一些配置。 var bot = BotFactory.create(botId, botSecret, botToken, configuration);

订阅事件

你可以通过 subscribe 来订阅全部或指定类型的事件。

订阅全部事件

使用 subscribe 注册一个普通的事件处理器,此处理器会接收并处理所有类型的事件。

bot.subscribe { raw -> // raw 代表事件的原始JSON字符串 // this: Signal.Dispatch, 也就是解析出来的事件结构体 println("event: $this") println("event.data: $data") println("raw: $raw") }

订阅指定类型的事件

使用扩展函数 subscribe 注册一个针对具体 Signal.Dispatch 事件类型的事件处理器, 它只有在接收到的 Signal.Dispatch 与目标类型一致时才会处理。

此示例展示处理 AtMessageCreate 也就公域是消息事件, 并在对方发送了包含 "stop" 的文本时终止 bot。

// 注册一个普通的事件处理器,此处理器会接收并处理所有类型的事件 bot.subscribe<AtMessageCreate> { if ("stop" in data.content) { // 终止 bot bot.cancel() } }

订阅全部事件

使用 subscribe 注册一个普通的事件处理器,此处理器会接收并处理所有类型的事件。

bot.subscribe(EventProcessors.async((event, raw) -> { // raw 代表事件的原始JSON字符串 // event: Signal.Dispatch, 也就是解析出来的事件结构体 System.out.println("event: " + event); System.out.println("event.data: " + event.getData()); System.out.println("raw: " + raw); // 异步处理器必须返回 CompletableFuture return CompletableFuture.completedFuture(null); }));
bot.subscribe(EventProcessors.block((event, raw) -> { // raw 代表事件的原始JSON字符串 // event: Signal.Dispatch, 也就是解析出来的事件结构体 System.out.println("event: " + event); System.out.println("event.data: " + event.getData()); System.out.println("raw: " + raw); }));

订阅指定类型的事件

使用 subscribe 注册一个指定了具体类型的事件处理器, 它只有在接收到的 Signal.Dispatch 与目标类型一致时才会处理。

此示例展示处理 AtMessageCreate 也就公域是消息事件, 并在对方发送了包含 "stop" 的文本时终止 bot。

bot.subscribe(EventProcessors.async(AtMessageCreate.class, (event, raw) -> { if (event.getData().getContent().contains("stop")) { // 终止 bot bot.cancel(); } return CompletableFuture.completedFuture(null); }));
bot.subscribe(EventProcessors.block(AtMessageCreate.class, (event, raw) -> { if (event.getData().getContent().contains("stop")) { // 终止 bot bot.cancel(); } }));

启动 Bot

Bot 被启动之前,它不会与服务器建立连接,也不会收到并处理事件。

bot.start() // 启动bot bot.join() // 挂起并直到bot被关闭
var future = bot // 启动bot .startAsync() // 转为直到bot被终止后结束的 future .thenCompose(r -> bot.asFuture()); // 阻塞线程或者怎么样都行 future.join();
// 启动bot bot.startBlocking(); // 阻塞当前线程,直到bot被终止。 bot.joinBlocking();

关闭 Bot

使用 cancel 即可关闭一个 Bot。被关闭的 Bot 不能再次启动。

bot.cancel() bot.cancel(reason) // 或可以提供一个 reason: Throwable
bot.cancel(); bot.cancel(reason); // 或可以提供一个 Throwable reason

组件库 QGBot

当你在配合 simbot 使用组件库(simbot-component-qq-guild-core )的时候, 你可能会更需要了解 love.forte.simbot.component.qguild.bot.QGBot。 它是作为一个 simbot 组件库的 Bot 的实现 (simbot标准API中的 Bot 接口,不是上面提到的 stdlib bot)。

源 stdlib Bot

组件库的 QGBot 是在上文提到过的 标准库 Bot 的基础上进行构建与扩展的。

你可以在 QGBot 中通过属性 source 获取到其对应的源 Bot

val sourceBot = bot.source
var sourceBot = bot.getSource();

构建 QGBot

Application 中安装 QQGuildBotManager 后即可使用其注册、启动一个 QGBot 了。

安装 QQGuildBotManager

val app = launchSimpleApplication { install(QQGuildComponent) // 别忘了安装 Component 标识 install(QQGuildBotManager) { // 可选地。进行一些配置,比如一个所有 Bot 共享的父级 CoroutineContext } }

Kotlin 中可以选择使用扩展函数 useQQGuild 来简化代码:

val app = launchSimpleApplication { // 同时安装组件标识和BotManager useQQGuild() // 或.. // 可选地进行一些配置 useQQGuild { // 可选地对 BotManager 进行一些配置 botManager { // ... } } }
var appAsync = Applications.launchApplicationAsync(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory); }); // 可以转化为 CompletableFuture 然后进行一些操作 var appFuture = appAsync.asFuture();
var app = Applications.launchApplicationBlocking(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory); });

你可以可选地进行一些配置。

var appAsync = Applications.launchApplicationAsync(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory, config -> { // 比如让所有的 Bot 都默认的使用一个 4 守护线程的线程池 // 这里仅供示例,真实配置请按照项目实际情况来。 config.setCoroutineContext( ExecutorsKt.from(Executors.newFixedThreadPool(4, r -> { final var t = new Thread(r); t.setDaemon(true); return t; })) ); }); }); // 可以转化为 CompletableFuture 然后进行一些操作 var appFuture = appAsync.asFuture();
var app = Applications.launchApplicationBlocking(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory, config -> { // 比如让所有的 Bot 都默认使用虚拟线程线程池, // 这样就可以在事件处理器中相对更安全的使用 **阻塞API** 了。 // 这里仅供示例,真实配置请按照项目实际情况来。 config.setCoroutineContext( ExecutorsKt.from(Executors.newVirtualThreadPerTaskExecutor()) ); }); });

构建完 Application 后,我们寻找 QQGuildBotManager ,并注册、启动一个 bot。

val app = launchSimpleApplication { useQQGuild() } val bot = app.botManagers.get<QQGuildBotManager>() .register("APP ID", "SECRET", "TOKEN") { // 可选地进行一些配置 } // 启动它 bot.start() // 挂起它 bot.join() // 或者直接挂起 app app.join()

Kotlin 中也可以使用扩展函数 qgGuildBots 来简化操作。

val app = launchSimpleApplication { useQQGuild() } app.qgGuildBots { val bot = register("APP ID", "SECRET", "TOKEN") { // 可选地进行一些配置 } // 启动 bot bot.start() } // 挂起 app app.join()
var appAsync = Applications.launchApplicationAsync(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory); }); var future = appAsync.asFuture() .thenCompose(app -> { // 找到 QQGuildBotManager var botManager = app.getBotManagers().stream().filter(it -> it instanceof QQGuildBotManager) .map(it -> (QQGuildBotManager) it) .findFirst() .orElseThrow(); // 注册 bot var bot = botManager.register("APP ID", "SECRET", "TOKEN", config -> { // 可选地进行一些配置 }); // 启动 bot, // 启动完 bot 后,返回 app 作为 future,并在外部直接 join app. return bot.startAsync() .thenCompose(($) -> app.asFuture()); }); future.join();
var app = Applications.launchApplicationBlocking(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory); }); // 找到 QQGuildBotManager var botManager = app.getBotManagers().stream().filter(it -> it instanceof QQGuildBotManager) .map(it -> (QQGuildBotManager) it) .findFirst() .orElseThrow(); // 注册 bot var bot = botManager.register("APP ID", "SECRET", "TOKEN", config -> { // 可选地进行一些配置 }); // 启动 bot bot.startBlocking(); // 阻塞bot bot.joinBlocking(); // 或直接阻塞 app app.joinBlocking();

监听事件

实际上,在组件库中监听事件与某个具体的 Bot 无关。 你可以前往参考 Simple Robot 应用手册: 事件监听与处理

你可以在 组件模块的标准 Event 实现 或API文档中找到所有可用于 simbot 中的事件类型。它们大多与 API 模块中定义的事件类型有一些对应规则。

此处使用 公域消息事件 QGAtMessageCreateEvent 作为例子:

val app = launchSimpleApplication { useQQGuild() } // 注册一个事件处理器,处理 QGAtMessageCreateEvent 类型的事件 app.eventDispatcher.listen<QGAtMessageCreateEvent> { atMessageEvent -> // QGAtMessageCreateEvent // this: EventListenerContext println("Event: $atMessageEvent") println("Context: $this") // result. EventResult.empty() } // 注册 bot... app.qgGuildBots { ... } app.join()
var appAsync = Applications.launchApplicationAsync(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory); }); var future = appAsync.asFuture() .thenCompose(app -> { app.getEventDispatcher().register( EventListeners.async( QGAtMessageCreateEvent.class, (context, event) -> { System.out.println("Event: " + event); System.out.println("Context: " + context); // 返回异步结果 return CompletableFuture.completedFuture(EventResult.empty()); } ) ); // 注册 bot var bot = ... // 启动 bot, 或者之类的各种操作 return ... }); future.join();
var app = Applications.launchApplicationBlocking(Simple.INSTANCE, builder -> { builder.install(QQGuildComponent.Factory); builder.install(QQGuildBotManager.Factory, config -> { // 如果使用完全的阻塞API,建议配置调度器为虚拟线程调度器,来更好的避免线程匮乏的问题。 // 这里仅供示例,真实配置请按照项目实际情况来。 config.setCoroutineContext( ExecutorsKt.from(Executors.newVirtualThreadPerTaskExecutor()) ); }); }); // 注册一个事件处理器,处理 QGAtMessageCreateEvent 类型的事件 app.getEventDispatcher().register(EventListeners.block( QGAtMessageCreateEvent.class, (context, event) -> { System.out.println("Event: " + event); System.out.println("Context: " + context); return EventResult.empty(); } )); // 注册bot... app.getBotManagers()... var bot = ... bot.startBlocking(); // 阻塞 app app.joinBlocking();

频道操作

对频道 (QGGuild) 以及之下的子频道(QGChannel )、频道成员(QGMember) 的操作, 都是在 QGBotguildRelation 中开始的。

val bot: QGBot = ... val guildRelation = bot.guildRelation
QGBot bot = ... var guildRelation = bot.getGuildRelation()

通过获取到的 QGGuildRelation ,可以用来获取 QGGuildQGChannel 等信息。

val guildRelation = bot.guildRelation // 寻找指定ID的频道服务器 val guild = guildRelation.guild(1234L.ID) // 获取全部 QGGuild 的流 guildRelation.guilds .asFlow() // 可以转成 Flow .collect { ... } // 额外提供可以指定批次数量和 lastId 的API guildRelation.guilds(lastId = null, batch = 100) .asFlow() // 可以转成 Flow .collect { ... } // 额外提供了一些直接查询子频道的API // 寻找指定ID的子频道 val channel = guildRelation.channel(1234L.ID) // 寻找指定ID的聊天子频道,可用来发消息的那种 val chatChannel = guildRelation.chatChannel(1234L.ID) // 寻找指定ID的帖子子频道 val forumChannel = guildRelation.forumChannel(1234L.ID)
final var guildRelation = bot.getGuildRelation(); // 寻找指定ID的频道服务器 guildRelation.getGuildAsync(Identifies.of(1234L)) .thenAccept(guild -> { ... }); // 获取全部 QGGuild 的流 guildRelation.getGuilds() // 可以转化成 Flux .transform(SuspendReserves.flux()) .subscribe(guild -> { ... }); // 额外提供可以指定批次数量和 lastId 的API guildRelation.guilds(null, 100) // 可以转化成 Flux .transform(SuspendReserves.flux()) .subscribe(guild -> { ... }); // 额外提供了一些直接查询子频道的API // 寻找指定ID的子频道 guildRelation.getChannelAsync(Identifies.of(1234L)) .thenAccept(channel -> { ... }); // 寻找指定ID的聊天子频道,可用来发消息的那种 guildRelation.getChatChannelAsync(Identifies.of(1234L)) .thenAccept(chatChannel -> { ... }); // 寻找指定ID的帖子子频道 guildRelation.getForumChannelAsync(Identifies.of(1234L)) .thenAccept(forumChannel -> { ... });
var guildRelation = bot.getGuildRelation(); // 寻找指定ID的频道服务器 var guildValue = guildRelation.getGuild(Identifies.of(1234L)); // 获取全部 QGGuild 的流 var guilds = guildRelation.getGuilds(); // 可以转成 stream 或者 list Collectables.asStream(guilds) .forEach(guild -> { ... }); // 额外提供可以指定批次数量和 lastId 的API var guildList = guildRelation.guilds(null, 100) // 可以转化成 list .transform(SuspendReserves.list()); // 额外提供了一些直接查询子频道的API // 寻找指定ID的子频道 var channel = guildRelation.getChannel(Identifies.of(1234L)) // 寻找指定ID的聊天子频道,可用来发消息的那种 var chatChannel = guildRelation.getChatChannel(Identifies.of(1234L)) // 寻找指定ID的帖子子频道 var forumChannel = guildRelation.getForumChannel(Identifies.of(1234L))

消息发送

除了使用频道中获取到的子频道对象 QGChannelsend 直接发送、 使用消息事件中的 reply 发送消息等方式以外, QGBot 本身提供了一些可以跳过获取频道这一环节、直接根据 ID 发送消息的 API QGBot.sendTo

val bot: QGBot = ... bot.sendTo("channel id".ID, "消息内容") bot.sendTo("channel id".ID, "消息内容".toText() + At("user id".ID))
QGBot bot = ... var sendTask1 = bot.sendToAsync(Identifies.of("channel id"), "消息内容"); var sendTask2 = bot.sendToAsync(Identifies.of("channel id"), Messages.of( Text.of("文本消息"), At.of(Identifies.of("user id")) )); // 如果有需要,记得异常处理 sendTask1.exceptionally(err -> ...); sendTask2.exceptionally(err -> ...);

或者有顺序地执行这两个任务:

QGBot bot = ... var sendTask = bot.sendToAsync(Identifies.of("channel id"), "消息内容") .thenCompose(($) -> bot.sendToAsync(Identifies.of("channel id"), Messages.of( Text.of("文本消息"), At.of(Identifies.of("user id")) )));

如果希望在事件处理器中,处理器需要等待所有异步任务完成后再进行下一个处理器,则返回这个 future, 否则会不会等待。

QGBot bot = ... var sendTask1 = bot.sendToAsync(...); var sendTask2 = bot.sendToAsync(...); return CompletableFuture.allOf(sendTask1, sendTask2) .thenApply($ -> EventResult.empty()); // 任务全部完成后,返回事件结果
QGBot bot = ... bot.sendToBlocking(Identifies.of("channel id"), "消息内容"); bot.sendToBlocking(Identifies.of("channel id"), Messages.of( Text.of("文本消息"), At.of(Identifies.of("user id")) ));
Last modified: 19 September 2024