botrs 0.2.9

A Rust QQ Bot framework based on QQ Guild Bot API
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
452
453
454
455
456
457
458
459
460
# 上下文 API 参考

`Context` 结构体在事件处理器中提供对 API 客户端和身份验证令牌的访问。它是事件处理器方法的标准参数,允许您与 QQ 频道 API 进行交互。

## 概述

```rust
pub struct Context {
    pub api: BotApi,
    pub token: Token,
}
```

`Context` 包含:
- `api`: 用于发送请求到 QQ 频道 API 的客户端
- `token`: 用于身份验证的令牌

## 字段

### `api`

提供对所有 QQ 频道 REST API 端点的访问。

```rust
pub api: BotApi
```

通过此字段,您可以:
- 发送消息到频道和私信
- 管理频道和子频道
- 处理成员权限
- 上传文件和媒体
- 管理机器人设置

#### 示例

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    // 使用 API 客户端发送消息
    let params = MessageParams::new_text("你好!");
    let result = ctx.api.post_message_with_params(
        &ctx.token,
        &message.channel_id,
        params
    ).await;
}
```

### `token`

包含机器人身份验证凭据的令牌。

```rust
pub token: Token
```

令牌用于:
- API 请求的身份验证
- 授权头的生成
- 访问令牌管理

#### 示例

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    // 令牌会自动用于身份验证
    let channels = ctx.api.get_guild_channels(&ctx.token, &guild_id).await?;
}
```

## 使用方法

### 发送消息

#### 文本消息

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    let params = MessageParams::new_text("简单文本消息");
    let _ = ctx.api.post_message_with_params(
        &ctx.token,
        &message.channel_id,
        params
    ).await;
}
```

#### 富文本消息

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    let embed = Embed::new()
        .title("标题")
        .description("描述")
        .color(0x00ff00);
    
    let params = MessageParams::new_embed(embed);
    let _ = ctx.api.post_message_with_params(
        &ctx.token,
        &message.channel_id,
        params
    ).await;
}
```

#### 回复消息

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    let params = MessageParams::new_text("这是一个回复")
        .with_reply(&message.id);
    
    let _ = ctx.api.post_message_with_params(
        &ctx.token,
        &message.channel_id,
        params
    ).await;
}
```

### 频道管理

#### 获取频道信息

```rust
async fn guild_create(&self, ctx: Context, guild: Guild) {
    match ctx.api.get_guild(&ctx.token, &guild.id).await {
        Ok(guild_info) => {
            println!("频道名称: {}", guild_info.name.unwrap_or_default());
        }
        Err(e) => {
            eprintln!("获取频道信息失败: {}", e);
        }
    }
}
```

#### 获取子频道列表

```rust
async fn ready(&self, ctx: Context, ready: Ready) {
    for guild in &ready.guilds {
        match ctx.api.get_guild_channels(&ctx.token, &guild.id).await {
            Ok(channels) => {
                println!("频道 {} 有 {} 个子频道", guild.id, channels.len());
            }
            Err(e) => {
                eprintln!("获取子频道失败: {}", e);
            }
        }
    }
}
```

### 成员管理

#### 获取成员信息

```rust
async fn guild_member_add(&self, ctx: Context, member: Member) {
    if let Some(guild_id) = &member.guild_id {
        match ctx.api.get_guild_member(&ctx.token, guild_id, &member.user.id).await {
            Ok(member_info) => {
                println!("新成员: {:?}", member_info.nick);
            }
            Err(e) => {
                eprintln!("获取成员信息失败: {}", e);
            }
        }
    }
}
```

#### 获取成员列表

```rust
async fn ready(&self, ctx: Context, ready: Ready) {
    for guild in &ready.guilds {
        match ctx.api.get_guild_members(&ctx.token, &guild.id, None, None).await {
            Ok(members) => {
                println!("频道 {} 有 {} 个成员", guild.id, members.len());
            }
            Err(e) => {
                eprintln!("获取成员列表失败: {}", e);
            }
        }
    }
}
```

### 文件上传

#### 上传图片

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    if let Some(content) = &message.content {
        if content.trim() == "!upload_image" {
            // 读取本地图片文件
            let image_data = std::fs::read("path/to/image.png").unwrap();
            
            match ctx.api.post_message_with_file(
                &ctx.token,
                &message.channel_id,
                "image.png",
                &image_data,
                "png"
            ).await {
                Ok(_) => println!("图片上传成功"),
                Err(e) => eprintln!("图片上传失败: {}", e),
            }
        }
    }
}
```

### 私信处理

```rust
async fn direct_message_create(&self, ctx: Context, dm: DirectMessage) {
    // 回复私信
    let params = MessageParams::new_text("感谢您的私信!");
    
    if let Some(guild_id) = &dm.guild_id {
        let _ = ctx.api.post_direct_message_with_params(
            &ctx.token,
            guild_id,
            &dm.channel_id,
            params
        ).await;
    }
}
```

### 群组消息处理

```rust
async fn group_message_create(&self, ctx: Context, group_msg: GroupMessage) {
    if let Some(content) = &group_msg.content {
        if content.contains("帮助") {
            let help_text = "这里是帮助信息...";
            let _ = group_msg.reply(&ctx.api, &ctx.token, help_text).await;
        }
    }
}
```

## 错误处理

