Skip to content

Commit 12a3a33

Browse files
authored
Merge branch 'discord-py-slash-commands:master' into master
2 parents 7a04a81 + 0ba0d02 commit 12a3a33

File tree

8 files changed

+272
-98
lines changed

8 files changed

+272
-98
lines changed

discord_slash/client.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,11 @@ def wrapper(cmd):
876876
def permission(self, guild_id: int, permissions: list):
877877
"""
878878
Decorator that add permissions. This will set the permissions for a single guild, you can use it more than once for each command.
879+
879880
:param guild_id: ID of the guild for the permissions.
880881
:type guild_id: int
881-
:param permissions: Permission requirements of the slash command. Default ``None``.
882-
:type permissions: dict
883-
882+
:param permissions: List of permissions to be set for the specified guild.
883+
:type permissions: list
884884
"""
885885

886886
def wrapper(cmd):
@@ -908,9 +908,9 @@ def add_component_callback(
908908
:param Coroutine callback: The coroutine to be called when the component is interacted with. Must accept a single argument with the type :class:`.context.ComponentContext`.
909909
:param messages: If specified, only interactions from the message given will be accepted. Can be a message object to check for, or the message ID or list of previous two. Empty list will mean that no interactions are accepted.
910910
:type messages: Union[discord.Message, int, list]
911-
:param components: If specified, only interactions with ``custom_id``s of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
911+
:param components: If specified, only interactions with ``custom_id`` of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
912912
:type components: Union[str, dict, list]
913-
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either `messages`` or ``components`` must be specified.
913+
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either ``messages`` or ``components`` must be specified.
914914
:type use_callback_name: bool
915915
:param component_type: The type of the component to avoid collisions with other component types. See :class:`.model.ComponentType`.
916916
:type component_type: Optional[int]
@@ -961,14 +961,14 @@ def extend_component_callback(
961961
custom_id: str = None,
962962
):
963963
"""
964-
Registers existing callback object (:class:`.model.ComponentType`)
964+
Registers existing callback object (:class:`.model.ComponentCallbackObject`)
965965
for specific combination of message_id, custom_id, component_type.
966966
967967
:param callback_obj: callback object.
968968
:type callback_obj: model.ComponentCallbackObject
969969
:param message_id: If specified, only removes the callback for the specific message ID.
970970
:type message_id: Optional[.model]
971-
:param custom_id: The `custom_id` of the component.
971+
:param custom_id: The ``custom_id`` of the component.
972972
:type custom_id: Optional[str]
973973
:raises: .error.DuplicateCustomID, .error.IncorrectFormat
974974
"""
@@ -988,7 +988,7 @@ def get_component_callback(
988988
989989
:param message_id: If specified, only removes the callback for the specific message ID.
990990
:type message_id: Optional[.model]
991-
:param custom_id: The `custom_id` of the component.
991+
:param custom_id: The ``custom_id`` of the component.
992992
:type custom_id: Optional[str]
993993
:param component_type: The type of the component. See :class:`.model.ComponentType`.
994994
:type component_type: Optional[int]
@@ -1014,7 +1014,7 @@ def remove_component_callback(
10141014
10151015
:param message_id: If specified, only removes the callback for the specific message ID.
10161016
:type message_id: Optional[int]
1017-
:param custom_id: The `custom_id` of the component.
1017+
:param custom_id: The ``custom_id`` of the component.
10181018
:type custom_id: Optional[str]
10191019
:param component_type: The type of the component. See :class:`.model.ComponentType`.
10201020
:type component_type: Optional[int]
@@ -1067,9 +1067,9 @@ def component_callback(
10671067
10681068
:param messages: If specified, only interactions from the message given will be accepted. Can be a message object to check for, or the message ID or list of previous two. Empty list will mean that no interactions are accepted.
10691069
:type messages: Union[discord.Message, int, list]
1070-
:param components: If specified, only interactions with ``custom_id``s of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
1070+
:param components: If specified, only interactions with ``custom_id`` of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
10711071
:type components: Union[str, dict, list]
1072-
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either `messages`` or ``components`` must be specified.
1072+
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either ``messages`` or ``components`` must be specified.
10731073
:type use_callback_name: bool
10741074
:param component_type: The type of the component to avoid collisions with other component types. See :class:`.model.ComponentType`.
10751075
:type component_type: Optional[int]

discord_slash/cog_ext.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ async def ping(self, ctx: SlashContext):
5050
:param connector: Kwargs connector for the command. Default ``None``.
5151
:type connector: dict
5252
"""
53+
if not permissions:
54+
permissions = {}
5355

5456
def wrapper(cmd):
57+
decorator_permissions = getattr(cmd, "__permissions__", None)
58+
if decorator_permissions:
59+
permissions.update(decorator_permissions)
60+
5561
desc = description or inspect.getdoc(cmd)
5662
if options is None:
5763
opts = manage_commands.generate_options(cmd, desc, connector)
@@ -138,8 +144,14 @@ async def group_say(self, ctx: SlashContext, text: str):
138144
base_description = base_description or base_desc
139145
subcommand_group_description = subcommand_group_description or sub_group_desc
140146
guild_ids = guild_ids if guild_ids else []
147+
if not base_permissions:
148+
base_permissions = {}
141149

142150
def wrapper(cmd):
151+
decorator_permissions = getattr(cmd, "__permissions__", None)
152+
if decorator_permissions:
153+
base_permissions.update(decorator_permissions)
154+
143155
desc = description or inspect.getdoc(cmd)
144156
if options is None:
145157
opts = manage_commands.generate_options(cmd, desc, connector)
@@ -177,6 +189,25 @@ def wrapper(cmd):
177189
return wrapper
178190

179191

192+
def permission(guild_id: int, permissions: list):
193+
"""
194+
Decorator that add permissions. This will set the permissions for a single guild, you can use it more than once for each command.
195+
196+
:param guild_id: ID of the guild for the permissions.
197+
:type guild_id: int
198+
:param permissions: List of permissions to be set for the specified guild.
199+
:type permissions: list
200+
"""
201+
202+
def wrapper(cmd):
203+
if not getattr(cmd, "__permissions__", None):
204+
cmd.__permissions__ = {}
205+
cmd.__permissions__[guild_id] = permissions
206+
return cmd
207+
208+
return wrapper
209+
210+
180211
def cog_component(
181212
*,
182213
messages: typing.Union[int, discord.Message, list] = None,
@@ -190,9 +221,9 @@ def cog_component(
190221
191222
:param messages: If specified, only interactions from the message given will be accepted. Can be a message object to check for, or the message ID or list of previous two. Empty list will mean that no interactions are accepted.
192223
:type messages: Union[discord.Message, int, list]
193-
:param components: If specified, only interactions with ``custom_id``s of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
224+
:param components: If specified, only interactions with ``custom_id`` of given components will be accepted. Defaults to the name of ``callback`` if ``use_callback_name=True``. Can be a custom ID (str) or component dict (actionrow or button) or list of previous two.
194225
:type components: Union[str, dict, list]
195-
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either `messages`` or ``components`` must be specified.
226+
:param use_callback_name: Whether the ``custom_id`` defaults to the name of ``callback`` if unspecified. If ``False``, either ``messages`` or ``components`` must be specified.
196227
:type use_callback_name: bool
197228
:param component_type: The type of the component to avoid collisions with other component types. See :class:`.model.ComponentType`.
198229
:type component_type: Optional[int]

discord_slash/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Discord Slash Constants"""
22

3-
__version__ = "2.0.1"
3+
__version__ = "2.2.0"
44

55
BASE_API = "https://discord.com/api/v8"

discord_slash/context.py

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ class InteractionContext:
2323
:ivar bot: discord.py client.
2424
:ivar _http: :class:`.http.SlashCommandRequest` of the client.
2525
:ivar _logger: Logger instance.
26+
:ivar data: The raw data of the interaction.
27+
:ivar values: The values sent with the interaction. Currently for selects.
2628
:ivar deferred: Whether the command is current deferred (loading state)
2729
:ivar _deferred_hidden: Internal var to check that state stays the same
2830
:ivar responded: Whether you have responded with a message to the interaction.
2931
:ivar guild_id: Guild ID of the command message. If the command was invoked in DM, then it is ``None``
3032
:ivar author_id: User ID representing author of the command message.
3133
:ivar channel_id: Channel ID representing channel of the command message.
3234
:ivar author: User or Member instance of the command invoke.
35+
3336
"""
3437

3538
def __init__(
@@ -47,6 +50,8 @@ def __init__(
4750
self._logger = logger
4851
self.deferred = False
4952
self.responded = False
53+
self.data = _json["data"]
54+
self.values = _json["data"]["values"] if "values" in _json["data"] else None
5055
self._deferred_hidden = False # To check if the patch to the deferred response matches
5156
self.guild_id = int(_json["guild_id"]) if "guild_id" in _json.keys() else None
5257
self.author_id = int(
@@ -188,15 +193,22 @@ async def send(
188193
"The top level of the components list must be made of ActionRows!"
189194
)
190195

196+
if allowed_mentions is not None:
197+
if self.bot.allowed_mentions is not None:
198+
allowed_mentions = self.bot.allowed_mentions.merge(allowed_mentions).to_dict()
199+
else:
200+
allowed_mentions = allowed_mentions.to_dict()
201+
else:
202+
if self.bot.allowed_mentions is not None:
203+
allowed_mentions = self.bot.allowed_mentions.to_dict()
204+
else:
205+
allowed_mentions = {}
206+
191207
base = {
192208
"content": content,
193209
"tts": tts,
194210
"embeds": [x.to_dict() for x in embeds] if embeds else [],
195-
"allowed_mentions": allowed_mentions.to_dict()
196-
if allowed_mentions
197-
else self.bot.allowed_mentions.to_dict()
198-
if self.bot.allowed_mentions
199-
else {},
211+
"allowed_mentions": allowed_mentions,
200212
"components": components or [],
201213
}
202214
if hidden:
@@ -283,7 +295,7 @@ class ComponentContext(InteractionContext):
283295
:ivar component: Component data retrieved from the message. Not available if the origin message was ephemeral.
284296
:ivar origin_message: The origin message of the component. Not available if the origin message was ephemeral.
285297
:ivar origin_message_id: The ID of the origin message.
286-
298+
:ivar selected_options: The options selected (only for selects)
287299
"""
288300

289301
def __init__(
@@ -309,6 +321,8 @@ def __init__(
309321
)
310322
self.component = self.origin_message.get_component(self.custom_id)
311323

324+
self.selected_options = _json["data"]["values"] if self.component_type == 3 else None
325+
312326
async def defer(self, hidden: bool = False, edit_origin: bool = False):
313327
"""
314328
'Defers' the response, showing a loading state to the user
@@ -373,42 +387,71 @@ async def edit_origin(self, **fields):
373387
"""
374388
_resp = {}
375389

376-
content = fields.get("content")
377-
if content:
378-
_resp["content"] = str(content)
390+
try:
391+
content = fields["content"]
392+
except KeyError:
393+
pass
394+
else:
395+
if content is not None:
396+
content = str(content)
397+
_resp["content"] = content
398+
399+
try:
400+
components = fields["components"]
401+
except KeyError:
402+
pass
403+
else:
404+
if components is None:
405+
_resp["components"] = []
406+
else:
407+
_resp["components"] = components
408+
409+
try:
410+
embeds = fields["embeds"]
411+
except KeyError:
412+
# Nope
413+
pass
414+
else:
415+
if not isinstance(embeds, list):
416+
raise error.IncorrectFormat("Provide a list of embeds.")
417+
if len(embeds) > 10:
418+
raise error.IncorrectFormat("Do not provide more than 10 embeds.")
419+
_resp["embeds"] = [e.to_dict() for e in embeds]
420+
421+
try:
422+
embed = fields["embed"]
423+
except KeyError:
424+
pass
425+
else:
426+
if "embeds" in _resp:
427+
raise error.IncorrectFormat("You can't use both `embed` and `embeds`!")
428+
429+
if embed is None:
430+
_resp["embeds"] = []
431+
else:
432+
_resp["embeds"] = [embed.to_dict()]
379433

380-
embed = fields.get("embed")
381-
embeds = fields.get("embeds")
382434
file = fields.get("file")
383435
files = fields.get("files")
384-
components = fields.get("components")
385-
386-
if components:
387-
_resp["components"] = components
388436

389-
if embed and embeds:
390-
raise error.IncorrectFormat("You can't use both `embed` and `embeds`!")
391-
if file and files:
437+
if files is not None and file is not None:
392438
raise error.IncorrectFormat("You can't use both `file` and `files`!")
393439
if file:
394440
files = [file]
395-
if embed:
396-
embeds = [embed]
397-
if embeds:
398-
if not isinstance(embeds, list):
399-
raise error.IncorrectFormat("Provide a list of embeds.")
400-
elif len(embeds) > 10:
401-
raise error.IncorrectFormat("Do not provide more than 10 embeds.")
402-
_resp["embeds"] = [x.to_dict() for x in embeds]
403441

404442
allowed_mentions = fields.get("allowed_mentions")
405-
_resp["allowed_mentions"] = (
406-
allowed_mentions.to_dict()
407-
if allowed_mentions
408-
else self.bot.allowed_mentions.to_dict()
409-
if self.bot.allowed_mentions
410-
else {}
411-
)
443+
if allowed_mentions is not None:
444+
if self.bot.allowed_mentions is not None:
445+
_resp["allowed_mentions"] = self.bot.allowed_mentions.merge(
446+
allowed_mentions
447+
).to_dict()
448+
else:
449+
_resp["allowed_mentions"] = allowed_mentions.to_dict()
450+
else:
451+
if self.bot.allowed_mentions is not None:
452+
_resp["allowed_mentions"] = self.bot.allowed_mentions.to_dict()
453+
else:
454+
_resp["allowed_mentions"] = {}
412455

413456
if not self.responded:
414457
if files and not self.deferred:

discord_slash/dpy_overrides.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def get_component(self, custom_id: int) -> typing.Optional[dict]:
2323
"""
2424
for row in self.components:
2525
for component in row["components"]:
26-
if component["custom_id"] == custom_id:
26+
if "custom_id" in component and component["custom_id"] == custom_id:
2727
return component
2828

2929

0 commit comments

Comments
 (0)