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

# Sending messages

> Send text, embeds, files and polls, reply, edit, delete, react, pin and read the history of a channel

Everything that can receive a message has a `send()`: a channel, a user, a member, a webhook. A message is a text, or an object with everything it contains.

```ts src/commands/announce.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';

export default command({
  name: 'announce',
  description: 'Posts an announcement in this channel',
  options: {
    text: { type: 'string', description: 'What to announce', required: true },
  },
  async run({ interaction, channel, options }) {
    await channel.send({
      embeds: [{ title: 'Announcement', description: options.text, color: 0x5865f2 }],
    });
    await interaction.reply({ content: 'Posted!', ephemeral: true });
  },
});
```

That is all: `channel` is the channel the command was used in, and `send()` posts there. The message is checked before it leaves (a text over 2000 characters, too many embeds...): a mistake is an error in your code with a plain message, not a refusal of Discord.

> [!NOTE]
>
> Every field a message takes is in [Message options](https://www.chapterjs.org/reference/message-options.md), and everything a message you receive has is in [Message](https://www.chapterjs.org/reference/message.md).

## Where a message goes

| You have | Write |
| - | - |
| A channel (`channel`, `message.channel`, one of `guild.channels`) | `channel.send(...)` |
| A message to answer | `message.reply(...)` |
| A user or a member to write to in private | `user.send(...)`, `member.send(...)` |
| A command to answer | `interaction.reply(...)`: see [Answering](https://www.chapterjs.org/commands/answering.md) |
| A webhook | `webhook.send(...)`: see [Webhook](https://www.chapterjs.org/reference/webhook.md) |

They all take the same thing. A text:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function hello(channel: TextChannel) {
  await channel.send('Hello!');
}
```

Or an object, when the message has more than a text, or is sent differently:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function quietly(channel: TextChannel) {
  await channel.send({
    content: 'A new version is out: https://example.com/changelog',
    silent: true, // no notification sound
    suppressEmbeds: true, // no preview of the link
  });
}
```

A message needs at least a content, an embed, a file, a component, a sticker or a poll. Everything else is optional, and every `send()` returns the `Message` that was sent, so you can react to it, pin it or edit it later.

## Replying

`message.reply()` answers a message in its channel: Discord shows the message it answers above yours.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function thank(message: Message) {
  await message.reply('Thanks!');
}
```

A reply notifies the author of the message by default. To answer without pinging, pass `allowedMentions`:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function quietReply(message: Message) {
  await message.reply({
    content: 'Noted.',
    allowedMentions: { repliedUser: false },
  });
}
```

`send()` can reply too: pass the message (or its id) as `replyTo`.

## Embeds

An embed is a box with a title, a description, fields, an image, a footer... Pass up to 10 in `embeds`:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function report(channel: TextChannel, newMembers: number, messages: number) {
  await channel.send({
    embeds: [
      {
        title: 'Weekly report',
        description: 'Everything went well.',
        color: 0x5865f2,
        fields: [
          { name: 'New members', value: String(newMembers), inline: true },
          { name: 'Messages', value: String(messages), inline: true },
        ],
        footer: { text: 'Generated automatically' },
        timestamp: new Date().toISOString(),
      },
    ],
  });
}
```

Discord limits what an embed holds (256 characters for a title, 25 fields, 6000 characters for all the embeds of a message...). Going over one throws before anything is sent, with the field and the limit in the message:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
The title of embed 1 is 300 characters long: Discord accepts 256 at most.
```

The limits are listed in [Embed limits](https://www.chapterjs.org/reference/message-options.md#embed-limits).

## Files

The pictures and documents your bot sends live in the `public/` folder of your project, next to `src/`. `asset()` picks one: your editor lists every file of the folder as you type, and a path that does not exist is underlined.

```ts src/commands/chart.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { asset, command } from 'chapterjs';

export default command({
  name: 'chart',
  description: 'Shows the chart of the week',
  async run({ interaction }) {
    await interaction.reply({
      files: [asset('charts/week.png', { description: 'Messages per day this week' })],
      // An embed can show an attached file, by its name.
      embeds: [{ title: 'This week', image: { url: 'attachment://week.png' } }],
    });
  },
});
```

The file is read when the message is sent, never before. A file sent under another name takes `name`; `spoiler: true` hides it until it is clicked. Two files of one message can't have the same name: Discord tells them apart by it.

In `chapterjs dev`, a file added to `public/` is offered by the editor right away:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
↻ public/ changed: types updated
```

A file made by your code (a chart drawn on the fly, a text) is given with its content: a text, a `Buffer`, a `Uint8Array`, an `ArrayBuffer` or a `Blob`. Up to 10 files per message, from `public/` or made, together.

A text too long for a message can go in a file instead:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function sendLog(channel: TextChannel, log: string) {
  await channel.send({
    content: 'Here is the full log.',
    files: [{ name: 'log.txt', data: log }],
  });
}
```

## Mentions

A mention is a text: write it in `content` with the helpers of ChapterJS, or with what a structure gives when put in a text (`${member}`, `${channel}`, `${role}`).

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { roleMention, timestamp, type GuildMember, type TextChannel } from 'chapterjs';

async function welcome(channel: TextChannel, member: GuildMember, helpersRoleId: string) {
  await channel.send(
    `Welcome ${member}! You joined ${timestamp(member.joinedAt ?? new Date(), 'R')}. ` +
      `${roleMention(helpersRoleId)} will help you in ${channel}.`
  );
}
```

By default a message notifies everyone it mentions, roles and @everyone included. `allowedMentions` says who really gets a notification, whatever the text contains:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function quote(message: Message) {
  // Repeats what someone wrote without pinging anyone.
  await message.reply({
    content: message.content,
    allowedMentions: { parse: [], repliedUser: false },
  });
}
```

* `parse` is the kinds of mentions of the text that notify: `['users']`, `['roles']`, `['everyone']`, or `[]` for nobody.
* `users` and `roles` list the ids that may be notified, whatever `parse` says.

The helpers (`userMention`, `roleMention`, `channelMention`, `timestamp`...) are listed in [Helpers](https://www.chapterjs.org/reference/helpers.md).

## Polls

A poll is a question with up to 10 answers. People vote in Discord; the poll closes after `duration` hours (24 by default).

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function ask(channel: TextChannel) {
  await channel.send({
    poll: {
      question: { text: 'Pizza or pasta?' },
      answers: [
        { pollMedia: { text: 'Pizza', emoji: { name: '🍕' } } },
        { pollMedia: { text: 'Pasta', emoji: { name: '🍝' } } },
      ],
      duration: 24, // hours
      allowMultiselect: false,
    },
  });
}
```

## Editing

`message.edit()` changes a message the bot wrote: its text, its embeds, its components, its files, and whether the previews of its links show. Only what you pass changes.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function countdown(channel: TextChannel) {
  const message = await channel.send('Starting in 3...');
  for (const n of [2, 1]) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    await message.edit(`Starting in ${n}...`);
  }
  await message.edit({ content: 'Go!', embeds: [] }); // also removes the embeds
}
```

A message written by someone else can't be edited. What `edit()` accepts is listed in [MessageEditOptions](https://www.chapterjs.org/reference/message-options.md#messageeditoptions).

## Deleting

`message.delete()` deletes one message; the reason goes to the audit log of the server.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function removeInvite(message: Message) {
  if (!message.content.includes('discord.gg/')) return;
  await message.delete('Invite link');
}
```

To delete many at once, `channel.bulkDelete()` takes messages or ids and returns how many were deleted. Discord refuses to delete messages older than two weeks in bulk: they are left out.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { TextChannel } from 'chapterjs';

async function clear(channel: TextChannel, count: number) {
  const messages = await channel.fetchMessages({ limit: count });
  const deleted = await channel.bulkDelete(messages, 'Channel cleared');
  await channel.send(`Deleted ${deleted} messages.`);
}
```

## Reacting

`message.react()` adds a reaction of the bot: a standard emoji, or a custom emoji of the server, as Discord writes it (`<:name:id>`) or as one of `guild.emojis`.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function vote(message: Message) {
  await message.react('👍');
  await message.react('👎');
}
```

`message.reactions` lists the reactions a message has, with how many of each; `fetchReactionUsers()` asks who reacted with one emoji. `removeReaction()` removes the reaction of the bot (or of a given user), `clearReactions()` removes every reaction, or every reaction of one emoji.

## Pinning

`message.pin()` and `message.unpin()` pin and unpin a message in its channel; `channel.fetchPins()` lists the pinned messages, latest pin first.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message } from 'chapterjs';

async function keep(message: Message) {
  if (!message.pinned) await message.pin('Kept by the bot');
}
```

## Reading the history

`channel.fetchMessages()` asks Discord for the latest messages of a channel, newest first, 100 at most per call. `before`, `after` and `around` pick where to read from, so you can go further back:

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import type { Message, TextChannel } from 'chapterjs';

async function lastMessagesOf(channel: TextChannel, authorId: string, count: number) {
  const found: Message[] = [];
  let before: Message | undefined;
  while (found.length < count) {
    const page = await channel.fetchMessages({ limit: 100, before });
    for (const message of page) {
      if (message.author.id === authorId) found.push(message);
    }
    before = page.at(-1);
    if (!before || page.length < 100) break;
  }
  return found.slice(0, count);
}
```

`channel.fetchMessage(id)` gets one message by its id. `channel.messages` is what the bot remembers of the channel without asking Discord: it is empty by default, since most bots never read an old message. See [what the bot remembers](https://www.chapterjs.org/memory.md).

> [!TIP]
>
> A message you receive in an [event](https://www.chapterjs.org/events/messages.md) is already a `Message`: no need to fetch it to reply, react or delete.

## Buttons, menus and layout

A message can hold buttons and select menus, declared with `button()` and `select()`, which run your code when they are used, and be built out of texts, sections, galleries and containers instead of a text and embeds:

```ts src/commands/vote.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { command } from 'chapterjs';
import yes from '../components/yes';

export default command({
  name: 'vote',
  description: 'Starts a vote',
  async run({ interaction }) {
    await interaction.reply({ content: 'Pizza tonight?', components: [yes] });
  },
});
```

```ts src/components/yes.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
import { button } from 'chapterjs';

export default button({
  label: 'Yes!',
  emoji: '🍕',
  style: 'success',
  async run({ interaction, user }) {
    await interaction.reply({ content: `${user} is in.` });
  },
});
```

`components` only takes what ChapterJS made, never JSON: the ids that tell a click apart are written by the framework. How to write them is in [Buttons](https://www.chapterjs.org/components.md), [Select menus](https://www.chapterjs.org/components/selects.md), [Forms](https://www.chapterjs.org/components/modals.md) and [Embeds and layout](https://www.chapterjs.org/components/embeds.md).

> [card] [Events about messages](https://www.chapterjs.org/events/messages.md)
>
> React when a message is sent, edited or deleted.

> [card] [Message options](https://www.chapterjs.org/reference/message-options.md)
>
> Every field a message takes, with the limits.


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