botrs 0.2.8

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
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
# Error Types API Reference

BotRS provides a comprehensive error system through the `BotError` enum, which covers all possible failure scenarios when working with the QQ Guild API. This guide covers the different error types, their meanings, and how to handle them effectively.

## Overview

```rust
use botrs::{BotError, Result};

// All BotRS functions return Result<T, BotError>
pub type Result<T> = std::result::Result<T, BotError>;
```

The `BotError` enum is the primary error type used throughout BotRS. It implements standard traits like `std::error::Error`, `Debug`, and `Display` for easy integration with Rust's error handling ecosystem.

## Error Types

### Network Errors

#### `Http`

HTTP client errors from the underlying reqwest library.

```rust
BotError::Http(reqwest::Error)
```

**Common causes:**
- Network connectivity issues
- DNS resolution failures
- Connection timeouts
- SSL/TLS errors

**Example:**
```rust
match api.get_guild(&token, "guild_id").await {
    Err(BotError::Http(e)) if e.is_timeout() => {
        println!("Request timed out, retrying...");
    }
    Err(BotError::Http(e)) if e.is_connect() => {
        println!("Connection failed: {}", e);
    }
    _ => {}
}
```

#### `WebSocket`

WebSocket connection errors from the gateway.

```rust
BotError::WebSocket(Box<tokio_tungstenite::tungstenite::Error>)
```

**Common causes:**
- Gateway connection failures
- WebSocket protocol errors
- Network interruptions
- Invalid WebSocket frames

**Example:**
```rust
match error {
    BotError::WebSocket(ws_error) => {
        println!("WebSocket error: {}", ws_error);
        // Gateway will automatically attempt reconnection
    }
    _ => {}
}
```

#### `Timeout`

Network timeout errors.

```rust
BotError::Timeout
```

**Handling:**
```rust
match api.get_message(&token, channel_id, message_id).await {
    Err(BotError::Timeout) => {
        println!("Request timed out, implementing retry logic");
        // Implement exponential backoff retry
    }
    _ => {}
}
```

### API Response Errors

#### `Api`

Generic API errors with status code and message.

```rust
BotError::Api { code: u32, message: String }
```

**Example:**
```rust
match error {
    BotError::Api { code, message } => {
        match code {
            400 => println!("Bad request: {}", message),
            500 => println!("Server error: {}", message),
            _ => println!("API error {}: {}", code, message),
        }
    }
    _ => {}
}
```

#### `AuthenticationFailed`

Authentication errors (401 status).

```rust
BotError::AuthenticationFailed(String)
```

**Common causes:**
- Invalid bot token
- Expired credentials
- Incorrect app ID or secret

**Handling:**
```rust
match error {
    BotError::AuthenticationFailed(msg) => {
        eprintln!("Authentication failed: {}", msg);
        // Check and refresh bot credentials
    }
    _ => {}
}
```

#### `Forbidden`

Permission denied errors (403 status).

```rust
BotError::Forbidden(String)
```

**Common causes:**
- Missing bot permissions
- Insufficient role hierarchy
- Channel access restrictions

**Example:**
```rust
match api.delete_message(&token, channel_id, message_id).await {
    Err(BotError::Forbidden(msg)) => {
        println!("Missing permissions to delete message: {}", msg);
    }
    _ => {}
}
```

#### `NotFound`

Resource not found errors (404 status).

```rust
BotError::NotFound(String)
```

**Common causes:**
- Invalid channel/guild/user IDs
- Deleted resources
- Inaccessible content

#### `MethodNotAllowed`

HTTP method not allowed (405 status).

```rust
BotError::MethodNotAllowed(String)
```

#### `SequenceNumber`

Rate limiting errors (429 status).

```rust
BotError::SequenceNumber(String)
```

#### `Server`

Server errors (500, 504 status).

```rust
BotError::Server(String)
```

### Rate Limiting

#### `RateLimit`

Structured rate limiting information.

```rust
BotError::RateLimit { retry_after: u64 }
```

**Handling:**
```rust
match error {
    BotError::RateLimit { retry_after } => {
        println!("Rate limited, retry after {} seconds", retry_after);
        tokio::time::sleep(Duration::from_secs(retry_after)).await;
        // Retry the operation
    }
    _ => {}
}
```

### Data and Parsing Errors

#### `Json`

JSON serialization/deserialization errors.

```rust
BotError::Json(serde_json::Error)
```

**Common causes:**
- Malformed API responses
- Schema mismatches
- Invalid JSON data

#### `Serde`

Alternative name for JSON errors (legacy).

```rust
BotError::Serde(serde_json::Error)
```

#### `InvalidData`

Invalid data format errors.

```rust
BotError::InvalidData(String)
```

