随着rust的学习推进,发现旧版本的rust实现较为劣质,现阶段正在进行重构
本项目不作为核心项目开发,项目的迭代时间并不确定
请不要使用以下sdk,当前页面仅作保留,后续将继续使用该页面进行sdk更新跟进
架构概述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ┌─────────────────────────────────────────────┐ │ 宿主 (luo9_bot, Rust) │ │ ├── WebSocket 连接 (Napcat) │ │ ├── 事件路由 (message/event/notice) │ │ ├── 插件加载器 (DLL/SO) │ │ └── Cron 调度器 │ └─────────────────┬───────────────────────────┘ │ FFI 消息总线 (luo9_core) │ ┌─────────────────┴───────────────────────────┐ │ 插件 (DLL/SO, 任意语言) │ │ ├── 独立 OS 线程运行 │ │ ├── 通过 Bus 订阅/发布消息 │ │ └── 通过 Bot 发送消息 │ └─────────────────────────────────────────────┘
|
总线 Topic
| Topic |
方向 |
用途 |
luo9_message |
宿主 → 插件 |
QQ 消息(私聊/群聊) |
luo9_meta_event |
宿主 → 插件 |
元事件(心跳/生命周期) |
luo9_notice |
宿主 → 插件 |
通知事件(好友/群变动等) |
luo9_task |
插件 ↔ 宿主 |
定时任务请求/事件 |
luo9_send |
插件 → 宿主 |
消息发送请求 |
指令解析(Command)
Command 库提供高效的指令前缀解析,支持自定义前缀字符和三种前缀模式。
| 模式 |
说明 |
示例 |
Required(char) |
必须有指定前缀才解析成功 |
/echo hello |
Optional(char) |
前缀可选 |
echo hello 或 /echo hello |
None |
不检查前缀,直接解析 |
echo hello |
基本用法
Rust
1 2 3 4 5 6 7 8 9
| use luo9_sdk::command::{Command, PrefixMode};
match Command::parse(msg, "echo", PrefixMode::Required('/')) { Some(cmd) => { let args = cmd.args_raw(); Bot::send_private_msg(user_id, CString::new(args).unwrap()); } None => return, }
|
C++
1 2 3 4 5 6 7 8
| #include "command.h"
auto cmd = Command::parse(msg, "echo", PrefixMode::Optional('/'));
if (!cmd.empty()) { std::string args = cmd.args_raw(); Bot::send_private_msg(user_id, args); }
|
链式匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if let Some(cmd) = Command::parse(msg, "epic", PrefixMode::None) { cmd.on("提醒开启", |_args| { }) .on("提醒关闭", |_args| { }) .on("状态", |_args| { }) .otherwise(|| { }); }
|
常用方法
| 方法 |
Rust |
C++ |
说明 |
| 获取指令名 |
cmd.name() |
cmd.name() |
匹配到的指令名称 |
| 获取原始参数 |
cmd.args_raw() |
cmd.args_raw() |
去除指令名后的参数字符串 |
| 是否有参数 |
cmd.has_args() |
cmd.has_args() |
返回 bool |
| 参数个数 |
cmd.args_count() |
cmd.args_count() |
返回参数数量 |
| 获取指定参数 |
cmd.arg_at(0) |
cmd.arg_at(0) |
返回第 n 个参数 |
消息发送(Bot)
插件通过 Bot 发送消息,内部通过 luo9_send topic 传递给宿主:
1 2 3 4 5 6
| use luo9_sdk::Bot; use std::ffi::CString;
Bot::send_group_msg(group_id, CString::new("你好").unwrap()); Bot::send_private_msg(user_id, CString::new("你好").unwrap());
|
1 2 3 4 5
| #include "bot.h"
Bot::send_group_msg(group_id, "你好"); Bot::send_private_msg(user_id, "你好");
|
消息接收(Payload)
从总线接收的消息通过 BusPayload 解析:
1 2 3 4 5 6 7 8 9 10 11 12
| use luo9_sdk::payload::BusPayload;
let json = Bus::topic("luo9_message").wait_pop(subscriber_id)?; match BusPayload::parse(&json) { Some(BusPayload::Message(msg)) => { } Some(BusPayload::MetaEvent(event)) => { } Some(BusPayload::Notice(notice)) => { } None => { } }
|
定时任务(Task)
插件通过 luo9_task topic 注册/取消定时任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let request = serde_json::json!({ "action": "schedule", "task_name": "my_task", "cron": "0 0 9 * * *", "payload": "任意数据" }); Bus::topic("luo9_task").publish(&request.to_string())?;
let cancel = serde_json::json!({ "action": "cancel", "task_name": "my_task" }); Bus::topic("luo9_task").publish(&cancel.to_string())?;
|
Cron 表达式为 6 字段格式 秒 分 时 日 月 周,支持 ? L W # 等特殊字符。详见 洛玖定时任务系统。
插件开发模板
Rust
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| use luo9_sdk::bus::Bus; use luo9_sdk::command::{Command, PrefixMode}; use luo9_sdk::payload::BusPayload; use luo9_sdk::Bot; use std::ffi::CString;
pub fn plugin_main() { let sub_id = Bus::topic("luo9_message").subscribe().unwrap();
loop { let json = Bus::topic("luo9_message").wait_pop(sub_id).unwrap(); if let Some(BusPayload::Message(msg)) = BusPayload::parse(&json) { handle_message(msg.group_id.unwrap_or(0), msg.user_id, &msg.message); } } }
fn handle_message(group_id: u64, user_id: u64, msg: &str) { match Command::parse(msg, "echo", PrefixMode::Required('/')) { Some(cmd) => { let reply = CString::new(cmd.args_raw()).unwrap(); Bot::send_group_msg(group_id, reply); } None => return, }; }
|
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include "bus.h" #include "command.h" #include "bot.h" #include "payload.h"
void plugin_main() { auto sub_id = Bus::topic("luo9_message").subscribe();
while (true) { auto json = Bus::topic("luo9_message").wait_pop(sub_id); auto payload = BusPayload::parse(json);
if (payload.is_message()) { handle_message(payload.group_id(), payload.user_id(), payload.message()); } } }
void handle_message(uint64_t group_id, uint64_t user_id, const std::string& msg) { auto cmd = Command::parse(msg, "echo", PrefixMode::Required('/')); if (!cmd.empty()) { Bot::send_group_msg(group_id, cmd.args_raw()); } }
|
FFI 接口
插件必须导出:
1
| extern "C" fn plugin_main();
|
luo9_core 暴露的核心 FFI 函数:
| 函数 |
用途 |
luo9_bus_init() |
初始化总线单例 |
luo9_bus_subscribe(topic) |
订阅 topic,返回 subscriber_id |
luo9_bus_publish(topic, payload) |
发布消息 |
luo9_bus_pop(topic, sub_id) |
非阻塞取消息 |
luo9_bus_wait_pop(topic, sub_id) |
阻塞取消息 |
luo9_bus_free_string(ptr) |
释放 bus 返回的字符串 |
群聊事件
群消息通过 luo9_message topic 推送,message_type 为 "group"。
私聊事件
私聊消息通过 luo9_message topic 推送,message_type 为 "private"。
通知事件
| 事件名称 |
事件含义 |
| friend_add |
好友添加 |
| friend_recall |
好友撤回 |
| group_admin |
群管理员变动 |
| group_ban |
群禁言 |
| group_increase |
群成员增加 |
| group_decrease |
群成员减少 |
| group_card |
群名片变更 |
| group_recall |
群消息撤回 |
| group_upload |
群文件上传 |
| poke |
戳一戳 |