Documentation
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# Onebot API

[![Crates.io Version](https://img.shields.io/crates/v/onebot-api)](https://crates.io/crates/onebot-api)
[![GitHub Release](https://img.shields.io/github/v/release/Ecamika/onebot-api)](https://github.com/Ecamika/onebot-api)

库如其名,这是一个Onebot V11协议的实现  
目前已完成对Onebot V11协议所有API的实现

# 核心概念

## `Client`

`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`

`CommunicationService` 是 `Client` 与底层协议交互的基础  
任意实现了 `CommunicationService` 特征 的结构都可作为与 `Client` 交互的服务

---
目前已实现的协议:

- 正向 WebSocket
- 反向 WebSocket
- SSE
- Http
- Http Post

```mermaid
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
```

# Usage

## Client用法

```rust
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)
	}
}
```

## 正向WebSocket

```rust
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();
}
```

## 反向WebSocket

```rust
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();
}
```

## Http

```rust
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();
}
```

## Http Post

```rust
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();
}
```

## SSE

```rust
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并无差别

### `SplitCombiner`

将事件接收与API发送分为两个不同服务实现  
服务分为 `send_side` 与 `read_side`  
其中,`send_side` 负责API发送服务,`read_side` 负责事件接收服务  
`send_side` 的事件通道由一个 processor task 负责  
processor 将 `send_side` 的API响应事件并入原事件通道,其余事件丢弃

```rust
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();
}
```

```mermaid
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
```

#### TIPS

传统的 WebSocket 并不支持 HTTP 3,但是 SSE 支持 HTTP 3  
因此,最初设计 `SplitCombiner` 时,就是用来组合 `HttpService` 和 `SseService`  
这样既可以享受 HTTP 3 带来的优势,同时在使用体验上也不输 WebSocket

### `BothEventCombiner`

详见 `SplitCombiner`  
与 `SplitCombiner` 的区别在于  
`BothEventCombiner` 会将 `send_side` 的所有事件均并入原事件通道  
因此,`BothEventCombiner` 不存在 processor task

```rust
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();
}
```

```mermaid
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
```

### TIPS

对于组合器,组合器与组合器之间也是可以被组合器所连接的  
因此,对于一个bot消息集群,可以通过多个 `BothEventCombiner` 来实现同一个client接收所有消息

```rust
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();
}
```

```mermaid
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实例的事件流

## `SegmentBuilder`

Onebot V11协议中,在发送消息时需要构造Segment Array  
库提供了所有Send Segment的类型,但手动构造它们还是太麻烦了  
于是就有了 `SegmentBuilder`

```rust
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`

```rust
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` 宏

```rust
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` 宏

## quick_operation
有时候,我们收到了一个事件,我们想直接对这个事件进行操作  
此时,若调用 `client` 上的对应api,效率未免太低了,还要一个个传参  
于是,我们提供了 quick_operation  
quick_operation 是一系列快速操作trait(虽然目前还没实现多少,但未来肯定会实现的!)  
用户可以直接在事件上进行对应的操作  

## `Selector`

实际上,因为事件分发使用了Arc的原因,同时为了追求事件零拷贝分发  
这就使得事件的处理变得十分困难(至少实现起来非常繁琐)  
于是,我们提供了 `Selector` 工具  
`Selector` 提供了类似迭代器的链式调用能力
```rust
use onebot_api::communication::utils::Client;
use onebot_api::communication::ws::WsService;
use onebot_api::quick_operation::QuickSendMsg;
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);
	client.start_service().await.unwrap();
	let mut r = client.subscribe_normal_event();
	while let Ok(event) = r.recv().await {
		event
			.selector()
			.message()
			.and_filter_self_id(|self_id| self_id == 123456)
			.message_event_selector()
			.group()
			.and_filter_user_id(|user_id| user_id == 114514)
			.and_filter_raw_message(|msg| msg.starts_with("/command"))
			.and_normal()
			.select_async(async |event| {
				event
					.send_msg(&client, text!("this is a command"), None)
					.await
			})
			.await
			.expect("not matched")
			.expect("can not send message");
	}
}
```
在这里,我们接收到了一个事件,此时,我们要求该事件:

1. 属于 `message` 类型
2. 要求机器人id为 123456
3. 要求该 `message` 为群组消息
4. 要求发送用户id为 114514
5. 要求原始消息内容前缀为 `/command`
6. 要求为正常消息

在以上条件全部满足后,将会执行 `select_async` 中传入的闭包  
其中,若任何一个条件不满足,则会直接返回 `None` ,不执行闭包  
对于以上所有 `filter` 函数,我们都提供了以下几个版本:

- 普通版本
- 链式调用版本(前缀 `and`- 异步版本(后缀 `async`- 链式调用异步版本(前缀 `and` + 后缀 `async`
当然,最后的 `select_async` 也有对应的异步版本

# Todo List

- `WsService` 自动重连 ✅
- `SseService` 自动重连 ❌ *(目前还没有方法能够在SSE连接突然断开后获得通知)*
- 更精细化的错误处理
    - `Client` 实现无 `anyhow::Result`    - 服务 task 实现无 `anyhow::Result`
    - 取消服务 task 错误静默处理
- 更完善的文档注释
- 自定义Event反序列化
- 更多的API!
    - napcat API
    - go-cqhttp API