### Connection and Gateway Errors

#### `Connection`

General connection errors.

```rust
BotError::Connection(String)
```

#### `Gateway`

Gateway-specific errors.

```rust
BotError::Gateway(String)
```

**Common causes:**
- Invalid session
- Gateway reconnection failures
- Protocol violations

#### `Session`

Session management errors.

```rust
BotError::Session(String)
```

### Configuration Errors

#### `Config`

Configuration-related errors.

```rust
BotError::Config(String)
```

**Common causes:**
- Invalid bot configuration
- Missing required settings
- Conflicting options

#### `Auth`

Authentication configuration errors.

```rust
BotError::Auth(String)
```

### System Errors

#### `Io`

I/O operation errors.

```rust
BotError::Io(std::io::Error)
```

**Common causes:**
- File system operations
- Network I/O failures
- Permission issues

#### `Url`

URL parsing errors.

```rust
BotError::Url(url::ParseError)
```

#### `Internal`

Internal framework errors.

```rust
BotError::Internal(String)
```

#### `NotImplemented`

Feature not yet implemented.

```rust
BotError::NotImplemented(String)
```

## Error Methods

### `is_retryable`

Determines if an error condition is retryable.

```rust
pub fn is_retryable(&self) -> bool
```

**Example:**
```rust
if error.is_retryable() {
    println!("Error is retryable, implementing retry logic");
    // Implement retry with backoff
} else {
    println!("Error is not retryable, giving up");
}
```

**Retryable errors:**
- HTTP timeouts and connection errors
- WebSocket errors
- Connection errors
- Timeout errors
- Gateway errors
- Rate limit errors

### `retry_after`

Gets the recommended retry delay in seconds.

```rust
pub fn retry_after(&self) -> Option<u64>
```

**Example:**
```rust
if let Some(delay) = error.retry_after() {
    println!("Retrying after {} seconds", delay);
    tokio::time::sleep(Duration::from_secs(delay)).await;
}
```

## Constructor Methods

### Error Creation

The `BotError` enum provides several constructor methods for creating specific error types:

```rust
// API error
let error = BotError::api(404, "Channel not found");

// Authentication error
let error = BotError::auth("Invalid token");

// Connection error
let error = BotError::connection("Failed to connect to gateway");

// Configuration error
let error = BotError::config("Missing app ID");

// Invalid data error
let error = BotError::invalid_data("Invalid message format");

// Gateway error
let error = BotError::gateway("Session expired");

// Session error
let error = BotError::session("Invalid session ID");

// Internal error
let error = BotError::internal("Unexpected state");

// Rate limit error
let error = BotError::rate_limit(60);

// Not implemented error
let error = BotError::not_implemented("Feature coming soon");
```

## Error Handling Patterns

### Basic Error Handling

```rust
use botrs::{BotError, Result};

async fn send_message_safely(
    ctx: &Context,
    channel_id: &str,
    content: &str,
) -> Result<()> {
    match ctx.send_message(channel_id, content).await {
        Ok(_) => {
            println!("Message sent successfully");
            Ok(())
        }
        Err(e) => {
            eprintln!("Failed to send message: {}", e);
            Err(e)
        }
    }
}
```

### Comprehensive Error Handling

```rust
async fn handle_api_operation(
    api: &BotApi,
    token: &Token,
    guild_id: &str,
) -> Result<Guild> {
    match api.get_guild(token, guild_id).await {
        Ok(guild) => Ok(guild),
        Err(BotError::NotFound(msg)) => {
            println!("Guild not found: {}", msg);
            Err(BotError::NotFound(msg))
        }
        Err(BotError::Forbidden(msg)) => {
            println!("Access denied: {}", msg);
            Err(BotError::Forbidden(msg))
        }
        Err(BotError::RateLimit { retry_after }) => {
            println!("Rate limited, waiting {} seconds", retry_after);
            tokio::time::sleep(Duration::from_secs(retry_after)).await;
            // Retry the operation
            api.get_guild(token, guild_id).await
        }
        Err(BotError::Server(msg)) => {
            println!("Server error: {}", msg);
            // Implement retry logic for server errors
            Err(BotError::Server(msg))
        }
        Err(e) => {
            eprintln!("Unexpected error: {}", e);
            Err(e)
        }
    }
}
```

### Retry Logic with Error Handling

