> ## Documentation Index
> Fetch the complete documentation index at: https://www.chapterjs.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Voice

> React to people joining, leaving and moving between voice channels, muting or starting a stream

When someone joins a voice channel, the `voiceJoin` events run:

```ts src/events/hello.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { event } from 'chapterjs';

export default event({
  name: 'voiceJoin',
  async run({ voice: { member, channel } }) {
    // A voice channel has its own text chat: the bot writes there.
    await channel.send(`${member.displayName} joined ${channel.name}.`);
  },
});
```

That is all. Your function receives `voice` and the server, `guild`: `voice.member` is who joined, `voice.channel` the [`VoiceChannel`](https://www.chapterjs.org/reference/channels/voice) (a stage channel is one too, with `channel.isStage`), and `voice.state` their [`VoiceState`](https://www.chapterjs.org/reference/data#voicestate): muted, deafened, streaming, camera on...

> [!NOTE]
>
> The four voice events, with what they give, are in the [reference](https://www.chapterjs.org/reference/events.md#voicejoin). Your bot joining a voice channel itself, to play sound there, is in [Playing in voice](https://www.chapterjs.org/voice.md).

## Four events

Discord sends one kind of change for voice; ChapterJS tells you what it was, by comparing with what the person was doing before:

| Event | Runs when | Your function receives |
| - | - | - |
| `voiceJoin` | Someone joins a voice channel | `voice` (`member`, `channel`, `state`), `guild` |
| `voiceLeave` | Someone leaves, or is disconnected | `voice` (`member`, `channel` they left, `state` as it was), `guild` |
| `voiceMove` | Someone goes to another voice channel, or is moved | `voice` (`member`, `from`, `to`, `state`), `guild` |
| `voiceUpdate` | Someone in a channel changes: mute, deafen, stream, camera | `voice` (`member`, `channel`, `state` now, `before` as it was), `guild` |

A move is only a `voiceMove`: not a `voiceLeave` followed by a `voiceJoin`. And `voiceUpdate` never runs for a join, a leave or a move.

```ts src/events/live.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { event } from 'chapterjs';

export default event({
  name: 'voiceUpdate',
  async run({ voice: { member, channel, state, before } }) {
    if (state.selfStream && !before.selfStream) {
      await channel.send(`${member.displayName} is live!`);
    }
  },
});
```

## The channel someone left

`voiceLeave` always gives the channel, even when it was deleted with people in it: the bot keeps the channel of each person while they are in voice.

```ts src/events/empty.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { event } from 'chapterjs';

export default event({
  name: 'voiceLeave',
  run({ voice: { member, channel }, guild }) {
    console.log(`${member.displayName} left ${channel.name} of ${guild.name}`);
  },
});
```

## What the bot remembers

To tell a join from a move or a mute, the bot remembers who is in a voice channel, and forgets them when they leave. It only does when a file listens to one of these events: without one, Discord does not send voice changes at all. It is little: one small entry per person in voice right now.

## Intents

The four events need `GUILD_VOICE_STATES`, which is not privileged. They only happen in servers: there is no `where` option.

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
ℹ Intents computed from your files: GUILDS, GUILD_VOICE_STATES
```

> [card] [Moderation and the server](https://www.chapterjs.org/events/server.md)
>
> Bans, the audit log, invites, emojis, scheduled events, threads.

> [card] [Events reference](https://www.chapterjs.org/reference/events.md#voicejoin)
>
> Every field of every event.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.