-
Notifications
You must be signed in to change notification settings - Fork 183
docs: redo events guide #1474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
LordOfPolls
merged 5 commits into
interactions-py:unstable
from
AstreaTSS:event-guide-update
Jul 10, 2023
Merged
docs: redo events guide #1474
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9657da7
docs: start work on updating the event guide
AstreaTSS 3db96a1
docs: finish up new event doc
AstreaTSS c20907e
docs: update error handling/tracking notes
AstreaTSS 5f012db
docs: update with review suggestions/changes
AstreaTSS 7a3f11c
feat: add back example usage
AstreaTSS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,79 +1,138 @@ | ||
| # Events | ||
|
|
||
| Events are dispatched whenever a subscribed event gets sent by Discord. | ||
| Events (in interactions.py) are pieces of information that are sent whenever something happens in Discord or in the library itself - this includes channel updates, message sending, the bot starting up, and more. | ||
|
|
||
| ## What Events Can I Get | ||
| ## Intents | ||
|
|
||
| What events you subscribe to are defined at startup by setting your `Intents`. | ||
|
|
||
| `Intents.DEFAULT` is a good place to start if your bot is new and small, otherwise, it is recommended to take your time and go through them one by one. | ||
| ```python | ||
| bot = Client(intents=Intents.DEFAULT) | ||
| bot.start("Put your token here") | ||
| ``` | ||
| By default, interactions.py automatically uses every intent but privileged intents (discussed in a bit). This means you're receiving data about *a lot* of events - it's nice to have those intents while starting out, but we heavily encourage narrowing them so that your bot uses less memory and isn't slowed down by processing them. | ||
|
|
||
| For more information, please visit the API reference [here](/interactions.py/API Reference/API Reference/models/Discord/enums/#internal.models.discord.enums.Intents). | ||
| There are two ways of setting them. We'll use the `GUILDS` and `GUILD_INVITES` intents as an example, but you should decide what intents you need yourself. | ||
|
|
||
| ## Hey Listen!!! | ||
| === ":one: Directly through `Intents`" | ||
| ```python | ||
| from interactions import Intents | ||
| bot = Client(intents=Intents.GUILDS | Intents.GUILD_INVITES) | ||
| ``` | ||
|
|
||
| Now you can receive events. To respond to them, you need to register a callback. Callbacks should be lower-case, use `_` instead of spaces and start with `on_`. | ||
| Depending on how you register your callbacks that's not a requirement, but it is a good habit nonetheless. | ||
| === ":two: `Intents.new`" | ||
| ```python | ||
| from interactions import Intents | ||
| bot = Client(intents=Intents.new(guilds=True, guild_invites=True)) | ||
| ``` | ||
|
|
||
| For example, the event callback for the `ChannelCreate` event should be called `on_channel_create`. | ||
| Some intents are deemed to have sensitive content by Discord and so have extra restrictions on them - these are called **privileged intents.** As of right now, these include *message content, guild members, and presences.* These require extra steps to enable them for your bot: | ||
|
|
||
| You can find all events and their signatures [here](/interactions.py/API Reference/API Reference/events/discord/). | ||
| 1. Go to the [Discord developer portal](https://discord.com/developers/applications/). | ||
| 2. Select your application. | ||
| 3. In the "Bot" tab, go to the "Privileged Gateway Intents" category and scroll down to the privileged intents you want. | ||
| 4. Enable the toggle. | ||
| - **If your bot is verified or in more than 100 servers, you need to apply for the intent through Discord in order to toggle it.** | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Be aware that your `Intents` must be set to receive the event you are looking for. | ||
| Then, you can specify it in your bot just like the other intents. | ||
|
|
||
| --- | ||
| !!! danger | ||
| `Intents.ALL` is a shortcut provided by interactions.py to enable *every single intents, including privileged intents.* This is very useful while testing bots, **but this shortcut is an incredibly bad idea to use when actually running your bots for use.** As well as adding more strain on the bot (as discussed earlier with normal intents), this is just a bad idea privacy wise: your bot likely does not need to know that much data. | ||
|
|
||
| There are two ways to register events. **Decorators** are the recommended way to do this. | ||
| For more information, please visit the API reference about Intents [at this page](/interactions.py/API Reference/API Reference/models/Discord/enums/#interactions.models.discord.enums.Intents). | ||
|
|
||
| === ":one: Decorators" | ||
| ```python | ||
| from interactions import listen | ||
| from interactions.api.events import ChannelCreate | ||
| ## Event Listening | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @listen() | ||
| async def on_channel_create(event: ChannelCreate): | ||
| # this event is called when a channel is created in a guild where the bot is | ||
| After your intents have been properly configured, you can start to listen to events. Say, if you wanted to listen to channels being created in a guild the bot can see, then all you would have to do is this: | ||
|
|
||
| print(f"Channel created with name: {event.channel.name}") | ||
| ``` | ||
| ```python | ||
| from interactions import listen | ||
| from interactions.api.events import ChannelCreate | ||
|
|
||
| You can also use `@listen` with any function names: | ||
| @listen(ChannelCreate) | ||
| async def channel_create_handler(event: ChannelCreate): | ||
| print(f"Channel created with name: {event.channel.name}") | ||
| ``` | ||
|
|
||
| As you can see, the `listen` statement marks a function to receieve (or, well, listen to) a specific event - we specify which event to receive by passing in the *event object*, which an object that contains all information about an event. Whenever that events happens in Discord, it triggers our function to run, passing the event object into it. Here, we get the channel that the event contains and send out its name to the terminal. | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ???+ note "Difference from other Python Discord libraries" | ||
| If you come from some other Python Discord libraries, or even come from older versions of interactions.py, you might have noticed how the above example uses an *event object* - IE a `ChannelCreate` object - instead of passing the associated object with that event - IE a `Channel` (or similar) object - into the function. This is intentional - by using event objects, we have greater control of what information we can give to you. | ||
|
|
||
| For pretty much every event object, the object associated with that event is still there, just as an attribute. Here, the channel is in `event.channel` - you'll usually find the object in other events in a similar format. | ||
| Update events usually use `event.before` and `event.after` too. | ||
|
|
||
| While the above is the recommended format for listening to events (as you can be sure that you specified the right event), there are other methods for specifying what event you're listening to: | ||
|
|
||
| ???+ warning "Event name format for some methods" | ||
| You may notice how some of these methods require the event name to be `on_all_in_this_case`. The casing itself is called *snake case* - it uses underscores to indicate either a literal space or a gap between words, and exclusively uses lowercase otherwise. To transform an event object, which is in camel case (more specifically, Pascal case), to snake case, first take a look at the letters that are capital, make them lowercase, and add an underscore before those letters *unless it's the first letter of the name of the object*. | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| For example, looking at **C**hannel**C**reate, we can see two capital letters. Making them lowercase makes it **c**hannel**c**reate, and then adding an underscore before them makes them **c**hannel**_c**reate (notice how the first letter does *not* have a lowercase before them). | ||
|
|
||
| **However, there is one more step after this.** The methods that use the snake case spelling *also* require using `on_` before them - for example, our ChannelCreate is `on_channel_create`, not just `channel_create`. This matches the behavior of other Python Discord libraries. | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| If you're confused by any of this, stay away from methods that use this type of name formatting. | ||
|
|
||
| === ":one: Type Annotation" | ||
| ```python | ||
| @listen(ChannelCreate) | ||
| async def my_function(event: ChannelCreate): | ||
| # you can pass the event | ||
| @listen() | ||
| async def channel_create_handler(event: ChannelCreate): | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ... | ||
| ``` | ||
|
|
||
| === ":two: String in `listen`" | ||
| ```python | ||
| @listen("on_channel_create") | ||
| async def my_function(event: ChannelCreate): | ||
| # you can also pass the event name as a string | ||
| async def channel_create_handler(event): | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ... | ||
| ``` | ||
|
|
||
| === ":three: Function name" | ||
| ```python | ||
| @listen() | ||
| async def my_function(event: ChannelCreate): | ||
| # you can also use the typehint of `event` | ||
| async def on_channel_create(event): | ||
| ... | ||
| ``` | ||
|
|
||
| === ":two: Manual Registration" | ||
| You can also register the events manually. This gives you the most flexibility, but it's not the cleanest. | ||
| ## Other Notes About Events | ||
|
|
||
| ```python | ||
| from interactions import Listener | ||
| from interactions.api.events import ChannelCreate | ||
| ### No Argument Events | ||
|
|
||
| Some events may have no information to pass - the information is the event itself. This happens a lot with the internal events - events that are specific to interactions.py, not Discord. | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| async def on_channel_create(event: ChannelCreate): | ||
| # this event is called when a channel is created in a guild where the bot is | ||
| Whenever this happens, you can specify the event to simply not pass anything into the function, as can be seen with the startup event: | ||
|
|
||
| print(f"Channel created with name: {event.channel.name}") | ||
| ```python | ||
| from interactions.api.events import Startup | ||
|
|
||
| @listen(Startup) | ||
| async def startup_func(): | ||
| ... | ||
| ``` | ||
|
|
||
| bot = Client(intents=Intents.DEFAULT) | ||
| bot.add_listener(Listener(func=on_channel_create, event="on_channel_create")) | ||
| bot.start("Put your token here") | ||
| ``` | ||
| Using a parameter for these objects still works, though. | ||
AstreaTSS marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Disabling Default Listeners | ||
|
|
||
| Some internal events, like `ModalCompletion`, have default listeners that perform niceties like logging the command/interaction logged. You may not want this, however, and may want to completely override this behavior without subclassiung `Client`. If so, you can acheive it through `disable_default_listeners`: | ||
|
|
||
| ```python | ||
| from interactions.api.events import ModalCompletion | ||
|
|
||
| @listen(ModalCompletion, disable_default_listeners=True) | ||
| async def my_modal_completion(event: ModalCompletion): | ||
| print("I now control ModalCompletion!") | ||
| ``` | ||
|
|
||
| A lot of times, this behavior is used for custom error tracking. If so, [take a look at the error tracking guide](../25 Error Tracking) for a guide on that. | ||
|
|
||
| ## Events to Listen To | ||
|
|
||
| There are a plethora of events that you can listen to. You can find a list of events that are currently supported through the two links below - every class listened on these two pages are available for you, though be aware that your `Intents` must be set appropriately to receive the event you are looking for. | ||
|
|
||
| - [Discord Events](/interactions.py/API Reference/API Reference/events/discord/) | ||
| - [Internal Events](/interactions.py/API Reference/API Reference/events/internal/) | ||
|
|
||
| ### Frequently Used Events | ||
|
|
||
| - [Startup](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.Startup) is an event, as its name implies, that runs when the bot is first started up - more specifically, it runs when the bot is first ready to do actions. This is a good place to set up tools or libraries that require an asynchronous function. | ||
| - [Error](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.Error) and its many, *many* subclasses about specific types of errors trigger whenever an error occurs while the bot is running. If you want error *tracking* (IE just logging the errors you get to fix them later on), then [take a look at the error tracking guide](../25 Error Tracking). Otherwise, you can do specific error handling using these events (ideally with `disable_default_listeners` turned on) to provide custom messages for command errors. | ||
| - [Component](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.Component), [ButtonPressed](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.ButtonPressed), [Select](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.Select), and [ModalCompletion](/interactions.py/API Reference/API Reference/events/internal/#interactions.api.events.internal.ModalCompletion) may be useful for you if you're trying to respond to component or modal interactions - take a look at the [component guide](../05 Components) or the [modal guide](../06 Modals) for more information. | ||
| - [MessageCreate](/interactions.py/API Reference/API Reference/discord/#interactions.api.events.discord.MessageCreate) is used whenever anyone sends a message to a channel the bot can see. This can be useful for automoderation, though note *message content is a privileged intent*, as talked about above. For prefixed/text commands in particular, we already have our own implementation - take a look at them [at this page](../26 Prefixed Commands). | ||
| - [GuildJoin](/interactions.py/API Reference/API Reference/events/discord/#interactions.api.events.discord.GuildJoin) and [GuildLeft](/interactions.py/API Reference/API Reference/events/discord/#interactions.api.events.discord.GuildLeft) are, as you can expect, events that are sent whenever the bot joins and leaves a guild. Note that for `GuildJoin`, the event triggers for *every guild on startup* - it's best to have a check to see if the bot is ready through `bot.is_ready` and ignore this event if it isn't. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.