### 基础错误处理

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    let params = MessageParams::new_text("测试消息");
    
    match ctx.api.post_message_with_params(&ctx.token, &message.channel_id, params).await {
        Ok(sent_message) => {
            println!("消息发送成功,ID: {}", sent_message.id);
        }
        Err(BotError::RateLimited(retry_after)) => {
            println!("触发速率限制,{}秒后重试", retry_after);
            tokio::time::sleep(Duration::from_secs(retry_after)).await;
            // 重试逻辑
        }
        Err(BotError::Authentication(_)) => {
            eprintln!("身份验证失败,检查令牌");
        }
        Err(BotError::Network(_)) => {
            eprintln!("网络错误,稍后重试");
        }
        Err(e) => {
            eprintln!("其他错误: {}", e);
        }
    }
}
```

### 高级错误处理

```rust
use botrs::{BotError, Result};
use tracing::{warn, error};

impl MyEventHandler {
    async fn safe_send_message(&self, ctx: &Context, channel_id: &str, content: &str) -> Result<Message> {
        let params = MessageParams::new_text(content);
        
        for attempt in 1..=3 {
            match ctx.api.post_message_with_params(&ctx.token, channel_id, params.clone()).await {
                Ok(message) => return Ok(message),
                Err(BotError::RateLimited(retry_after)) => {
                    warn!("速率限制,尝试 {}/3,{}秒后重试", attempt, retry_after);
                    tokio::time::sleep(Duration::from_secs(retry_after)).await;
                }
                Err(BotError::Network(_)) if attempt < 3 => {
                    warn!("网络错误,尝试 {}/3,1秒后重试", attempt);
                    tokio::time::sleep(Duration::from_secs(1)).await;
                }
                Err(e) => {
                    error!("发送消息失败: {}", e);
                    return Err(e);
                }
            }
        }
        
        Err(BotError::Custom("重试次数已用尽".to_string()))
    }
}
```

## 实用工具

### 消息构建器

```rust
impl MyEventHandler {
    fn build_help_message(&self) -> MessageParams {
        let embed = Embed::new()
            .title("机器人帮助")
            .description("可用命令列表")
            .field("!ping", "测试机器人响应", false)
            .field("!help", "显示此帮助信息", false)
            .field("!stats", "显示统计信息", false)
            .color(0x3498db)
            .timestamp(chrono::Utc::now());
        
        MessageParams::new_embed(embed)
    }
    
    async fn send_help(&self, ctx: &Context, channel_id: &str) -> Result<()> {
        let params = self.build_help_message();
        ctx.api.post_message_with_params(&ctx.token, channel_id, params).await?;
        Ok(())
    }
}
```

### 权限检查

```rust
impl MyEventHandler {
    async fn check_admin_permission(&self, ctx: &Context, guild_id: &str, user_id: &str) -> bool {
        match ctx.api.get_guild_member(&ctx.token, guild_id, user_id).await {
            Ok(member) => {
                // 检查成员是否有管理员权限
                member.roles.iter().any(|role| role.permissions & 0x8 != 0) // ADMINISTRATOR
            }
            Err(_) => false,
        }
    }
}
```

### 批量操作

```rust
impl MyEventHandler {
    async fn send_to_multiple_channels(&self, ctx: &Context, guild_id: &str, content: &str) {
        match ctx.api.get_guild_channels(&ctx.token, guild_id).await {
            Ok(channels) => {
                let text_channels: Vec<_> = channels.iter()
                    .filter(|ch| ch.channel_type == ChannelType::Text)
                    .collect();
                
                for channel in text_channels {
                    let params = MessageParams::new_text(content);
                    if let Err(e) = ctx.api.post_message_with_params(
                        &ctx.token, 
                        &channel.id, 
                        params
                    ).await {
                        warn!("向频道 {} 发送消息失败: {}", channel.id, e);
                    }
                    
                    // 避免速率限制
                    tokio::time::sleep(Duration::from_millis(100)).await;
                }
            }
            Err(e) => {
                error!("获取频道列表失败: {}", e);
            }
        }
    }
}
```

## 最佳实践

### 并发安全

```rust
use std::sync::Arc;
use tokio::sync::Semaphore;

struct RateLimitedHandler {
    semaphore: Arc<Semaphore>,
}

impl RateLimitedHandler {
    pub fn new() -> Self {
        Self {
            semaphore: Arc::new(Semaphore::new(5)), // 最多5个并发请求
        }
    }
    
    async fn safe_api_call<F, T>(&self, f: F) -> Result<T>
    where
        F: std::future::Future<Output = Result<T>>,
    {
        let _permit = self.semaphore.acquire().await.unwrap();
        f.await
    }
}
```

### 资源管理

```rust
impl MyEventHandler {
    async fn cleanup_resources(&self, ctx: &Context) {
        // 清理临时文件
        // 关闭数据库连接
        // 保存状态等
    }
}
```

### 配置管理

```rust
#[derive(Clone)]
struct BotConfig {
    admin_users: Vec<String>,
    allowed_channels: Vec<String>,
    command_prefix: String,
}

struct ConfigurableBot {
    config: BotConfig,
}

impl ConfigurableBot {
    fn is_admin(&self, user_id: &str) -> bool {
        self.config.admin_users.contains(&user_id.to_string())
    }
    
    fn is_allowed_channel(&self, channel_id: &str) -> bool {
        self.config.allowed_channels.is_empty() || 
        self.config.allowed_channels.contains(&channel_id.to_string())
    }
}
```

## 另请参阅

- [`BotApi`]./bot-api.md - 详细的 API 客户端参考
- [`Token`]./token.md - 身份验证令牌管理
- [`EventHandler`]./event-handler.md - 事件处理器实现
- [消息处理指南]/zh/guide/messages.md - 消息发送和处理
- [错误处理指南]/zh/guide/error-handling.md - 错误处理策略