botrs 0.12.2

A Rust QQ Bot framework based on QQ Guild Bot API
Documentation
# Performance

The framework is a thin layer on top of `reqwest` and `tokio-tungstenite`; the things that move the needle are gateway-side knobs (shards and intents) and how you share the `BotApi` and `Token` between tasks. Generic Rust performance advice doesn't belong here.

## Narrow your intents

Every `Intents` flag you enable adds another category of dispatch the gateway has to push and your handler has to ignore (or handle). `Intents::default()` is already trimmed to public flags; for non-trivial bots, replace it with the explicit set you actually consume:

```rust
let intents = Intents::none()
    .with_public_guild_messages()
    .with_direct_message();
```

The savings come both from gateway bandwidth and from skipping deserialization for events you don't want.

## Sharding

`Client::start()` reads the recommended shard count from `BotApi::get_gateway` (`gateway_info.shards`). Each shard is a separate WebSocket connection driven by the session manager. You don't normally configure this — the count comes from QQ's side and reflects your bot's guild population.

For very small bots a single shard is fine and uses the least resource. For larger bots, the session manager opens multiple shards in sequence, throttled by `Gateway::session_start_interval(max_concurrency)`, so you don't trip the daily session-start budget. If your handler is CPU-light but you have many guilds, increasing concurrency on QQ's side (which you negotiate via `gateway_info.session_start_limit.max_concurrency`) reduces the spread between shards coming online.

The reconnect throttle is implemented as `round(2 / max_concurrency)` with a 1-second floor; do not bypass it. See [gateway](/guide/gateway) for the details.

## Share `BotApi` with `Arc`

`Context::api` is already `Arc<BotApi>`. When you spawn work from inside a handler, clone the `Arc` (and the `Token`) instead of moving the `Context`:

```rust
async fn message_create(&self, ctx: Context, msg: Message) {
    let api = ctx.api.clone();
    let token = ctx.token.clone();
    tokio::spawn(async move {
        let _ = api.post_message_with_params(&token, "channel", params).await;
    });
}
```

Cloning `Arc<BotApi>` is an atomic increment; cloning `Token` copies two short strings. Both are cheap relative to a single HTTP request. Don't build a new `BotApi` per call — the `reqwest::Client` it wraps maintains its own connection pool, and rebuilding it throws that pool away.

## Keep handlers non-blocking

Handler calls are awaited serially per shard. Long-running synchronous work in a handler delays the next event for that shard. Either keep the work small or `tokio::spawn` it as shown above. The framework gives you no other backpressure mechanism — the event channel is unbounded and will grow if your handler can't keep up.

## HTTP timeout

The default `HttpClient` timeout is 30 seconds (`botrs::DEFAULT_TIMEOUT`). Lower it with `Client::with_config(... , timeout_secs, ...)` if your latency budget is tighter than that, but remember that uploads and large-payload responses can legitimately take time. A too-aggressive timeout produces `BotError::Timeout` flapping rather than performance.