chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1090 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bot
|
|
3
|
+
description: "Discord.js SDK for building Discord bots with slash commands and gateway events in JavaScript/TypeScript"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "14.24.0"
|
|
7
|
+
updated-on: "2026-03-01"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "discord,bot,slash-commands,gateway,sdk"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Discord.js JavaScript/TypeScript SDK Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are a Discord.js coding expert. Help me with writing code using the Discord API calling the official libraries and SDKs.
|
|
15
|
+
|
|
16
|
+
You can find the official SDK documentation and code samples here:
|
|
17
|
+
https://discord.js.org/docs/packages/discord.js/14.24.0
|
|
18
|
+
|
|
19
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
20
|
+
|
|
21
|
+
Always use the discord.js library to interact with the Discord API, which is the standard library for all Discord bot interactions in JavaScript/TypeScript. Do not use legacy libraries or unofficial SDKs.
|
|
22
|
+
|
|
23
|
+
- **Library Name:** discord.js
|
|
24
|
+
- **NPM Package:** `discord.js`
|
|
25
|
+
- **Current Version:** 14.24.0 (v14)
|
|
26
|
+
- **Legacy Libraries:** discord.js v12, discord.js v13 are outdated and not recommended
|
|
27
|
+
|
|
28
|
+
**Installation:**
|
|
29
|
+
|
|
30
|
+
- **Correct:** `npm install discord.js`
|
|
31
|
+
|
|
32
|
+
**APIs and Usage:**
|
|
33
|
+
|
|
34
|
+
- **Correct:** `import { Client, GatewayIntentBits, Events } from 'discord.js'`
|
|
35
|
+
- **Correct:** `const client = new Client({ intents: [...] })`
|
|
36
|
+
- **Correct:** `client.on(Events.MessageCreate, ...)`
|
|
37
|
+
- **Correct:** `interaction.reply(...)`
|
|
38
|
+
- **Incorrect:** `Discord.Client` (use `Client` instead)
|
|
39
|
+
- **Incorrect:** `client.on('message', ...)` (use `client.on(Events.MessageCreate, ...)`)
|
|
40
|
+
- **Incorrect:** `MessageEmbed` (use `EmbedBuilder` in v14)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Install discord.js using npm:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install discord.js
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Environment Variables:**
|
|
51
|
+
|
|
52
|
+
Create a `.env` file with your bot token:
|
|
53
|
+
|
|
54
|
+
```env
|
|
55
|
+
DISCORD_TOKEN=your_bot_token_here
|
|
56
|
+
CLIENT_ID=your_application_id_here
|
|
57
|
+
GUILD_ID=your_test_server_id_here
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Install dotenv to load environment variables:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install dotenv
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Initialization
|
|
67
|
+
|
|
68
|
+
The `discord.js` library requires creating a `Client` instance with appropriate intents.
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import { Client, GatewayIntentBits } from 'discord.js';
|
|
72
|
+
import dotenv from 'dotenv';
|
|
73
|
+
|
|
74
|
+
dotenv.config();
|
|
75
|
+
|
|
76
|
+
const client = new Client({
|
|
77
|
+
intents: [
|
|
78
|
+
GatewayIntentBits.Guilds,
|
|
79
|
+
GatewayIntentBits.GuildMessages,
|
|
80
|
+
GatewayIntentBits.MessageContent,
|
|
81
|
+
GatewayIntentBits.GuildMembers,
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Gateway Intents
|
|
89
|
+
|
|
90
|
+
Intents are named groups of pre-defined WebSocket events that your bot will receive. You must specify the intents your bot needs.
|
|
91
|
+
|
|
92
|
+
**Common Intents:**
|
|
93
|
+
|
|
94
|
+
- `GatewayIntentBits.Guilds` - Guild-related events (required for most bots)
|
|
95
|
+
- `GatewayIntentBits.GuildMessages` - Message events in guilds
|
|
96
|
+
- `GatewayIntentBits.MessageContent` - Access to message content (privileged)
|
|
97
|
+
- `GatewayIntentBits.GuildMembers` - Member join/leave events (privileged)
|
|
98
|
+
- `GatewayIntentBits.GuildPresences` - Presence updates (privileged)
|
|
99
|
+
- `GatewayIntentBits.DirectMessages` - DM events
|
|
100
|
+
|
|
101
|
+
**Privileged Intents:**
|
|
102
|
+
|
|
103
|
+
For `MessageContent`, `GuildMembers`, and `GuildPresences`, you must enable them in the Discord Developer Portal under your application's Bot settings.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const client = new Client({
|
|
107
|
+
intents: [
|
|
108
|
+
GatewayIntentBits.Guilds,
|
|
109
|
+
GatewayIntentBits.GuildMessages,
|
|
110
|
+
GatewayIntentBits.MessageContent, // Privileged - enable in Developer Portal
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Basic Bot Setup
|
|
116
|
+
|
|
117
|
+
### Ready Event
|
|
118
|
+
|
|
119
|
+
The `Ready` event fires when the bot successfully connects to Discord:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { Client, GatewayIntentBits, Events } from 'discord.js';
|
|
123
|
+
|
|
124
|
+
const client = new Client({
|
|
125
|
+
intents: [GatewayIntentBits.Guilds],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
client.once(Events.ClientReady, (readyClient) => {
|
|
129
|
+
console.log(`Logged in as ${readyClient.user.tag}`);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Message Events
|
|
136
|
+
|
|
137
|
+
Listen to messages in channels:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
import { Client, GatewayIntentBits, Events } from 'discord.js';
|
|
141
|
+
|
|
142
|
+
const client = new Client({
|
|
143
|
+
intents: [
|
|
144
|
+
GatewayIntentBits.Guilds,
|
|
145
|
+
GatewayIntentBits.GuildMessages,
|
|
146
|
+
GatewayIntentBits.MessageContent,
|
|
147
|
+
],
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
client.on(Events.MessageCreate, (message) => {
|
|
151
|
+
// Ignore messages from bots
|
|
152
|
+
if (message.author.bot) return;
|
|
153
|
+
|
|
154
|
+
if (message.content === '!ping') {
|
|
155
|
+
message.reply('Pong!');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Slash Commands
|
|
163
|
+
|
|
164
|
+
Slash commands are the primary way to interact with Discord bots. They provide a better user experience with autocomplete and validation.
|
|
165
|
+
|
|
166
|
+
### Registering Slash Commands
|
|
167
|
+
|
|
168
|
+
Create a separate file to register commands with Discord's API:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
import { REST, Routes, SlashCommandBuilder } from 'discord.js';
|
|
172
|
+
import dotenv from 'dotenv';
|
|
173
|
+
|
|
174
|
+
dotenv.config();
|
|
175
|
+
|
|
176
|
+
const commands = [
|
|
177
|
+
new SlashCommandBuilder()
|
|
178
|
+
.setName('ping')
|
|
179
|
+
.setDescription('Replies with Pong!')
|
|
180
|
+
.toJSON(),
|
|
181
|
+
new SlashCommandBuilder()
|
|
182
|
+
.setName('user')
|
|
183
|
+
.setDescription('Get info about a user')
|
|
184
|
+
.addUserOption((option) =>
|
|
185
|
+
option
|
|
186
|
+
.setName('target')
|
|
187
|
+
.setDescription('The user')
|
|
188
|
+
.setRequired(true)
|
|
189
|
+
)
|
|
190
|
+
.toJSON(),
|
|
191
|
+
new SlashCommandBuilder()
|
|
192
|
+
.setName('server')
|
|
193
|
+
.setDescription('Get info about the server')
|
|
194
|
+
.toJSON(),
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
|
|
198
|
+
|
|
199
|
+
(async () => {
|
|
200
|
+
try {
|
|
201
|
+
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
|
202
|
+
|
|
203
|
+
// Register commands globally (takes up to 1 hour to propagate)
|
|
204
|
+
const data = await rest.put(
|
|
205
|
+
Routes.applicationCommands(process.env.CLIENT_ID),
|
|
206
|
+
{ body: commands }
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// OR register to a specific guild (instant update - for testing)
|
|
210
|
+
// const data = await rest.put(
|
|
211
|
+
// Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
|
|
212
|
+
// { body: commands }
|
|
213
|
+
// );
|
|
214
|
+
|
|
215
|
+
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error(error);
|
|
218
|
+
}
|
|
219
|
+
})();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Handling Slash Command Interactions
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
import { Client, GatewayIntentBits, Events } from 'discord.js';
|
|
226
|
+
|
|
227
|
+
const client = new Client({
|
|
228
|
+
intents: [GatewayIntentBits.Guilds],
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
232
|
+
if (!interaction.isChatInputCommand()) return;
|
|
233
|
+
|
|
234
|
+
if (interaction.commandName === 'ping') {
|
|
235
|
+
await interaction.reply('Pong!');
|
|
236
|
+
} else if (interaction.commandName === 'user') {
|
|
237
|
+
const user = interaction.options.getUser('target');
|
|
238
|
+
await interaction.reply(`User: ${user.tag}, ID: ${user.id}`);
|
|
239
|
+
} else if (interaction.commandName === 'server') {
|
|
240
|
+
await interaction.reply(
|
|
241
|
+
`Server: ${interaction.guild.name}, Members: ${interaction.guild.memberCount}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Advanced Slash Command Options
|
|
250
|
+
|
|
251
|
+
```javascript
|
|
252
|
+
new SlashCommandBuilder()
|
|
253
|
+
.setName('echo')
|
|
254
|
+
.setDescription('Echoes your message')
|
|
255
|
+
.addStringOption((option) =>
|
|
256
|
+
option
|
|
257
|
+
.setName('message')
|
|
258
|
+
.setDescription('The message to echo')
|
|
259
|
+
.setRequired(true)
|
|
260
|
+
.setMaxLength(2000)
|
|
261
|
+
)
|
|
262
|
+
.addBooleanOption((option) =>
|
|
263
|
+
option
|
|
264
|
+
.setName('ephemeral')
|
|
265
|
+
.setDescription('Should the reply be private?')
|
|
266
|
+
.setRequired(false)
|
|
267
|
+
)
|
|
268
|
+
.addIntegerOption((option) =>
|
|
269
|
+
option
|
|
270
|
+
.setName('number')
|
|
271
|
+
.setDescription('A number')
|
|
272
|
+
.setMinValue(1)
|
|
273
|
+
.setMaxValue(100)
|
|
274
|
+
)
|
|
275
|
+
.addChannelOption((option) =>
|
|
276
|
+
option
|
|
277
|
+
.setName('channel')
|
|
278
|
+
.setDescription('Select a channel')
|
|
279
|
+
)
|
|
280
|
+
.addRoleOption((option) =>
|
|
281
|
+
option
|
|
282
|
+
.setName('role')
|
|
283
|
+
.setDescription('Select a role')
|
|
284
|
+
)
|
|
285
|
+
.toJSON()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Choices in Slash Commands
|
|
289
|
+
|
|
290
|
+
Add predefined choices to string or number options:
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
new SlashCommandBuilder()
|
|
294
|
+
.setName('choose')
|
|
295
|
+
.setDescription('Choose an option')
|
|
296
|
+
.addStringOption((option) =>
|
|
297
|
+
option
|
|
298
|
+
.setName('option')
|
|
299
|
+
.setDescription('Select an option')
|
|
300
|
+
.setRequired(true)
|
|
301
|
+
.addChoices(
|
|
302
|
+
{ name: 'Option A', value: 'option_a' },
|
|
303
|
+
{ name: 'Option B', value: 'option_b' },
|
|
304
|
+
{ name: 'Option C', value: 'option_c' }
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
.toJSON()
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Autocomplete
|
|
311
|
+
|
|
312
|
+
Enable autocomplete for dynamic options:
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
// In command definition
|
|
316
|
+
new SlashCommandBuilder()
|
|
317
|
+
.setName('search')
|
|
318
|
+
.setDescription('Search for something')
|
|
319
|
+
.addStringOption((option) =>
|
|
320
|
+
option
|
|
321
|
+
.setName('query')
|
|
322
|
+
.setDescription('Search query')
|
|
323
|
+
.setRequired(true)
|
|
324
|
+
.setAutocomplete(true)
|
|
325
|
+
)
|
|
326
|
+
.toJSON()
|
|
327
|
+
|
|
328
|
+
// In interaction handler
|
|
329
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
330
|
+
if (interaction.isAutocomplete()) {
|
|
331
|
+
const focusedValue = interaction.options.getFocused();
|
|
332
|
+
|
|
333
|
+
// Fetch or generate choices based on focusedValue
|
|
334
|
+
const choices = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
|
|
335
|
+
const filtered = choices.filter((choice) =>
|
|
336
|
+
choice.startsWith(focusedValue.toLowerCase())
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
await interaction.respond(
|
|
340
|
+
filtered.map((choice) => ({ name: choice, value: choice }))
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Embeds
|
|
347
|
+
|
|
348
|
+
Embeds allow you to send rich, formatted messages with images, fields, and more.
|
|
349
|
+
|
|
350
|
+
### Basic Embed
|
|
351
|
+
|
|
352
|
+
```javascript
|
|
353
|
+
import { EmbedBuilder } from 'discord.js';
|
|
354
|
+
|
|
355
|
+
const embed = new EmbedBuilder()
|
|
356
|
+
.setTitle('Embed Title')
|
|
357
|
+
.setDescription('This is an embed description')
|
|
358
|
+
.setColor(0x0099ff)
|
|
359
|
+
.setTimestamp()
|
|
360
|
+
.setFooter({ text: 'Footer text' });
|
|
361
|
+
|
|
362
|
+
await interaction.reply({ embeds: [embed] });
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Advanced Embed
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
import { EmbedBuilder } from 'discord.js';
|
|
369
|
+
|
|
370
|
+
const embed = new EmbedBuilder()
|
|
371
|
+
.setColor('#0099ff')
|
|
372
|
+
.setTitle('Advanced Embed')
|
|
373
|
+
.setURL('https://discord.js.org')
|
|
374
|
+
.setAuthor({
|
|
375
|
+
name: 'Author Name',
|
|
376
|
+
iconURL: 'https://i.imgur.com/AfFp7pu.png',
|
|
377
|
+
url: 'https://discord.js.org',
|
|
378
|
+
})
|
|
379
|
+
.setDescription('Some description here')
|
|
380
|
+
.setThumbnail('https://i.imgur.com/AfFp7pu.png')
|
|
381
|
+
.addFields(
|
|
382
|
+
{ name: 'Regular field title', value: 'Some value here' },
|
|
383
|
+
{ name: '\u200B', value: '\u200B' }, // Blank field
|
|
384
|
+
{ name: 'Inline field title', value: 'Some value here', inline: true },
|
|
385
|
+
{ name: 'Inline field title', value: 'Some value here', inline: true }
|
|
386
|
+
)
|
|
387
|
+
.addFields({ name: 'Inline field title', value: 'Some value here', inline: true })
|
|
388
|
+
.setImage('https://i.imgur.com/AfFp7pu.png')
|
|
389
|
+
.setTimestamp()
|
|
390
|
+
.setFooter({
|
|
391
|
+
text: 'Some footer text here',
|
|
392
|
+
iconURL: 'https://i.imgur.com/AfFp7pu.png',
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
await channel.send({ embeds: [embed] });
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Embed Colors
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
// Hexadecimal
|
|
402
|
+
.setColor('#0099ff')
|
|
403
|
+
|
|
404
|
+
// Integer
|
|
405
|
+
.setColor(0x0099ff)
|
|
406
|
+
|
|
407
|
+
// RGB array
|
|
408
|
+
.setColor([0, 153, 255])
|
|
409
|
+
|
|
410
|
+
// Predefined colors
|
|
411
|
+
import { Colors } from 'discord.js';
|
|
412
|
+
.setColor(Colors.Blue)
|
|
413
|
+
.setColor(Colors.Red)
|
|
414
|
+
.setColor(Colors.Green)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Buttons
|
|
418
|
+
|
|
419
|
+
Buttons are interactive components that users can click.
|
|
420
|
+
|
|
421
|
+
### Basic Button
|
|
422
|
+
|
|
423
|
+
```javascript
|
|
424
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
|
425
|
+
|
|
426
|
+
const row = new ActionRowBuilder()
|
|
427
|
+
.addComponents(
|
|
428
|
+
new ButtonBuilder()
|
|
429
|
+
.setCustomId('primary')
|
|
430
|
+
.setLabel('Click me!')
|
|
431
|
+
.setStyle(ButtonStyle.Primary)
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
await interaction.reply({
|
|
435
|
+
content: 'Click a button!',
|
|
436
|
+
components: [row],
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Multiple Buttons
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
|
444
|
+
|
|
445
|
+
const row = new ActionRowBuilder()
|
|
446
|
+
.addComponents(
|
|
447
|
+
new ButtonBuilder()
|
|
448
|
+
.setCustomId('primary')
|
|
449
|
+
.setLabel('Primary')
|
|
450
|
+
.setStyle(ButtonStyle.Primary),
|
|
451
|
+
new ButtonBuilder()
|
|
452
|
+
.setCustomId('secondary')
|
|
453
|
+
.setLabel('Secondary')
|
|
454
|
+
.setStyle(ButtonStyle.Secondary),
|
|
455
|
+
new ButtonBuilder()
|
|
456
|
+
.setCustomId('success')
|
|
457
|
+
.setLabel('Success')
|
|
458
|
+
.setStyle(ButtonStyle.Success),
|
|
459
|
+
new ButtonBuilder()
|
|
460
|
+
.setCustomId('danger')
|
|
461
|
+
.setLabel('Danger')
|
|
462
|
+
.setStyle(ButtonStyle.Danger)
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
await interaction.reply({
|
|
466
|
+
content: 'Choose a button!',
|
|
467
|
+
components: [row],
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Link Button
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
const row = new ActionRowBuilder()
|
|
475
|
+
.addComponents(
|
|
476
|
+
new ButtonBuilder()
|
|
477
|
+
.setLabel('Visit Website')
|
|
478
|
+
.setURL('https://discord.js.org')
|
|
479
|
+
.setStyle(ButtonStyle.Link)
|
|
480
|
+
);
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Handling Button Interactions
|
|
484
|
+
|
|
485
|
+
```javascript
|
|
486
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
487
|
+
if (!interaction.isButton()) return;
|
|
488
|
+
|
|
489
|
+
if (interaction.customId === 'primary') {
|
|
490
|
+
await interaction.reply({ content: 'You clicked Primary!', ephemeral: true });
|
|
491
|
+
} else if (interaction.customId === 'danger') {
|
|
492
|
+
await interaction.reply({ content: 'Danger button clicked!', ephemeral: true });
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Disabled Buttons
|
|
498
|
+
|
|
499
|
+
```javascript
|
|
500
|
+
new ButtonBuilder()
|
|
501
|
+
.setCustomId('disabled')
|
|
502
|
+
.setLabel('Disabled')
|
|
503
|
+
.setStyle(ButtonStyle.Primary)
|
|
504
|
+
.setDisabled(true)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Select Menus
|
|
508
|
+
|
|
509
|
+
Select menus (dropdowns) allow users to choose from multiple options.
|
|
510
|
+
|
|
511
|
+
### String Select Menu
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
import { ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from 'discord.js';
|
|
515
|
+
|
|
516
|
+
const row = new ActionRowBuilder()
|
|
517
|
+
.addComponents(
|
|
518
|
+
new StringSelectMenuBuilder()
|
|
519
|
+
.setCustomId('select')
|
|
520
|
+
.setPlaceholder('Make a selection!')
|
|
521
|
+
.addOptions(
|
|
522
|
+
new StringSelectMenuOptionBuilder()
|
|
523
|
+
.setLabel('Option 1')
|
|
524
|
+
.setDescription('This is option 1')
|
|
525
|
+
.setValue('option_1'),
|
|
526
|
+
new StringSelectMenuOptionBuilder()
|
|
527
|
+
.setLabel('Option 2')
|
|
528
|
+
.setDescription('This is option 2')
|
|
529
|
+
.setValue('option_2'),
|
|
530
|
+
new StringSelectMenuOptionBuilder()
|
|
531
|
+
.setLabel('Option 3')
|
|
532
|
+
.setDescription('This is option 3')
|
|
533
|
+
.setValue('option_3')
|
|
534
|
+
)
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
await interaction.reply({
|
|
538
|
+
content: 'Choose an option:',
|
|
539
|
+
components: [row],
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Handling Select Menu Interactions
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
547
|
+
if (!interaction.isStringSelectMenu()) return;
|
|
548
|
+
|
|
549
|
+
if (interaction.customId === 'select') {
|
|
550
|
+
const selected = interaction.values[0];
|
|
551
|
+
await interaction.reply({ content: `You selected: ${selected}`, ephemeral: true });
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### User Select Menu
|
|
557
|
+
|
|
558
|
+
```javascript
|
|
559
|
+
import { UserSelectMenuBuilder } from 'discord.js';
|
|
560
|
+
|
|
561
|
+
const row = new ActionRowBuilder()
|
|
562
|
+
.addComponents(
|
|
563
|
+
new UserSelectMenuBuilder()
|
|
564
|
+
.setCustomId('user_select')
|
|
565
|
+
.setPlaceholder('Select a user')
|
|
566
|
+
.setMinValues(1)
|
|
567
|
+
.setMaxValues(3)
|
|
568
|
+
);
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Role Select Menu
|
|
572
|
+
|
|
573
|
+
```javascript
|
|
574
|
+
import { RoleSelectMenuBuilder } from 'discord.js';
|
|
575
|
+
|
|
576
|
+
const row = new ActionRowBuilder()
|
|
577
|
+
.addComponents(
|
|
578
|
+
new RoleSelectMenuBuilder()
|
|
579
|
+
.setCustomId('role_select')
|
|
580
|
+
.setPlaceholder('Select a role')
|
|
581
|
+
);
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Channel Select Menu
|
|
585
|
+
|
|
586
|
+
```javascript
|
|
587
|
+
import { ChannelSelectMenuBuilder, ChannelType } from 'discord.js';
|
|
588
|
+
|
|
589
|
+
const row = new ActionRowBuilder()
|
|
590
|
+
.addComponents(
|
|
591
|
+
new ChannelSelectMenuBuilder()
|
|
592
|
+
.setCustomId('channel_select')
|
|
593
|
+
.setPlaceholder('Select a channel')
|
|
594
|
+
.addChannelTypes(ChannelType.GuildText, ChannelType.GuildVoice)
|
|
595
|
+
);
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
## Modals
|
|
599
|
+
|
|
600
|
+
Modals are pop-up forms that allow users to submit text input.
|
|
601
|
+
|
|
602
|
+
### Creating a Modal
|
|
603
|
+
|
|
604
|
+
```javascript
|
|
605
|
+
import { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
|
|
606
|
+
|
|
607
|
+
const modal = new ModalBuilder()
|
|
608
|
+
.setCustomId('feedback_modal')
|
|
609
|
+
.setTitle('Feedback Form');
|
|
610
|
+
|
|
611
|
+
const nameInput = new TextInputBuilder()
|
|
612
|
+
.setCustomId('name_input')
|
|
613
|
+
.setLabel('What is your name?')
|
|
614
|
+
.setStyle(TextInputStyle.Short)
|
|
615
|
+
.setRequired(true);
|
|
616
|
+
|
|
617
|
+
const feedbackInput = new TextInputBuilder()
|
|
618
|
+
.setCustomId('feedback_input')
|
|
619
|
+
.setLabel('Your feedback')
|
|
620
|
+
.setStyle(TextInputStyle.Paragraph)
|
|
621
|
+
.setPlaceholder('Tell us what you think!')
|
|
622
|
+
.setRequired(true)
|
|
623
|
+
.setMaxLength(1000);
|
|
624
|
+
|
|
625
|
+
const firstActionRow = new ActionRowBuilder().addComponents(nameInput);
|
|
626
|
+
const secondActionRow = new ActionRowBuilder().addComponents(feedbackInput);
|
|
627
|
+
|
|
628
|
+
modal.addComponents(firstActionRow, secondActionRow);
|
|
629
|
+
|
|
630
|
+
await interaction.showModal(modal);
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Handling Modal Submissions
|
|
634
|
+
|
|
635
|
+
```javascript
|
|
636
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
637
|
+
if (!interaction.isModalSubmit()) return;
|
|
638
|
+
|
|
639
|
+
if (interaction.customId === 'feedback_modal') {
|
|
640
|
+
const name = interaction.fields.getTextInputValue('name_input');
|
|
641
|
+
const feedback = interaction.fields.getTextInputValue('feedback_input');
|
|
642
|
+
|
|
643
|
+
await interaction.reply({
|
|
644
|
+
content: `Thank you for your feedback, ${name}!`,
|
|
645
|
+
ephemeral: true,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## Context Menus
|
|
652
|
+
|
|
653
|
+
Context menus appear when right-clicking on a user or message.
|
|
654
|
+
|
|
655
|
+
### User Context Menu
|
|
656
|
+
|
|
657
|
+
```javascript
|
|
658
|
+
import { ContextMenuCommandBuilder, ApplicationCommandType } from 'discord.js';
|
|
659
|
+
|
|
660
|
+
const command = new ContextMenuCommandBuilder()
|
|
661
|
+
.setName('Get User Info')
|
|
662
|
+
.setType(ApplicationCommandType.User)
|
|
663
|
+
.toJSON();
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Message Context Menu
|
|
667
|
+
|
|
668
|
+
```javascript
|
|
669
|
+
const command = new ContextMenuCommandBuilder()
|
|
670
|
+
.setName('Report Message')
|
|
671
|
+
.setType(ApplicationCommandType.Message)
|
|
672
|
+
.toJSON();
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Handling Context Menu Interactions
|
|
676
|
+
|
|
677
|
+
```javascript
|
|
678
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
679
|
+
if (!interaction.isContextMenuCommand()) return;
|
|
680
|
+
|
|
681
|
+
if (interaction.commandName === 'Get User Info') {
|
|
682
|
+
const user = interaction.targetUser;
|
|
683
|
+
await interaction.reply({
|
|
684
|
+
content: `User: ${user.tag}, ID: ${user.id}`,
|
|
685
|
+
ephemeral: true,
|
|
686
|
+
});
|
|
687
|
+
} else if (interaction.commandName === 'Report Message') {
|
|
688
|
+
const message = interaction.targetMessage;
|
|
689
|
+
await interaction.reply({
|
|
690
|
+
content: `Reported message from ${message.author.tag}`,
|
|
691
|
+
ephemeral: true,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
## Sending Messages
|
|
698
|
+
|
|
699
|
+
### Reply to Interaction
|
|
700
|
+
|
|
701
|
+
```javascript
|
|
702
|
+
// Regular reply
|
|
703
|
+
await interaction.reply('Hello!');
|
|
704
|
+
|
|
705
|
+
// Ephemeral reply (only visible to user)
|
|
706
|
+
await interaction.reply({ content: 'Secret message!', ephemeral: true });
|
|
707
|
+
|
|
708
|
+
// Reply with embed
|
|
709
|
+
await interaction.reply({ embeds: [embed] });
|
|
710
|
+
|
|
711
|
+
// Reply with buttons
|
|
712
|
+
await interaction.reply({ content: 'Click!', components: [row] });
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Send Message to Channel
|
|
716
|
+
|
|
717
|
+
```javascript
|
|
718
|
+
// Get channel by ID
|
|
719
|
+
const channel = client.channels.cache.get('channel_id');
|
|
720
|
+
await channel.send('Hello, channel!');
|
|
721
|
+
|
|
722
|
+
// From interaction
|
|
723
|
+
await interaction.channel.send('Message in this channel!');
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Edit Reply
|
|
727
|
+
|
|
728
|
+
```javascript
|
|
729
|
+
await interaction.reply('Original message');
|
|
730
|
+
await interaction.editReply('Edited message');
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### Follow-up Messages
|
|
734
|
+
|
|
735
|
+
```javascript
|
|
736
|
+
await interaction.reply('First message');
|
|
737
|
+
await interaction.followUp('Second message');
|
|
738
|
+
await interaction.followUp({ content: 'Third message', ephemeral: true });
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Deferred Replies
|
|
742
|
+
|
|
743
|
+
For long-running operations:
|
|
744
|
+
|
|
745
|
+
```javascript
|
|
746
|
+
await interaction.deferReply();
|
|
747
|
+
// Do long operation...
|
|
748
|
+
await interaction.editReply('Done!');
|
|
749
|
+
|
|
750
|
+
// Or defer with ephemeral
|
|
751
|
+
await interaction.deferReply({ ephemeral: true });
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
## Permissions
|
|
755
|
+
|
|
756
|
+
### Check User Permissions
|
|
757
|
+
|
|
758
|
+
```javascript
|
|
759
|
+
if (interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
|
760
|
+
await interaction.reply('You are an admin!');
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
import { PermissionFlagsBits } from 'discord.js';
|
|
764
|
+
|
|
765
|
+
if (interaction.member.permissions.has(PermissionFlagsBits.ManageMessages)) {
|
|
766
|
+
// User can manage messages
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Command Permissions
|
|
771
|
+
|
|
772
|
+
```javascript
|
|
773
|
+
new SlashCommandBuilder()
|
|
774
|
+
.setName('admin')
|
|
775
|
+
.setDescription('Admin only command')
|
|
776
|
+
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
|
777
|
+
.toJSON()
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### Check Bot Permissions
|
|
781
|
+
|
|
782
|
+
```javascript
|
|
783
|
+
const botMember = interaction.guild.members.me;
|
|
784
|
+
if (botMember.permissions.has(PermissionFlagsBits.ManageRoles)) {
|
|
785
|
+
// Bot can manage roles
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
## Events
|
|
790
|
+
|
|
791
|
+
### Common Events
|
|
792
|
+
|
|
793
|
+
```javascript
|
|
794
|
+
import { Events } from 'discord.js';
|
|
795
|
+
|
|
796
|
+
// Bot ready
|
|
797
|
+
client.once(Events.ClientReady, (c) => {
|
|
798
|
+
console.log(`Ready! Logged in as ${c.user.tag}`);
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
// Message created
|
|
802
|
+
client.on(Events.MessageCreate, (message) => {
|
|
803
|
+
console.log(`Message: ${message.content}`);
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// Interaction created
|
|
807
|
+
client.on(Events.InteractionCreate, (interaction) => {
|
|
808
|
+
console.log(`Interaction: ${interaction.type}`);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Guild member added
|
|
812
|
+
client.on(Events.GuildMemberAdd, (member) => {
|
|
813
|
+
console.log(`${member.user.tag} joined ${member.guild.name}`);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Guild member removed
|
|
817
|
+
client.on(Events.GuildMemberRemove, (member) => {
|
|
818
|
+
console.log(`${member.user.tag} left ${member.guild.name}`);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
// Message deleted
|
|
822
|
+
client.on(Events.MessageDelete, (message) => {
|
|
823
|
+
console.log(`Message deleted: ${message.content}`);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// Message updated
|
|
827
|
+
client.on(Events.MessageUpdate, (oldMessage, newMessage) => {
|
|
828
|
+
console.log(`Message edited from "${oldMessage.content}" to "${newMessage.content}"`);
|
|
829
|
+
});
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
## Fetching Data
|
|
833
|
+
|
|
834
|
+
### Fetch User
|
|
835
|
+
|
|
836
|
+
```javascript
|
|
837
|
+
const user = await client.users.fetch('user_id');
|
|
838
|
+
console.log(user.tag);
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Fetch Member
|
|
842
|
+
|
|
843
|
+
```javascript
|
|
844
|
+
const member = await interaction.guild.members.fetch('user_id');
|
|
845
|
+
console.log(member.displayName);
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
### Fetch Channel
|
|
849
|
+
|
|
850
|
+
```javascript
|
|
851
|
+
const channel = await client.channels.fetch('channel_id');
|
|
852
|
+
await channel.send('Hello!');
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Fetch Guild
|
|
856
|
+
|
|
857
|
+
```javascript
|
|
858
|
+
const guild = await client.guilds.fetch('guild_id');
|
|
859
|
+
console.log(guild.name);
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### Fetch Messages
|
|
863
|
+
|
|
864
|
+
```javascript
|
|
865
|
+
// Fetch last 10 messages
|
|
866
|
+
const messages = await channel.messages.fetch({ limit: 10 });
|
|
867
|
+
|
|
868
|
+
// Fetch specific message
|
|
869
|
+
const message = await channel.messages.fetch('message_id');
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
## Error Handling
|
|
873
|
+
|
|
874
|
+
### Basic Error Handling
|
|
875
|
+
|
|
876
|
+
```javascript
|
|
877
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
878
|
+
try {
|
|
879
|
+
if (interaction.isChatInputCommand()) {
|
|
880
|
+
// Handle command
|
|
881
|
+
await interaction.reply('Success!');
|
|
882
|
+
}
|
|
883
|
+
} catch (error) {
|
|
884
|
+
console.error(error);
|
|
885
|
+
|
|
886
|
+
if (interaction.replied || interaction.deferred) {
|
|
887
|
+
await interaction.followUp({
|
|
888
|
+
content: 'There was an error executing this command!',
|
|
889
|
+
ephemeral: true,
|
|
890
|
+
});
|
|
891
|
+
} else {
|
|
892
|
+
await interaction.reply({
|
|
893
|
+
content: 'There was an error executing this command!',
|
|
894
|
+
ephemeral: true,
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Common Errors
|
|
902
|
+
|
|
903
|
+
```javascript
|
|
904
|
+
// Unknown Interaction
|
|
905
|
+
if (interaction.isRepliable() && !interaction.replied) {
|
|
906
|
+
try {
|
|
907
|
+
await interaction.reply('...');
|
|
908
|
+
} catch (error) {
|
|
909
|
+
if (error.code === 10062) {
|
|
910
|
+
console.log('Unknown interaction - likely expired');
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Missing Permissions
|
|
916
|
+
catch (error) {
|
|
917
|
+
if (error.code === 50013) {
|
|
918
|
+
console.log('Missing permissions');
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Unknown Channel
|
|
923
|
+
catch (error) {
|
|
924
|
+
if (error.code === 10003) {
|
|
925
|
+
console.log('Unknown channel');
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
## Command Handler Structure
|
|
931
|
+
|
|
932
|
+
Organize commands into separate files:
|
|
933
|
+
|
|
934
|
+
```javascript
|
|
935
|
+
// commands/ping.js
|
|
936
|
+
import { SlashCommandBuilder } from 'discord.js';
|
|
937
|
+
|
|
938
|
+
export const data = new SlashCommandBuilder()
|
|
939
|
+
.setName('ping')
|
|
940
|
+
.setDescription('Replies with Pong!');
|
|
941
|
+
|
|
942
|
+
export async function execute(interaction) {
|
|
943
|
+
await interaction.reply('Pong!');
|
|
944
|
+
}
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
```javascript
|
|
948
|
+
// index.js
|
|
949
|
+
import { Client, Collection, GatewayIntentBits, Events } from 'discord.js';
|
|
950
|
+
import { fileURLToPath } from 'url';
|
|
951
|
+
import { dirname, join } from 'path';
|
|
952
|
+
import { readdirSync } from 'fs';
|
|
953
|
+
|
|
954
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
955
|
+
|
|
956
|
+
const client = new Client({
|
|
957
|
+
intents: [GatewayIntentBits.Guilds],
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
client.commands = new Collection();
|
|
961
|
+
|
|
962
|
+
const commandsPath = join(__dirname, 'commands');
|
|
963
|
+
const commandFiles = readdirSync(commandsPath).filter((file) => file.endsWith('.js'));
|
|
964
|
+
|
|
965
|
+
for (const file of commandFiles) {
|
|
966
|
+
const filePath = join(commandsPath, file);
|
|
967
|
+
const command = await import(filePath);
|
|
968
|
+
|
|
969
|
+
if ('data' in command && 'execute' in command) {
|
|
970
|
+
client.commands.set(command.data.name, command);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
client.on(Events.InteractionCreate, async (interaction) => {
|
|
975
|
+
if (!interaction.isChatInputCommand()) return;
|
|
976
|
+
|
|
977
|
+
const command = client.commands.get(interaction.commandName);
|
|
978
|
+
|
|
979
|
+
if (!command) {
|
|
980
|
+
console.error(`No command matching ${interaction.commandName} was found.`);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
try {
|
|
985
|
+
await command.execute(interaction);
|
|
986
|
+
} catch (error) {
|
|
987
|
+
console.error(error);
|
|
988
|
+
|
|
989
|
+
const errorMessage = {
|
|
990
|
+
content: 'There was an error while executing this command!',
|
|
991
|
+
ephemeral: true,
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
if (interaction.replied || interaction.deferred) {
|
|
995
|
+
await interaction.followUp(errorMessage);
|
|
996
|
+
} else {
|
|
997
|
+
await interaction.reply(errorMessage);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
## Advanced Features
|
|
1006
|
+
|
|
1007
|
+
### Collectors
|
|
1008
|
+
|
|
1009
|
+
Collect button or select menu interactions:
|
|
1010
|
+
|
|
1011
|
+
```javascript
|
|
1012
|
+
const filter = (i) => i.customId === 'primary' && i.user.id === interaction.user.id;
|
|
1013
|
+
const collector = interaction.channel.createMessageComponentCollector({
|
|
1014
|
+
filter,
|
|
1015
|
+
time: 60000, // 1 minute
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
collector.on('collect', async (i) => {
|
|
1019
|
+
await i.update({ content: 'Button clicked!', components: [] });
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
collector.on('end', (collected) => {
|
|
1023
|
+
console.log(`Collected ${collected.size} interactions.`);
|
|
1024
|
+
});
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
### Reactions
|
|
1028
|
+
|
|
1029
|
+
```javascript
|
|
1030
|
+
const message = await channel.send('React to this!');
|
|
1031
|
+
await message.react('👍');
|
|
1032
|
+
await message.react('👎');
|
|
1033
|
+
|
|
1034
|
+
// Reaction collector
|
|
1035
|
+
const filter = (reaction, user) => {
|
|
1036
|
+
return reaction.emoji.name === '👍' && user.id === interaction.user.id;
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
const collector = message.createReactionCollector({ filter, time: 60000 });
|
|
1040
|
+
|
|
1041
|
+
collector.on('collect', (reaction, user) => {
|
|
1042
|
+
console.log(`${user.tag} reacted with ${reaction.emoji.name}`);
|
|
1043
|
+
});
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
### Threads
|
|
1047
|
+
|
|
1048
|
+
```javascript
|
|
1049
|
+
// Create thread from message
|
|
1050
|
+
const thread = await message.startThread({
|
|
1051
|
+
name: 'Thread Name',
|
|
1052
|
+
autoArchiveDuration: 60,
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
// Create thread in channel
|
|
1056
|
+
const thread = await channel.threads.create({
|
|
1057
|
+
name: 'New Thread',
|
|
1058
|
+
autoArchiveDuration: 60,
|
|
1059
|
+
reason: 'Discussion thread',
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
// Send message to thread
|
|
1063
|
+
await thread.send('Hello in thread!');
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
### Voice Connections
|
|
1067
|
+
|
|
1068
|
+
For voice support, install `@discordjs/voice`:
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
1071
|
+
npm install @discordjs/voice
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
```javascript
|
|
1075
|
+
import { joinVoiceChannel } from '@discordjs/voice';
|
|
1076
|
+
|
|
1077
|
+
const connection = joinVoiceChannel({
|
|
1078
|
+
channelId: channel.id,
|
|
1079
|
+
guildId: channel.guild.id,
|
|
1080
|
+
adapterCreator: channel.guild.voiceAdapterCreator,
|
|
1081
|
+
});
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
## Useful Links
|
|
1085
|
+
|
|
1086
|
+
- Documentation: https://discord.js.org/docs/packages/discord.js/14.24.0
|
|
1087
|
+
- Guide: https://discordjs.guide/
|
|
1088
|
+
- Discord API Docs: https://discord.com/developers/docs
|
|
1089
|
+
- Developer Portal: https://discord.com/developers/applications
|
|
1090
|
+
- Community Server: https://discord.gg/djs
|