10 |
11 | Welcome! The purpose of this guide is to make your journey with `disnake` easier, whether you're an experienced
12 | developer just getting into coding Discord bots, or an advanced bot developer who has decided to proceed with `disnake`
13 | as their library of choice.
14 |
15 | The concept we will be going over, include:
16 |
17 | - How to get started on working with bots;
18 | - How to create and organize commands, using cogs/extensions;
19 | - Working with databases (such as [`sqlite`][sqlite-docs] and [`mongodb (motor)`][motor-docs]);
20 | - Using the [`AutoShardedClient`](https://disnake.readthedocs.io/en/stable/api.html#disnake.AutoShardedClient) class
21 | to shard your bot;
22 | - A plethora of examples with popular topics along with in-depth explanation, and much more!
23 |
24 | [sqlite-docs]: https://docs.python.org/3/library/sqlite3.html
25 | [motor-docs]: https://motor.readthedocs.io/en/stable/
26 |
27 | This guide will showcase the various features and events that our library has, while giving you an idea of how these
28 | functions work together as well as how the syntax looks in production.
29 |
30 | ## Development
31 |
32 | For more info on development/contributions, see [CONTRIBUTING.md](./.github/CONTRIBUTING.md).
33 |
34 |
35 |
44 |
45 |
--------------------------------------------------------------------------------
/code-samples/getting-started/creating-commands/main.py:
--------------------------------------------------------------------------------
1 | import disnake
2 | from disnake.ext import commands
3 |
4 | import os
5 | from dotenv import load_dotenv
6 |
7 | load_dotenv()
8 |
9 | bot = commands.Bot(test_guilds=[1234, 5678])
10 |
11 |
12 | @bot.event
13 | async def on_ready():
14 | print("The bot is ready!")
15 |
16 |
17 | @bot.slash_command()
18 | async def ping(inter):
19 | await inter.response.send_message("Pong!")
20 |
21 |
22 | @bot.slash_command()
23 | async def server(inter):
24 | await inter.response.send_message(
25 | f"Server name: {inter.guild.name}\nTotal members: {inter.guild.member_count}"
26 | )
27 |
28 |
29 | @bot.slash_command()
30 | async def user(inter):
31 | await inter.response.send_message(f"Your tag: {inter.author}\nYour ID: {inter.author.id}")
32 |
33 |
34 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"]
35 | bot.run("YOUR_BOT_TOKEN")
36 |
--------------------------------------------------------------------------------
/code-samples/getting-started/creating-commands/secrets.env:
--------------------------------------------------------------------------------
1 | # The token used here has been mentioned as an example.
2 | YOUR_BOT_TOKEN=OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg
3 |
--------------------------------------------------------------------------------
/code-samples/getting-started/initial-files/main.py:
--------------------------------------------------------------------------------
1 | import disnake
2 | from disnake.ext import commands
3 |
4 | import os
5 | from dotenv import load_dotenv
6 |
7 | load_dotenv()
8 |
9 | bot = commands.Bot()
10 |
11 |
12 | @bot.event
13 | async def on_ready():
14 | print("The bot is ready!")
15 |
16 |
17 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"]
18 | bot.run("YOUR_BOT_TOKEN")
19 |
--------------------------------------------------------------------------------
/code-samples/getting-started/initial-files/secrets.env:
--------------------------------------------------------------------------------
1 | # The token used here has been mentioned as an example.
2 | YOUR_BOT_TOKEN=OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg
3 |
--------------------------------------------------------------------------------
/code-samples/interactions/modals/create-tag-low.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | import disnake
4 | from disnake import TextInputStyle
5 | from disnake.ext import commands
6 |
7 | bot = commands.Bot()
8 |
9 |
10 | @bot.slash_command()
11 | async def tags_low(inter: disnake.AppCmdInter):
12 | """Sends a Modal to create a tag but with a low-level implementation."""
13 | await inter.response.send_modal(
14 | title="Create Tag",
15 | custom_id="create_tag_low",
16 | components=[
17 | disnake.ui.TextInput(
18 | label="Name",
19 | placeholder="Bar Tag",
20 | custom_id="name",
21 | style=TextInputStyle.short,
22 | max_length=50,
23 | ),
24 | disnake.ui.TextInput(
25 | label="Description",
26 | placeholder="Lorem ipsum dolor sit amet.",
27 | custom_id="description",
28 | style=TextInputStyle.paragraph,
29 | ),
30 | ],
31 | )
32 | try:
33 | modal_inter: disnake.ModalInteraction = await bot.wait_for(
34 | "modal_submit",
35 | check=lambda i: i.custom_id == "create_tag_low" and i.author.id == inter.author.id,
36 | timeout=300,
37 | )
38 | except asyncio.TimeoutError:
39 | # user didn't submit the modal, so a timeout error is raised
40 | # we don't have any action to take, so just return early
41 | return
42 |
43 | embed = disnake.Embed(title="Tag Creation")
44 | for key, value in modal_inter.text_values.items():
45 | embed.add_field(name=key.capitalize(), value=value[:1024], inline=False)
46 | await modal_inter.response.send_message(embed=embed)
47 |
48 |
49 | bot.run("YOUR_BOT_TOKEN")
50 |
--------------------------------------------------------------------------------
/code-samples/interactions/modals/create-tag.py:
--------------------------------------------------------------------------------
1 | import disnake
2 | from disnake.ext import commands
3 | from disnake import TextInputStyle
4 |
5 |
6 | bot = commands.Bot(command_prefix="!")
7 |
8 |
9 | class MyModal(disnake.ui.Modal):
10 | def __init__(self):
11 | # The details of the modal, and its components
12 | components = [
13 | disnake.ui.TextInput(
14 | label="Name",
15 | placeholder="Foo Tag",
16 | custom_id="name",
17 | style=TextInputStyle.short,
18 | max_length=50,
19 | ),
20 | disnake.ui.TextInput(
21 | label="Description",
22 | placeholder="Lorem ipsum dolor sit amet.",
23 | custom_id="description",
24 | style=TextInputStyle.paragraph,
25 | ),
26 | ]
27 | super().__init__(title="Create Tag", components=components)
28 |
29 | # The callback received when the user input is completed.
30 | async def callback(self, inter: disnake.ModalInteraction):
31 | embed = disnake.Embed(title="Tag Creation")
32 | for key, value in inter.text_values.items():
33 | embed.add_field(
34 | name=key.capitalize(),
35 | value=value[:1024],
36 | inline=False,
37 | )
38 | await inter.response.send_message(embed=embed)
39 |
40 |
41 | @bot.slash_command()
42 | async def tags(inter: disnake.AppCmdInter):
43 | """Sends a Modal to create a tag."""
44 | await inter.response.send_modal(modal=MyModal())
45 |
46 |
47 | bot.run("YOUR_BOT_TOKEN")
48 |
--------------------------------------------------------------------------------
/code-samples/popular-topics/embeds/dict-embed.py:
--------------------------------------------------------------------------------
1 | import disnake
2 | from disnake.ext import commands
3 |
4 | import datetime
5 |
6 | bot = commands.Bot(command_prefix="!", test_guilds=[123456789])
7 |
8 |
9 | @bot.event
10 | async def on_ready():
11 | print("The bot is ready!")
12 |
13 |
14 | @bot.command()
15 | async def dictembed(ctx):
16 |
17 | embed_dict = {
18 | "title": "Embed Title",
19 | "description": "Embed Description",
20 | "color": 0xFEE75C,
21 | "timestamp": datetime.datetime.now().isoformat(),
22 | "author": {
23 | "name": "Embed Author",
24 | "url": "https://disnake.dev/",
25 | "icon_url": "https://disnake.dev/assets/disnake-logo.png",
26 | },
27 | "thumbnail": {"url": "https://disnake.dev/assets/disnake-logo.png"},
28 | "fields": [
29 | {"name": "Regular Title", "value": "Regular Value", "inline": "false"},
30 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"},
31 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"},
32 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"},
33 | ],
34 | "image": {"url": "https://disnake.dev/assets/disnake-banner-thin.png"},
35 | "footer": {
36 | "text": "Embed Footer",
37 | "icon_url": "https://disnake.dev/assets/disnake-logo.png",
38 | },
39 | }
40 |
41 | await ctx.send(embed=disnake.Embed.from_dict(embed_dict))
42 |
43 |
44 | bot.run("YOUR_BOT_TOKEN")
45 |
--------------------------------------------------------------------------------
/code-samples/popular-topics/embeds/full-embed.py:
--------------------------------------------------------------------------------
1 | import disnake
2 | from disnake.ext import commands
3 |
4 | import datetime
5 |
6 | bot = commands.Bot(command_prefix="!", test_guilds=[123456789])
7 |
8 |
9 | @bot.event
10 | async def on_ready():
11 | print("The bot is ready!")
12 |
13 |
14 | @bot.command()
15 | async def fullembed(ctx):
16 |
17 | embed = disnake.Embed(
18 | title="Embed Title",
19 | description="Embed Description",
20 | color=disnake.Colour.yellow(),
21 | url="https://disnake.dev/",
22 | timestamp=datetime.datetime.now(),
23 | )
24 |
25 | embed.set_author(
26 | name="Embed Author",
27 | url="https://disnake.dev/",
28 | icon_url="https://disnake.dev/assets/disnake-logo.png",
29 | )
30 |
31 | embed.set_thumbnail(url="https://disnake.dev/assets/disnake-logo.png")
32 |
33 | embed.set_image(url="https://disnake.dev/assets/disnake-banner-thin.png")
34 |
35 | embed.add_field(name="Regular Title", value="Regular Value", inline=False)
36 |
37 | embed.add_field(name="Inline Title", value="Inline Value", inline=True)
38 | embed.add_field(name="Inline Title", value="Inline Value", inline=True)
39 | embed.add_field(name="Inline Title", value="Inline Value", inline=True)
40 |
41 | embed.set_footer(
42 | text="Embed Footer",
43 | icon_url="https://disnake.dev/assets/disnake-logo.png",
44 | )
45 |
46 | await ctx.send(embed=embed)
47 |
48 |
49 | bot.run("YOUR_BOT_TOKEN")
50 |
--------------------------------------------------------------------------------
/guide/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/guide/docs/contributing.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | hide_sidebar: true
3 | ---
4 |
5 | # Contributing
6 |
7 | First off, thanks for taking the time to contribute. This guide uses [Docusaurus](https://docusaurus.io/) for building the guide itself, and [`pre-commit`](https://pre-commit.com/) for various formatting/cleanup tasks, like formatting Python codeblocks. Make sure to install their respective CLIs, as they're utilized by our `npm` scripts.
8 |
9 | ## Prerequisites
10 |
11 | - node v16 or newer
12 | - pre-commit
13 |
14 | ## Local development
15 |
16 | Clone the repo to your desired folder, and `cd` into it.
17 |
18 | ```
19 | git clone https://github.com/DisnakeDev/guide.git
20 | cd guide/guide
21 | ```
22 |
23 | Further, install the dependencies using `npm install`.
24 |
25 | ### Starting the server
26 |
27 | To build and serve a preview of your site locally, run the following commands while in root:
28 |
29 | ```
30 | cd guide
31 | npm run start
32 | ```
33 |
34 | This will serve the site on `http://localhost:3000/`. To specify the port of the devserver, you can use the `--port` option.
35 |
36 | ```
37 | npm run start -- --port=8000
38 | ```
39 |
40 | ## Linting
41 |
42 | Linting your edits/additions is critical to ensure the consistency of the material with the rest of the guide.
43 |
44 | ## Using components
45 |
46 | ### ``
47 |
48 | ### ``
49 |
50 | ### ``
51 |
52 | ## Commit convention
53 |
--------------------------------------------------------------------------------
/guide/docs/faq/administrative.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | hide_table_of_contents: true
3 | ---
4 |
5 | # Administrative
6 |
7 |
8 |
--------------------------------------------------------------------------------
/guide/docs/faq/coroutines.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: A list of FAQs regarding how coroutines and async functions work.
3 | keywords: [disnake, bot, guide, tutorial, python, coroutines]
4 | ---
5 |
6 | # Coroutines
7 |
8 | Questions regarding coroutines and asyncio belong here.
9 |
10 | :::note
11 |
12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future.
13 |
14 | :::
15 |
16 | ### What is a coroutine?
17 |
18 | A [coroutine](https://docs.python.org/3/library/asyncio-task.html#coroutine) is a function that must be invoked with `await` or `yield from`. When Python encounters an `await` it stops the function's execution at that point and works on other things until it comes back to that point and finishes off its work. This allows for your program to be doing multiple things at the same time without using threads or complicated multiprocessing.
19 |
20 | **If you forget to await a coroutine then the coroutine will not run. Never forget to await a coroutine.**
21 |
22 | ### Where can I use `await`?
23 |
24 | You can only use `await` inside `async def` functions and nowhere else.
25 |
26 | ### What does "blocking" mean?
27 |
28 | In asynchronous programming a blocking call is essentially all the parts of the function that are not `await`. Do not despair however, because not all forms of blocking are bad! Using blocking calls is inevitable, but you must work to make sure that you don't excessively block functions. Remember, if you block for too long then your bot will freeze since it has not stopped the function's execution at that point to do other things.
29 |
30 | If logging is enabled, this library will attempt to warn you that blocking is occurring with the message: `Heartbeat blocked for more than N seconds.` See [Setting Up Logging](https://docs.disnake.dev/en/stable/logging.html#logging-setup) for details on enabling logging.
31 |
32 | A common source of blocking for too long is something like [`time.sleep`](https://docs.python.org/3/library/time.html#time.sleep). Don't do that. Use [`asyncio.sleep`](https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep)
33 | instead. Similar to this example:
34 |
35 | ```python
36 | # Bad
37 | time.sleep(10)
38 |
39 | # Good
40 | await asyncio.sleep(10)
41 | ```
42 |
43 | Another common source of blocking for too long is using HTTP requests with the famous module [Requests: HTTP for Humans™](https://docs.python-requests.org/en/latest/). While [Requests: HTTP for Humans™](https://docs.python-requests.org/en/latest/) is an amazing module for non-asynchronous programming, it is not a good choice for [`asyncio`](https://docs.python.org/3/library/asyncio.html#module-asyncio) because certain requests can block the event loop too long. Instead, use the [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html) library which is installed on the side with this library.
44 |
45 | Consider the following example:
46 |
47 | ```python
48 | # Bad
49 | r = requests.get("http://aws.random.cat/meow")
50 | if r.status_code == 200:
51 | json = r.json()
52 | await channel.send(json["file"])
53 |
54 | # Good
55 | async with aiohttp.ClientSession() as session:
56 | async with session.get("http://aws.random.cat/meow") as r:
57 | if r.status == 200:
58 | json = await r.json()
59 | await channel.send(json["file"])
60 | ```
61 |
--------------------------------------------------------------------------------
/guide/docs/faq/extensions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: A list of FAQs regarding the 'commands', 'tasks' and 'ui' extensions.
3 | keywords: [disnake, bot, guide, tutorial, python, extensions]
4 | ---
5 |
6 | # Extensions
7 |
8 | Questions regarding `disnake.ext.commands`, `disnake.ext.tasks` and `disnake.ui` belong here.
9 |
10 | :::note
11 |
12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future.
13 |
14 | :::
15 |
16 | ### Why does `on_message` make my commands stop working?
17 |
18 | Overriding the default provided `on_message` forbids any extra commands from running. To fix this, add a
19 | `bot.process_commands(message)` line at the end of your `on_message`. For example:
20 |
21 | ```python
22 | @bot.event
23 | async def on_message(message):
24 | # Do some extra stuff here
25 |
26 | await bot.process_commands(message)
27 | ```
28 |
29 | Alternatively, you can place your `on_message` logic into a **listener**. In this setup, you should not
30 | manually call `bot.process_commands()`. This also allows you to do multiple things asynchronously in response
31 | to a message. Example:
32 |
33 | ```python
34 | @bot.listen("on_message")
35 | async def whatever_you_want_to_call_it(message):
36 | await do_stuff_here()
37 | # Do not process commands here
38 | ```
39 |
40 | ### Why do my arguments require quotes?
41 |
42 | In a simple command defined as:
43 |
44 | ```python
45 | @bot.command()
46 | async def echo(ctx, message: str):
47 | await ctx.send(message)
48 | ```
49 |
50 | Calling it via `?echo a b c` will only fetch the first argument and disregard the rest. To fix this you should either call
51 | it via `?echo "a b c"` or change the signature to have "consume rest" behaviour. Example:
52 |
53 | ```python
54 | @bot.command()
55 | async def echo(ctx, *, message: str):
56 | await ctx.send(message)
57 | ```
58 |
59 | This will allow you to use `?echo a b c` without needing the quotes.
60 |
61 | ### How do I get the original `message`?
62 |
63 | The contains an attribute, message to get the original
64 | message.
65 |
66 | Example:
67 |
68 | ```python
69 | @bot.command()
70 | async def length(ctx):
71 | await ctx.send(f"Your message is {len(ctx.message.content)} characters long.")
72 | ```
73 |
74 | ### How do I make a subcommand?
75 |
76 | Use the group() decorator. This will transform the callback into a which will allow you to add commands into
77 | the group operating as "subcommands". These groups can be arbitrarily nested as well.
78 |
79 | Example:
80 |
81 | ```python
82 | @bot.group()
83 | async def git(ctx):
84 | if ctx.invoked_subcommand is None:
85 | await ctx.send("Invalid git command passed...")
86 |
87 |
88 | @git.command()
89 | async def push(ctx, remote: str, branch: str):
90 | await ctx.send(f"Pushing to {remote} {branch}.")
91 | ```
92 |
93 | This could then be used as `?git push origin master`.
94 |
95 | ### Which components can I use with modals?
96 |
97 | The only component that can currently be used with modals is TextInput. The API does not have integration for any other inputs as of now.
98 |
99 | ### Do modals support autocomplete?
100 |
101 | Modals (or text inputs) do not support autocomplete. This is a Discord limitation.
102 |
--------------------------------------------------------------------------------
/guide/docs/faq/general.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: A list of general FAQs regarding library usage.
3 | keywords: [disnake, bot, guide, tutorial, python, general]
4 | ---
5 |
6 | # General
7 |
8 | General questions regarding library usage belong here.
9 |
10 | :::note
11 |
12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future.
13 |
14 | :::
15 |
16 | ### Where can I find usage examples?
17 |
18 | Example code can be found in the [examples folder](https://github.com/DisnakeDev/disnake/tree/master/examples) in the repository.
19 |
20 | ### How do I set the "Playing" status?
21 |
22 | The `activity` keyword argument may be passed in the commands.Bot constructor or Bot.change_presence, given an object.
23 |
24 | The constructor may be used for static activities, while Bot.change_presence may be used to update the activity at runtime.
25 |
26 | :::warning
27 |
28 | It is highly discouraged to use Bot.change_presence or API calls in as this event may be called many times while running, not just once. There is a high chance of disconnecting if presences are changed right after connecting.
29 |
30 | :::
31 |
32 | The status type (playing, listening, streaming, watching) can be set using the enum.
33 | For memory optimisation purposes, some activities are offered in slimmed-down versions:
34 |
35 | -
36 | -
37 |
38 | Putting both of these pieces of info together, you get the following:
39 |
40 | ```python
41 | bot = commands.Bot(..., activity=disnake.Game(name="My game"))
42 |
43 | # Or, for watching:
44 | activity = disnake.Activity(
45 | name="My activity",
46 | type=disnake.ActivityType.watching,
47 | )
48 | bot = commands.Bot(..., activity=activity)
49 | ```
50 |
51 | ### How do I send a message to a specific channel?
52 |
53 | You must get or fetch the channel directly and then call the appropriate method. Example:
54 |
55 | ```python
56 | # If Bot.get_channel returns None because the channel is not in the Bot's cache,
57 | # make an API call to fetch the channel
58 | channel = bot.get_channel(12324234183172) or await bot.fetch_channel(12324234183172)
59 |
60 | await channel.send("hello")
61 | ```
62 |
63 | ### How do I send a DM?
64 |
65 | Get the or object and call . For example:
66 |
67 | ```python
68 | user = bot.get_user(381870129706958858)
69 | await user.send("👀")
70 | ```
71 |
72 | If you are responding to an event, such as , you already have the object via :
73 |
74 | ```python
75 | await message.author.send("👋")
76 | ```
77 |
78 | ### How do I get the ID of a sent message?
79 |
80 | The method returns the that was sent.
81 | The ID of a message can be accessed via :
82 |
83 | ```python
84 | message = await channel.send("hmm…")
85 | message_id = message.id
86 | ```
87 |
88 | ### How do I upload an image?
89 |
90 | To upload something to Discord you have to use the object.
91 |
92 | A accepts two parameters, the file-like object (or file path) and the filename
93 | to pass to Discord when uploading.
94 |
95 | If you want to upload an image it's as simple as:
96 |
97 | ```python
98 | await channel.send(file=disnake.File("my_file.png"))
99 | ```
100 |
101 | If you have a file-like object you can do as follows:
102 |
103 | ```python
104 | with open("my_file.png", "rb") as fp:
105 | await channel.send(file=disnake.File(fp, "new_filename.png"))
106 | ```
107 |
108 | To upload multiple files, you can use the `files` keyword argument instead of `file`:
109 |
110 | ```python
111 | my_files = [
112 | disnake.File("result.zip"),
113 | disnake.File("teaser_graph.png"),
114 | ]
115 | await channel.send(files=my_files)
116 | ```
117 |
118 | If you want to upload something from a URL, you will have to use an HTTP request using [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html)
119 | and then pass an [io.BytesIO](https://docs.python.org/3/library/io.html#io.BytesIO) instance
120 | to like so:
121 |
122 | ```python
123 | import io
124 | import aiohttp
125 |
126 | async with aiohttp.ClientSession() as session:
127 | async with session.get(my_url) as resp:
128 | if resp.status != 200:
129 | return await channel.send("Could not download file...")
130 | data = io.BytesIO(await resp.read())
131 | await channel.send(file=disnake.File(data, "cool_image.png"))
132 | ```
133 |
134 | ### How can I add a reaction to a message?
135 |
136 | You use the method.
137 |
138 | If you want to use unicode emoji, you must pass a valid unicode code point in a string. In your code, you can write this in a few different ways:
139 |
140 | - `'👍'`
141 | - `'\U0001F44D'`
142 | - `'\N{THUMBS UP SIGN}'`
143 |
144 | Quick example:
145 |
146 | ```python
147 | emoji = "\N{THUMBS UP SIGN}"
148 | # or '\U0001f44d' or '👍'
149 | await message.add_reaction(emoji)
150 | ```
151 |
152 | In case you want to use emoji that come from a message, you already get their code points in the content without needing
153 | to do anything special. You **cannot** send `':thumbsup:'` style shorthands.
154 |
155 | For custom emoji, you should pass an instance of . You can also pass a `'<:name:id>'` string, but if you
156 | can use said emoji, you should be able to use to get an emoji via ID or use
157 | or on or collections.
158 |
159 | The name and ID of a custom emoji can be found with the client by prefixing `:custom_emoji:` with a backslash.
160 | For example, sending the message `\:python3:` with the client will result in `<:python3:232720527448342530>`.
161 |
162 | Quick example:
163 |
164 | ```python
165 | # If you have the ID already
166 | emoji = bot.get_emoji(310177266011340803)
167 | await message.add_reaction(emoji)
168 |
169 | # No ID, do a lookup
170 | emoji = disnake.utils.get(guild.emojis, name="LUL")
171 | if emoji:
172 | await message.add_reaction(emoji)
173 |
174 | # If you have the name and ID of a custom emoji:
175 | emoji = "<:python3:232720527448342530>"
176 | await message.add_reaction(emoji)
177 | ```
178 |
179 | ### How do I pass a coroutine to the player's "after" function?
180 |
181 | The library's music player launches on a separate thread, ergo it does not execute inside a coroutine.
182 | This does not mean that it is not possible to call a coroutine in the `after` parameter. To do so you must pass a callable
183 | that wraps up a couple of aspects.
184 |
185 | The first gotcha that you must be aware of is that calling a coroutine is not a thread-safe operation. Since we are
186 | technically in another thread, we must take caution in calling thread-safe operations so things do not bug out. Luckily for
187 | us, [`asyncio`](https://docs.python.org/3/library/asyncio.html#module-asyncio) comes with a [`asyncio.run_coroutine_threadsafe`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run_coroutine_threadsafe) function that allows us to call
188 | a coroutine from another thread.
189 |
190 | However, this function returns a [`Future`](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future) and to actually call it we have to fetch its result. Putting all of
191 | this together we can do the following:
192 |
193 | ```python
194 | def my_after(error):
195 | coro = some_channel.send("Song is done!")
196 | fut = asyncio.run_coroutine_threadsafe(coro, bot.loop)
197 | try:
198 | fut.result()
199 | except:
200 | # An error happened sending the message
201 | pass
202 |
203 |
204 | voice.play(disnake.FFmpegPCMAudio(url), after=my_after)
205 | ```
206 |
207 | ### How do I run something in the background?
208 |
209 | Check the [`background_task.py` example](https://github.com/DisnakeDev/disnake/blob/master/examples/background_task.py).
210 |
211 | ### How do I get a specific model?
212 |
213 | There are multiple ways of doing this. If you have a specific model's ID then you can use
214 | one of the following functions:
215 |
216 | -
217 | -
218 | -
219 | -
220 | -
221 | -
222 | -
223 |
224 | The following use an HTTP request:
225 |
226 | -
227 | -
228 | -
229 | -
230 | -
231 | -
232 | -
233 |
234 | If the functions above do not help you, then use of or would serve some use in finding
235 | specific models.
236 |
237 | Quick example:
238 |
239 | ```python
240 | # Find a guild by name
241 | guild = disnake.utils.get(bot.guilds, name="My Server")
242 |
243 | # Make sure to check if it's found
244 | if guild is not None:
245 | # Find a channel by name
246 | channel = disnake.utils.get(guild.text_channels, name="cool-channel")
247 | ```
248 |
249 | ### How do I make a web request?
250 |
251 | To make a request, you should use a non-blocking library.
252 | This library already uses and requires a 3rd party library for making requests, [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html).
253 |
254 | Quick example:
255 |
256 | ```python
257 | async with aiohttp.ClientSession() as session:
258 | async with session.get("http://aws.random.cat/meow") as r:
259 | if r.status == 200:
260 | js = await r.json()
261 | ```
262 |
263 | See [`aiohttp`'s full documentation](http://aiohttp.readthedocs.io/en/stable/) for more information.
264 |
265 | ### How do I use a local image file for an embed image?
266 |
267 | Setting a local image for the embed can be done using the `file` argument of method. It accepts a object.
268 |
269 | Quick example:
270 |
271 | ```python
272 | embed = disnake.Embed()
273 | embed.set_image(file=disnake.File("path/to/file.png"))
274 | await channel.send(embed=embed)
275 | ```
276 |
277 | ### Is there an event for audit log entries being created?
278 |
279 | As of version 2.8, there's now an event for it, called .
280 |
--------------------------------------------------------------------------------
/guide/docs/faq/good-practices.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: A section that lists practices you should follow while making your Discord bot.
3 | keywords: [disnake, bot, guide, tutorial, good practices, python]
4 | ---
5 |
6 | # Good practices
7 |
8 | ## Running code when a cog is loaded
9 |
10 | Most people are used to running everything in `__init__` but that doesn't allow running async code. In this case you can
11 | overwrite the special `cog_load` method.
12 |
13 | ```python {5,6}
14 | class MyCog(commands.Cog):
15 | def __init__(self, bot: commands.Bot):
16 | self.bot = bot
17 |
18 | async def cog_load(self):
19 | self.data = await bot.fetch_database_data()
20 |
21 | @commands.slash_command()
22 | async def command(self, interaction: disnake.ApplicationCommandInteraction, user: disnake.User):
23 | await interaction.response.send_message(self.data[user.id])
24 | ```
25 |
26 | ## Reloading your bot
27 |
28 | One of the lesser known disnake features is the `reload` kwarg for your bot. It reloads extensions every time they are
29 | edited; allowing you to test your code in real-time. This is especially useful if startup times for your bot are very
30 | slow, since only one extension will be reloaded at a time.
31 |
32 | ```python {3}
33 | from disnake.ext import commands
34 |
35 | bot = commands.Bot(..., reload=True)
36 | ```
37 |
38 | :::caution
39 |
40 | This should be used purely for debugging. Please do not use this in production.
41 |
42 | :::
43 |
44 | ## Converting arguments in commands
45 |
46 | discord.py used to have `Converter` classes to convert arguments if they are provided. These were however very hard to
47 | use with type-checkers because the type of the parameter is never actually the converter itself.
48 |
49 | Disnake aims to eliminate this issue by only allowing conversion of builtin types like `disnake.Member`,
50 | `disnake.Emoji`, etc. If you ever want to have your own converter you have to use the `converter` argument in `Param`.
51 |
52 |
53 |
54 |
55 | ```python {10}
56 | async def convert_data(inter: disnake.ApplicationCommandInteraction, arg: str):
57 | parts = arg.split(",")
58 | return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()}
59 |
60 |
61 | @commands.slash_command()
62 | async def command(
63 | self,
64 | inter: disnake.ApplicationCommandInteraction,
65 | data: Dict[str, Any] = commands.Param(converter=convert_data),
66 | ):
67 | ...
68 | ```
69 |
70 |
71 |
72 |
73 | ```python
74 | class DataConverter(commands.Converter):
75 | async def convert(self, ctx: commands.Context, arg: str):
76 | parts = arg.split(",")
77 | return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()}
78 |
79 |
80 | @commands.command()
81 | async def command(self, ctx: commands.Context, data: DataConverter):
82 | ...
83 | ```
84 |
85 |
86 |
87 |
88 | If you absolutely want to be able to use classes you may pass in a class method. Alternatively, set a method of the
89 | class to be the converter using `converter_method`.
90 |
91 |
92 |
93 |
94 | ```python {9-12,19}
95 | from dataclasses import dataclass
96 |
97 |
98 | @dataclass
99 | class Data:
100 | a: str
101 | b: int
102 |
103 | @classmethod
104 | async def from_option(cls, inter: disnake.CommandInteraction, arg: str):
105 | a, b = arg.split(",")
106 | return cls(a, int(b))
107 |
108 |
109 | @commands.slash_command()
110 | async def command(
111 | self,
112 | inter: disnake.CommandInteraction,
113 | data: Data = commands.Param(converter=Data.from_option),
114 | ):
115 | ...
116 | ```
117 |
118 |
119 |
120 |
121 | ```python {9-12,19}
122 | from dataclasses import dataclass
123 |
124 |
125 | @dataclass
126 | class Data:
127 | a: str
128 | b: int
129 |
130 | @commands.converter_method
131 | async def from_option(cls, inter: disnake.CommandInteraction, arg: str):
132 | a, b = arg.split(",")
133 | return cls(a, int(b))
134 |
135 |
136 | @commands.slash_command()
137 | async def command(
138 | self,
139 | inter: disnake.CommandInteraction,
140 | data: Data,
141 | ):
142 | ...
143 | ```
144 |
145 |
146 |
147 |
148 | ## Context command targets
149 |
150 | Instead of using `inter.target` you should be using a parameter of your command.
151 |
152 | ```python
153 | @commands.user_command()
154 | async def command(self, inter: disnake.ApplicationCommandInteraction, user: disnake.User):
155 | await inter.response.send_message(f"Used on {user.mention}")
156 | ```
157 |
158 | ## Slash command descriptions
159 |
160 | You may use docstrings for command and option descriptions. Everything before `Parameters` is the command description.
161 | Everything after `Parameters` are the option descriptions.
162 |
163 | ```python
164 | @commands.slash_command()
165 | async def command(
166 | self,
167 | inter: disnake.ApplicationCommandInteraction,
168 | category: str,
169 | item: str,
170 | details: bool,
171 | ):
172 | """Show item info
173 |
174 | Parameters
175 | ----------
176 | category: The category to search
177 | item: The item to display
178 | details: Whether to get the details of this time
179 | """
180 | ```
181 |
182 | ## Guild-only commands
183 |
184 | While disnake does provide a `@commands.guild_only()` decorator, it still makes you handle `guild` being optional in
185 | case you're using linters. To solve this you should be using `GuildCommandInteraction`.
186 |
187 | ```python
188 | # before
189 | @commands.slash_command()
190 | @commands.guild_only()
191 | async def command(inter: disnake.ApplicationCommandInteraction):
192 | assert inter.guild is not None
193 | await inter.send(inter.guild.name)
194 |
195 |
196 | # after
197 | @commands.slash_command()
198 | async def command(inter: disnake.GuildCommandInteraction):
199 | await inter.send(inter.guild.name)
200 | ```
201 |
--------------------------------------------------------------------------------
/guide/docs/faq/intro.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /faq
3 | hide_table_of_contents: true
4 | description: This is a list of Frequently Asked Questions regarding using `disnake` and its extension modules.
5 | keywords: [disnake, bot, guide, tutorial, python, faq]
6 | ---
7 |
8 | # Frequently Asked Questions
9 |
10 | This is a list of Frequently Asked Questions regarding using `disnake` and its extension modules. Feel free to suggest a
11 | new at the [support server](https://discord.gg/disnake), or submit one via pull requests on [the repository](https://github.com/DisnakeDev/guide/pulls).
12 |
13 | ## Legend
14 |
15 | - `bot` refers to the commands.Bot object. For example, `bot = commands.Bot(command_prefix="!", ...)`.
16 | - `ctx` refers to the commands.Context object, while `inter` refers to a version of the disnake.Interaction object. For example, `async def command(ctx: commands.Context, ...):`
17 |
--------------------------------------------------------------------------------
/guide/docs/getting-started/creating-commands.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: This article goes in depth on creating commands in your Discord bot.
3 | keywords: [disnake, bot, guide, tutorial, creating, commands, python]
4 | ---
5 |
6 | # Creating commands
7 |
8 | :::info
9 |
10 | This page is a follow-up, and the base code used is from the previous page ([Initial files](./initial-files.mdx)). The
11 | code can be found on the GitHub repository
12 | [here](https://github.com/DisnakeDev/guide/tree/main/code-samples/getting-started/initial-files).
13 |
14 | :::
15 |
16 | Discord also allows developers to register
17 | [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provides users a
18 | first-class way of interacting directly with your application. These slash commands shall be covered by the guide
19 | [here](/interactions/slash-commands), in the **Interactions** section.
20 |
21 | ## A note on prefix commands
22 |
23 | Bot commands that are initiated when a keyword is used along with a specified prefix (such as `!` or `$`) are known as
24 | **prefix commands** (are also often referred to as **text commands**).
25 |
26 | :::caution Message Intent - Privileged
27 |
28 | It is to be noted that handling prefix commands require the **message intent**, which allows the bot to get content and
29 | data of messages sent by users. This intent has recently been privileged, i.e., it needs to be manually enabled for the
30 | bot application, and its requirement will eventually be reviewed if your bot is in over 100 servers.
31 |
32 | You can read more about the message intent [here][message-intent-article].
33 |
34 | :::
35 |
36 | Therefore, to minimize the permissions your bot has to use, we will be covering prefix commands under the **Popular
37 | Topics** section, and advancing with the basics of slash commands in this article; more advanced topics of the same will
38 | be covered in the [**Interactions**](/interactions) section.
39 |
40 | ## Registering commands
41 |
42 | This section covers the bare minimum to get you started with registering slash commands. Once again, you can refer to
43 | [this page](/interactions/slash-commands) for an in-depth coverage of topics, including guild commands,
44 | global commands, options, option types, autocomplete and choices.
45 |
46 | Now, we shall continue with the base code used in the previous section.
47 |
48 | ```python title="main.py"
49 | import disnake
50 | from disnake.ext import commands
51 |
52 | bot = commands.Bot()
53 |
54 |
55 | @bot.event
56 | async def on_ready():
57 | print("The bot is ready!")
58 |
59 |
60 | bot.run("YOUR_BOT_TOKEN")
61 | ```
62 |
63 | The first step is to use the `@bot.slash_command` coroutine, along with an `async` function in order to define the code
64 | for your slash command. Below is a script demonstrating the same (focus on the use of `inter`, which is short for
65 | `interaction`).
66 |
67 | ```python title="main.py" {12-14}
68 | import disnake
69 | from disnake.ext import commands
70 |
71 | bot = commands.Bot()
72 |
73 |
74 | @bot.event
75 | async def on_ready():
76 | print("The bot is ready!")
77 |
78 |
79 | @bot.slash_command()
80 | async def ping(inter):
81 | ...
82 |
83 |
84 | bot.run("YOUR_BOT_TOKEN")
85 | ```
86 |
87 | The `inter` passed into the function is analogous to context, or `ctx` used in prefix commands - it passes through all
88 | information relative to the interaction - data regarding the user who initiated the command, as an example. It is also
89 | necessary for replying to the use of the command.
90 |
91 | :::note Using `ctx` vs. `inter`
92 |
93 | If you have experience with coding bots with [`discord.py`](https://discordpy.readthedocs.io/en/latest), you would be
94 | familiar with using `ctx` as an abbreviation for passing context into the function. This guide will primarily be using
95 | `inter`, as it is short for `interaction` and refers to .
96 | Of course, you're open to using your preferred abbreviation in code.
97 |
98 | :::
99 |
100 | ### Registering commands in specific guilds
101 |
102 | Note that servers are referred to as "guilds" in the Discord API and disnake library. On running the above code, the
103 | slash command will be registered globally, and will be accessible on all servers the bot is in.
104 |
105 | You can also use the `guild_ids` argument for the command to only be registered in a list of specified servers, for example your development server.
106 |
107 | ```python title="main.py" {12-14}
108 | import disnake
109 | from disnake.ext import commands
110 |
111 | bot = commands.Bot()
112 |
113 |
114 | @bot.event
115 | async def on_ready():
116 | print("The bot is ready!")
117 |
118 |
119 | @bot.slash_command(guild_ids=[1234, 5678]) # Your server IDs go here.
120 | async def ping(inter):
121 | ...
122 |
123 |
124 | bot.run("YOUR_BOT_TOKEN")
125 | ```
126 |
127 | :::tip Using `test_guilds` in `commands.Bot()`
128 |
129 | When you have multiple commands registered under the same test guilds, it is convenient to only have your `guild_ids`
130 | defined once. Therefore, you can use the `test_guilds` argument in the `commands.Bot()` instance instead of passing
131 | `guild_ids` to every single command -
132 |
133 | ```python
134 | bot = commands.Bot(test_guilds=[1234, 5678])
135 | ```
136 |
137 | :::
138 |
139 | Now that you're all set with registering the slash command, you can proceed with responding to the initiated command.
140 |
141 | ## Responding to commands
142 |
143 | You can respond to a slash command initiated by the user, using `inter.response.send_message()`. It is analogous to
144 | using `ctx.send()`, in that you can respond to the interaction with embeds, files, buttons/select menus or just plain
145 | text.
146 |
147 | ```python title="main.py" {14}
148 | import disnake
149 | from disnake.ext import commands
150 |
151 | bot = commands.Bot()
152 |
153 |
154 | @bot.event
155 | async def on_ready():
156 | print("The bot is ready!")
157 |
158 |
159 | @bot.slash_command(guild_ids=[1234, 5678])
160 | async def ping(inter):
161 | await inter.response.send_message("Pong!")
162 |
163 |
164 | bot.run("YOUR_BOT_TOKEN")
165 | ```
166 |
167 |
168 |
169 |
170 |
171 |
172 | ping
173 |
174 |
175 | Pong!
176 |
177 |
178 |
179 |
180 | ### Server info command
181 |
182 | `inter.guild` refers to the guild the interaction was sent in (a Guild
183 | instance), which exposes properties such as `.name` or `.member_count`.
184 |
185 | ```python title="main.py" {12-16}
186 | import disnake
187 | from disnake.ext import commands
188 |
189 | bot = commands.Bot()
190 |
191 |
192 | @bot.event
193 | async def on_ready():
194 | print("The bot is ready!")
195 |
196 |
197 | @bot.slash_command()
198 | async def server(inter):
199 | await inter.response.send_message(
200 | f"Server name: {inter.guild.name}\nTotal members: {inter.guild.member_count}"
201 | )
202 |
203 |
204 | bot.run("YOUR_BOT_TOKEN")
205 | ```
206 |
207 |
208 |
209 |
210 |
211 |
212 | server
213 |
214 |
215 | Server name: Disnake Guide
216 | Total members: 2
217 |
218 |
219 |
220 |
221 | :::tip
222 |
223 | Refer to the Guild documentation for a list of all the available
224 | properties and methods.
225 |
226 | :::
227 |
228 | You could also display the date the server was created, or the server's verification level. You would do those in the
229 | same manner - use `inter.guild.created_at` or `inter.guild.verification_level`, respectively.
230 |
231 | ### User info command
232 |
233 | A "user" refers to a Discord user. `inter.author` refers to the user the interaction was sent by
234 | (a User instance in DM contexts, or a Member instance in server contexts), which exposes properties
235 | such as `.name` or `.id`. (Using just `inter.author` will give the user's full tag.)
236 |
237 | ```python title="main.py" {12-14}
238 | import disnake
239 | from disnake.ext import commands
240 |
241 | bot = commands.Bot()
242 |
243 |
244 | @bot.event
245 | async def on_ready():
246 | print("The bot is ready!")
247 |
248 |
249 | @bot.slash_command()
250 | async def user(inter):
251 | await inter.response.send_message(f"Your tag: {inter.author}\nYour ID: {inter.author.id}")
252 |
253 |
254 | bot.run("YOUR_BOT_TOKEN")
255 | ```
256 |
257 |
258 |
259 |
260 |
261 |
262 | server
263 |
264 |
265 | Your tag: AbhigyanTrips#1234
266 | Your ID: 123456789012345678
267 |
268 |
269 |
270 |
271 | :::tip
272 |
273 | Refer to the and documentation for a list
274 | of all the available properties and methods.
275 |
276 | :::
277 |
278 | And there you have it!
279 |
280 | ## Resulting Code
281 |
282 |
283 |
284 | [message-intent-article]: https://support-dev.discord.com/hc/en-us/articles/4404772028055
285 |
--------------------------------------------------------------------------------
/guide/docs/getting-started/initial-files.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: An in-depth article on initializing your Discord bot project.
3 | keywords: [disnake, bot, guide, tutorial, initial files, initialization, python]
4 | ---
5 |
6 | # Initial files
7 |
8 | Once you [add your bot to a server](../prerequisites/creating-your-application#inviting-your-bot), the next step is to
9 | start coding and get it online! Let's start by creating a `.env` file for your bot token and a main file for your bot
10 | application.
11 |
12 | ## Creating configuration files
13 |
14 | As explained in the "What is a token, anyway?" section, your token is essentially your bot's password, and you should
15 | protect it as best as possible. This can be done through a `.env` file, or by using environment variables.
16 |
17 | Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the `Bot`
18 | page to copy your token.
19 |
20 | ### Using environment variables
21 |
22 | Environment variables are special values for your environment (e.g., terminal session, docker container, or environment
23 | variable file). You can pass these values into your code's scope so that you can use them.
24 |
25 | :::note
26 |
27 | When referring to a `.env` file, keep in mind that you can name this file whatever you prefer. For example, the file can
28 | be named `token.env` or `secret.env`.
29 |
30 | :::
31 |
32 | Storing data in a `.env` file is a common way of keeping your sensitive values safe. Create a `.env` file in your
33 | project directory and paste in your token. You can access your token inside other files by using `os.environ`.
34 |
35 |
36 |
37 |
38 | ```python
39 | # Importing "os" module.
40 | import os
41 |
42 | # Getting .env value.
43 | # You can name this variable in the script however you like.
44 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"]
45 | ```
46 |
47 |
48 |
49 |
50 | ```python
51 | # Each line in a .env file should hold a KEY=value pair.
52 | YOUR_BOT_TOKEN = OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg
53 | ```
54 |
55 |
56 |
57 |
58 | :::danger
59 |
60 | If you're using Git, you should not commit this file and should ignore it via `.gitignore`. The values of a `.env` file are still accessible to anyone if they are able to view the file.
61 |
62 | :::
63 |
64 | You can use the [python-dotenv package][python-dotenv] to either load the `env` variables into the environment, or make
65 | a `config` dict out of the env values.
66 |
67 |
68 |
69 |
70 | ```python
71 | import os
72 | from dotenv import load_dotenv
73 |
74 | load_dotenv() # Take environment variables from .env.
75 |
76 | # Using the variables in your application, which uses environment variables
77 | # (e.g. from 'os.environ()' or 'os.getenv()')
78 | # as if they came from the actual environment.
79 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"]
80 | ```
81 |
82 |
83 |
84 |
85 | ```python
86 | from dotenv import dotenv_values
87 |
88 | config = dotenv_values(".env") # Makes a dict out of the values.
89 |
90 | # Thus, we get
91 | # config = {YOUR_BOT_TOKEN: OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg}
92 | # which can be used as:
93 | YOUR_BOT_TOKEN = config["YOUR_BOT_TOKEN"]
94 | ```
95 |
96 |
97 |
98 |
99 | Keep in mind that the values imported from the `.env` file are **in string format**. Therefore if you would like to,
100 | say, use them for calculations, you'll have to convert them via `int()`
101 |
102 | :::info Online editors (Glitch, Replit, etc.)
103 |
104 | While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual
105 | private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's
106 | documentation and help articles for more information on how to keep sensitive values safe:
107 |
108 | - Glitch: [Storing secrets in .env][glitch-article]
109 | - Replit: [Secrets and environment variables][replit-article]
110 |
111 | :::
112 |
113 | ### Git and `.gitignore`
114 |
115 | Git is a fantastic tool to keep track of your code changes and allows you to upload progress to services like
116 | [GitHub][github], [GitLab][gitlab], or [Bitbucket][bitbucket]. While this is super useful to share code with other
117 | developers, it also bears the risk of uploading your configuration files with sensitive values!
118 |
119 | You can specify files that Git should ignore in its versioning systems with a `.gitignore` file. Create a `.gitignore`
120 | file in your project directory and add the names of the files and folders you want to ignore:
121 |
122 | ```
123 | __pycache__/
124 | secrets.env
125 | config.json
126 | ```
127 |
128 | :::tip
129 |
130 | [`__pycache__/`][pycache] has been included in `.gitignore` as it is simply cache that helps loading and running your
131 | script faster (this is an oversimplification). As it is of no particular importance, and is recompiled every time a
132 | change is made in the script, it is better to not commit the directory.
133 |
134 | Also, you can specify certain extensions/directories in `.gitignore` files, as per the requirements of your project -
135 | [here is an example][gitignore-example]. Check out the [Git documentation on `.gitignore`][gitignore-article] for more
136 | information!
137 |
138 | :::
139 |
140 | ## Creating the main file
141 |
142 | Open your code editor and create a new file. We suggest that you save the file as `main.py` or `bot.py`, but you may
143 | name it whatever you wish.
144 |
145 | Here's the base code to get you started:
146 |
147 | ```python title="main.py"
148 | # Import the necessary libraries.
149 | import disnake
150 | from disnake.ext import commands
151 |
152 | # Creating a commands.Bot() instance, and assigning it to "bot"
153 | bot = commands.Bot()
154 |
155 | # When the bot is ready, run this code.
156 | @bot.event
157 | async def on_ready():
158 | print("The bot is ready!")
159 |
160 |
161 | # Login to Discord with the bot's token.
162 | bot.run("YOUR_BOT_TOKEN")
163 | ```
164 |
165 | This is how you create a bot instance for your Discord bot and login to Discord. Open your terminal and run
166 | `python3 main.py` to start the process. If you see "The bot is ready!" after a few seconds, you're good to go!
167 |
168 | :::tip
169 |
170 | After closing the process with `Ctrl + C`, you can press the up arrow on your keyboard to bring up the latest commands
171 | you've run in the terminal. Pressing up and then enter after closing the process is a quick way to start it up again.
172 |
173 | :::
174 |
175 | ## Resulting code
176 |
177 |
178 |
179 | [python-dotenv]: https://pypi.org/project/python-dotenv/
180 | [glitch-article]: https://glitch.happyfox.com/kb/article/18
181 | [replit-article]: https://docs.replit.com/repls/secrets-environment-variables
182 | [github]: https://github.com/
183 | [gitlab]: https://about.gitlab.com/
184 | [bitbucket]: https://bitbucket.org/product
185 | [pycache]: https://stackoverflow.com/questions/16869024/what-is-pycache
186 | [gitignore-example]: https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore
187 | [gitignore-article]: https://git-scm.com/docs/gitignore
188 |
--------------------------------------------------------------------------------
/guide/docs/getting-started/using-cogs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: An in-depth section on creating cogs for your Discord bot.
3 | keywords: [disnake, bot, guide, tutorial, cogs, extensions, python]
4 | ---
5 |
6 | # Creating cogs/extensions
7 |
8 | [**Cogs**](https://docs.disnake.dev/en/stable/ext/commands/cogs.html) are analogous to modules that extend the core of
9 | the bot - thus adding to its functions and abilities. They also allow you to break down and organize your bot's
10 | collection of commands/listeners (which is useful when your bot's command list becomes quite extensive).
11 |
12 | :::note
13 |
14 | Cogs are typically used alongside with **Extensions**. You can read more about them
15 | [in the documentation](https://docs.disnake.dev/en/stable/ext/commands/extensions.html).
16 |
17 | :::
18 |
19 | ## Creating files
20 |
21 | What we code here will extend on the code from [**Initial Files**](./initial-files.mdx), and thus we assume that there
22 | already exists a `main.py` file in your directory which initiates a `commands.Bot()` instance.
23 |
24 | ```python title="main.py"
25 | import disnake
26 | from disnake.ext import commands
27 |
28 | bot = commands.Bot()
29 |
30 |
31 | @bot.event
32 | async def on_ready():
33 | print("The bot is ready!")
34 |
35 |
36 | bot.run("YOUR_BOT_TOKEN")
37 | ```
38 |
39 | Next, we will create a new Python file either in the same directory as `main.py` or in a subfolder (which is
40 | recommended). For the sake of being organized, we will create the cog file in a separate `cogs` folder, and name the
41 | file `ping.py` just for the "ping" command. If you're strictly following the guide, your file directory should look
42 | something like this:
43 |
44 | ```
45 | .
46 | ├── cogs/
47 | │ └── ping.py
48 | ├── main.py
49 | └── secrets.env
50 | ```
51 |
52 | ## Initial code
53 |
54 | ### Inheriting a class
55 |
56 | The first step is to create a class in `ping.py` that inherits from the `commands.Cog`, along with a constructor that
57 | takes in the `bot` object as its only argument and saves it. This is where we will be putting in our commands.
58 |
59 | ```python title="ping.py"
60 | from disnake.ext import commands
61 |
62 |
63 | class PingCommand(commands.Cog):
64 | """This will be for a ping command."""
65 |
66 | def __init__(self, bot: commands.Bot):
67 | self.bot = bot
68 | ```
69 |
70 | :::tip
71 |
72 | We typehint `bot: commands.Bot` here to give the IDE an idea of what the argument type is. It is not necessary, but can
73 | be useful in development when adding certain arguments to your commands.
74 |
75 | :::
76 |
77 | ### Registering commands
78 |
79 | Now, to define a slash command inside the class, we will use the `commands.slash_command()` decorator. It functions the same as
80 | `bot.slash_command()`, but works in cogs as well.
81 |
82 | ```python title="ping.py" {10-13}
83 | import disnake
84 | from disnake.ext import commands
85 |
86 |
87 | class PingCommand(commands.Cog):
88 | """This will be for a ping command."""
89 |
90 | def __init__(self, bot: commands.Bot):
91 | self.bot = bot
92 |
93 | @commands.slash_command()
94 | async def ping(self, inter: disnake.ApplicationCommandInteraction):
95 | """Get the bot's current websocket latency."""
96 | await inter.response.send_message(f"Pong! {round(self.bot.latency * 1000)}ms")
97 | ```
98 |
99 | Note that we're using `self` as the first argument, since the command function is inside a class.
100 |
101 | ### Adding `setup` function
102 |
103 | The last thing that needs to be added to this file is a `setup` function, so that disnake can load the cog when it is
104 | added to `main.py`.
105 |
106 | ```python title="ping.py" {16,17}
107 | import disnake
108 | from disnake.ext import commands
109 |
110 |
111 | class PingCommand(commands.Cog):
112 | """This will be for a ping command."""
113 |
114 | def __init__(self, bot: commands.Bot):
115 | self.bot = bot
116 |
117 | @commands.slash_command()
118 | async def ping(self, inter: disnake.ApplicationCommandInteraction):
119 | """Get the bot's current websocket latency."""
120 | await inter.response.send_message(f"Pong! {round(self.bot.latency * 1000)}ms")
121 |
122 |
123 | def setup(bot: commands.Bot):
124 | bot.add_cog(PingCommand(bot))
125 | ```
126 |
127 | This cog file can now be added to the bot via the `bot.load_extension()` method in `main.py`.
128 |
129 | ```python title="main.py"
130 | import disnake
131 | from disnake.ext import commands
132 |
133 | bot = commands.Bot()
134 |
135 |
136 | @bot.event
137 | async def on_ready():
138 | print("The bot is ready!")
139 |
140 |
141 | bot.load_extension("cogs.ping") # Note: We did not append the .py extension.
142 |
143 | bot.run("YOUR_BOT_TOKEN")
144 | ```
145 |
146 | Note that we've input `"cogs.ping"` here, since the `ping.py` file is inside the `cogs` folder. Therefore, the basic
147 | syntax for loading an extension is `bot.load_extension(".")`.
148 |
149 | And that concludes the use of cogs with `disnake`! You can now create multiple cogs to group and organize your
150 | commands/events, and load/unload them via your main file. More information on special cog methods and meta options can
151 | be found [in the documentation](https://docs.disnake.dev/en/stable/ext/commands/cogs.html).
152 |
153 | ## Syntax changes
154 |
155 | Cogs represent a fairly drastic change in the way you write commands and bots, so here's a list you can come back to for
156 | the primary syntax used in cogs:
157 |
158 | - Each cog is a Python class that subclasses commands.Cog.
159 | - Decorators for commands in cogs:
160 | - Command - commands.command()
161 | - Slash command - commands.slash_command()
162 | - User command - commands.user_command()
163 | - Message command - commands.message_command()
164 | - Every listener is marked with the commands.Cog.listener() decorator.
165 | - Cogs are then registered with the Bot.add_cog call, and are subsequently removed with the Bot.remove_cog call.
166 |
167 |
168 | Source: Disnake Documentation
169 |
170 |
171 | Cogs represent a fairly drastic change in the way you write commands and bots. Thus, it is recommended that you get
172 | familiar with this syntax - since the code we use in this section is largely tailored for cogs.
173 |
--------------------------------------------------------------------------------
/guide/docs/interactions/buttons.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: They refer to views, buttons and select menus that can be added to the messages your bot sends.
3 | ---
4 |
5 | # Buttons
6 |
7 | Components allow users to interact with your bot through interactive UI elements. In this section, we will discuss how bots can receive and process information from these elements through the library.
8 |
9 |
10 |
11 |
12 |
13 |
14 | buttons
15 |
16 |
17 | Need help?
18 |
19 |
20 | Yes
21 | No
22 |
23 |
24 |
25 |
26 |
27 |
28 | The code for this command is given below.
29 |
30 | ```python title="buttons.py"
31 | # At the top of the file.
32 | import disnake
33 | from disnake.ext import commands
34 |
35 | # The slash command that responds with a message.
36 | @bot.slash_command()
37 | async def buttons(inter: disnake.ApplicationCommandInteraction):
38 | await inter.response.send_message(
39 | "Need help?",
40 | components=[
41 | disnake.ui.Button(label="Yes", style=disnake.ButtonStyle.success, custom_id="yes"),
42 | disnake.ui.Button(label="No", style=disnake.ButtonStyle.danger, custom_id="no"),
43 | ],
44 | )
45 |
46 |
47 | @bot.listen("on_button_click")
48 | async def help_listener(inter: disnake.MessageInteraction):
49 | if inter.component.custom_id not in ["yes", "no"]:
50 | # We filter out any other button presses except
51 | # the components we wish to process.
52 | return
53 |
54 | if inter.component.custom_id == "yes":
55 | await inter.response.send_message("Contact us at https://discord.gg/disnake!")
56 | elif inter.component.custom_id == "no":
57 | await inter.response.send_message("Got it. Signing off!")
58 | ```
59 |
60 | ## Building and sending buttons
61 |
62 | ## Button styles
63 |
64 | | Name | Syntax | Color |
65 | | --------- | ----------------------------------------------------------------- | ------- |
66 | | Primary | `ButtonStyle.primary` / `ButtonStyle.blurple` | Blurple |
67 | | Secondary | `ButtonStyle.secondary` / `ButtonStyle.grey` / `ButtonStyle.gray` | Grey |
68 | | Success | `ButtonStyle.success` / `ButtonStyle.green ` | Green |
69 | | Danger | `ButtonStyle.danger` / `ButtonStyle.red` | Red |
70 | | Link | `ButtonStyle.link` / `ButtonStyle.url` | Grey |
71 |
72 |
73 |
74 |
75 |
86 |
87 |
88 |
89 |
90 | :::note
91 |
92 | `Link` buttons _cannot_ have a `custom_id`, and _do not_ send an interaction event when clicked.
93 |
94 | :::
95 |
96 | ### Disabled buttons
97 |
98 | ## Receiving button callback
99 |
100 | ## Views vs. low-level components
101 |
--------------------------------------------------------------------------------
/guide/docs/interactions/context-menus.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: They are available under Apps in user/message context menus, responding with the resolved user or message on which the action was taken.
3 | ---
4 |
5 | # Context Menus
6 |
7 | These application commands are situated under the "Apps" section when you right-click a user or a message. They act like interactions, except they take a or object along with .
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | :::note
17 |
18 | For more specific annotations, you can use or instead of for the respective commands.
19 |
20 | :::
21 |
22 | ## User commands
23 |
24 |
25 |
26 |
27 |
28 | User Info
29 |
30 |
31 | Username: AbhigyanTrips#2474
32 |
33 | Bot: False
34 |
35 |
36 |
37 |
38 | The function below should have only one required argument, which is an instance of .
39 |
40 | ```python
41 | @bot.user_command(name="User Info")
42 | async def avatar(inter: disnake.ApplicationCommandInteraction, user: disnake.User):
43 | await inter.response.send_message(f"Username: {user}\nBot: {user.bot}")
44 | ```
45 |
46 | ## Message commands
47 |
48 |
49 |
50 |
51 |
52 | Reverse
53 |
54 |
55 | .desrever si egassem sihT
56 |
57 |
58 |
59 |
60 | The function below should have only one required argument, which is an instance of .
61 |
62 | ```python
63 | @bot.message_command(name="Reverse")
64 | async def reverse(inter: disnake.ApplicationCommandInteraction, message: disnake.Message):
65 | # Reversing the message and sending it back.
66 | await inter.response.send_message(message.content[::-1])
67 | ```
68 |
69 | ## Context menu notes
70 |
71 | - Context menu commands cannot have subcommands or any other options.
72 | - Responses to context menu commands and their permissions function the same as slash commands.
73 |
--------------------------------------------------------------------------------
/guide/docs/interactions/images/context-menus-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/context-menus-intro.png
--------------------------------------------------------------------------------
/guide/docs/interactions/images/message-components-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/message-components-intro.png
--------------------------------------------------------------------------------
/guide/docs/interactions/images/modals-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/modals-preview.png
--------------------------------------------------------------------------------
/guide/docs/interactions/images/slash-commands-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/slash-commands-intro.png
--------------------------------------------------------------------------------
/guide/docs/interactions/images/string-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/string-select.png
--------------------------------------------------------------------------------
/guide/docs/interactions/intro.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /interactions
3 | hide_table_of_contents: true
4 | description: This section covers application commands and message components.
5 | ---
6 |
7 | # Interactions
8 |
9 | **Interactions** here refers to client-integrated elements that a bot can create - giving users a way to interact directly with your application. Currently, interactions can be classified under three categories:
10 |
11 |
47 |
48 |
49 | They are available under Apps in user/message context menus, responding with the resolved
50 | user or message on which the action was taken.
51 |
52 |
66 |
67 |
68 | They refer to views, buttons and select menus that can be added to the messages your bot
69 | sends.{' '}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | :::note Interactions and Bot Users
80 |
81 | Interactions are essentially webhooks under the hood (and responding to interactions doesn't require a bot token). Thus, you can have an interaction-only application that can be accessed by users **without having a bot user** in the guild.
82 |
83 | Since `disnake` is primarily focused on using the gateway events, you will still require a bot user. You can check out other libraries like [`hikari`](https://www.hikari-py.dev/) for utilizing this feature.
84 |
85 | :::
86 |
--------------------------------------------------------------------------------
/guide/docs/interactions/modals.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Modals act as client-integrated popups, that can prompt the user for text input.
3 | keywords: [disnake, bot, guide, tutorial, modals, text inputs, python]
4 | ---
5 |
6 | # Modals & Text Inputs
7 |
8 | Modals are how you can prompt users for further detailed input. They act as client-integrated popups, and work in tandem
9 | with interactive components called [Text Inputs](https://discord.com/developers/docs/interactions/message-components#text-inputs).
10 | These inputs can have various formats to accept information from the user on prompt, and use the callback to process that information.
11 |
12 | This section goes in-depth on using modals with other interactive components, and responding to interactions with them.
13 |
14 | ## Modal preview
15 |
16 | Here is an example of how a modal may look. We will go over modal construction in the next part of this article.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Once the user inputs information into the modal, the bot will receive a callback that includes the values. As an example, this information has been portrayed in an embed below.
25 |
26 |
27 |
28 |
29 |
30 |
31 | This is the tag's name.
32 |
33 | This is the tag's description. It can be as long as 4000 characters.
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | This was done using the following code.
42 |
43 | ```python title="modals.py"
44 | # At the top of the file.
45 | import disnake
46 | from disnake.ext import commands
47 | from disnake import TextInputStyle
48 |
49 | # Subclassing the modal.
50 | class MyModal(disnake.ui.Modal):
51 | def __init__(self):
52 | # The details of the modal, and its components
53 | components = [
54 | disnake.ui.TextInput(
55 | label="Name",
56 | placeholder="Foo Tag",
57 | custom_id="name",
58 | style=TextInputStyle.short,
59 | max_length=50,
60 | ),
61 | disnake.ui.TextInput(
62 | label="Description",
63 | placeholder="Lorem ipsum dolor sit amet.",
64 | custom_id="description",
65 | style=TextInputStyle.paragraph,
66 | ),
67 | ]
68 | super().__init__(title="Create Tag", components=components)
69 |
70 | # The callback received when the user input is completed.
71 | async def callback(self, inter: disnake.ModalInteraction):
72 | embed = disnake.Embed(title="Tag Creation")
73 | for key, value in inter.text_values.items():
74 | embed.add_field(
75 | name=key.capitalize(),
76 | value=value[:1024],
77 | inline=False,
78 | )
79 | await inter.response.send_message(embed=embed)
80 |
81 |
82 | bot = commands.Bot(command_prefix="!")
83 |
84 |
85 | @bot.slash_command()
86 | async def tags(inter: disnake.AppCmdInter):
87 | """Sends a Modal to create a tag."""
88 | await inter.response.send_modal(modal=MyModal())
89 |
90 |
91 | bot.run("YOUR_BOT_TOKEN")
92 | ```
93 |
94 | The implementation of this command using the lower-level interface can be found [here](https://github.com/DisnakeDev/guide/tree/main/code-samples/interactions/modals/create-tag-low.py).
95 |
96 | ## Creating a modal
97 |
98 | A modal can be created and sent using two different methods, just like other message components:
99 |
100 | - Subclassing disnake.ui.Modal to define the components, and sending it through the `modal` parameter of interaction.send_modal (as done in the example above).
101 | - Defining the attributes and components of the modal inside interaction.send_modal itself. This is considered a "lower-level" implementation of modals - we will use the same term to refer to it in this section.
102 |
103 | A disnake.ui.Modal object has the following attributes:
104 |
105 | - `title` - The title of the modal.
106 | - `custom_id` - An ID specified to the modal. This can be accessed via .
107 | - `timeout` - The time (in seconds) after which the modal is removed from cache. Defaults to **600 seconds**.
108 | - `components` - The list of components to be displayed in the modal. Limited to **5 action rows**.
109 |
110 | :::caution
111 |
112 | Modals without timeouts are not supported. There is no event dispatched when the modal is closed by the user, and thus the modal would not be removed from cache without a timeout.
113 |
114 | :::
115 |
116 | These attributes can be used while creating a modal, as follows.
117 |
118 | ```python title="modals.py" {4-9}
119 | # Subclassing the modal.
120 | class MyModal(disnake.ui.Modal):
121 | def __init__(self):
122 | super().__init__(
123 | title="Modal Title",
124 | custom_id="modal_id",
125 | timeout=300,
126 | components=[...],
127 | )
128 |
129 |
130 | # Using the lower-level interface. (inside a function)
131 | await inter.response.send_modal(
132 | title="Modal Title",
133 | custom_id="modal_id",
134 | components=[...],
135 | )
136 | ```
137 |
138 | :::note
139 |
140 | It is recommended that you pseudo-randomize a modal's `custom_id` by storing the interaction's ID in it. The reason being that a user can close a modal without triggering an event, and reopen one with the same command. In such cases, the `wait_for` for the old modal will still be active, which will resume execution for both commands.
141 |
142 | In the lower-level interface, you can implement this like so:
143 |
144 | ```diff title="modals.py"
145 | - custom_id="create_tag_low",
146 | + custom_id=f"create_tag_low-{inter.id}",
147 |
148 | % in wait_for
149 | - check=lambda i: i.custom_id == "create_tag_low" and i.author.id == inter.author.id,
150 | + check=lambda i: i.custom_id == f"create_tag_low-{inter.id}",
151 | ```
152 |
153 | :::
154 |
155 | ## `TextInput` components
156 |
157 | `TextInput` is a component in itself, like buttons and select menus. It is responsible for receiving user input in the form of text, and cannot be used outside of modals. It has the following customizable attributes.
158 |
159 | - `label` - The label of the text input.
160 | - `custom_id` - An ID specified to the text input.
161 | - `style` - Utilizes one of for the component. Defaults to short.
162 | - `placeholder` - The placeholder text that is shown if nothing is entered.
163 | - `value` - The pre-filled value of the text input (up to `max_length`).
164 | - `required` - Whether the text input is required. Defaults to `True`.
165 | - `min_length` and `max_length` - Set the minimum and maximum length of the inputs respectively.
166 |
167 | These attributes can be used per `TextInput` component, as follows.
168 |
169 | ```python title="textinput.py"
170 | disnake.ui.TextInput(
171 | label="Name",
172 | placeholder="Foo Tag",
173 | custom_id="name",
174 | style=TextInputStyle.short,
175 | max_length=50,
176 | )
177 | ```
178 |
179 | The attribute currently has two styles, which have multiple aliases:
180 |
181 | - `short` - Represents a single-line text input component. Also called `single_line`.
182 | - `long` - Represents a multi-line text input component. Also called `multi_line` or `paragraph`.
183 |
184 | ## Modal callback methods
185 |
186 | ### `callback()`
187 |
188 | It refers to the callback associated with the modal, and returns a object which includes the values input by the user. It is overriden by subclasses, allowing the developer to define the action done on the modal input received.
189 |
190 | ```python title="modals.py" {5,6}
191 | class MyModal(disnake.ui.Modal):
192 | def __init__(self):
193 | super().__init__(...)
194 |
195 | async def callback(self, inter: disnake.ModalInteraction):
196 | await inter.response.send_message("User input was received!")
197 | ```
198 |
199 | If you're using the lower-level interface for sending the modal, you'll have to utilize an event/listener to check for the custom ID and respond to the interaction. There is an event for this purpose.
200 |
201 | ```python title="modals.py" {10-14}
202 | @bot.slash_command()
203 | async def tags(inter: disnake.ApplicationCommandInteraction):
204 | ...
205 | await inter.response.send_modal(..., custom_id="modal_custom_id")
206 |
207 |
208 | ...
209 |
210 |
211 | @bot.event()
212 | async def on_modal_submit(inter: disnake.ModalInteraction):
213 |
214 | if inter.custom_id == "modal_custom_id":
215 | await do_stuff_here()
216 | ```
217 |
218 | You can alternatively use bot.wait_for() for this purpose, inside the command itself.
219 |
220 | ```python title="modals.py" {}
221 | @bot.slash_command()
222 | async def tags(inter: disnake.ApplicationCommandInteraction):
223 | ...
224 | await inter.response.send_modal(..., custom_id="modal_custom_id")
225 |
226 | modal_inter: disnake.ModalInteraction = await bot.wait_for(
227 | "modal_submit",
228 | check=lambda i: i.custom_id == "modal_custom_id" and i.author.id == inter.author.id,
229 | timeout=300,
230 | )
231 | ```
232 |
233 | ### `on_error()`
234 |
235 | Received when the modal submission encounters an error. It returns the error as well as a object that can be used to respond to the user. The default implementation prints the traceback to `stderr`.
236 |
237 | ````python title="modals.py" {5,6}
238 | class MyModal(disnake.ui.Modal):
239 | def __init__(self):
240 | super().__init__(...)
241 |
242 | async def on_error(self, error: Exception, inter: disnake.ModalInteraction):
243 | await inter.response.send_message(f"An error occurred!\n```{error}```")
244 | ````
245 |
246 | ### `on_timeout()`
247 |
248 | Called when the user does not respond before specified timeout; the modal is removed from cache without an interaction being made.
249 |
250 | :::note
251 |
252 | No interaction object is passed to the on_timeout() method. It is to be subclassed solely for backend functions.
253 |
254 | :::
255 |
256 | ## Modal notes
257 |
258 | - The only component that can currently be used with modals is TextInput. Buttons and select menus cannot be used inside modals.
259 | - You can add components to a disnake.ui.Modal object using the append_component() method. To directly add text inputs, use add_text_input() instead.
260 | - The defaults to `short`, if not specified for the component.
261 | - All TextInput components are required by default.
262 |
263 | ## Modal limits
264 |
265 | There are a few limits to be aware of while using modals due to the API's limitations. Here is a quick reference you can come back to:
266 |
267 | - A modal can have a total of **5 action rows**.
268 | - A modal's `title` has a maximum length of **45 characters**.
269 | - The text input `label` has a maximum length of **45 characters**.
270 | - The text input `placeholder` is limited to **100 characters**.
271 | - The `custom_id` is limited to **100 characters**, for both modals and text inputs.
272 | - The pre-defined `value` has a maximum length of **4000 characters** (overriden by the developer-defined limit).
273 | - The minimum and maximum values of a text input can be set from **0-4000 characters** and **1-4000 characters** respectively.
274 |
275 | ## Resulting code
276 |
277 |
278 |
--------------------------------------------------------------------------------
/guide/docs/interactions/select-menus.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: They refer to views, buttons and select menus that can be added to the messages your bot sends.
3 | ---
4 |
5 | # Select Menus
6 |
7 | Select Menus allow users to interact with your bot through an interactive dropdown component. This component provides selectable options for your users to choose from.
8 | You can require users to select a minimum/maximum number of options using min_values and max_values.
9 |
10 | Select Menus are available as the following types:
11 |
12 | - disnake.ui.StringSelect Allows you to input custom string options
13 | for users to select from, up to 25
14 | - disnake.ui.UserSelect User or Member objects as options
15 | - disnake.ui.RoleSelect Role objects as options
16 | - disnake.ui.ChannelSelect Channel objects as options
17 |
18 | The options for user, role, and channel select menus are populated by Discord, and currently cannot be limited to a specific subset. See [below](#other-selects) for details.
19 |
20 | This guide will walk you through a pretty basic use-case for a `StringSelect` dropdowns using [Views](#example-of-stringselects) and [low-level components](#views-vs-low-level-components).
21 | If you need more examples, you can always check the examples in the [disnake repo](https://github.com/DisnakeDev/disnake/tree/master/examples).
22 |
23 | :::note
24 | A message can have a maximum of 5 rows of components. Select components each take a single row, therefore you cannot have more than 5 Select components per message
25 | :::
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ### Example of `StringSelect`s
34 |
35 |
36 |
37 |
38 | ```python
39 | import os
40 |
41 | import disnake
42 | from disnake.ext import commands
43 |
44 |
45 | # Defines the StringSelect that contains animals that your users can choose from
46 | class AnimalDropdown(disnake.ui.StringSelect):
47 | def __init__(self):
48 |
49 | # Define the options that will be displayed inside the dropdown.
50 | # You may not have more than 25 options.
51 | # There is a `value` keyword that is being omitted, which is useful if
52 | # you wish to display a label to the user, but handle a different value
53 | # here within the code, like an index number or database id.
54 | options = [
55 | disnake.SelectOption(label="Dog", description="Dogs are your favorite type of animal"),
56 | disnake.SelectOption(label="Cat", description="Cats are your favorite type of animal"),
57 | disnake.SelectOption(
58 | label="Snake", description="Snakes are your favorite type of animal"
59 | ),
60 | disnake.SelectOption(
61 | label="Gerbil", description="Gerbils are your favorite type of animal"
62 | ),
63 | ]
64 |
65 | # We will include a placeholder that will be shown until an option has been selected.
66 | # The min and max values indicate the minimum and maximum number of options to be selected -
67 | # in this example we will only allow one option to be selected.
68 | super().__init__(
69 | placeholder="Choose an animal",
70 | min_values=1,
71 | max_values=1,
72 | options=options,
73 | )
74 |
75 | # This callback is called each time a user has selected an option
76 | async def callback(self, inter: disnake.MessageInteraction):
77 | # Use the interaction object to respond to the interaction.
78 | # `self` refers to this StringSelect object, and the `values`
79 | # attribute contains a list of the user's selected options.
80 | # We only want the first (and in this case, only) one.
81 | await inter.response.send_message(f"Your favorite type of animal is: {self.values[0]}")
82 |
83 |
84 | # Now that we have created the Select object with its options and callback, we need to attach it to a
85 | # View that will be displayed to the user in the command response.
86 | #
87 | # Views have a default timeout of 180s, at which the bot will stop listening for those events.
88 | # You may pass any float here, or `None` if you wish to remove the timeout.
89 | # Note: If `None` is passed, this view will persist only until the bot is restarted. If you wish to have persistent views,
90 | # consider using low-level components or check out the persistent view example:
91 | # https://github.com/DisnakeDev/disnake/blob/master/examples/views/persistent.py
92 | class DropDownView(disnake.ui.View):
93 | def __init__(self):
94 | # You would pass a new `timeout=` if you wish to alter it, but
95 | # we will leave it empty for this example so that it uses the default 180s.
96 | super().__init__()
97 |
98 | # Now let's add the `StringSelect` object we created above to this view
99 | self.add_item(AnimalDropdown())
100 |
101 |
102 | # Finally, we create a basic bot instance with a command that will utilize the created view and dropdown.
103 | bot = commands.Bot()
104 |
105 |
106 | @bot.listen()
107 | async def on_ready():
108 | print(f"Logged in as {bot.user} (ID: {bot.user.id})\n------")
109 |
110 |
111 | @bot.slash_command()
112 | async def animals(inter: disnake.ApplicationCommandInteraction):
113 | """Sends a message with our dropdown containing the animals"""
114 |
115 | # Create the view with our dropdown object
116 | view = DropDownView()
117 |
118 | # Respond to the interaction with a message and our view
119 | await inter.response.send_message("What is your favorite type of animal?", view=view)
120 |
121 |
122 | if __name__ == "__main__":
123 | bot.run(os.getenv("BOT_TOKEN"))
124 | ```
125 |
126 |
127 |
128 |
129 | ```python
130 | # Instead of subclassing `disnake.ui.StringSelect`, this example shows how to use the
131 | # `@disnake.ui.string_select` decorator directly inside the View to create the dropdown
132 | # component.
133 | class AnimalView(disnake.ui.View):
134 | def __init__(self):
135 | super().__init__()
136 |
137 | # If you wish to pass a previously defined sequence of values to this `View` so that
138 | # you may have dynamic options, you can do so by defining them within this __init__ method.
139 | # `self.animal_callback.options = [...]`
140 |
141 | @disnake.ui.string_select(
142 | placeholder="Choose an animal",
143 | options=[
144 | disnake.SelectOption(label="Dog", description="Dogs are your favorite type of animal"),
145 | disnake.SelectOption(label="Cat", description="Cats are your favorite type of animal"),
146 | disnake.SelectOption(
147 | label="Snake", description="Snakes are your favorite type of animal"
148 | ),
149 | disnake.SelectOption(
150 | label="Gerbil", description="Gerbils are your favorite type of animal"
151 | ),
152 | ],
153 | min_values=1,
154 | max_values=1,
155 | )
156 | async def animal_callback(
157 | self, select: disnake.ui.StringSelect, inter: disnake.MessageInteraction
158 | ):
159 | # The main difference in this callback is that we access the `StringSelect` through the
160 | # parameter passed to the callback, vs the subclass example where we access it via `self`
161 | await inter.response.send_message(f"You favorite type of animal is: {select.values[0]}")
162 | ```
163 |
164 |
165 |
166 |
167 | ### Other Selects
168 |
169 | The three other select components available are constructed and can be used in the same manner.
170 | The main difference is that we do not create nor pass any options to these Select objects as Discord will provide these options to the user automatically.
171 | The selected option(s) that are available in `self.values` will be the selected object(s) rather than the string values.
172 |
173 | - `UserSelect.values` will return a list of disnake.User or disnake.Member
174 | - `RoleSelect.values` will return a list of disnake.Role
175 | - `ChannelSelect.values` returns a list of disnake.abc.GuildChannel, disnake.Thread, or disnake.PartialMessageable
176 |
177 | :::note
178 |
179 | disnake.ui.ChannelSelect has an extra keyword argument that is
180 | not available to the other SelectMenu types.
181 |
182 | `channel_types` will allow you to specify the types of channels made available as options (omit to show all available channels):
183 |
184 | - `channel_types=[ChannelType.text]` to display only guild text channels as options
185 | - `channel_types=[ChannelType.voice, ChannelType.stage_voice]` to display only guild voice and stage channels as options
186 | - etc.
187 |
188 | See disnake.ChannelType to see more channel types.
189 | :::
190 |
191 | ### Handling View Timeouts
192 |
193 | When a View times out, the bot will no longer listen for these events, and your users will receive an error `This interaction failed`.
194 |
195 | To avoid this, you have a couple of options when the view times out:
196 |
197 | 1. Disable the components within the view so that they are no longer interactive.
198 | 2. Remove the view altogether, leaving only the original message without components.
199 |
200 | For this example we will disable the components using an `on_timeout` method. However, if you wish to remove the View completely you can pass `view=None` (instead of `view=self` like the example below).
201 |
202 | We'll continue with the `subclassing.py` example from above, only altering the relevant parts:
203 |
204 | ```python title="viewtimeout.py"
205 | ...
206 |
207 |
208 | class DropDownView(disnake.ui.View):
209 |
210 | message: disnake.Message # adding to typehint the future `message` attribute
211 |
212 | def __init__(self):
213 | # this time we will set the timeout to 30.0s
214 | super().__init__(timeout=30.0)
215 | # now let's add the `StringSelect` object we created above to this view
216 | self.add_item(AnimalDropdown())
217 |
218 | # To handle this timeout, we'll need to override the default `on_timeout` method within the `View``
219 | async def on_timeout(self):
220 | # Now we will edit the original command response so that it displays the disabled view.
221 | # Since `self.children` returns a list of the components attached to this `View` and we want
222 | # to prevent the components from remaining interactive after the timeout, we can easily loop
223 | # over its components and disable them.
224 | for child in self.children:
225 | if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
226 | child.disabled = True
227 |
228 | # Now, edit the message with this updated `View`
229 | await self.message.edit(view=self)
230 |
231 |
232 | ...
233 | # The only changes we need to make to handle the updated view is to fetch the original response after,
234 | # so that the message can be edited later.
235 | # This is necessary because `interaction.send` methods do not return a message object
236 | @bot.slash_command()
237 | async def animals(inter: disnake.ApplicationCommandInteraction):
238 | """Sends a message with our dropdown containing the animals"""
239 |
240 | # Create the view with our dropdown object.
241 | view = DropDownView()
242 |
243 | # Respond to the interaction with a message and our view.
244 | await inter.response.send_message("What is your favorite type of animal?", view=view)
245 |
246 | # This will add a new `message` attribute to the `DropDownView` that will be edited
247 | # once the view has timed out
248 | view.message = await inter.original_response()
249 |
250 |
251 | ...
252 | ```
253 |
254 | ### Views vs. low-level components
255 |
256 | As an alternative to using `View`s, it is possible to use Select Menus as low-level components.
257 | These components do not need to be sent as part of a View and can be sent as is.
258 |
259 | Note that any component being sent in this manner must have a `custom_id` explicitly set. Component interactions are sent to all listeners,
260 | which means the `custom_id` should be unique for each component to be able to identify the component in your code.
261 |
262 | The main advantage of this is that listeners, by nature, are persistent and will remain fully functional over bot reloads. Listeners are stored in the bot
263 | strictly once, and are shared by all components. Because of this, the memory footprint will generally be smaller than that of an equivalent view.
264 |
265 | The example below will be similar to the above examples, however will be executed as a low level component instead.
266 |
267 | ```python title="low_level_dropdown.py"
268 | @bot.slash_command()
269 | async def animals(inter: disnake.ApplicationCommandInteraction):
270 | """Sends a message with our dropdown containing the animals"""
271 |
272 | await inter.response.send_message(
273 | "What is your favorite type of animal?",
274 | components=[
275 | disnake.ui.StringSelect(
276 | custom_id="fav_animal",
277 | options=["Dog", "Cat", "Snake", "Gerbil"],
278 | )
279 | ],
280 | )
281 |
282 |
283 | # Now we create the listener that will handle the users's selection(s), similarly to the callback we used above.
284 | @bot.listen("on_dropdown")
285 | async def fav_animal_listener(inter: disnake.MessageInteraction):
286 | # First we should check if the interaction is for the `fav_animal` dropdown we created
287 | # and ignore if it isn't.
288 | if inter.component.custom_id != "fav_animal":
289 | return
290 |
291 | # Now we can respond with the user's favorite animal
292 | await inter.response.send_message(f"Your favorite type of animal is: {inter.values[0]}")
293 | ```
294 |
295 | :::note
296 | These component listeners can be used inside cogs as well. Simply replace `@bot.listen()` with `@commands.Cog.listener()` and
297 | be sure to pass `self` as the first argument of the listener function
298 | :::
299 |
--------------------------------------------------------------------------------
/guide/docs/interactions/slash-commands.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: They can be accessed via the textbox, and return the values the user submitted with the command.
3 | ---
4 |
5 | # Slash Commands
6 |
7 | A **slash command** is one of the types of client-integrated interactions a bot can create. Unlike prefix commands, these do not require the message content intent.
8 |
9 | This section will go in-depth on the creation, types and handling of slash commands with `disnake`.
10 |
11 | :::note
12 |
13 | A bot needs to be authorized with the `applications.commands` scope in order to create slash commands in a guild. We recommend adding this scope to your bot invite links.
14 |
15 | :::
16 |
17 | ## Defining slash commands
18 |
19 | To create a slash command, use the @Bot.slash_command decorator.
20 |
21 | ```python
22 | @bot.slash_command(
23 | name="ping",
24 | description="Returns websocket latency.",
25 | )
26 | async def ping(inter: disnake.ApplicationCommandInteraction):
27 | ...
28 | ```
29 |
30 |
31 |
32 |
33 |
34 |
35 | ping
36 |
37 |
38 | Latency: `42ms`!
39 |
40 |
41 |
42 |
43 | If you're using cogs, the @commands.slash_command decorator should be used instead.
44 |
45 | ```python
46 | class Meta(commands.Cog):
47 | """Meta commands."""
48 |
49 | @commands.slash_command(
50 | name="ping",
51 | description="Returns websocket latency.",
52 | )
53 | async def ping(self, inter: disnake.ApplicationCommandInteraction):
54 | ...
55 | ```
56 |
57 | ### Parameters
58 |
59 | While some commands can exist without arguments, most commands will need to take user input to be useful. Adding an option is as simple as defining a parameter on the callback.
60 |
61 | Here's an example of a command with one integer option (without a description):
62 |
63 | ```python
64 | @bot.slash_command(description="Multiplies the number by 7")
65 | async def multiply(inter, number: int):
66 | await inter.response.send_message(number * 7)
67 | ```
68 |
69 | To make a parameter optional, provide a default value for the option:
70 |
71 | ```python
72 | @bot.slash_command(description="Multiplies the number by a multiplier")
73 | async def multiply(inter, number: int, multiplier: int = 7):
74 | await inter.response.send_message(number * multiplier)
75 | ```
76 |
77 |
80 |
81 | ## Registering commands
82 |
83 | Unlike prefix commands, slash commands must be registered with Discord first, otherwise they won't show up if you press "/".
84 | By default, the library registers and updates your slash commands automatically.
85 | It does so on bot start or when cogs are loaded, unloaded, or reloaded.
86 |
87 | :::note
88 |
89 | The library avoids unnecessary API requests during this process.
90 | If the registered commands match the commands in your code, no API requests are made.
91 | Otherwise only one bulk overwrite request is done.
92 |
93 | :::
94 |
95 | If you want to disable the automatic registration, set the `sync_commands` parameter of `commands.Bot` to `False`.
96 | If you want to see how exactly the list of registered commands changes, set the `sync_commands_debug` parameter to `True`.
97 |
98 | ```python
99 | bot = commands.Bot("!", sync_commands_debug=True)
100 | ```
101 |
102 | It will print (or use the logger, if enabled) which commands were added, edited, deleted or left untouched.
103 |
104 | ### Global commands
105 |
106 | Global slash commands are visible everywhere, including your bot DMs.
107 | If you don't specify the `test_guilds` parameter of `commands.Bot`, all your slash commands are global by default.
108 |
109 | Global commands are visible and usable on most devices right away.
110 |
111 | ### Guild commands
112 |
113 | If you specify the `test_guilds` parameter of `commands.Bot`, all slash commands in your code are no longer global.
114 | The following code will register all slash commands only in 2 guilds:
115 |
116 | ```python
117 | bot = commands.Bot("!", test_guilds=[123456789, 987654321]) # guild IDs
118 | ```
119 |
120 | If you want to keep some of your slash commands global and make some of them local, specify the `guild_ids` parameter of `@bot.slash_command()`:
121 |
122 | ```python
123 | bot = commands.Bot("!")
124 |
125 |
126 | @bot.slash_command()
127 | async def global_command(inter):
128 | """This command is visible everywhere"""
129 | ...
130 |
131 |
132 | @bot.slash_command(guild_ids=[123456789])
133 | async def local_command(inter):
134 | """This command is visible in one guild"""
135 | ...
136 | ```
137 |
138 | ## Responding to commands {#responding}
139 |
140 | ### Sending responses {#response}
141 |
142 | The response attribute returns a InteractionResponse
143 | instance that has four ways of responding to an ApplicationCommandInteraction.
144 | A response can only be done once.
145 | If you want to send secondary messages, consider using a followup webhook instead.
146 |
147 | :::warning
148 |
149 | An interaction can only be responded to **once**. After a response is made, **no more responses can be made.** See the [followup](#followup) object for sending messages after responding.
150 | :::
151 |
152 | 1. send_message - Sends response message.
153 | 1. edit_message - Edits message, for example in
154 | component or component+modal interactions. Cannot be used with application commands.
155 | 1. defer - Defers the interaction.
156 | 1. send_modal - Sends a
157 | Modal
158 | as a response.
159 |
160 | :::note
161 |
162 | If you're going to run long processes (more than 3 seconds) before responding, you should first defer the interaction,
163 | as interactions expire after 3 seconds and later responses will fail.
164 | Deferring an interaction response shows a loading indicator to the user, and gives you more time to prepare a complete response.
165 | Once your response is ready, you can edit the original response using the Interaction.edit_original_response method.
166 |
167 | :::
168 |
169 | ```python title="example.py"
170 | @bot.slash_command()
171 | async def ping(inter: ApplicationCommandInteraction):
172 | await inter.response.send_message("Pong!")
173 |
174 |
175 | @bot.slash_command()
176 | async def defer(inter: ApplicationCommandInteraction):
177 | await inter.response.defer()
178 | await asyncio.sleep(10)
179 | await inter.edit_original_response(content="The wait is over, my comrades!")
180 | ```
181 |
182 | ### Followups
183 |
184 | Followups are a way to send a message after responding. There are two important restrictions for when a followup can be used:
185 |
186 | - The interaction must have been responded to (see [responding](#responses)).
187 | - The interaction must not be expired (i.e. hasn't exceeded the 15 minute limit).
188 | Checking if an interaction has expired can be done with ApplicationCommandInteraction.is_expired.
189 |
190 | At their core, followups are simply Webhook instances. The only special thing about them is that the `wait` parameter is treated as if it is always set to `True`.
191 |
192 | Take this as an example of how followups could be used:
193 |
194 | ```python
195 | @bot.slash_command()
196 | async def timer(inter: disnake.ApplicationCommandInteraction, seconds: int):
197 | await inter.response.send_message(f"Setting a timer for {seconds} seconds.")
198 | await asyncio.sleep(seconds)
199 | await inter.followup.send(f"{inter.author.mention}, your timer expired!")
200 | ```
201 |
202 |
203 |
204 |
205 |
206 |
207 | timer
208 |
209 |
210 | Setting a timer for 30 seconds.
211 |
212 |
213 |
214 | Setting a timer for 30 seconds.
215 |
216 | , your timer expired!
217 |
218 |
219 |
220 |
--------------------------------------------------------------------------------
/guide/docs/intro.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /
3 | description: The purpose of this guide is to make your journey with disnake easier.
4 | keywords: [disnake, bot, guide, api, python]
5 | ---
6 |
7 | # Introduction
8 |
9 | Welcome! The purpose of this guide is to make your journey with `disnake` easier, whether you're an experienced
10 | developer just getting into coding Discord bots, or an advanced bot developer who has decided to proceed with `disnake`
11 | as their library of choice.
12 |
13 | The concepts this guide will be going over include:
14 |
15 | - How to get started on working with bots
16 | - How to create and organize commands, using cogs/extensions
17 | - Working with databases (such as [`sqlite (aiosqlite)`][sqlite-docs] and [`mongodb (motor)`][motor-docs])
18 | - Using the [`AutoShardedClient`](https://disnake.readthedocs.io/en/stable/api.html#disnake.AutoShardedClient) class
19 | to shard your bot
20 | - A plethora of examples with popular topics along with in-depth explanation, and much more!
21 |
22 | [sqlite-docs]: https://aiosqlite.omnilib.dev/en/latest/
23 | [motor-docs]: https://motor.readthedocs.io/en/stable/tutorial-asyncio.html
24 |
25 | This guide will showcase the various features and events that the library has,
26 | while giving you an idea of how these functions work together as well as how a project might look in production.
27 |
28 | ## Disclaimer
29 |
30 | We appreciate the process of making Discord bots, but creating a bot with `disnake` requires a decent amount of
31 | experience on working with Python and object-oriented programming. While you _can_ go forward and make a bot with little
32 | to no knowledge of Python or programming, doing so may hinder your progress and cause issues in the future.
33 |
34 | If you don't know Python but would like to learn more, here are a few resources to get you started:
35 |
36 | - [Think Python](https://greenteapress.com/thinkpython/html/index.html), a free online book.
37 | - [Codecademy's course](https://www.codecademy.com/learn/learn-python-3) for learning Python 3.
38 | - [LearnPython](https://www.learnpython.org/), an interactive tutorial for absolute beginners.
39 | - [A Byte of Python by Swaroop C.H.](https://python.swaroopch.com/), an introductory text for people with no previous
40 | programming experience.
41 |
42 | An extensive list of these resources can be found on
43 | [Python's official website](https://wiki.python.org/moin/BeginnersGuide/NonProgrammers). Once you feel
44 | a little more experienced with Python, you can come back here to get started!
45 |
46 | ## Links & Credits
47 |
48 | This guide is made using [**Docusaurus**](https://docusaurus.io/), a static site generator geared towards building
49 | project documentation. The package utilized for Discord-like message elements is Danktuary's [`@discord-message-components/react`](https://www.npmjs.com/package/@discord-message-components/react).
50 |
51 | The idea of building a guide that showcases the use of `disnake`'s syntax and features was inspired from
52 | [**Discord.js**](https://discord.js.org/), one of the most popular JavaScript Discord libraries; their guide can be
53 | found [here](https://discordjs.guide/). The credit for the initial directory and articles structure goes to them.
54 |
55 | We also thank all contributors on the repository, who strive to make the guide better.
56 |
57 | Lastly, a huge thanks to **you**, the members of the `disnake` community, for supporting our library and choosing us for
58 | your bot development journey. We as the authors of this guide aim to familiarize users on coding Discord bots with our
59 | library, and your support gives us a boost to make the guide better, one commit at a time.
60 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/errors.mdx:
--------------------------------------------------------------------------------
1 | # Error Handling
2 |
3 | :::note
4 |
5 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/ext/commands/commands.html#error-handling), and inherited from `discord.py`. It will most likely be rewritten in the future.
6 |
7 | :::
8 |
9 | When our commands fail to parse we will, by default, receive a noisy error in `stderr` of our console that tells us
10 | that an error has happened and has been silently ignored.
11 |
12 | In order to handle our errors, we must use something called an error handler. There is a global error handler, called on_command_error() which works
13 | like any other event in the Event Reference. This global error handler is called for every error reached.
14 |
15 | Most of the time however, we want to handle an error local to the command itself. Luckily, commands come with local error
16 | handlers that allow us to do just that. First we decorate an error handler function with Command.error():
17 |
18 | ```python
19 | @bot.command()
20 | async def info(ctx, *, member: disnake.Member):
21 | """Tells you some info about the member."""
22 | msg = f"{member} joined on {member.joined_at} and has {len(member.roles)} roles."
23 | await ctx.send(msg)
24 |
25 |
26 | @info.error
27 | async def info_error(ctx, error):
28 | if isinstance(error, commands.BadArgument):
29 | await ctx.send("I could not find that member...")
30 | ```
31 |
32 | The first parameter of the error handler is the Context while the second one is an exception that is derived from CommandError. A list of errors is found in the Exceptions page of the documentation.
33 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/embeds-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/embeds-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/intents-bot-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-bot-tab.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/intents-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/intents-privileged.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-privileged.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/monetization-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/monetization-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/monetization-response.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/monetization-response.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/permissions-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/permissions-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/reactions-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/reactions-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/threads-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/threads-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/images/webhooks-intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/webhooks-intro.png
--------------------------------------------------------------------------------
/guide/docs/popular-topics/intents.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: This section covers a list of intents your bot might require.
3 | keywords: [disnake, bot, guide, tutorial, python, gateway intents]
4 | ---
5 |
6 | # Gateway Intents
7 |
8 | :::note
9 |
10 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/intents.html), and inherited from `discord.py`. It will most likely be rewritten in the future.
11 |
12 | :::
13 |
14 | An intent basically allows a bot to subscribe to specific buckets of events. The events that correspond to each intent is documented in the individual attribute of the Intents documentation.
15 |
16 | These intents are passed to the constructor of commands.Bot or its subclasses (, , ) with the `intents` argument.
17 |
18 | If intents are not passed, then the library defaults to every intent being enabled except the privileged intents, currently , and .
19 |
20 | ## What intents are needed?
21 |
22 | The intents that are necessary for your bot can only be dictated by yourself. Each attribute in the class documents what events it corresponds to and what kind of cache it enables.
23 |
24 | For example, if you want a bot that functions without spammy events like presences or typing then we could do the following:
25 |
26 | ```python title="intents.py" {3-5}
27 | import disnake
28 |
29 | intents = disnake.Intents.default()
30 | intents.typing = False
31 | intents.presences = False
32 |
33 | # Somewhere else:
34 | client = disnake.Client(intents=intents)
35 | # or,
36 | from disnake.ext import commands
37 |
38 | bot = commands.Bot(command_prefix=commands.when_mentioned_or("!"), intents=intents)
39 | ```
40 |
41 | :::note
42 |
43 | This doesn't enable since it's a privileged intent.
44 |
45 | :::
46 |
47 | Another example showing a bot that only deals with messages and guild information:
48 |
49 | ```python title="intents.py" {3}
50 | import disnake
51 |
52 | intents = disnake.Intents(messages=True, guilds=True)
53 | # If you also want reaction events enable the following:
54 | # intents.reactions = True
55 |
56 | # Somewhere else:
57 | client = disnake.Client(intents=intents)
58 | # or
59 | from disnake.ext import commands
60 |
61 | bot = commands.Bot(command_prefix=commands.when_mentioned, intents=intents)
62 | ```
63 |
64 | ## Privileged Intents
65 |
66 | With the API change requiring bot authors to specify intents, some intents were restricted further and require more manual steps. These intents are called **privileged intents**.
67 |
68 | A privileged intent is one that requires you to go to the developer portal and manually enable it. To enable privileged intents do the following:
69 |
70 | 1. Make sure you're logged on to the [Discord website](https://discord.com).
71 | 2. Navigate to the [application page](https://discord.com/developers/applications).
72 | 3. Click on the bot you want to enable privileged intents for.
73 | 4. Navigate to the bot tab on the left side of the screen.
74 |
75 |
76 |
81 |
82 |
83 | 5. Scroll down to the "Privileged Gateway Intents" section and enable the ones you want.
84 |
85 |
86 |
91 |
92 |
93 | :::warning
94 |
95 | Enabling privileged intents when your bot is in over 100 guilds requires going through [bot verification](https://support.discord.com/hc/en-us/articles/360040720412). If your bot is already verified and you would like to enable a privileged intent you must go through [Discord support](https://dis.gd/contact) and talk to them about it.
96 |
97 | :::
98 |
99 | :::note
100 |
101 | Even if you enable intents through the developer portal, you still have to enable the intents through code as well.
102 |
103 | :::
104 |
105 | ## Do I need privileged intents?
106 |
107 | This is a quick checklist to see if you need specific privileged intents.
108 |
109 | ### Message Content Intent
110 |
111 | - Whether you want a prefix that isn't the bot mention.
112 | - Whether you want to see what the content of a message was. This includes content, embeds, attachments, and components.
113 |
114 | ### Presence Intent
115 |
116 | - Whether you use at all to track member statuses.
117 | - Whether you use or to check member's activities.
118 |
119 | ### Member Intent
120 |
121 | - Whether you track member joins or member leaves, corresponds to and events.
122 | - Whether you want to track member updates such as nickname or role changes.
123 | - Whether you want to track user updates such as usernames, avatars, discriminators, etc.
124 | - Whether you want to request the guild member list through or .
125 | - Whether you want high accuracy member cache under .
126 |
127 | ## Member Cache
128 |
129 | Along with intents, Discord now further restricts the ability to cache members and expects bot authors to cache as little as is necessary. However, to properly maintain a cache the intent
130 | is required in order to track the members who left and properly evict them.
131 |
132 | To aid with member cache where we don't need members to be cached, the library now has a flag to control the member cache. The documentation page for the class goes over the specific policies that are possible.
133 |
134 | It should be noted that certain things do not need a member cache since Discord will provide full member information if possible. For example:
135 |
136 | - will have be a member even
137 | if cache is disabled.
138 | - will have the member parameter be a member even
139 | if cache is disabled.
140 | - will have the user parameter be a member when in a guild
141 | even if cache is disabled.
142 | - will have be
143 | a member when in a guild even if cache is disabled.
144 | - The reaction add events do not contain additional information when in direct messages. This is a Discord limitation.
145 | - The reaction removal events do not have member information. This is a Discord limitation.
146 |
147 | Other events that take a will require the use of the member cache. If absolute accuracy over the member cache is desirable, then it is advisable to have the intent enabled.
148 |
149 | ## Retrieving Members
150 |
151 | If the cache is disabled or you disable chunking guilds at startup, we might still need a way to load members. The library offers a few ways to do this:
152 |
153 | -
154 |
155 | - Used to query members by a prefix matching nickname or username.
156 | - This can also be used to query members by their user ID.
157 | - This uses the gateway and not the HTTP.
158 |
159 | -
160 |
161 | - This can be used to fetch the entire member list through the gateway.
162 |
163 | -
164 |
165 | - Used to fetch a member by ID through the HTTP API.
166 |
167 | -
168 |
169 | - Used to fetch a large number of members through the HTTP API.
170 |
171 | It should be noted that the gateway has a strict rate limit of 120 requests per 60 seconds.
172 |
173 | ## Troubleshooting
174 |
175 | Some common issues relating to the mandatory intent change.
176 |
177 | ### Where'd my members go?
178 |
179 | Due to an [API change](#member-cache) Discord is now forcing developers who want member caching to explicitly opt-in to it. This is a Discord mandated change and there is no way to bypass it. In order to get members back you have to explicitly enable the [members privileged intent](#privileged-intents) and
180 | change the attribute to true.
181 |
182 | For example:
183 |
184 | ```python title="intents.py" {4}
185 | import disnake
186 |
187 | intents = disnake.Intents.default()
188 | intents.members = True
189 |
190 | # Somewhere else:
191 | client = disnake.Client(intents=intents)
192 | # or
193 | from disnake.ext import commands
194 |
195 | bot = commands.Bot(command_prefix=commands.when_mentioned, intents=intents)
196 | ```
197 |
198 | ### Why do most messages have no content?
199 |
200 | After August 31st 2022, Discord will start blocking message content from being sent to bots that don't have the message content intent enabled.
201 |
202 | If you are on version 2.4 or before, your bot will be able to access message content without the intent enabled in the code. However, as of version 2.5, it is required to enable message_content to receive message content over the gateway.
203 |
204 | :::note
205 |
206 | Starting with version 2.5, or any version once the intent deadline has passed, you will be required to turn on the message content intent in the [Discord Developer Portal](https://discord.com/developers/applications) as well.
207 |
208 | :::
209 |
210 | Message content refers to four attributes on the object:
211 |
212 | - content
213 | - embeds
214 | - attachments
215 | - components
216 |
217 | You will always receive message content in the following cases even without the message content intent:
218 |
219 | - Messages the bot sends
220 | - Messages the bot receives in DMs
221 | - Messages in which the bot is mentioned
222 | - Messages received as part of an interaction (for example, a message command)
223 |
224 | ### Why does `on_ready` take so long to fire?
225 |
226 | As part of the API change regarding intents, Discord also changed how members are loaded in the beginning. Originally the library could request 75 guilds at once and only request members from guilds that have the Guild.large attribute set to `True`. With the new intent changes, Discord mandates that we can only send 1 guild per request. This causes a 75x slowdown which is further compounded by the fact that _all_ guilds, not just large guilds are being requested.
227 |
228 | There are a few solutions to fix this.
229 |
230 | 1. Request the privileged presences intent along with the privileged members intent and enable both of them. This allows the initial member list to contain online members just like the old gateway. Note that we're still limited to 1 guild per request but the number of guilds we request is significantly reduced.
231 |
232 | 2. Disable member chunking by setting `chunk_guilds_at_startup` to `False` when constructing a client. Then, when chunking for a guild is necessary you can use the various techniques to [retrieve members](#retrieving-members).
233 |
234 | To illustrate the slowdown caused by the API change, take a bot who is in 840 guilds and 95 of these guilds are "large" (over 250 members).
235 |
236 | Under the original system this would result in 2 requests to fetch the member list (75 guilds, 20 guilds) roughly taking 60 seconds. With but not this requires 840 requests, with a rate limit of 120 requests per 60 seconds means that due to waiting for the rate limit it totals to around 7 minutes of waiting for the rate limit to fetch all the members. With both Intents.members and Intents.presences we mostly get the old behaviour so we're only required to request for the 95 guilds that are large, this is slightly less than our rate limit so it's close to the original timing to fetch the member list.
237 |
238 | Unfortunately due to this change being required from Discord there is nothing that the library can do to mitigate this.
239 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/intro.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /popular-topics
3 | hide_table_of_contents: true
4 | description:
5 | This section covers several Discord client and gateway features extensively, including their methods and various
6 | use-cases.
7 | ---
8 |
9 | # Popular Topics
10 |
11 | This section covers several Discord client and gateway features extensively, including their methods and various
12 | use-cases.
13 |
14 |
135 |
136 | ## Resulting Code
137 |
138 |
139 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/monetization.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Add premium features to your bot.
3 | ---
4 |
5 | # Monetization
6 |
7 | :::note
8 |
9 | Monetization is limited to **verified** apps/bots.
10 |
11 | :::
12 |
13 | Premium Apps offer developers the ability to monetize their application through either **monthly recurring subscriptions** or **one-time purchases**, natively on Discord.
14 | This allows you to e.g. limit specific commands or other functionality to premium users/guilds.
15 |
16 | Not all applications are eligible - your app must be verified, part of a developer team, and use [slash commands](/interactions/slash-commands) or the privileged `Message Content` intent, among other things.
17 |
18 | ## Initial setup
19 |
20 | To get started, visit the [official documentation](https://discord.com/developers/docs/monetization/overview) to see the full list of requirements, and configure your app for monetization by following the steps outlined there.
21 |
22 | ## Entitlements
23 |
24 | Entitlements represent access to the premium functionality of your application. These can be granted to users or guilds, depending on the type of SKU you set up in the previous step.
25 |
26 | In [interactions](/interactions) (e.g. slash commands), the entitlements for the invoking user/guild are easily accessible using .
27 |
28 | Outside of interactions, you can fetch entitlements using Client.entitlements(), optionally only fetching entitlements of a specific user or guild. Note that this may include expired entitlements, unless you pass the `exclude_ended` parameter.
29 |
30 | To check whether an entitlement is still active, you can use Entitlement.is_active().
31 |
32 | ### Premium interactions
33 |
34 | This is usually the main way to provide premium functionality.
35 | Commands are not preemptively marked as "premium-only" - instead, you may respond with a premium button, prompting users to upgrade/purchase a specific SKU:
36 |
37 | ```python
38 | sku_id = 1234432112344321
39 |
40 |
41 | @bot.slash_command()
42 | async def command(inter: disnake.ApplicationCommandInteraction):
43 | if not any(e.sku_id == sku_id for e in inter.entitlements):
44 | await inter.response.send(
45 | content="Upgrade now to get access to this feature!",
46 | components=[disnake.ui.Button(sku_id=sku_id)],
47 | )
48 | return # skip remaining code
49 | ...
50 | ```
51 |
52 |
53 |
58 |
59 |
60 | ### Events
61 |
62 | Whenever users make a purchase in your app, you will receive an event. For subscriptions, entitlements are granted indefinitely until the user decides to cancel their subscription, in which case you will receive a event when the subscription ends.
63 | Note that entitlement events are not emitted immediately when a subscription is canceled, only at the end of the subscription period. In this case, the entitlement's ends_at attribute reflects the date indicating when the subscription (and entitlement) ended.
64 |
65 | :::note
66 | While an event also exists, it will not fire when a subscription expires; it only occurs e.g. in case of refunds or due to manual removal by Discord.
67 | :::
68 |
69 | ## Subscriptions
70 |
71 | Subscriptions are used for products with recurring monthly payments. These should not be used to determine access to premium features, they are only meant for lifecycle management purposes.
72 |
73 | Similarly to entitlements, you will receive an event whenever a subscription is created. An event is emitted when a user cancels their subscription; canceled subscriptions remain valid until the end of the subscription period. Further details about subscription lifecycles can be found in the [official documentation](https://discord.com/developers/docs/monetization/implementing-app-subscriptions#supporting-subscriptions).
74 |
75 | To obtain the subscriptions of a user to a particular SKU, you can use .
76 |
77 | ### Testing subscriptions
78 |
79 | For subscription SKUs, you can create test entitlements using and delete them using , which allows you to test your implementation in various subscription states. These entitlements do not expire and therefore have no start/end date.
80 |
81 | If you want to test the full payment flow, you can go through the same upgrade steps as any other user of your application would - all members of the app's associated team automatically receive a 100% discount on the subscription. Note that you cannot delete these entitlements, unlike the test entitlements mentioned before.
82 |
83 | ## One-time purchases
84 |
85 | One-time purchases can be durable (i.e. permanent) or consumable.
86 | Just like subscriptions, access to these items is represented by entitlements, which you can receive in entitlement events or interactions.
87 |
88 | Users may only have one unconsumed entitlement for an SKU at a time. To consume an entitlement, use and process/store the state of the item in your application where applicable.
89 |
90 | For further lifecycle details and other considerations, visit the [official documentation](https://discord.com/developers/docs/monetization/implementing-one-time-purchases#how-onetime-purchases-work) for one-time purchases.
91 |
92 | ### Testing one-time purchases
93 |
94 | Unlike subscriptions, one-time purchases may only be tested through the Application Test Mode, not via test entitlements.
95 | To test one-time purchases without being charged, [enable Application Test Mode](https://discord.com/developers/docs/monetization/implementing-one-time-purchases#using-application-test-mode) for your app and visit the app's store page.
96 | Entitlements tied to one-time purchases made this way will have a `type` of test_mode_purchase.
97 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/reactions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Create polls, paginate your commands, and more.
3 | hide_table_of_contents: true
4 | ---
5 |
6 | # Reactions
7 |
8 |
9 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/threads.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: An extensive article on methods and events to be used with Discord Threads.
3 | keywords: [disnake, bot, guide, tutorial, thread, python]
4 | ---
5 |
6 | # Threads
7 |
8 | Threads are Messageable objects, and can be thought of as
9 | sub-channels inside existing channels. They allow organization of multiple topics in a channel by temporarily giving
10 | them a separate space.
11 |
12 | ## Thread-related methods
13 |
14 | ### Creating and deleting threads
15 |
16 | A thread can be created by using the create_thread()
17 | method on a Message or TextChannel object.
18 |
19 | ```python title="threads.py"
20 | # Using the 'Message' object
21 | message = channel.fetch_message(1234567890)
22 | await message.create_thread(
23 | name="This message will act as the thread's starting message.",
24 | auto_archive_duration=60,
25 | )
26 |
27 | # Using the 'TextChannel' object
28 | channel = bot.get_channel(...)
29 | await channel.create_thread(
30 | name="This thread requires a starting message to be specified.",
31 | auto_archive_duration=60,
32 | message=message,
33 | )
34 | ```
35 |
36 | In order to delete a thread, you can use the delete() method on
37 | the Thread object.
38 |
39 | ```python title="threads.py"
40 | thread = channel.get_thread(...) # You can also use bot.get_channel(...)
41 |
42 | await thread.delete()
43 | ```
44 |
45 | ### Joining and leaving threads
46 |
47 | Both joining and leaving a thread require you to have a Thread object,
48 | on which you can use the join() and leave() methods for the respective action.
49 |
50 | ```python title="threads.py"
51 | thread = channel.get_thread(...) # You can also use bot.get_channel(...)
52 |
53 | # Joining a thread.
54 | await thread.join()
55 |
56 | # Leaving a thread.
57 | await thread.leave()
58 | ```
59 |
60 | It is recommended to use a `try-except` loop here, in case that the thread is not joinable by the bot user; this can be
61 | due to missing permissions.
62 |
63 | ### Archiving, unarchiving and locking threads
64 |
65 | Archiving a thread essentially makes it "read-only" for non-moderators - where they can view older messages, but not
66 | send messages themselves. Locked threads can only be unarchived by users who have the `manage_threads` permission.
67 |
68 | Threads have an **auto-archive duration** - a period of time after which the thread is archived automatically without
69 | being configured by a moderator. This duration can also be set by a bot user while creating or editing a thread.
70 |
71 | ```python title="threads.py"
72 | thread = channel.get_thread(...)
73 |
74 | await thread.edit(auto_archive_duration=60)
75 | ```
76 |
77 | Configuring a thread to be archived, unarchived or locked can be done using the edit() method, via the `archived` and `locked` parameters. Both of
78 | these attributes accept a boolean value.
79 |
80 | ```python title="threads.py"
81 | thread = channel.get_thread(...)
82 |
83 | # Archiving a thread
84 | await thread.edit(archived=True) # Set to 'False', to unarchive the thread
85 |
86 | # Locking a thread
87 | await thread.edit(locked=True)
88 | ```
89 |
90 | ### Public and private threads
91 |
92 | Public threads are accessible to all members that can view the thread's parent channel. Such threads can be created
93 | using the create_thread() method, as covered in
94 | [a previous section](#creating-and-deleting-threads).
95 |
96 | Private threads are those which are only accessible to moderators and the members invited by the thread creator. A
97 | private thread can be created by specifying the `type` in the create_thread() method as private_thread.
98 |
99 | ```python title="threads.py" {6}
100 | channel = bot.get_channel(...)
101 |
102 | await channel.create_thread(
103 | name="Thread Title",
104 | auto_archive_duration=60,
105 | type=disnake.ChannelType.private_thread,
106 | )
107 | ```
108 |
109 | :::note
110 |
111 | A private thread can only be created on a TextChannel object. The
112 | type that can be specified under create_thread() can
113 | be `public_thread`, `private_thread` or `news_thread`.
114 |
115 | :::
116 |
117 | ## Thread-related events
118 |
119 | Threads introduce some new gateway events, which are listed below. You can find more information on these
120 | [in the documentation](https://docs.disnake.dev/en/stable/api.html#disnake.on_thread_join).
121 |
122 | - on_thread_join
123 |
124 | - on_thread_remove
125 |
126 | - on_thread_member_join
127 |
128 | - on_thread_member_remove
129 |
130 | - on_thread_delete
131 |
132 | - on_thread_update
133 |
--------------------------------------------------------------------------------
/guide/docs/popular-topics/webhooks.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Send messages in a text channel without a bot user.
3 | hide_table_of_contents: true
4 | ---
5 |
6 | # Webhooks
7 |
8 |
9 |
--------------------------------------------------------------------------------
/guide/docs/prerequisites/creating-your-application.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: A short article on how to create an application for your Discord bot.
3 | keywords: [disnake, bot, guide, tutorial, create, application, python]
4 | ---
5 |
6 | # Creating your application
7 |
8 | The steps mentioned further in this markdown are essentially a copy of the steps
9 | [mentioned in the documentation](https://docs.disnake.dev/en/stable/discord.html). Therefore, you can follow the steps
10 | from either resource.
11 |
12 | :::note
13 |
14 | If you have already made your bot application, you can skip this document and move on to
15 | [`initial-files`](../getting-started/initial-files.mdx).
16 |
17 | :::
18 |
19 | ## Creating a bot account
20 |
21 | In order to work with the library and the Discord API in general, we must first create a Discord Bot account.
22 |
23 | Creating a Bot account is a pretty straightforward process.
24 |
25 | 1. Make sure you're logged on to the [Discord website](https://www.discord.com).
26 |
27 | 2. Navigate to the [application page](https://discord.com/developers/applications).
28 |
29 | 3. Click on the `New Application` button.
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 4. Give the application a name and click `Create`.
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 5. Navigate to the `Bot` tab. A bot account is added automatically when the application is created.
48 |
49 | 6. Make sure that `Public Bot` is ticked if you want others to invite your bot.
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | - You should also make sure that `Require OAuth2 Code Grant` is unchecked unless you are developing a service that
59 | needs it. If you're unsure, then **leave it unchecked**.
60 |
61 | 7. Copy the token by clicking `Reset Token` and then using the `Copy` button.
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | - Click "Yes, do it!" when asked.
71 | - This is **not** the Client Secret at the General Information page.
72 |
73 | And that's it. You now have a bot account and you can login with that token.
74 |
75 | ## Regarding bot tokens
76 |
77 | :::danger
78 |
79 | This section is critical, so pay close attention. It explains what your bot token is, as well as the security aspects of
80 | it.
81 |
82 | :::
83 |
84 | ### What is a token, anyway?
85 |
86 | It should be worth noting that the token is **essentially your bot's password**. You should **never** share this with
87 | someone else. In doing so, someone can log in to your bot and do malicious things, such as leaving servers, ban all
88 | members inside a server, or pinging everyone maliciously.
89 |
90 | Tokens look like this: `OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg` (don't worry, we immediately reset
91 | this token before even posting it here!). If it's any shorter and looks more like this:
92 | `CR8UbizwLgyES9LuHK7eIrXHbRTuqoEs`, you copied your client secret instead. Make sure to copy the token if you want your
93 | bot to work!
94 |
95 | ### Token leak scenario
96 |
97 | Let's imagine that you have a bot on over 1,000 servers, and it took you many, many months of coding and patience to get
98 | it on that amount. Your token gets leaked somewhere, and now someone else has it. That person can:
99 |
100 | - Spam every server your bot is on;
101 | - DM spam as many users as possible;
102 | - Delete as many channels as possible;
103 | - Kick or ban as many server members as possible;
104 | - Make your bot leave all of the servers it has joined;
105 | - Access and damage the underlying infrastructure (your server).
106 |
107 | All that and much, much more. Sounds pretty terrible, right? So make sure to keep your token as safe as possible! In the
108 | [initial files](../getting-started/initial-files.mdx) page of the guide, we cover how to safely store your token in a
109 | configuration file.
110 |
111 | :::caution
112 |
113 | If you accidentally leaked your token, click the “Regenerate” button as soon as possible. This revokes your old token
114 | and re-generates a new one. Now you need to use the new token to login.
115 |
116 | :::
117 |
118 | ### Discord's system messages
119 |
120 | System messages are official messages that Discord sends to alert you of an account issue. These messages can range from
121 | a variety of payment issues, support ticket updates, and ToS violation warnings. You can also expect a system message if
122 | your bot token has been posted on the internet publicly, and was found by Discord.
123 |
124 | **Such a message will look as follows:**
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | :::caution
133 |
134 | Since we do not know the extent to which Discord searches for bot tokens, or the time taken to inform the user about
135 | where the bot was found, we recommend not fully depending on this feature. But incase your bot token _is_ found on the
136 | internet publicly (say, on [a GitHub repository](https://www.github.com/)), you can expect Discord to send a system
137 | message to the account the bot is linked to.
138 |
139 | :::
140 |
141 | Note that Discord will never ask for your password or account token, and a system message will never request for the
142 | same. System messages from Discord will have a verified `SYSTEM` tag, and the bar at the bottom of the DM will mention -
143 | "This thread is reserved for official Discord notifications".
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | You can read more about the authenticity of these messages [in this article][discord-sys-msg-page].
152 |
153 | [discord-sys-msg-page]: https://support.discord.com/hc/en-us/articles/360036118732-Discord-System-Messages
154 |
155 | ## Inviting your bot
156 |
157 | So, you've made the bot account, but it is not actually in any server. If you want to invite your bot you must create an
158 | invite URL for it.
159 |
160 | 1. Make sure you're logged on to the [Discord website][discord-page].
161 |
162 | 2. Navigate to the [application page][discord-app-page].
163 |
164 | 3. Click on your bot's page.
165 |
166 | 4. Go to the `OAuth2` tab.
167 |
168 |
169 |
170 |
182 |
183 |
184 | - If you would like to integrate slash commands and other interactions into your bot, make sure to check the
185 | `applications.commands` scope as well.
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | 6. Tick the permissions required for your bot to function under `Bot Permissions`.
195 |
196 | - Please be aware of the consequences of requiring your bot to have the `Administrator` permission.
197 |
198 | - Bot owners must have 2FA enabled for certain actions and permissions when added in servers that have Server-Wide
199 | 2FA enabled. Check the [2FA support page][discord-2fa-page] for more information.
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | 7. Now the resulting URL can be used to add your bot to a server. Copy and paste the URL into your browser, choose a
209 | server to invite the bot to, and click `Authorize`.
210 |
211 | :::info
212 |
213 | The person adding the bot needs "Manage Server" permissions to do so.
214 |
215 | :::
216 |
217 | If you want to generate this URL dynamically at run-time inside your bot and using the interface, you can use .
218 |
219 | [discord-page]: https://www.discord.com
220 | [discord-app-page]: https://discord.com/developers/applications
221 | [discord-2fa-page]: https://support.discord.com/hc/en-us/articles/219576828-Setting-up-Two-Factor-Authentication
222 |
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/application-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/application-name.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/copy-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/copy-token.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/new-application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/new-application.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/new-bot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/new-bot.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/oauth-application-commands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-application-commands.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/oauth-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-page.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/oauth-permissions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-permissions.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/oauth-scopes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-scopes.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/system-dm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/system-dm.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/images/system-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/system-message.png
--------------------------------------------------------------------------------
/guide/docs/prerequisites/installing-disnake.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: An article on the various ways you can install disnake.
3 | keywords: [disnake, bot, guide, tutorial, install, python]
4 | hide_table_of_contents: true
5 | ---
6 |
7 | # Installing disnake
8 |
9 | :::note
10 |
11 | To use disnake, you'll first need to install [Python][python]. Like most other
12 | [`discord.py`](https://discordpy.readthedocs.io/en/latest) forks, disnake supports Python 3.8 or higher. For a full
13 | walkthrough on installing Python, we suggest following
14 | [this Real Python article](https://realpython.com/installing-python/), or
15 | [The Hitchhiker's Guide to Python](https://docs.python-guide.org/starting/installation/).
16 |
17 | :::
18 |
19 | To use disnake, you'll need to install it via `pip`, which is Python's standard package manager. Since pip comes
20 | standard with Python 3.4 and above, there is no need to separately install it.
21 |
22 | To install the library without full voice support, you can just run the following command:
23 |
24 |
25 |
26 |
27 | ```
28 | py -3 -m pip install -U disnake
29 | ```
30 |
31 |
32 |
33 |
34 | ```
35 | python3 -m pip install -U disnake
36 | ```
37 |
38 |
39 |
40 |
41 | ```
42 | python3 -m pip install -U disnake
43 | ```
44 |
45 |
46 |
47 |
48 | Otherwise to get voice support you should run the following command:
49 |
50 |
51 |
52 |
53 | ```
54 | py -3 -m pip install -U "disnake[voice]"
55 | ```
56 |
57 |
58 |
59 |
60 | ```
61 | python3 -m pip install -U "disnake[voice]"
62 | ```
63 |
64 |
65 |
66 |
67 | ```
68 | python3 -m pip install -U "disnake[voice]"
69 | ```
70 |
71 |
72 |
73 |
74 | To install the development version, do the following:
75 |
76 | ```
77 | $ pip install -U git+https://github.com/DisnakeDev/disnake#egg=disnake[speed,voice]
78 | ```
79 |
80 | :::info
81 |
82 | While installing voice on Linux, you must install the following packages via your favourite package manager (e.g. `apt`,
83 | `dnf`, etc.) before running the above commands:
84 |
85 | - libffi-dev (or `libffi-devel` on some systems)
86 | - python-dev (e.g. `python3.6-dev` for Python 3.6)
87 |
88 | :::
89 |
90 | And that's it! With all the necessities installed, you're almost ready to start coding your bot.
91 |
92 | [python]: https://www.python.org/downloads/
93 | [brew]: https://brew.sh/
94 | [opensource-linux]: https://opensource.com/article/20/4/install-python-linux
95 |
--------------------------------------------------------------------------------
/guide/docs/prerequisites/migrating-from-dpy.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Learn the syntax differences between `discord.py` and `disnake`, along with various methods to migrate.
3 | ---
4 |
5 | # Migrating from discord.py
6 |
7 | After the discontinuation of `discord.py` (refer
8 | [this gist](https://gist.github.com/Rapptz/4a2f62751b9600a31a0d3c78100287f1)), many forks of the API wrapper branched
9 | onward to maintain the library, in order to keep it updated with the latest features and Discord API changes - `disnake`
10 | is one such fork.
11 |
12 | Thus, if you've chosen `disnake` as your fork of choice in order to implement interactions/components and other
13 | features, this page will help you understand the changes in syntax, and aim for making your migrating process as smooth
14 | as possible.
15 |
16 | ## Differences between libraries
17 |
18 | `disnake` is based on `discord.py 2.0`, which had major syntax changes from its previous version. Therefore, if you're
19 | shifting to `disnake` from a version of `discord.py` lower than 2.0, you will have to make some important syntax changes
20 | in your code. You can refer [this page](https://gist.github.com/apple502j/f75b4f24652f04de85c7084ffd73ec58) for the full
21 | list of breaking changes in `discord.py 2.0`, though we will list some primary API reference changes here:
22 |
23 | - Methods and attributes that returned `TextChannel`, etc can now return `Thread`.
24 | - Attributes that returned `Asset` are renamed, e.g. attributes ending with `_url` (i.e. `avatar_url`) are changed to
25 | `avatar.url`. `User.avatar` returns `None` in case the default avatar is used.
26 | - `on_presence_update` replaces `on_member_update` for updates to `Member.status` and `Member.activities`.
27 | - Webhooks are changed significantly: `WebhookAdapter` is removed, and synchronous requests using `requests` is now
28 | inside `SyncWebhook`.
29 | - `edit` methods no longer updates cache and instead returns modified instance.
30 | - `Client.logout` is removed; use `Client.close` instead.
31 | - `Message.type` for replies are now `MessageType.reply`.
32 | - `Reaction.custom_emoji` property is changed to `Reaction.is_custom_emoji` method.
33 | - `missing_perms` attributes and arguments are renamed to `missing_permissions`.
34 | - Many arguments are now specified as positional-only or keyword-only; e.g. `oauth_url` now takes keyword-only
35 | arguments, and methods starting with `get_` or `fetch_` take positional-only arguments.
36 |
37 | ## Changing requirements
38 |
39 | In order to avoid conflicts between the libraries, you must uninstall `discord.py`. You can do so by using the following
40 | command in your terminal:
41 |
42 |
43 |
44 |
45 | ```
46 | py -3 -m pip uninstall discord
47 | ```
48 |
49 |
50 |
51 |
52 | ```
53 | python3 -m pip uninstall discord
54 | ```
55 |
56 |
57 |
58 |
59 | ```
60 | python3 -m pip uninstall discord
61 | ```
62 |
63 |
64 |
65 |
66 | To install `disnake`, you can follow the instructions on [this page](./installing-disnake).
67 |
68 | ## Rewriting your bot
69 |
70 | As discussed above, rewriting your code from an older `discord.py` version to `disnake` will require some major syntax
71 | changes. But if you're migrating from `discord.py 2.0`, all that's left now is changing the library references
72 | throughout the code, since the base code for both the libraries is practically the same.
73 |
74 | There are two ways to switch between libraries:
75 |
76 | #### Replace `discord` with `disnake`
77 |
78 | 1. Import `disnake` into your code (and delete the lines where you import `discord`).
79 |
80 | ```py
81 | import disnake
82 | from disnake.ext import commands
83 | ```
84 |
85 | 2. With your favorite editor, replace every `discord` reference in your code with `disnake` (this is fairly simple, if
86 | your editor has a "Find & Replace" tool).
87 |
88 | #### Import `disnake as discord`
89 |
90 | Import `disnake as discord` into your code (and delete the lines where you import `discord`). This reduces the effort of
91 | changing all references throughout your code.
92 |
93 | ```py
94 | import disnake as discord
95 | from disnake.ext import commands
96 | ```
97 |
98 | And that's it! Since `disnake` is a fork of `discord.py`, it inherits a lot of similarities - though we recommend you to
99 | always run your code to fix any possible issues.
100 |
--------------------------------------------------------------------------------
/guide/docusaurus.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | module.exports = {
4 | title: 'Disnake Guide',
5 | url: 'https://guide.disnake.dev/',
6 | favicon: 'public/favicon.ico',
7 |
8 | organizationName: 'DisnakeDev',
9 | projectName: 'guide',
10 | baseUrl: '/',
11 | trailingSlash: false,
12 |
13 | onBrokenLinks: 'warn',
14 | onBrokenMarkdownLinks: 'warn',
15 |
16 | plugins: ['docusaurus-plugin-sass'],
17 |
18 | presets: [
19 | [
20 | 'classic',
21 | /** @type {import('@docusaurus/preset-classic').Options} */
22 | ({
23 | docs: {
24 | routeBasePath: '/',
25 | sidebarPath: require.resolve('./sidebars.js'),
26 | editUrl: 'https://github.com/DisnakeDev/guide/edit/main/guide',
27 | showLastUpdateAuthor: true,
28 | showLastUpdateTime: true,
29 | },
30 | theme: {
31 | customCss: [require.resolve('./src/styles/index.scss')],
32 | },
33 | }),
34 | ],
35 | ],
36 |
37 | themeConfig: {
38 | colorMode: {
39 | respectPrefersColorScheme: true,
40 | },
41 | metadata: [
42 | {
43 | name: 'og:image',
44 | content: 'https://guide.disnake.dev/public/disnake-meta-image.png',
45 | },
46 | {
47 | name: 'theme-color',
48 | content: '#f0c43f',
49 | },
50 | {
51 | name: 'twitter:card',
52 | content: 'summary',
53 | },
54 | ],
55 | navbar: {
56 | title: 'Disnake',
57 | logo: {
58 | alt: 'Disnake Logo',
59 | src: 'public/disnake-logo.png',
60 | href: 'https://disnake.dev/',
61 | },
62 | items: [
63 | {
64 | to: 'https://docs.disnake.dev/',
65 | label: 'Docs',
66 | position: 'left',
67 | },
68 | {
69 | to: '/',
70 | label: 'Guide',
71 | position: 'left',
72 | },
73 | {
74 | to: 'https://docs.disnake.dev/en/stable/api/index.html',
75 | label: 'API Reference',
76 | position: 'left',
77 | },
78 | {
79 | href: 'https://github.com/DisnakeDev/disnake',
80 | className: 'navbar-item-github',
81 | position: 'right',
82 | },
83 | ],
84 | },
85 | docs: {
86 | sidebar: {
87 | autoCollapseCategories: true,
88 | },
89 | },
90 | prism: {
91 | theme: require('./src/utils/prismLight'),
92 | darkTheme: require('./src/utils/prismDark'),
93 | },
94 | algolia: {
95 | appId: 'WPCP8YA273',
96 | apiKey: '5a21886b115baa4f6819b23d0a1e87c0',
97 | indexName: 'guide-disnake',
98 | placeholder: 'Search',
99 | },
100 | },
101 | };
102 |
--------------------------------------------------------------------------------
/guide/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "disnake-guide",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "docusaurus start",
7 | "build": "docusaurus build",
8 | "deploy": "docusaurus deploy",
9 | "serve": "docusaurus serve",
10 | "swizzle": "docusaurus swizzle"
11 | },
12 | "repository": "https://github.com/DisnakeDev/guide.git",
13 | "author": "abhigyantrips ",
14 | "homepage": "https://guide.disnake.dev",
15 | "bugs": {
16 | "url": "https://github.com/DisnakeDev/guide/issues"
17 | },
18 | "dependencies": {
19 | "@discord-message-components/react": "^0.2.1"
20 | },
21 | "browserslist": {
22 | "production": [
23 | ">0.5%",
24 | "not dead",
25 | "not op_mini all"
26 | ],
27 | "development": [
28 | "last 1 chrome version",
29 | "last 1 firefox version",
30 | "last 1 safari version"
31 | ]
32 | },
33 | "devDependencies": {
34 | "@docusaurus/core": "^2.2.0",
35 | "@docusaurus/preset-classic": "^2.2.0",
36 | "@mdx-js/react": "^1.6.22",
37 | "clsx": "^1.1.1",
38 | "docusaurus-plugin-sass": "^0.2.2",
39 | "eslint": "^8.6.0",
40 | "eslint-plugin-mdx": "^1.16.0",
41 | "prism-react-renderer": "^1.2.1",
42 | "react": "^17.0.2",
43 | "react-dom": "^17.0.2",
44 | "react-native": "^0.66.4",
45 | "sass": "^1.49.9"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/guide/sidebars.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
4 | const sidebars = {
5 | guideSidebar: [
6 | {
7 | type: 'doc',
8 | label: 'Introduction',
9 | id: 'intro',
10 | },
11 | {
12 | type: 'category',
13 | label: 'Prerequisites',
14 | items: [
15 | 'prerequisites/installing-disnake',
16 | 'prerequisites/migrating-from-dpy',
17 | 'prerequisites/creating-your-application',
18 | ],
19 | },
20 | {
21 | type: 'category',
22 | label: 'Getting Started',
23 | items: ['getting-started/initial-files', 'getting-started/creating-commands', 'getting-started/using-cogs'],
24 | },
25 | {
26 | type: 'category',
27 | label: 'Interactions',
28 | link: { type: 'doc', id: 'interactions/intro' },
29 | items: [
30 | 'interactions/slash-commands',
31 | 'interactions/context-menus',
32 | 'interactions/buttons',
33 | 'interactions/select-menus',
34 | 'interactions/modals',
35 | ],
36 | },
37 | {
38 | type: 'category',
39 | label: 'Popular Topics',
40 | link: { type: 'doc', id: 'popular-topics/intro' },
41 | items: [
42 | 'popular-topics/threads',
43 | 'popular-topics/embeds',
44 | 'popular-topics/reactions',
45 | 'popular-topics/webhooks',
46 | 'popular-topics/permissions',
47 | 'popular-topics/errors',
48 | 'popular-topics/intents',
49 | 'popular-topics/monetization',
50 | ],
51 | },
52 | {
53 | type: 'category',
54 | label: 'Frequently Asked Questions',
55 | link: { type: 'doc', id: 'faq/intro' },
56 | items: ['faq/general', 'faq/administrative', 'faq/coroutines', 'faq/extensions', 'faq/good-practices'],
57 | },
58 | ],
59 | };
60 |
61 | module.exports = sidebars;
62 |
--------------------------------------------------------------------------------
/guide/src/components/DocsLink.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function DocsLink(props) {
4 | const basePath = 'https://docs.disnake.dev/en/stable'
5 | const docsPath = basePath + (props.ext ? `/ext/${props.ext}` : '') + (props.ext === 'tasks' ? '/index.html' : '/api.html') + (props.reference ? `#${props.reference}` : '')
6 | const docsText = (props.children ? props.children : (props.ext ? props.reference.replace(`disnake.ext.${props.ext}.`, "") : props.reference.replace("disnake.", "")))
7 |
8 | return(
9 | {docsText}
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/guide/src/components/ResultingCode.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {useLocation} from "@docusaurus/router";
3 |
4 | export default function ResultingCode() {
5 | const location = useLocation();
6 | const codeSampleURL = 'https://github.com/DisnakeDev/guide/tree/main/code-samples/';
7 |
8 | return(
9 | <>
10 |
11 | The code showcased in this section can be found on our GitHub repository{' '}
12 | here.
13 |