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,1130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bot
|
|
3
|
+
description: "Discord.py SDK for building Discord bots with slash commands and gateway events in Python"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "2.6.4"
|
|
7
|
+
updated-on: "2026-03-01"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "discord,bot,slash-commands,gateway,sdk"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Discord.py Python SDK Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are a discord.py 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://discordpy.readthedocs.io/en/stable/
|
|
18
|
+
|
|
19
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
20
|
+
|
|
21
|
+
Always use the discord.py library to interact with the Discord API, which is the standard library for all Discord bot interactions in Python. Do not use legacy libraries or unofficial SDKs.
|
|
22
|
+
|
|
23
|
+
- **Library Name:** discord.py
|
|
24
|
+
- **PyPI Package:** `discord.py`
|
|
25
|
+
- **Current Version:** 2.6.4
|
|
26
|
+
- **Legacy Libraries:** discord.py v1.x is outdated and not recommended
|
|
27
|
+
|
|
28
|
+
**Installation:**
|
|
29
|
+
|
|
30
|
+
- **Correct:** `pip install discord.py`
|
|
31
|
+
- **Correct (with voice support):** `pip install discord.py[voice]`
|
|
32
|
+
|
|
33
|
+
**APIs and Usage:**
|
|
34
|
+
|
|
35
|
+
- **Correct:** `import discord`
|
|
36
|
+
- **Correct:** `from discord.ext import commands`
|
|
37
|
+
- **Correct:** `bot = commands.Bot(command_prefix='!', intents=intents)`
|
|
38
|
+
- **Correct:** `@bot.command()`
|
|
39
|
+
- **Correct:** `await interaction.response.send_message(...)`
|
|
40
|
+
- **Incorrect:** `discord.Client()` without intents (use `discord.Client(intents=intents)`)
|
|
41
|
+
- **Incorrect:** `@client.event` in cogs (use `@commands.Cog.listener()`)
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
Install discord.py using pip:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install discord.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**With Voice Support:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install discord.py[voice]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Environment Variables:**
|
|
58
|
+
|
|
59
|
+
Create a `.env` file with your bot token:
|
|
60
|
+
|
|
61
|
+
```env
|
|
62
|
+
DISCORD_TOKEN=your_bot_token_here
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Install python-dotenv to load environment variables:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install python-dotenv
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Initialization
|
|
72
|
+
|
|
73
|
+
The `discord.py` library requires creating a bot with appropriate intents.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import discord
|
|
77
|
+
from discord.ext import commands
|
|
78
|
+
import os
|
|
79
|
+
from dotenv import load_dotenv
|
|
80
|
+
|
|
81
|
+
load_dotenv()
|
|
82
|
+
|
|
83
|
+
intents = discord.Intents.default()
|
|
84
|
+
intents.message_content = True
|
|
85
|
+
intents.members = True
|
|
86
|
+
|
|
87
|
+
bot = commands.Bot(command_prefix='!', intents=intents)
|
|
88
|
+
|
|
89
|
+
bot.run(os.getenv('DISCORD_TOKEN'))
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Intents
|
|
93
|
+
|
|
94
|
+
Intents specify which events your bot will receive from Discord. You must explicitly enable intents.
|
|
95
|
+
|
|
96
|
+
**Common Intents:**
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
intents = discord.Intents.default()
|
|
100
|
+
intents.message_content = True # Privileged - enable in Developer Portal
|
|
101
|
+
intents.members = True # Privileged - enable in Developer Portal
|
|
102
|
+
intents.presences = True # Privileged - enable in Developer Portal
|
|
103
|
+
intents.guilds = True
|
|
104
|
+
intents.messages = True
|
|
105
|
+
intents.reactions = True
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**All Intents (for development):**
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
intents = discord.Intents.all()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Minimal Intents:**
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
intents = discord.Intents.default()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Privileged Intents:**
|
|
121
|
+
|
|
122
|
+
For `message_content`, `members`, and `presences`, you must enable them in the Discord Developer Portal under your application's Bot settings.
|
|
123
|
+
|
|
124
|
+
## Basic Bot Setup
|
|
125
|
+
|
|
126
|
+
### Using Commands Extension
|
|
127
|
+
|
|
128
|
+
The recommended way to build bots:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import discord
|
|
132
|
+
from discord.ext import commands
|
|
133
|
+
import os
|
|
134
|
+
|
|
135
|
+
intents = discord.Intents.default()
|
|
136
|
+
intents.message_content = True
|
|
137
|
+
|
|
138
|
+
bot = commands.Bot(command_prefix='!', intents=intents)
|
|
139
|
+
|
|
140
|
+
@bot.event
|
|
141
|
+
async def on_ready():
|
|
142
|
+
print(f'{bot.user} has connected to Discord!')
|
|
143
|
+
|
|
144
|
+
@bot.command()
|
|
145
|
+
async def ping(ctx):
|
|
146
|
+
await ctx.send('Pong!')
|
|
147
|
+
|
|
148
|
+
bot.run(os.getenv('DISCORD_TOKEN'))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Using Client Only
|
|
152
|
+
|
|
153
|
+
For simpler bots without commands:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
import discord
|
|
157
|
+
import os
|
|
158
|
+
|
|
159
|
+
intents = discord.Intents.default()
|
|
160
|
+
intents.message_content = True
|
|
161
|
+
|
|
162
|
+
client = discord.Client(intents=intents)
|
|
163
|
+
|
|
164
|
+
@client.event
|
|
165
|
+
async def on_ready():
|
|
166
|
+
print(f'{client.user} has logged in!')
|
|
167
|
+
|
|
168
|
+
@client.event
|
|
169
|
+
async def on_message(message):
|
|
170
|
+
if message.author == client.user:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
if message.content.startswith('!hello'):
|
|
174
|
+
await message.channel.send('Hello!')
|
|
175
|
+
|
|
176
|
+
client.run(os.getenv('DISCORD_TOKEN'))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Commands
|
|
180
|
+
|
|
181
|
+
### Basic Command
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
@bot.command()
|
|
185
|
+
async def hello(ctx):
|
|
186
|
+
await ctx.send(f'Hello {ctx.author.mention}!')
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Command with Arguments
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
@bot.command()
|
|
193
|
+
async def say(ctx, *, message: str):
|
|
194
|
+
await ctx.send(message)
|
|
195
|
+
|
|
196
|
+
@bot.command()
|
|
197
|
+
async def add(ctx, a: int, b: int):
|
|
198
|
+
await ctx.send(f'{a} + {b} = {a + b}')
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Command with Optional Arguments
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
@bot.command()
|
|
205
|
+
async def greet(ctx, name: str = None):
|
|
206
|
+
if name is None:
|
|
207
|
+
await ctx.send(f'Hello {ctx.author.name}!')
|
|
208
|
+
else:
|
|
209
|
+
await ctx.send(f'Hello {name}!')
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Command Aliases
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
@bot.command(aliases=['p'])
|
|
216
|
+
async def ping(ctx):
|
|
217
|
+
await ctx.send('Pong!')
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Command with Description
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
@bot.command(
|
|
224
|
+
name='info',
|
|
225
|
+
description='Get bot information',
|
|
226
|
+
help='Displays information about the bot'
|
|
227
|
+
)
|
|
228
|
+
async def info(ctx):
|
|
229
|
+
await ctx.send('This is my Discord bot!')
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Converters
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
@bot.command()
|
|
236
|
+
async def kick(ctx, member: discord.Member, *, reason: str = 'No reason provided'):
|
|
237
|
+
await member.kick(reason=reason)
|
|
238
|
+
await ctx.send(f'{member.mention} has been kicked.')
|
|
239
|
+
|
|
240
|
+
@bot.command()
|
|
241
|
+
async def ban(ctx, user: discord.User, *, reason: str = None):
|
|
242
|
+
await ctx.guild.ban(user, reason=reason)
|
|
243
|
+
await ctx.send(f'{user.mention} has been banned.')
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Slash Commands (Application Commands)
|
|
247
|
+
|
|
248
|
+
Slash commands provide a better user experience with autocomplete and validation.
|
|
249
|
+
|
|
250
|
+
### Basic Slash Command
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
import discord
|
|
254
|
+
from discord import app_commands
|
|
255
|
+
|
|
256
|
+
intents = discord.Intents.default()
|
|
257
|
+
client = discord.Client(intents=intents)
|
|
258
|
+
tree = app_commands.CommandTree(client)
|
|
259
|
+
|
|
260
|
+
@tree.command(name='ping', description='Replies with Pong!')
|
|
261
|
+
async def ping(interaction: discord.Interaction):
|
|
262
|
+
await interaction.response.send_message('Pong!')
|
|
263
|
+
|
|
264
|
+
@client.event
|
|
265
|
+
async def on_ready():
|
|
266
|
+
await tree.sync()
|
|
267
|
+
print(f'{client.user} is ready!')
|
|
268
|
+
|
|
269
|
+
client.run(os.getenv('DISCORD_TOKEN'))
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Slash Command with Parameters
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
@tree.command(name='greet', description='Greet a user')
|
|
276
|
+
@app_commands.describe(user='The user to greet', message='Custom greeting message')
|
|
277
|
+
async def greet(interaction: discord.Interaction, user: discord.Member, message: str = 'Hello'):
|
|
278
|
+
await interaction.response.send_message(f'{message}, {user.mention}!')
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Slash Command with Choices
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
@tree.command(name='color', description='Choose a color')
|
|
285
|
+
@app_commands.describe(color='Your favorite color')
|
|
286
|
+
@app_commands.choices(color=[
|
|
287
|
+
app_commands.Choice(name='Red', value='red'),
|
|
288
|
+
app_commands.Choice(name='Blue', value='blue'),
|
|
289
|
+
app_commands.Choice(name='Green', value='green'),
|
|
290
|
+
])
|
|
291
|
+
async def color(interaction: discord.Interaction, color: app_commands.Choice[str]):
|
|
292
|
+
await interaction.response.send_message(f'You chose {color.name}!')
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Autocomplete
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
async def fruit_autocomplete(
|
|
299
|
+
interaction: discord.Interaction,
|
|
300
|
+
current: str,
|
|
301
|
+
) -> list[app_commands.Choice[str]]:
|
|
302
|
+
fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']
|
|
303
|
+
return [
|
|
304
|
+
app_commands.Choice(name=fruit, value=fruit)
|
|
305
|
+
for fruit in fruits if current.lower() in fruit.lower()
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
@tree.command(name='fruit', description='Choose a fruit')
|
|
309
|
+
@app_commands.autocomplete(fruit=fruit_autocomplete)
|
|
310
|
+
async def fruit(interaction: discord.Interaction, fruit: str):
|
|
311
|
+
await interaction.response.send_message(f'You chose: {fruit}')
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Hybrid Commands
|
|
315
|
+
|
|
316
|
+
Hybrid commands work as both slash commands and text commands:
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
from discord.ext import commands
|
|
320
|
+
|
|
321
|
+
bot = commands.Bot(command_prefix='!', intents=intents)
|
|
322
|
+
|
|
323
|
+
@bot.hybrid_command(name='ping', description='Check bot latency')
|
|
324
|
+
async def ping(ctx):
|
|
325
|
+
await ctx.send(f'Pong! Latency: {round(bot.latency * 1000)}ms')
|
|
326
|
+
|
|
327
|
+
@bot.event
|
|
328
|
+
async def on_ready():
|
|
329
|
+
await bot.tree.sync()
|
|
330
|
+
print(f'{bot.user} is ready!')
|
|
331
|
+
|
|
332
|
+
bot.run(os.getenv('DISCORD_TOKEN'))
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Embeds
|
|
336
|
+
|
|
337
|
+
Embeds allow you to send rich, formatted messages.
|
|
338
|
+
|
|
339
|
+
### Basic Embed
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
embed = discord.Embed(
|
|
343
|
+
title='Embed Title',
|
|
344
|
+
description='This is an embed description',
|
|
345
|
+
color=discord.Color.blue()
|
|
346
|
+
)
|
|
347
|
+
await ctx.send(embed=embed)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Advanced Embed
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
embed = discord.Embed(
|
|
354
|
+
title='Advanced Embed',
|
|
355
|
+
description='This is a detailed embed',
|
|
356
|
+
color=0x00ff00,
|
|
357
|
+
url='https://discordpy.readthedocs.io/',
|
|
358
|
+
timestamp=discord.utils.utcnow()
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
embed.set_author(
|
|
362
|
+
name='Author Name',
|
|
363
|
+
url='https://discordpy.readthedocs.io/',
|
|
364
|
+
icon_url='https://i.imgur.com/AfFp7pu.png'
|
|
365
|
+
)
|
|
366
|
+
embed.set_thumbnail(url='https://i.imgur.com/AfFp7pu.png')
|
|
367
|
+
embed.set_image(url='https://i.imgur.com/AfFp7pu.png')
|
|
368
|
+
|
|
369
|
+
embed.add_field(name='Field 1', value='Value 1', inline=True)
|
|
370
|
+
embed.add_field(name='Field 2', value='Value 2', inline=True)
|
|
371
|
+
embed.add_field(name='Field 3', value='Value 3', inline=False)
|
|
372
|
+
|
|
373
|
+
embed.set_footer(text='Footer text', icon_url='https://i.imgur.com/AfFp7pu.png')
|
|
374
|
+
|
|
375
|
+
await ctx.send(embed=embed)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Embed Colors
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
# Using Color class
|
|
382
|
+
embed.color = discord.Color.red()
|
|
383
|
+
embed.color = discord.Color.blue()
|
|
384
|
+
embed.color = discord.Color.green()
|
|
385
|
+
embed.color = discord.Color.gold()
|
|
386
|
+
|
|
387
|
+
# Using hex
|
|
388
|
+
embed.color = 0x00ff00
|
|
389
|
+
|
|
390
|
+
# Using RGB
|
|
391
|
+
embed.color = discord.Color.from_rgb(255, 0, 0)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Buttons
|
|
395
|
+
|
|
396
|
+
Buttons are interactive UI components.
|
|
397
|
+
|
|
398
|
+
### Basic Button
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
import discord
|
|
402
|
+
from discord.ui import Button, View
|
|
403
|
+
|
|
404
|
+
class MyView(View):
|
|
405
|
+
@discord.ui.button(label='Click me!', style=discord.ButtonStyle.primary)
|
|
406
|
+
async def button_callback(self, interaction: discord.Interaction, button: Button):
|
|
407
|
+
await interaction.response.send_message('Button clicked!', ephemeral=True)
|
|
408
|
+
|
|
409
|
+
@bot.command()
|
|
410
|
+
async def buttons(ctx):
|
|
411
|
+
view = MyView()
|
|
412
|
+
await ctx.send('Click a button:', view=view)
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Multiple Buttons
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
class MyView(View):
|
|
419
|
+
@discord.ui.button(label='Primary', style=discord.ButtonStyle.primary)
|
|
420
|
+
async def primary(self, interaction: discord.Interaction, button: Button):
|
|
421
|
+
await interaction.response.send_message('Primary clicked!', ephemeral=True)
|
|
422
|
+
|
|
423
|
+
@discord.ui.button(label='Secondary', style=discord.ButtonStyle.secondary)
|
|
424
|
+
async def secondary(self, interaction: discord.Interaction, button: Button):
|
|
425
|
+
await interaction.response.send_message('Secondary clicked!', ephemeral=True)
|
|
426
|
+
|
|
427
|
+
@discord.ui.button(label='Success', style=discord.ButtonStyle.success)
|
|
428
|
+
async def success(self, interaction: discord.Interaction, button: Button):
|
|
429
|
+
await interaction.response.send_message('Success clicked!', ephemeral=True)
|
|
430
|
+
|
|
431
|
+
@discord.ui.button(label='Danger', style=discord.ButtonStyle.danger)
|
|
432
|
+
async def danger(self, interaction: discord.Interaction, button: Button):
|
|
433
|
+
await interaction.response.send_message('Danger clicked!', ephemeral=True)
|
|
434
|
+
|
|
435
|
+
@bot.command()
|
|
436
|
+
async def buttons(ctx):
|
|
437
|
+
view = MyView()
|
|
438
|
+
await ctx.send('Choose a button:', view=view)
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Link Button
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
view = View()
|
|
445
|
+
button = Button(label='Visit Website', url='https://discordpy.readthedocs.io/')
|
|
446
|
+
view.add_item(button)
|
|
447
|
+
|
|
448
|
+
await ctx.send('Visit our docs:', view=view)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Disabled Button
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
@discord.ui.button(label='Disabled', style=discord.ButtonStyle.primary, disabled=True)
|
|
455
|
+
async def disabled_button(self, interaction: discord.Interaction, button: Button):
|
|
456
|
+
pass
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Button with Emoji
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
@discord.ui.button(label='Like', style=discord.ButtonStyle.primary, emoji='👍')
|
|
463
|
+
async def like_button(self, interaction: discord.Interaction, button: Button):
|
|
464
|
+
await interaction.response.send_message('Thanks!', ephemeral=True)
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Select Menus
|
|
468
|
+
|
|
469
|
+
Select menus (dropdowns) allow users to choose from multiple options.
|
|
470
|
+
|
|
471
|
+
### String Select Menu
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
import discord
|
|
475
|
+
from discord.ui import Select, View
|
|
476
|
+
|
|
477
|
+
class MyView(View):
|
|
478
|
+
@discord.ui.select(
|
|
479
|
+
placeholder='Choose an option...',
|
|
480
|
+
options=[
|
|
481
|
+
discord.SelectOption(label='Option 1', description='This is option 1', value='1'),
|
|
482
|
+
discord.SelectOption(label='Option 2', description='This is option 2', value='2'),
|
|
483
|
+
discord.SelectOption(label='Option 3', description='This is option 3', value='3'),
|
|
484
|
+
]
|
|
485
|
+
)
|
|
486
|
+
async def select_callback(self, interaction: discord.Interaction, select: Select):
|
|
487
|
+
await interaction.response.send_message(f'You selected: {select.values[0]}', ephemeral=True)
|
|
488
|
+
|
|
489
|
+
@bot.command()
|
|
490
|
+
async def dropdown(ctx):
|
|
491
|
+
view = MyView()
|
|
492
|
+
await ctx.send('Choose an option:', view=view)
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### User Select Menu
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
class MyView(View):
|
|
499
|
+
@discord.ui.select(cls=discord.ui.UserSelect, placeholder='Select a user')
|
|
500
|
+
async def user_select(self, interaction: discord.Interaction, select: discord.ui.UserSelect):
|
|
501
|
+
user = select.values[0]
|
|
502
|
+
await interaction.response.send_message(f'You selected: {user.mention}', ephemeral=True)
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Role Select Menu
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
class MyView(View):
|
|
509
|
+
@discord.ui.select(cls=discord.ui.RoleSelect, placeholder='Select a role')
|
|
510
|
+
async def role_select(self, interaction: discord.Interaction, select: discord.ui.RoleSelect):
|
|
511
|
+
role = select.values[0]
|
|
512
|
+
await interaction.response.send_message(f'You selected: {role.mention}', ephemeral=True)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Channel Select Menu
|
|
516
|
+
|
|
517
|
+
```python
|
|
518
|
+
class MyView(View):
|
|
519
|
+
@discord.ui.select(
|
|
520
|
+
cls=discord.ui.ChannelSelect,
|
|
521
|
+
placeholder='Select a channel',
|
|
522
|
+
channel_types=[discord.ChannelType.text, discord.ChannelType.voice]
|
|
523
|
+
)
|
|
524
|
+
async def channel_select(self, interaction: discord.Interaction, select: discord.ui.ChannelSelect):
|
|
525
|
+
channel = select.values[0]
|
|
526
|
+
await interaction.response.send_message(f'You selected: {channel.mention}', ephemeral=True)
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Modals
|
|
530
|
+
|
|
531
|
+
Modals are pop-up forms for user input.
|
|
532
|
+
|
|
533
|
+
### Basic Modal
|
|
534
|
+
|
|
535
|
+
```python
|
|
536
|
+
import discord
|
|
537
|
+
from discord.ui import Modal, TextInput
|
|
538
|
+
|
|
539
|
+
class FeedbackModal(Modal, title='Feedback Form'):
|
|
540
|
+
name = TextInput(label='Name', placeholder='Your name here...', required=True)
|
|
541
|
+
feedback = TextInput(
|
|
542
|
+
label='Feedback',
|
|
543
|
+
style=discord.TextStyle.paragraph,
|
|
544
|
+
placeholder='Tell us what you think...',
|
|
545
|
+
required=True,
|
|
546
|
+
max_length=1000
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
async def on_submit(self, interaction: discord.Interaction):
|
|
550
|
+
await interaction.response.send_message(
|
|
551
|
+
f'Thanks for your feedback, {self.name.value}!',
|
|
552
|
+
ephemeral=True
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
@bot.command()
|
|
556
|
+
async def feedback(ctx):
|
|
557
|
+
await ctx.send('Click the button to give feedback!')
|
|
558
|
+
|
|
559
|
+
# To show modal from button
|
|
560
|
+
class FeedbackView(discord.ui.View):
|
|
561
|
+
@discord.ui.button(label='Give Feedback', style=discord.ButtonStyle.primary)
|
|
562
|
+
async def feedback_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
563
|
+
await interaction.response.send_modal(FeedbackModal())
|
|
564
|
+
|
|
565
|
+
@bot.command()
|
|
566
|
+
async def feedback(ctx):
|
|
567
|
+
view = FeedbackView()
|
|
568
|
+
await ctx.send('Click to provide feedback:', view=view)
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Modal from Slash Command
|
|
572
|
+
|
|
573
|
+
```python
|
|
574
|
+
@tree.command(name='feedback', description='Provide feedback')
|
|
575
|
+
async def feedback_slash(interaction: discord.Interaction):
|
|
576
|
+
await interaction.response.send_modal(FeedbackModal())
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Context Menus
|
|
580
|
+
|
|
581
|
+
Context menus appear when right-clicking on users or messages.
|
|
582
|
+
|
|
583
|
+
### User Context Menu
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
@tree.context_menu(name='Get User Info')
|
|
587
|
+
async def user_info(interaction: discord.Interaction, user: discord.Member):
|
|
588
|
+
await interaction.response.send_message(
|
|
589
|
+
f'User: {user.name}\nID: {user.id}\nJoined: {user.joined_at}',
|
|
590
|
+
ephemeral=True
|
|
591
|
+
)
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Message Context Menu
|
|
595
|
+
|
|
596
|
+
```python
|
|
597
|
+
@tree.context_menu(name='Report Message')
|
|
598
|
+
async def report_message(interaction: discord.Interaction, message: discord.Message):
|
|
599
|
+
await interaction.response.send_message(
|
|
600
|
+
f'Reported message from {message.author.mention}',
|
|
601
|
+
ephemeral=True
|
|
602
|
+
)
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
## Events
|
|
606
|
+
|
|
607
|
+
### Common Events
|
|
608
|
+
|
|
609
|
+
```python
|
|
610
|
+
@bot.event
|
|
611
|
+
async def on_ready():
|
|
612
|
+
print(f'{bot.user} has connected to Discord!')
|
|
613
|
+
print(f'Bot ID: {bot.user.id}')
|
|
614
|
+
|
|
615
|
+
@bot.event
|
|
616
|
+
async def on_message(message):
|
|
617
|
+
if message.author == bot.user:
|
|
618
|
+
return
|
|
619
|
+
|
|
620
|
+
if 'hello' in message.content.lower():
|
|
621
|
+
await message.channel.send('Hello!')
|
|
622
|
+
|
|
623
|
+
# Important: process commands after custom on_message logic
|
|
624
|
+
await bot.process_commands(message)
|
|
625
|
+
|
|
626
|
+
@bot.event
|
|
627
|
+
async def on_member_join(member):
|
|
628
|
+
channel = member.guild.system_channel
|
|
629
|
+
if channel is not None:
|
|
630
|
+
await channel.send(f'Welcome {member.mention}!')
|
|
631
|
+
|
|
632
|
+
@bot.event
|
|
633
|
+
async def on_member_remove(member):
|
|
634
|
+
channel = member.guild.system_channel
|
|
635
|
+
if channel is not None:
|
|
636
|
+
await channel.send(f'{member.name} has left the server.')
|
|
637
|
+
|
|
638
|
+
@bot.event
|
|
639
|
+
async def on_message_delete(message):
|
|
640
|
+
print(f'Message deleted: {message.content}')
|
|
641
|
+
|
|
642
|
+
@bot.event
|
|
643
|
+
async def on_message_edit(before, after):
|
|
644
|
+
print(f'Message edited from "{before.content}" to "{after.content}"')
|
|
645
|
+
|
|
646
|
+
@bot.event
|
|
647
|
+
async def on_reaction_add(reaction, user):
|
|
648
|
+
print(f'{user.name} added {reaction.emoji}')
|
|
649
|
+
|
|
650
|
+
@bot.event
|
|
651
|
+
async def on_guild_join(guild):
|
|
652
|
+
print(f'Joined guild: {guild.name}')
|
|
653
|
+
|
|
654
|
+
@bot.event
|
|
655
|
+
async def on_error(event, *args, **kwargs):
|
|
656
|
+
print(f'Error in {event}')
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
## Cogs
|
|
660
|
+
|
|
661
|
+
Cogs organize commands and listeners into modular classes.
|
|
662
|
+
|
|
663
|
+
### Basic Cog
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
from discord.ext import commands
|
|
667
|
+
|
|
668
|
+
class MyCog(commands.Cog):
|
|
669
|
+
def __init__(self, bot):
|
|
670
|
+
self.bot = bot
|
|
671
|
+
|
|
672
|
+
@commands.command()
|
|
673
|
+
async def hello(self, ctx):
|
|
674
|
+
await ctx.send('Hello from cog!')
|
|
675
|
+
|
|
676
|
+
@commands.Cog.listener()
|
|
677
|
+
async def on_message(self, message):
|
|
678
|
+
if message.author == self.bot.user:
|
|
679
|
+
return
|
|
680
|
+
print(f'Message from {message.author}: {message.content}')
|
|
681
|
+
|
|
682
|
+
async def setup(bot):
|
|
683
|
+
await bot.add_cog(MyCog(bot))
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Loading Cogs
|
|
687
|
+
|
|
688
|
+
```python
|
|
689
|
+
import asyncio
|
|
690
|
+
|
|
691
|
+
async def load_extensions():
|
|
692
|
+
for filename in os.listdir('./cogs'):
|
|
693
|
+
if filename.endswith('.py'):
|
|
694
|
+
await bot.load_extension(f'cogs.{filename[:-3]}')
|
|
695
|
+
|
|
696
|
+
async def main():
|
|
697
|
+
async with bot:
|
|
698
|
+
await load_extensions()
|
|
699
|
+
await bot.start(os.getenv('DISCORD_TOKEN'))
|
|
700
|
+
|
|
701
|
+
asyncio.run(main())
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Cog with Slash Commands
|
|
705
|
+
|
|
706
|
+
```python
|
|
707
|
+
from discord import app_commands
|
|
708
|
+
|
|
709
|
+
class SlashCog(commands.Cog):
|
|
710
|
+
def __init__(self, bot):
|
|
711
|
+
self.bot = bot
|
|
712
|
+
|
|
713
|
+
@app_commands.command(name='ping', description='Ping command')
|
|
714
|
+
async def ping(self, interaction: discord.Interaction):
|
|
715
|
+
await interaction.response.send_message('Pong!')
|
|
716
|
+
|
|
717
|
+
@commands.Cog.listener()
|
|
718
|
+
async def on_ready(self):
|
|
719
|
+
print(f'{self.__class__.__name__} cog is ready!')
|
|
720
|
+
|
|
721
|
+
async def setup(bot):
|
|
722
|
+
await bot.add_cog(SlashCog(bot))
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
## Permissions
|
|
726
|
+
|
|
727
|
+
### Check User Permissions
|
|
728
|
+
|
|
729
|
+
```python
|
|
730
|
+
@bot.command()
|
|
731
|
+
@commands.has_permissions(administrator=True)
|
|
732
|
+
async def admin(ctx):
|
|
733
|
+
await ctx.send('You are an admin!')
|
|
734
|
+
|
|
735
|
+
@bot.command()
|
|
736
|
+
@commands.has_permissions(manage_messages=True)
|
|
737
|
+
async def purge(ctx, amount: int):
|
|
738
|
+
await ctx.channel.purge(limit=amount + 1)
|
|
739
|
+
await ctx.send(f'Deleted {amount} messages.', delete_after=5)
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### Check Bot Permissions
|
|
743
|
+
|
|
744
|
+
```python
|
|
745
|
+
@bot.command()
|
|
746
|
+
@commands.bot_has_permissions(manage_roles=True)
|
|
747
|
+
async def mute(ctx, member: discord.Member):
|
|
748
|
+
# Bot can manage roles
|
|
749
|
+
await ctx.send(f'{member.mention} has been muted.')
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
### Custom Permission Checks
|
|
753
|
+
|
|
754
|
+
```python
|
|
755
|
+
def is_owner():
|
|
756
|
+
async def predicate(ctx):
|
|
757
|
+
return ctx.author.id == 123456789 # Your user ID
|
|
758
|
+
return commands.check(predicate)
|
|
759
|
+
|
|
760
|
+
@bot.command()
|
|
761
|
+
@is_owner()
|
|
762
|
+
async def secret(ctx):
|
|
763
|
+
await ctx.send('Secret command for owner only!')
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Slash Command Permissions
|
|
767
|
+
|
|
768
|
+
```python
|
|
769
|
+
@tree.command(name='admin', description='Admin only command')
|
|
770
|
+
@app_commands.default_permissions(administrator=True)
|
|
771
|
+
async def admin_slash(interaction: discord.Interaction):
|
|
772
|
+
await interaction.response.send_message('Admin command!', ephemeral=True)
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
## Error Handling
|
|
776
|
+
|
|
777
|
+
### Command Error Handling
|
|
778
|
+
|
|
779
|
+
```python
|
|
780
|
+
@bot.event
|
|
781
|
+
async def on_command_error(ctx, error):
|
|
782
|
+
if isinstance(error, commands.MissingRequiredArgument):
|
|
783
|
+
await ctx.send('Missing required argument!')
|
|
784
|
+
elif isinstance(error, commands.MissingPermissions):
|
|
785
|
+
await ctx.send("You don't have permission to use this command!")
|
|
786
|
+
elif isinstance(error, commands.BotMissingPermissions):
|
|
787
|
+
await ctx.send("I don't have permission to do that!")
|
|
788
|
+
elif isinstance(error, commands.CommandNotFound):
|
|
789
|
+
pass # Ignore command not found
|
|
790
|
+
else:
|
|
791
|
+
print(f'Error: {error}')
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### Local Error Handler
|
|
795
|
+
|
|
796
|
+
```python
|
|
797
|
+
@bot.command()
|
|
798
|
+
async def divide(ctx, a: int, b: int):
|
|
799
|
+
result = a / b
|
|
800
|
+
await ctx.send(f'{a} / {b} = {result}')
|
|
801
|
+
|
|
802
|
+
@divide.error
|
|
803
|
+
async def divide_error(ctx, error):
|
|
804
|
+
if isinstance(error, commands.BadArgument):
|
|
805
|
+
await ctx.send('Please provide valid numbers!')
|
|
806
|
+
elif isinstance(error, ZeroDivisionError):
|
|
807
|
+
await ctx.send("Cannot divide by zero!")
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
### Interaction Error Handling
|
|
811
|
+
|
|
812
|
+
```python
|
|
813
|
+
@tree.error
|
|
814
|
+
async def on_app_command_error(interaction: discord.Interaction, error: app_commands.AppCommandError):
|
|
815
|
+
if isinstance(error, app_commands.CommandOnCooldown):
|
|
816
|
+
await interaction.response.send_message(
|
|
817
|
+
f'Command is on cooldown. Try again in {error.retry_after:.2f}s',
|
|
818
|
+
ephemeral=True
|
|
819
|
+
)
|
|
820
|
+
elif isinstance(error, app_commands.MissingPermissions):
|
|
821
|
+
await interaction.response.send_message(
|
|
822
|
+
"You don't have permission to use this command!",
|
|
823
|
+
ephemeral=True
|
|
824
|
+
)
|
|
825
|
+
else:
|
|
826
|
+
await interaction.response.send_message('An error occurred!', ephemeral=True)
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
## Cooldowns
|
|
830
|
+
|
|
831
|
+
### Command Cooldown
|
|
832
|
+
|
|
833
|
+
```python
|
|
834
|
+
from discord.ext import commands
|
|
835
|
+
|
|
836
|
+
@bot.command()
|
|
837
|
+
@commands.cooldown(1, 60, commands.BucketType.user) # 1 use per 60 seconds per user
|
|
838
|
+
async def daily(ctx):
|
|
839
|
+
await ctx.send('You claimed your daily reward!')
|
|
840
|
+
|
|
841
|
+
@daily.error
|
|
842
|
+
async def daily_error(ctx, error):
|
|
843
|
+
if isinstance(error, commands.CommandOnCooldown):
|
|
844
|
+
await ctx.send(f'Try again in {error.retry_after:.2f} seconds.')
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### Slash Command Cooldown
|
|
848
|
+
|
|
849
|
+
```python
|
|
850
|
+
@tree.command(name='daily', description='Claim daily reward')
|
|
851
|
+
@app_commands.checks.cooldown(1, 60, key=lambda i: i.user.id)
|
|
852
|
+
async def daily_slash(interaction: discord.Interaction):
|
|
853
|
+
await interaction.response.send_message('Daily reward claimed!', ephemeral=True)
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
## Fetching Data
|
|
857
|
+
|
|
858
|
+
### Fetch User
|
|
859
|
+
|
|
860
|
+
```python
|
|
861
|
+
user = await bot.fetch_user(123456789)
|
|
862
|
+
print(user.name)
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Fetch Member
|
|
866
|
+
|
|
867
|
+
```python
|
|
868
|
+
member = await ctx.guild.fetch_member(123456789)
|
|
869
|
+
print(member.display_name)
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Fetch Channel
|
|
873
|
+
|
|
874
|
+
```python
|
|
875
|
+
channel = await bot.fetch_channel(123456789)
|
|
876
|
+
await channel.send('Hello!')
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### Fetch Guild
|
|
880
|
+
|
|
881
|
+
```python
|
|
882
|
+
guild = await bot.fetch_guild(123456789)
|
|
883
|
+
print(guild.name)
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### Fetch Messages
|
|
887
|
+
|
|
888
|
+
```python
|
|
889
|
+
# Fetch last 10 messages
|
|
890
|
+
messages = [message async for message in channel.history(limit=10)]
|
|
891
|
+
|
|
892
|
+
# Fetch specific message
|
|
893
|
+
message = await channel.fetch_message(123456789)
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
## Sending Messages
|
|
897
|
+
|
|
898
|
+
### Basic Send
|
|
899
|
+
|
|
900
|
+
```python
|
|
901
|
+
await ctx.send('Hello!')
|
|
902
|
+
await channel.send('Hello!')
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
### Send with Embed
|
|
906
|
+
|
|
907
|
+
```python
|
|
908
|
+
embed = discord.Embed(title='Title', description='Description')
|
|
909
|
+
await ctx.send(embed=embed)
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Send with Components
|
|
913
|
+
|
|
914
|
+
```python
|
|
915
|
+
view = MyView()
|
|
916
|
+
await ctx.send('Click a button:', view=view)
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
### Send Ephemeral (Slash Commands)
|
|
920
|
+
|
|
921
|
+
```python
|
|
922
|
+
await interaction.response.send_message('Secret message!', ephemeral=True)
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
### Send File
|
|
926
|
+
|
|
927
|
+
```python
|
|
928
|
+
file = discord.File('path/to/file.png')
|
|
929
|
+
await ctx.send(file=file)
|
|
930
|
+
|
|
931
|
+
# With embed
|
|
932
|
+
embed = discord.Embed(title='Image')
|
|
933
|
+
embed.set_image(url='attachment://file.png')
|
|
934
|
+
await ctx.send(embed=embed, file=file)
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
### Edit Message
|
|
938
|
+
|
|
939
|
+
```python
|
|
940
|
+
message = await ctx.send('Original message')
|
|
941
|
+
await message.edit(content='Edited message')
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
### Delete Message
|
|
945
|
+
|
|
946
|
+
```python
|
|
947
|
+
message = await ctx.send('This will be deleted')
|
|
948
|
+
await message.delete()
|
|
949
|
+
|
|
950
|
+
# Delete after delay
|
|
951
|
+
await ctx.send('Deleted in 5s', delete_after=5)
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
## Reactions
|
|
955
|
+
|
|
956
|
+
### Add Reaction
|
|
957
|
+
|
|
958
|
+
```python
|
|
959
|
+
message = await ctx.send('React to this!')
|
|
960
|
+
await message.add_reaction('👍')
|
|
961
|
+
await message.add_reaction('👎')
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### Remove Reaction
|
|
965
|
+
|
|
966
|
+
```python
|
|
967
|
+
await message.remove_reaction('👍', ctx.author)
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Clear Reactions
|
|
971
|
+
|
|
972
|
+
```python
|
|
973
|
+
await message.clear_reactions()
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
### Wait for Reaction
|
|
977
|
+
|
|
978
|
+
```python
|
|
979
|
+
message = await ctx.send('React with 👍')
|
|
980
|
+
await message.add_reaction('👍')
|
|
981
|
+
|
|
982
|
+
def check(reaction, user):
|
|
983
|
+
return user == ctx.author and str(reaction.emoji) == '👍'
|
|
984
|
+
|
|
985
|
+
try:
|
|
986
|
+
reaction, user = await bot.wait_for('reaction_add', timeout=60.0, check=check)
|
|
987
|
+
await ctx.send('Thanks for reacting!')
|
|
988
|
+
except asyncio.TimeoutError:
|
|
989
|
+
await ctx.send('You took too long!')
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
## Tasks and Loops
|
|
993
|
+
|
|
994
|
+
### Basic Loop
|
|
995
|
+
|
|
996
|
+
```python
|
|
997
|
+
from discord.ext import tasks
|
|
998
|
+
|
|
999
|
+
@tasks.loop(seconds=60)
|
|
1000
|
+
async def my_task():
|
|
1001
|
+
channel = bot.get_channel(123456789)
|
|
1002
|
+
await channel.send('This runs every minute!')
|
|
1003
|
+
|
|
1004
|
+
@my_task.before_loop
|
|
1005
|
+
async def before_my_task():
|
|
1006
|
+
await bot.wait_until_ready()
|
|
1007
|
+
|
|
1008
|
+
my_task.start()
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
### Loop with Time
|
|
1012
|
+
|
|
1013
|
+
```python
|
|
1014
|
+
import datetime
|
|
1015
|
+
|
|
1016
|
+
@tasks.loop(time=datetime.time(hour=12, minute=0))
|
|
1017
|
+
async def daily_task():
|
|
1018
|
+
channel = bot.get_channel(123456789)
|
|
1019
|
+
await channel.send('Daily message at noon!')
|
|
1020
|
+
|
|
1021
|
+
daily_task.start()
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
### Cancel Loop
|
|
1025
|
+
|
|
1026
|
+
```python
|
|
1027
|
+
my_task.stop()
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
## Voice
|
|
1031
|
+
|
|
1032
|
+
### Join Voice Channel
|
|
1033
|
+
|
|
1034
|
+
```python
|
|
1035
|
+
@bot.command()
|
|
1036
|
+
async def join(ctx):
|
|
1037
|
+
if ctx.author.voice:
|
|
1038
|
+
channel = ctx.author.voice.channel
|
|
1039
|
+
await channel.connect()
|
|
1040
|
+
else:
|
|
1041
|
+
await ctx.send('You are not in a voice channel!')
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### Leave Voice Channel
|
|
1045
|
+
|
|
1046
|
+
```python
|
|
1047
|
+
@bot.command()
|
|
1048
|
+
async def leave(ctx):
|
|
1049
|
+
if ctx.voice_client:
|
|
1050
|
+
await ctx.voice_client.disconnect()
|
|
1051
|
+
else:
|
|
1052
|
+
await ctx.send('I am not in a voice channel!')
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
### Play Audio
|
|
1056
|
+
|
|
1057
|
+
```python
|
|
1058
|
+
import discord
|
|
1059
|
+
from discord import FFmpegPCMAudio
|
|
1060
|
+
|
|
1061
|
+
@bot.command()
|
|
1062
|
+
async def play(ctx):
|
|
1063
|
+
if ctx.voice_client:
|
|
1064
|
+
source = FFmpegPCMAudio('audio.mp3')
|
|
1065
|
+
ctx.voice_client.play(source)
|
|
1066
|
+
else:
|
|
1067
|
+
await ctx.send('Bot is not in a voice channel!')
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
## Advanced Features
|
|
1071
|
+
|
|
1072
|
+
### Paginator
|
|
1073
|
+
|
|
1074
|
+
```python
|
|
1075
|
+
from discord.ext import menus
|
|
1076
|
+
|
|
1077
|
+
class MySource(menus.ListPageSource):
|
|
1078
|
+
def __init__(self, data):
|
|
1079
|
+
super().__init__(data, per_page=10)
|
|
1080
|
+
|
|
1081
|
+
async def format_page(self, menu, entries):
|
|
1082
|
+
offset = menu.current_page * self.per_page
|
|
1083
|
+
return '\n'.join(f'{i}. {v}' for i, v in enumerate(entries, start=offset))
|
|
1084
|
+
|
|
1085
|
+
@bot.command()
|
|
1086
|
+
async def pages(ctx):
|
|
1087
|
+
data = [f'Item {i}' for i in range(1, 100)]
|
|
1088
|
+
pages = menus.MenuPages(source=MySource(data))
|
|
1089
|
+
await pages.start(ctx)
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
### Wait For Message
|
|
1093
|
+
|
|
1094
|
+
```python
|
|
1095
|
+
@bot.command()
|
|
1096
|
+
async def ask(ctx):
|
|
1097
|
+
await ctx.send('What is your name?')
|
|
1098
|
+
|
|
1099
|
+
def check(m):
|
|
1100
|
+
return m.author == ctx.author and m.channel == ctx.channel
|
|
1101
|
+
|
|
1102
|
+
try:
|
|
1103
|
+
msg = await bot.wait_for('message', check=check, timeout=30.0)
|
|
1104
|
+
await ctx.send(f'Hello {msg.content}!')
|
|
1105
|
+
except asyncio.TimeoutError:
|
|
1106
|
+
await ctx.send('You took too long!')
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
### Webhooks
|
|
1110
|
+
|
|
1111
|
+
```python
|
|
1112
|
+
# Create webhook
|
|
1113
|
+
webhook = await channel.create_webhook(name='My Webhook')
|
|
1114
|
+
|
|
1115
|
+
# Send via webhook
|
|
1116
|
+
await webhook.send('Message from webhook!', username='Custom Name')
|
|
1117
|
+
|
|
1118
|
+
# Get webhooks
|
|
1119
|
+
webhooks = await channel.webhooks()
|
|
1120
|
+
for webhook in webhooks:
|
|
1121
|
+
print(webhook.name)
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
## Useful Links
|
|
1125
|
+
|
|
1126
|
+
- Documentation: https://discordpy.readthedocs.io/en/stable/
|
|
1127
|
+
- API Reference: https://discordpy.readthedocs.io/en/stable/api.html
|
|
1128
|
+
- Discord API Docs: https://discord.com/developers/docs
|
|
1129
|
+
- Developer Portal: https://discord.com/developers/applications
|
|
1130
|
+
- GitHub: https://github.com/Rapptz/discord.py
|