disnake
123 строки · 4.9 Кб
1# SPDX-License-Identifier: MIT
2
3"""An example on how to send and process components without using views."""
4
5import os
6
7import disnake
8from disnake.ext import commands
9
10bot = commands.Bot(command_prefix=commands.when_mentioned)
11
12
13# As an alternative to using views, it is possible to use a more low-level approach to components.
14# Firstly, components do not have to be sent as part of a view. Instead, they can be sent as-is.
15# Take special note of the fact that `custom_id`s have been explicitly set on these components.
16
17# The main advantage of this is that listeners are, by nature, persistent.
18# Each listener is stored on the bot strictly once, and are shared by all components.
19# Because of this, their memory footprint will generally be smaller than that of an equivalent view.
20
21
22@bot.command()
23async def send_button(ctx: commands.Context):
24await ctx.send(
25"Here's a button!",
26components=disnake.ui.Button(label="Click me!", custom_id="cool_button"),
27)
28
29
30@bot.command()
31async def send_select(ctx: commands.Context):
32await ctx.send(
33"Here's a select!",
34components=disnake.ui.StringSelect(options=["1", "2", "3"], custom_id="cool_select"),
35)
36
37
38# To send multiple components, they can simply be stored in a list. They will then automatically
39# fill out rows as they fit, similar to views if `row` is not set on the components. If a specific
40# row ordering is desired, simply store them in a list of lists or `disnake.ui.ActionRow`s instead:
41
42
43@bot.command()
44async def send_all_the_buttons(ctx: commands.Context):
45buttons = []
46for y in range(4):
47row = disnake.ui.ActionRow()
48buttons.append(row)
49for x in range(4):
50row.add_button(label=f"({x}, {y})", custom_id=f"gridbutton_{x}_{y}")
51
52await ctx.send("Here's a 4x4 grid of buttons:", components=buttons)
53
54
55# As-is, the components sent by these commands will do absolutely nothing. To remedy this, you
56# would use a listener. Think of the listener as an equivalent to a view button's callback.
57# However, the listener would function as a callback for **all** buttons. This is where the
58# `custom_id` comes in: by filtering for the correct custom_id, a listener can be made to respond
59# to a specific range of components.
60
61
62# In this case, listening for only buttons is sufficient, thus the `on_button_click`-event is used.
63# The equivalent event for select menus is `on_dropdown`. For modals, this is `on_modal_submit`.
64# Finally, for _any_ kind of message interaction, the relevant event is `on_message_interaction`.
65
66
67@bot.listen("on_button_click")
68async def cool_button_listener(inter: disnake.MessageInteraction):
69if inter.component.custom_id != "cool_button":
70# Since `inter.component` returns the component that triggered the interaction,
71# this is used to filter interactions for components other than the button we wish to
72# process with this listener.
73return
74
75# Thus, we end up with only buttons sent by the `send_button` command,
76# since those buttons were sent with `custom_id=cool_button`.
77# At this point, this listener is practically identical to the callback of a view button.
78await inter.response.send_message("You clicked the cool button!")
79
80
81# Similarly, a listener for the select menu can be created:
82
83
84@bot.listen("on_dropdown")
85async def cool_select_listener(inter: disnake.MessageInteraction):
86if inter.component.custom_id != "cool_select":
87# The same principle as for the button, any selects with the wrong `custom_id` are ignored.
88return
89
90await inter.response.send_message(f"You selected {inter.values}!")
91
92
93# Lastly, a more generic type of listener for the example with multiple buttons:
94
95
96@bot.listen("on_button_click")
97async def grid_listener(inter: disnake.MessageInteraction):
98if not inter.component.custom_id or not inter.component.custom_id.startswith("gridbutton"):
99# The same principle again, except this time we want all buttons that start with grid_button,
100# as there are now 16 different `custom_id`s. This is a much better idea than making 16
101# different listeners, one for each button, of course!
102return
103
104# Now we can extract the x/y data...
105_, x, y = inter.component.custom_id.split("_")
106await inter.response.send_message(f"You hit ({x}, {y}). You sunk my battleship!")
107
108
109# Since these `custom_id`s are stored on the buttons and the listeners aren't dependent on any kind
110# of state, the components handled this way will remain fully functional over bot reloads!
111
112# Note that listeners can also be added inside of cogs. For this, the only changes that would have
113# to be made are to use `commands.Cog.listener` instead of `bot.listen`, and the first argument of
114# the listener would have to be `self`.
115
116
117@bot.event
118async def on_ready():
119print(f"Logged in as {bot.user} (ID: {bot.user.id})\n------")
120
121
122if __name__ == "__main__":
123bot.run(os.getenv("BOT_TOKEN"))
124