关于本框架
孩子们我睡醒了
新版本文档还没写,目前的文档大部分都是AI帮我写的,将就看看吧,真的懒得写文档了
新版框架不再会有CQ码支持
Sora 是一个以轻量和易用为核心目标的多协议异步机器人开发框架
与旧版 Sora 不同,新版从零重构,原生支持多种协议适配:
框架采用模块化架构,提供属性指令路由、事件分发、消息等待等功能,同时保持简单直接的 API 设计
项目结构
| NuGet 包 |
版本 |
说明 |
HoshikawaKaguya.Sora |
 |
框架顶层 — SoraService、SoraServiceFactory、日志初始化 |
HoshikawaKaguya.Sora.Entities |
 |
共享实体 — 事件、消息段、信息类型、API 接口 |
HoshikawaKaguya.Sora.Core |
 |
核心工具 — 枚举、扩展方法、通用基础设施 |
HoshikawaKaguya.Sora.Command |
 |
属性指令路由 — [CommandGroup] + [Command] 声明式指令 |
HoshikawaKaguya.Sora.Adapter.Milky |
 |
Milky 协议适配器 |
HoshikawaKaguya.Sora.Adapter.OneBot11 [Deprecated] |
 |
OneBot v11 协议适配器 |
原生实现的 Protocol Adapter
Milky Adapter通过了较为较为完整的E2E测试
部分破坏性API/Event由于风控未作测试
这个框架基于 LuckyLilliaBot 完成开发调试,目前测试只使用了 LuckyLilliaBot 进行测试
Milky
基于 Milky HTTP API 的适配器,推荐优先使用。
Milky协议100%支持,并且这个框架目前是基于Milky在调试和开发
| 特性 |
说明 |
| 事件传输 |
SSE / WebSocket / WebHook 三选一(默认 WebSocket) |
| API 调用 |
HTTP REST |
| TLS |
支持(含客户端证书) |
| 自动重连 |
支持(SSE / WebSocket) |
SoraService service = SoraServiceFactory.Instance.CreateMilkyService(
new MilkyConfig { Host = "127.0.0.1", Port = 3000, AccessToken = "your-token" });
OneBot v11(已废弃)
由于OneBot v11常年无人维护且各家协议端实现都不一样 群魔乱舞,使用OneBot v11可能会遇到很多不兼容或者意想不到的情况
这个adapter目前只对LLBot做了测试,不再推荐使用OneBot v11协议,OneBot v11已经算是历史遗留了
HoshikawaKaguya.Sora.Adapter.OneBot11 的支持范围截至 Sora 2.2;后续版本停止维护,并退出构建、测试和发布。源码、专属测试和示例仅作历史参考,使用 OB11 的项目应固定在 Sora 2.2 或迁移到 Milky。
推荐直接drop ob11协议的使用,如果你的是老项目建议尽早迁移
基于 OneBot v11 的适配器,支持正向/反向 WebSocket。只支持Array格式上报
由于OneBot v11目前各协议端实现比较自由,所以有部分扩展API/Event并不支持(覆盖率在85%左右)
| 特性 |
说明 |
| 连接模式 |
正向 WS(Sora → OB11 Server)/ 反向 WS(OB11 Server → Sora) |
| TLS |
支持 |
| 心跳检测 |
支持 |
| 自动重连 |
支持(正向 WS) |
SoraService service = SoraServiceFactory.Instance.CreateOneBot11Service(
new OneBot11Config { Host = "127.0.0.1", Port = 6700, AccessToken = "your-token" });
需要开发自定义适配器?请参阅 Adapter 开发指南
快速开始
安装
dotnet add package HoshikawaKaguya.Sora
dotnet add package HoshikawaKaguya.Sora.Adapter.Milky # Milky 协议
最小示例
using Sora;
using Sora.Adapter.Milky;
// 创建 Milky 协议服务
SoraService service = SoraServiceFactory.Instance.CreateMilkyService(
new MilkyConfig
{
Host = "localhost",
Port = 3010,
AccessToken = "your-token"
});
// 注册消息事件
service.Events.OnMessageReceived += async e =>
{
string text = e.Message.Body.GetText();
if (text == "ping")
{
await e.Api.SendGroupMessageAsync(
e.Message.GroupId,
new MessageBody("pong"));
}
};
// 启动服务
await service.StartAsync();
await Task.Delay(-1);
属性指令
[CommandGroup(Name = "example", Prefix = "/")]
public static class MyCommands
{
[Command(Expressions = ["hello"], MatchType = MatchType.Full, Description = "Say hello")]
public static async ValueTask Hello(MessageReceivedEvent e)
{
MessageBody reply = new("Hello! 你好!");
if (e.Message.SourceType == MessageSourceType.Group)
await e.Api.SendGroupMessageAsync(e.Message.GroupId, reply);
else
await e.Api.SendFriendMessageAsync(e.Message.SenderId, reply);
}
}
// 注册程序集并扫描指令
AssemblyRegistration commands = service.Commands.RegisterAssembly(typeof(Program).Assembly);
commands.ScanCommandGroups();
// 动态指令:归属本程序集中的类型
commands.Registrar.RegisterDynamicCommand(typeof(MyCommands), async e => { /* ... */ }, ["ping"]);
指令以程序集为单位注册:多个程序集(例如外部插件)先按程序集优先级排序,再在每个程序集内按指令优先级执行,并可按程序集或类型动态启停、调整优先级和注销。完整说明见指令模块文档;从 2.x 升级见 V2 → V3 迁移指南。
配置 BlockUsers 后,框架在自动已读、连续对话和过滤器之前忽略该触发者的事件。SuperUsers 为对应事件设置 IsSuperUser;命令可用 [Command(SuperUserOnly = true)] 或动态注册的 superUserOnly: true 限制执行,群角色 PermissionLevel 仍需满足。框架从事件已有字段识别触发者,详见适配器与管线文档。
可以简单参考的文档
这些文档均由opus 4.6/GPT 6 Astra生成和少量人工修改,可能存在不准确的问题
懒得自己写文档了,好费劲
Agent 开发入口:项目级规范见 AGENTS.md,源码和测试分别见 src/AGENTS.md 与 tests/AGENTS.md。协议/测试审计工作流位于 .agents/skills/,可执行 CLI 用法见 tools/agent-audit/README.md。
开发注意事项
开源协议
本项目使用了 Apache-2.0 开源协议
这意味着在引用/修改本类库时需要遵守相关的协议规定
构建要求
- .NET 10 SDK
- C# 预览版语言特性(LangVersion = preview)
dotnet build Sora.slnx --configuration Release
测试
我vibe了90%的测试工程,但都经过了实际的验证测试,但可能还有我没发现的问题
好懒不想写这么多测试,好像1.x版本就压根没写
项目包含单元测试和功能测试(E2E),使用 xUnit v3 框架:
# 运行单元测试(无需外部依赖)
dotnet test Sora.slnx --filter "Category=Unit&FullyQualifiedName!~OneBot11" --no-build
# 运行公共能力与 Milky 测试(需要配置测试环境变量)
pwsh tests/scripts/Run-Tests.ps1 -Category All -Filter "FullyQualifiedName!~OneBot11"
功能测试采用双机器人完成E2E验证,需要配置 SORA_TEST_* 环境变量。部分破坏性API/Event并未测试(容易触发风控)
详见 测试说明。
关于 ISSUE
ISSUE 目前只接受 bug 的提交和新功能的建议
如果有使用问题或者不确定的问题请使用Discussions
请注意,开发者并没有义务回复您的问题。您应该具备基本的提问技巧。
如果不知道该怎么样提问,那么请在提问前阅读 提问的智慧
以下 ISSUE 会被直接关闭
- 提交 BUG 时没有使用 Template
- 提交当前版本下已经被修复的 BUG
- 询问问题
关于命名
Sora 这个名字来源于日语中"空"的罗马音
一拍脑袋想的.jpg
鸣谢
使用到的开源库
Newtonsoft.Json | Json 序列化/反序列化
Mapster | 对象映射
Serilog | 结构化日志
以下的依赖只被Onebot 11 adapter所使用
Fleck | 反向 WS 服务器
Websocket.Client | 正向 WS 客户端
System.Reactive | 响应式异步 API 支持
感谢 JetBrains 为开源项目提供免费的全家桶授权
本项目使用了 Rider 开发环境
评论