# Client and event handler

`Client` and `EventHandler` are the two pieces every bot is built around. `Client` owns the gateway connection and the HTTP layer; `EventHandler` is the trait you implement to react to events the gateway delivers.

## Constructing the client

```rust
let client = Client::new(token, intents, handler, is_sandbox)?;
```

The arguments:

- `token: Token` — your `Token::new(app_id, secret)` (or `Token::from_env()`).
- `intents: Intents` — see [Intents](/guide/intents).
- `handler: H where H: EventHandler` — your handler value (consumed by value, stored in an `Arc` internally).
- `is_sandbox: bool` — `true` selects the sandbox base URL, `false` production.

`Client::with_config(token, intents, handler, timeout_secs, is_sandbox)` is the same constructor with an extra HTTP timeout in seconds; the default is `botrs::DEFAULT_TIMEOUT` (30 s).

After construction, call `client.start().await`. The future resolves only after the gateway connection ends and the event channel drains. Internally `start` validates the token, fetches `/users/@me` and the gateway URL, spawns a session manager that runs each shard, and pumps events back into the event handler.

Useful read-only accessors on `Client`: `client.api()`, `client.http()`, `client.intents()`, `client.is_sandbox()`.

## The handler trait

`EventHandler` has one `async fn` per dispatched gateway event. Every method has a default empty implementation, so you only override the events you care about. Annotate the impl with `#[async_trait::async_trait]`.

```rust
struct MyBot;

#[async_trait::async_trait]
impl EventHandler for MyBot {
    async fn ready(&self, _ctx: Context, ready: Ready) {
        tracing::info!("ready as {}", ready.user.username);
    }

    async fn message_create(&self, ctx: Context, message: Message) {
        if message.is_from_bot() { return; }
        if message.content.as_deref() == Some("!ping") {
            let _ = message.reply(&ctx.api, &ctx.token, "pong").await;
        }
    }
}
```

## Available callbacks

The complete set of callbacks (each takes `&self, Context, <payload>`):

- Lifecycle: `ready`, `resumed`, `error(BotError)`, `unknown_event(GatewayEvent)`.
- Messages: `message_create`, `message_delete`, `direct_message_create`, `direct_message_delete`, `group_message_create`, `c2c_message_create`.
- Reactions: `message_reaction_add`, `message_reaction_remove`.
- Interactions: `interaction_create`.
- Audit: `message_audit_pass`, `message_audit_reject`.
- Guilds and channels: `guild_create`, `guild_update`, `guild_delete`, `channel_create`, `channel_update`, `channel_delete`.
- Members: `guild_member_add`, `guild_member_update`, `guild_member_remove`.
- Audio: `audio_start`, `audio_finish`, `on_mic`, `off_mic`, `audio_or_live_channel_member_enter`, `audio_or_live_channel_member_exit`.
- Forum: `forum_thread_create`/`_update`/`_delete`, `forum_post_create`/`_delete`, `forum_reply_create`/`_delete`, `forum_publish_audit_result`, plus the `open_forum_*` mirrors.
- C2C and group management: `friend_add`, `friend_del`, `c2c_msg_reject`, `c2c_msg_receive`, `subscribe_message_status`, `enter_aio`, `group_add_robot`, `group_del_robot`, `group_msg_reject`, `group_msg_receive`.

A callback only fires if the matching `Intents` flag is enabled — see the [Intents](/guide/intents) page for the mapping.

## The Context parameter

Every callback receives a `Context`:

```rust
pub struct Context {
    pub api: Arc<BotApi>,
    pub token: Token,
    pub bot_info: Option<BotInfo>,
}
```

`api` is the same `BotApi` the client built; cloning the `Arc` is cheap and the recommended way to hand it to spawned tasks. `token` is the same token the client started with. `bot_info` is filled in from `/users/@me` once the client starts.

`Context` also exposes a small set of high-level convenience methods (`ctx.send_message(channel_id, text)`, `ctx.reply_message(...)`, `ctx.send_group_message(...)`, etc.) that wrap `BotApi` calls. For everything else, go through `ctx.api`.

## The error callback

If event dispatch fails, the framework calls `EventHandler::error(&self, error: BotError)` once and continues processing the next event. The default implementation logs at `error!`. Override it if you want custom behavior — but the framework does not retry on your behalf, so handler-level retry must come from your own code.

## Spawning work from a handler

A handler call should return promptly so the next event can be dispatched. For long-running work, clone the `Arc<BotApi>` and `Token` out of `Context` and `tokio::spawn`:

```rust
async fn message_create(&self, ctx: Context, message: Message) {
    let api = ctx.api.clone();
    let token = ctx.token.clone();
    tokio::spawn(async move {
        // long-running work, then api.post_message_with_params(...).await
    });
}
```