```rust
use tokio::time::{sleep, Duration};

async fn retry_with_backoff<F, T, Fut>(
    operation: F,
    max_attempts: usize,
) -> Result<T>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = Result<T>>,
{
    let mut last_error = None;
    
    for attempt in 1..=max_attempts {
        match operation().await {
            Ok(result) => return Ok(result),
            Err(e) => {
                if !e.is_retryable() {
                    return Err(e);
                }
                
                let delay = e.retry_after().unwrap_or_else(|| {
                    // Exponential backoff: 1s, 2s, 4s, 8s, ...
                    std::cmp::min(1 << (attempt - 1), 60)
                });
                
                println!("Attempt {} failed: {}. Retrying in {}s", 
                        attempt, e, delay);
                
                if attempt < max_attempts {
                    sleep(Duration::from_secs(delay)).await;
                }
                
                last_error = Some(e);
            }
        }
    }
    
    Err(last_error.unwrap())
}
```

### Error Categorization

```rust
fn categorize_error(error: &BotError) -> &'static str {
    match error {
        BotError::Http(_) | BotError::WebSocket(_) | BotError::Timeout => "Network",
        BotError::AuthenticationFailed(_) | BotError::Auth(_) => "Authentication",
        BotError::Forbidden(_) | BotError::NotFound(_) => "Permission",
        BotError::RateLimit { .. } | BotError::SequenceNumber(_) => "Rate Limit",
        BotError::Json(_) | BotError::InvalidData(_) => "Data",
        BotError::Gateway(_) | BotError::Session(_) => "Gateway",
        BotError::Config(_) => "Configuration",
        BotError::Server(_) => "Server",
        _ => "Other",
    }
}

async fn handle_categorized_error(error: BotError) {
    let category = categorize_error(&error);
    
    match category {
        "Network" => {
            println!("Network issue detected, checking connectivity");
            // Implement network diagnostic logic
        }
        "Authentication" => {
            println!("Authentication issue, refreshing credentials");
            // Implement credential refresh logic
        }
        "Rate Limit" => {
            println!("Rate limit hit, backing off");
            // Implement rate limit handling
        }
        _ => {
            println!("Error category: {}, Error: {}", category, error);
        }
    }
}
```

## Error Extension Trait

### `IntoBotError`

Extension trait for converting generic errors to `BotError`.

```rust
pub trait IntoBotError<T> {
    fn with_context(self, context: &str) -> Result<T>;
}
```

**Usage:**
```rust
use botrs::IntoBotError;

let result = std::fs::read_to_string("config.json")
    .with_context("Failed to read configuration file")?;
```

## Production Error Handling

### Logging and Monitoring

```rust
use tracing::{error, warn, info};

async fn production_error_handler(error: BotError) {
    match &error {
        BotError::AuthenticationFailed(_) => {
            error!("Authentication failed: {}", error);
            // Alert operations team
        }
        BotError::RateLimit { retry_after } => {
            warn!("Rate limited for {} seconds", retry_after);
            // Update metrics
        }
        BotError::Server(_) => {
            error!("Server error: {}", error);
            // Check service health
        }
        _ => {
            info!("Handled error: {}", error);
        }
    }
}
```

### Error Metrics

```rust
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;

pub struct ErrorMetrics {
    counts: Arc<Mutex<HashMap<String, u64>>>,
}

impl ErrorMetrics {
    pub async fn record_error(&self, error: &BotError) {
        let error_type = match error {
            BotError::Http(_) => "http",
            BotError::WebSocket(_) => "websocket",
            BotError::RateLimit { .. } => "rate_limit",
            BotError::AuthenticationFailed(_) => "auth_failed",
            _ => "other",
        };
        
        let mut counts = self.counts.lock().await;
        *counts.entry(error_type.to_string()).or_insert(0) += 1;
    }
    
    pub async fn get_error_counts(&self) -> HashMap<String, u64> {
        self.counts.lock().await.clone()
    }
}
```

## Best Practices

### Error Handling Guidelines

1. **Always handle errors explicitly**: Don't ignore or unwrap errors in production code
2. **Use appropriate error types**: Match error handling to the specific error type
3. **Implement proper logging**: Log errors with sufficient context for debugging
4. **Respect rate limits**: Always handle rate limiting errors appropriately
5. **Provide user feedback**: Convert technical errors to user-friendly messages

### Error Prevention

1. **Validate inputs**: Check parameters before making API calls
2. **Handle edge cases**: Consider scenarios like missing permissions or invalid IDs
3. **Implement timeouts**: Set reasonable timeouts for all operations
4. **Use structured error handling**: Implement consistent error handling patterns

### Recovery Strategies

1. **Exponential backoff**: Use increasing delays for retries
2. **Circuit breakers**: Stop calling failing services temporarily
3. **Graceful degradation**: Provide fallback functionality when possible
4. **Health checks**: Monitor service health and adjust behavior accordingly

## See Also

- [`Client`]./client.md - Main bot client
- [`Context`]./context.md - API access in event handlers
- [`BotApi`]./bot-api.md - Direct API access
- [Error Handling Guide]/guide/error-handling - Comprehensive error handling strategies