库如其名,这是一个Onebot V11协议的实现
目前已完成对Onebot V11协议所有API的实现
Client 是高层客户端的入口,封装了API调用、事件推送等核心逻辑层服务
Client 内部使用了 flume 作为API调用通道(mpsc),tokio broadcast 作为事件通道(mpmc)
在与底层协议的交互方面,Client 内部使用了 特征对象 和 依赖注入 ,这使得 Client 具备 协议无关 的特性
因此,Client 需要且仅需要专注于 API调用 与 事件推送 等核心逻辑层服务,对于底层协议的交互,则使用外部依赖实现
由此,Client 实现了逻辑层与协议层的解耦,也使得 Client 具备运行时切换底层协议的能力
另外,由于 Client 与底层协议交互时使用了消息通道
因此,Client 天然具备 线程安全 并且不需要锁来防止竞态条件(Arc<Client>🤓☝️ | Arc<Mutex<Client>>👎😡)
对于资源管理方面,Client 实现了 Drop 特征,在 Client 析构时会自动清理其产生的所有资源
但 Client 并不会清理外部依赖所产生的资源,这依赖于外部依赖的析构函数(本库中所有实现了 CommunicationService 的结构都实现了 Drop 特征)
CommunicationService 是 Client 与底层协议交互的基础
任意实现了 CommunicationService 特征 的结构都可作为与 Client 交互的服务
目前已实现的协议:
- 正向 WebSocket
- 反向 WebSocket
- SSE
- Http
- Http Post
flowchart LR
A(Client) <-->|交互| B{CommunicationService}
C([WebSocket])
D([WebSocketReverse])
E([Http])
F([HttpPost])
G([SSE])
H([具体协议])
B -->|API调用| H
H -->|事件推送/API响应| B
H -.- C & D & E & F & G
use std::time::Duration;
use onebot_api::api::APISender;
use onebot_api::communication::utils::{Client, Event};
use onebot_api::communication::ws::WsService;
use onebot_api::event::EventReceiver;
use onebot_api::text;
#[tokio::main]
async fn main() {
let ws_service = WsService::new("wss://example.com", Some("example_token".to_string())).unwrap();
let client = Client::new(ws_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
let msg_id = client.send_private_msg(123456, text!("this is a {}", "message"), None).await.unwrap();
client.send_like(123456, Some(10)).await.unwrap();
let mut event_receiver = client.get_receiver();
while let Ok(event) = event_receiver.recv().await && let Event::Event(event) = &*event {
println!("{:#?}", event)
}
}use std::time::Duration;
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
#[tokio::main]
async fn main() {
let ws_service = WsService::new("wss://example.com", Some("example_token".to_string())).unwrap();
let client = Client::new(ws_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}use onebot_api::communication::utils::Client;
use onebot_api::communication::ws_reverse::WsReverseService;
use std::time::Duration;
#[tokio::main]
async fn main() {
let ws_reverse_service = WsReverseService::new("0.0.0.0:8080", Some("example_token".to_string()));
let client = Client::new(ws_reverse_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}use onebot_api::communication::utils::Client;
use std::time::Duration;
use onebot_api::communication::http::HttpService;
#[tokio::main]
async fn main() {
let http_service = HttpService::new("https://example.com", Some("example_token".to_string())).unwrap();
let client = Client::new(http_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}use onebot_api::communication::utils::Client;
use std::time::Duration;
use onebot_api::communication::http_post::HttpPostService;
#[tokio::main]
async fn main() {
let http_post_service = HttpPostService::new("0.0.0.0:8080", None, Some("example_secret".to_string())).unwrap();
let client = Client::new(http_post_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}use onebot_api::communication::utils::Client;
use std::time::Duration;
use onebot_api::communication::sse::SseService;
#[tokio::main]
async fn main() {
let sse_service = SseService::new("https://example.com/_events", Some("example_token".to_string())).unwrap();
let client = Client::new(sse_service, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}同时,该库设计了组合器来将不同的底层连接放在同一个Client上
例如,你可以创建一个SseService和一个HttpService,同时通过组合器将它们放在同一个Client上
其行为与直接用WsService并无差别
将事件接收与API发送分为两个不同服务实现
服务分为 send_side 与 read_side
其中,send_side 负责API发送服务,read_side 负责事件接收服务
send_side 的事件通道由一个 processor task 负责
processor 将 send_side 的API响应事件并入原事件通道,其余事件丢弃
use onebot_api::communication::utils::Client;
use std::time::Duration;
use onebot_api::communication::combiner::SplitCombiner;
use onebot_api::communication::http::HttpService;
use onebot_api::communication::sse::SseService;
#[tokio::main]
async fn main() {
let sse_service = SseService::new("https://example.com/_events", Some("example_token".to_string())).unwrap();
let http_service = HttpService::new("https://example.com", Some("example_token".to_string())).unwrap();
let combiner = SplitCombiner::new(http_service, sse_service);
let client = Client::new(combiner, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}flowchart LR
A(Client) <-->|交互| B{CommunicationService}
B -->|API调用| C[[SplitCombiner]]
C -->|事件推送/API响应| B
C -->|API调用| E([HttpService])
E -->|API响应| C
C ~~~ D([SseService])
D -->|事件推送| C
传统的 WebSocket 并不支持 HTTP 3,但是 SSE 支持 HTTP 3
因此,最初设计 SplitCombiner 时,就是用来组合 HttpService 和 SseService
这样既可以享受 HTTP 3 带来的优势,同时在使用体验上也不输 WebSocket
详见 SplitCombiner
与 SplitCombiner 的区别在于
BothEventCombiner 会将 send_side 的所有事件均并入原事件通道
因此,BothEventCombiner 不存在 processor task
use onebot_api::communication::combiner::BothEventCombiner;
use onebot_api::communication::ws_reverse::WsReverseService;
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
use std::time::Duration;
#[tokio::main]
async fn main() {
let ws_service = WsService::new("wss://example.com", Some("example_token".to_string())).unwrap();
let ws_reverse_service = WsReverseService::new("0.0.0.0:8080", Some("example_token".to_string()));
let combiner = BothEventCombiner::new(ws_service, ws_reverse_service);
let client = Client::new(combiner, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}flowchart LR
A(Client) <-->|交互| B{CommunicationService}
B -->|API调用| C[[BothEventCombiner]]
C -->|事件推送/API响应| B
C -->|API调用| E([WsService])
E -->|事件推送/API响应| C
C ~~~ D([WsReverseService])
D -->|事件推送| C
对于组合器,组合器与组合器之间也是可以被组合器所连接的
因此,对于一个bot消息集群,可以通过多个 BothEventCombiner 来实现同一个client接收所有消息
use std::time::Duration;
use onebot_api::communication::combiner::BothEventCombiner;
use onebot_api::communication::http_post::HttpPostService;
use onebot_api::communication::sse::SseService;
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
use onebot_api::communication::ws_reverse::WsReverseService;
#[tokio::main]
async fn main() {
let bot_1 = WsService::new("ws://127.0.0.1:5000", None).unwrap();
let bot_2 = WsReverseService::new("127.0.0.1:6000", None);
let bot_3 = SseService::new("http://127.0.0.1:7000", None).unwrap();
let bot_4 = HttpPostService::new("127.0.0.1:8000", None, None).unwrap();
let combiner_1 = BothEventCombiner::new(bot_1, bot_2);
let combiner_2 = BothEventCombiner::new(bot_3, bot_4);
let combiner = BothEventCombiner::new(combiner_1, combiner_2);
let client = Client::new(combiner, Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
}flowchart TD
A(Client) <-->|交互| B{CommunicationService}
C[[combiner]]
D[[combiner_1]]
E[[combiner_2]]
F([bot_1])
G([bot_2])
H([bot_3])
I([bot_4])
B -->|API调用| C
C -->|事件推送/API响应| B
C -->|API调用| D
D -->|事件推送/API响应| C
C ~~~ E
E -->|事件推送| C
D -->|API调用| F
F -->|事件推送/API响应| D
D ~~~ G
G -->|事件推送| D
E -->|API调用| H
H -->|事件推送/API响应| E
H ~~~ I
I -->|事件推送| E
- 使用
SplitCombiner:当你明确分离 发送 和 接收 时(例如刚才提到的SseService和HttpService) - 使用
BothEventCombiner:当你需要聚合多个独立bot实例的事件流
Onebot V11协议中,在发送消息时需要构造Segment Array
库提供了所有Send Segment的类型,但手动构造它们还是太麻烦了
于是就有了 SegmentBuilder
use std::time::Duration;
use onebot_api::api::APISender;
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
use onebot_api::message::SegmentBuilder;
#[tokio::main]
async fn main() {
let client = Client::new(WsService::new("ws://localhost:8080", None).unwrap(), Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
let segment = SegmentBuilder::new()
.text("this is an apple")
.image("https://example.com/apple.png")
.text("\n")
.text("this is a banana")
.image("https://example.com/banana.png")
.build();
client.send_private_msg(123456, segment, None).await.unwrap();
}当然,image 中的选项很多,如果你希望的话,库也提供了部分 segment 的 builder
use onebot_api::message::SegmentBuilder;
#[tokio::main]
async fn main() {
let segment = SegmentBuilder::new()
.text("this")
.image_builder("https://example.com/apple.png")
.cache(true)
.timeout(5)
.proxy(true)
.build()
.text("is an apple")
.build();
}当然,bot发送消息大部分情况都只是文本
每次都要创建 SegmentBuilder 还是太麻烦了
于是就有了 text 宏
use std::time::Duration;
use onebot_api::api::APISender;
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
use onebot_api::text;
#[tokio::main]
async fn main() {
let client = Client::new(WsService::new("ws://localhost:8080", None).unwrap(), Some(Duration::from_secs(5)), None, None);
client.start_service().await.unwrap();
let msg = "123456".to_string();
client.send_private_msg(123456, text!("this is a message: {}", msg), None).await.unwrap();
}在 text 宏的内部使用了 format 宏
因此,你可以像使用 println 宏一样使用 text 宏
WsService自动重连 ✅SseService自动重连 ❌ (目前还没有方法能够在SSE连接突然断开后获得通知)- 更精细化的错误处理
Client实现无anyhow::Result✅- 服务 task 实现无
anyhow::Result - 取消服务 task 错误静默处理
- 更完善的文档注释
- 自定义Event反序列化
- 更多的API!
- napcat API
- go-cqhttp API