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

# Helpers

> Mention users, channels, roles and commands, write dates everyone reads in their timezone, work with ids and image URLs

Small functions and types exported by `chapterjs` to write messages and work with ids and images.

> [!NOTE]
>
> How to send what they produce is in the [Messages](https://www.chapterjs.org/messages.md) guide; everything a message can contain is in [Message options](https://www.chapterjs.org/reference/message-options.md).

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

function welcome(member: GuildMember) {
  return `Welcome ${member}! You joined ${timestamp(member.joinedAt ?? new Date(), TimestampStyle.Relative)}.`;
}
```

## Mentions

A structure put in a text becomes its mention: `${user}` is `<@id>`, `${channel}` is `<#id>`, `${role}` is `<@&id>`. When you only have an id, use these functions. They build text; Discord turns it into a mention when the message is shown.

### userMention()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
userMention(userId: Snowflake): `<@${string}>`
```

`<@USER_ID>`: mentions a user.

### channelMention()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
channelMention(channelId: Snowflake): `<#${string}>`
```

`<#CHANNEL_ID>`: links a channel.

### roleMention()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
roleMention(roleId: Snowflake): `<@&${string}>`
```

`<@&ROLE_ID>`: mentions a role.

### commandMention()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
commandMention(name: string, commandId: Snowflake): `</${string}:${string}>`
```

`</NAME:COMMAND_ID>`: a clickable slash command. The name includes the subcommand group and subcommand when there are some (`"foo group bar"`).

### emojiMention()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
emojiMention(name: string, emojiId: Snowflake, animated?: boolean): string
```

`<:NAME:ID>` or `<a:NAME:ID>`: a custom emoji.

### GuildNavigation

Guild Navigation Types: links to a tab of the current server. Put the value in a message as it is.

| Name | Value | Opens |
| - | - | - |
| `GuildNavigation.Customize` | `'<id:customize>'` | The Channels & Roles tab, with the onboarding prompts |
| `GuildNavigation.Browse` | `'<id:browse>'` | The Browse Channels tab |
| `GuildNavigation.Guide` | `'<id:guide>'` | The Server Guide tab |
| `GuildNavigation.LinkedRoles` | `'<id:linked-roles>'` | The Linked Roles tab |

[Discord documentation](https://docs.discord.com/developers/reference#message-formatting)

## Dates

### timestamp()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
timestamp(date: Date | number, style?: TimestampStyle): string
```

`<t:TIMESTAMP>` or `<t:TIMESTAMP:STYLE>`: a date shown to each reader in their own timezone and language. Timestamps are expressed in seconds.

A `number` is milliseconds since the Unix epoch, like `Date.now()`. Something that is not a date throws a `TypeError`:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
timestamp() needs a valid date.
```

### TimestampStyle

How `timestamp()` is shown. Without a style, Discord uses `LongDateShortTime`.

| Name | Value | Shown as |
| - | - | - |
| `TimestampStyle.ShortTime` | `'t'` | 16:20 |
| `TimestampStyle.MediumTime` | `'T'` | 16:20:30 |
| `TimestampStyle.ShortDate` | `'d'` | 20/04/2021 |
| `TimestampStyle.LongDate` | `'D'` | April 20, 2021 |
| `TimestampStyle.LongDateShortTime` | `'f'` | April 20, 2021 at 16:20 (default) |
| `TimestampStyle.FullDateShortTime` | `'F'` | Tuesday, April 20, 2021 at 16:20 |
| `TimestampStyle.ShortDateShortTime` | `'s'` | 20/04/2021, 16:20 |
| `TimestampStyle.ShortDateMediumTime` | `'S'` | 20/04/2021, 16:20:30 |
| `TimestampStyle.Relative` | `'R'` | 4 years ago |

[Discord documentation](https://docs.discord.com/developers/reference#message-formatting-timestamp-styles)

## Ids

### Snowflake

A Discord id: a 64-bit number serialized as a string.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type Snowflake = string
```

Every `id` is one, and every option that takes an id takes a `Snowflake`. It is text because the number is too big for a JavaScript `number`: never turn it into one.

### isSnowflake()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
isSnowflake(value: unknown): value is Snowflake
```

Whether a value looks like a Discord id: used to reject a wrong value (a name, a mention, a number) before it is sent to Discord.

### snowflakeTimestamp()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
snowflakeTimestamp(id: Snowflake): number
```

When the thing a snowflake identifies was created, in milliseconds since the Unix epoch: `(snowflake >> 22) + 1420070400000`.

Structures already have `createdAt`; this is for an id alone.

### snowflakeFromTimestamp()

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
snowflakeFromTimestamp(timestamp: number | Date): Snowflake
```

The smallest snowflake created at a given time: lets `before` / `after` pagination start from a date.

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

async function sinceYesterday(channel: TextChannel) {
  const yesterday = Date.now() - 24 * 3600 * 1000;
  return channel.fetchMessages({ after: snowflakeFromTimestamp(yesterday), limit: 100 });
}
```

A date before Discord existed throws a `RangeError`:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
Discord ids start in 2015: the date is too early.
```

[Discord documentation](https://docs.discord.com/developers/reference#snowflakes)

## Images

### ImageOptions

What every method that ends with `URL` takes: `user.avatarURL()`, `member.avatarURL()`, `guild.iconURL()`, `guild.bannerURL()`, `guild.splashURL()`, `role.iconURL()`, `emoji.url()`...

**format** `ImageFormat`

The file format. By default: WebP, which Discord recommends since it works for still and animated images, whatever was uploaded.

**size** `ImageSize`

The size in pixels: a power of two between 16 and 4096.

**animated** `boolean`

`false` to get the first frame of an animated image. By default an animated image stays animated.

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

function bigAvatar(user: User) {
  return user.avatarURL({ format: 'png', size: 1024 });
}
```

What you get follows a few rules:

* Asking for `'gif'` on an image that is not animated gives a PNG instead.
* An animated image stays animated only in WebP (the default) or GIF; `animated: false` gives its first frame.
* A wrong value throws a `RangeError` before any URL is built:

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
"bmp" is not an image format. Use png, jpg, webp or gif.
```

```text theme={"theme":{"light":"github-light","dark":"vesper"}}
100 is not an image size. Use a power of two between 16 and 4096 (64, 128, 256...).
```

### ImageFormat

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type ImageFormat = 'png' | 'jpg' | 'jpeg' | 'webp' | 'gif'
```

### ImageSize

Image size can be any power of two between 16 and 4096.

```ts theme={"theme":{"light":"github-light","dark":"vesper"}}
type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096
```

[Discord documentation](https://docs.discord.com/developers/reference#image-formatting)


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