---
description: discord.js v14 rules (current options, deprecated ones to avoid, Discord's limits)
globs: **/*.ts, **/*.js
alwaysApply: false
---
# discord.js v14 rules

Use discord.js v14 (checked against 14.27). When unsure whether a method or option exists, check `node_modules/discord.js/typings/index.d.ts` instead of guessing. Most examples online are v13 or early v14.

## Use the current options, not the deprecated ones
- Ready event: `client.once(Events.ClientReady, (c) => ...)`. The `"ready"` event name is deprecated in favour of `"clientReady"`.
- Ephemeral replies: `interaction.reply({ content, flags: MessageFlags.Ephemeral })`. Don't write `ephemeral: true` (deprecated).
- Getting the reply back: `reply({ ..., withResponse: true })` returns an InteractionCallbackResponse; the message is `response.resource?.message`. Don't write `fetchReply: true` (deprecated).
- Where a command can be used: `.setContexts(InteractionContextType.Guild)` (or BotDM, PrivateChannel). Don't write `.setDMPermission()` (deprecated).
- Who can use a command by default: `.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)`. It applies to the whole command; for per-subcommand rules check `interaction.memberPermissions?.has(...)` in code.
- No v12/v13 APIs: `GatewayIntentBits` (not `Intents.FLAGS`), `EmbedBuilder` (not `MessageEmbed`), `ActionRowBuilder`/`ButtonBuilder` (not `MessageActionRow`/`MessageButton`), `Events.InteractionCreate` (not `"interaction"`).
- Narrow the interaction union with type guards (`isChatInputCommand()`, `isAutocomplete()`, `isButton()`, `inGuild()`), not casts.
- Required options: `interaction.options.getString("name", true)`. Without `true` it returns null when the option is missing.

## Discord's rules (from the Discord developer docs)
- Send the first response to every interaction within 3 seconds (reply, deferReply, update, deferUpdate, showModal, or respond for autocomplete). For slow work, `await interaction.deferReply()` first, then `editReply()`.
- The interaction token lasts 15 minutes. editReply and followUp fail after that.
- Respond once. After reply or deferReply, use editReply or followUp. In a catch block, check `interaction.replied || interaction.deferred` and use followUp, or you get InteractionAlreadyReplied.
- Autocomplete can't be deferred and returns at most 25 choices. Answer from memory or a fast local query.
- Message content is at most 2000 characters. Component custom_id is 1-100 characters.
- Button and select custom_ids come back from the user's client: parse and validate them like any user input.
- Anything a user typed (tags, names, reasons) goes out with `allowedMentions: { parse: [] }` so it can't ping @everyone, roles or people.

## Intents
- Slash-command bots need only `GatewayIntentBits.Guilds`. Add an intent only when a feature needs it.
- MessageContent, GuildMembers and GuildPresences are privileged. Enable them in the Developer Portal (Bot page) first, or the gateway closes the connection (close code 4014). Prefer a slash command over reading message content.

## Commands and registration
- Register commands with a separate script, not on every startup: `rest.put(Routes.applicationGuildCommands(clientId, guildId), { body })` for one test server (updates instantly), `Routes.applicationCommands(clientId)` for global.
- PUT is a bulk overwrite: commands missing from the body are deleted.
- Discord allows 200 command creates per day per server, so never register in a loop.
- Command names are lowercase, 1-32 characters; descriptions 1-100. `SlashCommandBuilder#toJSON()` validates them, so call it in a test.

## Safety
- The bot token comes from an env var (e.g. DISCORD_TOKEN) loaded from .env or the host's secret settings. Never print, log, commit or hard-code it. If it leaks, reset it in the Developer Portal.
- Wrap command handlers so one error logs the stack and sends the user an ephemeral message instead of "The application did not respond".
- Handle SIGINT/SIGTERM: `await client.destroy()`, close the database, then exit.
- A gateway bot is a long-running process. It can't run on serverless functions; host it on a VPS or a worker/background service.
