> ## 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.

# Reactions, polls and typing

> React to reactions added and removed, to votes in polls, and to people typing

When someone adds a reaction to a message, the `reactionAdd` events run. Here, a reaction gives a role:

```ts src/lib/reaction-roles.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
// The message people react to (right-click it → Copy Message ID), and the
// role each emoji gives.
export const ROLES_MESSAGE = '123456789012345678';
export const ROLES: Record<string, string> = { '🎮': 'Gamers', '🎨': 'Artists' };
```

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

export default event({
  name: 'reactionAdd',
  async run({ reaction: { messageId, emoji, member }, guild }) {
    if (messageId !== ROLES_MESSAGE || !emoji.name) return;
    const name = ROLES[emoji.name];
    const role = [...guild.roles.values()].find(role => role.name === name);
    if (role) await member.addRole(role, 'Reaction role');
  },
});
```

That is all. Like the [events about messages](https://www.chapterjs.org/events/messages.md), a reaction file only runs **for people, in servers** by default: reactions of bots, yours included, are left out, so `guild`, and the `channel` and `member` of the reaction, are always there.

Your function receives the `reaction` and the server, `guild`. `reaction.emoji` is a [`ReactionEmoji`](https://www.chapterjs.org/reference/data#reactionemoji): `name` is the character of a standard emoji (`'👍'`) or the name of a custom one, which also has an `id`.

> [!NOTE]
>
> The events of this page, with everything they give, are in the [reference](https://www.chapterjs.org/reference/events.md#reactionadd).

## The message itself

A reaction comes with the id of its message, not with the message: your bot [remembers no message](https://www.chapterjs.org/memory.md), so `message` is `null`. To read or change the message, ask for it with `fetchMessage()`:

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

export default event({
  name: 'reactionAdd',
  async run({ reaction: { emoji, channel, messageId } }) {
    if (emoji.name !== '⭐') return;
    const message = await channel.fetchMessage(messageId);
    console.log(`Starred: ${message.content}`);
  },
});
```

Only ask when you need it, after checking the emoji: a request per reaction adds up fast.

## Removed reactions

`reactionRemove` runs when someone takes their reaction back. Discord does not send the member with it, so `reaction.member` is only there when the bot remembers that person: ask for it with `fetchMember()`.

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

export default event({
  name: 'reactionRemove',
  async run({ reaction: { messageId, emoji, user }, guild }) {
    if (messageId !== ROLES_MESSAGE || !emoji.name) return;
    const name = ROLES[emoji.name];
    const role = [...guild.roles.values()].find(role => role.name === name);
    if (!role) return;
    const member = await guild.fetchMember(user);
    await member.removeRole(role, 'Reaction role');
  },
});
```

`reaction.user` is always there: when Discord only gives the id of someone the bot does not know, it asks for them once before your file runs.

`reactionClear` runs when every reaction of a message is removed at once, or every reaction of one emoji: it gives `reactions`, whose `emoji` is `null` in the first case and that emoji in the second.

## Options

The two options of the [events about messages](https://www.chapterjs.org/events/messages.md#where-a-file-listens) work the same here:

* `where: 'dm'` or `'both'` to listen in private messages too. With `'both'`, `guild` is `null` for a private reaction, and one `if (guild)` narrows everything.
* `bots: true` (for `reactionAdd` and `reactionRemove`) to receive the reactions of bots too, yours included: a file that reacts to reactions could otherwise answer its own forever.

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

export default event({
  name: 'reactionAdd',
  where: 'both',
  run({ reaction: { user, emoji }, guild }) {
    console.log(`${user.username} reacted ${emoji.name} ${guild ? `in ${guild.name}` : 'in private'}`);
  },
});
```

## Polls

`pollVoteAdd` and `pollVoteRemove` run when someone votes in a poll, or takes their vote back. They give the `vote` and the server; `vote.answerId` tells which answer: Discord numbers them in the order of the poll, `1` for the first one.

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

export default event({
  name: 'pollVoteAdd',
  run({ vote: { user, answerId, channel } }) {
    console.log(`${user.username} voted for answer ${answerId} in #${channel.name}`);
  },
});
```

They take `where` too. A poll is sent with a message: see [Sending messages](https://www.chapterjs.org/messages.md).

## Typing

`typingStart` runs when someone starts writing in a channel. It gives `typing` and the server; `typing.startedAt` says when.

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

export default event({
  name: 'typingStart',
  run({ typing: { member, channel, startedAt } }) {
    console.log(`${member.displayName} started typing in #${channel.name} at ${startedAt.toLocaleTimeString()}`);
  },
});
```

It takes `where`. Discord sends it a lot: keep what the file does short.

## Intents

Each of these events has its own intent, for servers and for private messages, and ChapterJS only asks for the ones your files need: a reaction file without `where` asks for the reactions of servers, nothing else. None of them is privileged.

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

> [card] [Voice](https://www.chapterjs.org/events/voice.md)
>
> Who joins, leaves or moves between voice channels.

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


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