Skip to content

Commit 4aeb865

Browse files
committed
Added Subcommand support
1 parent b867088 commit 4aeb865

File tree

1 file changed

+63
-21
lines changed

1 file changed

+63
-21
lines changed

discord_slash/client.py

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,26 @@ def add_slash_command(self,
5151
name: str = None,
5252
description: str = None,
5353
auto_convert: dict = None,
54-
guild_ids: list = None,
54+
guild_ids: typing.List[int] = None,
5555
options: list = None,
5656
has_subcommands: bool = False):
5757
"""
5858
Registers slash command to SlashCommand.
5959
6060
:param cmd: Command Coroutine.
61+
:type cmd: Coroutine
6162
:param name: Name of the slash command. Default name of the coroutine.
63+
:type name: str
6264
:param description: Description of the slash command. Default ``None``.
65+
:type description: str
6366
:param auto_convert: Dictionary of how to convert option values. Default ``None``.
67+
:type auto_convert: dict
6468
:param guild_ids: List of Guild ID of where the command will be used. Default ``None``, which will be global command.
65-
:param options: Options of the slash command. This will affect ``auto_convert`` and command data at Discord API. Default ``None``.
69+
:type guild_ids: List[int]
70+
:param options: Options of the slash command.
71+
:type options: list
6672
:param has_subcommands: Whether it has subcommand. Default ``False``.
67-
:return: ``None``
73+
:type has_subcommands: bool
6874
"""
6975
_cmd = {
7076
"func": cmd,
@@ -84,18 +90,24 @@ def add_subcommand(self,
8490
name=None,
8591
description: str = None,
8692
auto_convert: dict = None,
87-
guild_ids: list = None):
93+
guild_ids: typing.List[int] = None):
8894
"""
8995
Registers subcommand to SlashCommand.
9096
91-
:param cmd:
92-
:param base:
93-
:param subcommand_group:
94-
:param name:
95-
:param description:
96-
:param auto_convert:
97-
:param guild_ids:
98-
:return:
97+
:param cmd: Subcommand Coroutine.
98+
:type cmd: Coroutine
99+
:param base: Name of the base command.
100+
:type base: str
101+
:param subcommand_group: Name of the subcommand group, if any. Default ``None`` which represents there is no sub group.
102+
:type subcommand_group: str
103+
:param name: Name of the subcommand. Default name of the coroutine.
104+
:type name: str
105+
:param description: Description of the subcommand. Default ``None``.
106+
:type description: str
107+
:param auto_convert: Dictionary of how to convert option values. Default ``None``.
108+
:type auto_convert: dict
109+
:param guild_ids: List of guild ID of where the command will be used. Default ``None``, which will be global command.
110+
:type guild_ids: List[int]
99111
"""
100112
name = cmd.__name__ if not name else name
101113
_cmd = {
@@ -118,6 +130,7 @@ def add_subcommand(self,
118130
self.subcommands[base][subcommand_group][name] = _sub
119131
else:
120132
self.subcommands[base][name] = _sub
133+
self.logger.debug(f"Added subcommand `{base} {subcommand_group if subcommand_group else ''} {cmd.__name__ if not name else name}`")
121134

122135
def slash(self,
123136
*,
@@ -200,31 +213,44 @@ def subcommand(self,
200213
name=None,
201214
description: str = None,
202215
auto_convert: dict = None,
203-
guild_ids: int = None):
216+
guild_ids: typing.List[int] = None):
204217
"""
205218
Decorator that registers subcommand.\n
206-
Unlike discord.py, you don't need base command.\n
207-
Not implemented.
219+
Unlike discord.py, you don't need base command.
208220
209221
Example:
210222
211223
.. code-block:: python
212224
225+
# /group say <str>
213226
@slash.subcommand(base="group", name="say")
214227
async def _group_say(ctx, _str):
215228
await ctx.send(content=_str)
216229
230+
# /group kick user <user>
231+
@slash.subcommand(base="group",
232+
subcommand_group="kick",
233+
name="user",
234+
auto_convert={"user": "user"})
235+
async def _group_kick_user(ctx, user):
236+
...
237+
217238
.. note::
218239
Unlike normal slash command, this doesn't support ``options`` arg, since it will be very complicated.\n
219240
Also, subcommands won't be automatically registered to Discord API even if you set ``auto_register`` to ``True``.
220241
221242
:param base: Name of the base command.
243+
:type base: str
222244
:param subcommand_group: Name of the subcommand group, if any. Default ``None`` which represents there is no sub group.
245+
:type subcommand_group: str
223246
:param name: Name of the subcommand. Default name of the coroutine.
247+
:type name: str
224248
:param description: Description of the subcommand. Default ``None``.
249+
:type description: str
225250
:param auto_convert: Dictionary of how to convert option values. Default ``None``.
251+
:type auto_convert: dict
226252
:param guild_ids: List of guild ID of where the command will be used. Default ``None``, which will be global command.
227-
:return:
253+
:type guild_ids: List[int]
228254
"""
229255

230256
def wrapper(cmd):
@@ -315,15 +341,31 @@ async def on_socket_response(self, msg):
315341
return await self.handle_subcommand(ctx, to_use)
316342
args = await self.process_options(ctx.guild, to_use["data"]["options"], selected_cmd["auto_convert"]) \
317343
if "options" in to_use["data"] else []
318-
self.logger.debug(f"Command {to_use['data']['name']} invoked.")
319344
await selected_cmd["func"](ctx, *args)
320345

321346
async def handle_subcommand(self, ctx: model.SlashContext, data: dict):
322347
"""
323348
Coroutine for handling subcommand.
324-
Not implemented.
325349
326-
:param ctx:
327-
:param data:
350+
.. warning::
351+
Do not manually call this.
352+
353+
:param ctx: :class:`.model.SlashContext` instance.
354+
:param data: Gateway message.
328355
"""
329-
pass
356+
base = self.subcommands[data["data"]["name"]]
357+
sub = data["data"]["options"][0]
358+
sub_name = sub["name"]
359+
sub_opts = sub["options"] if "options" in sub else []
360+
for x in sub_opts:
361+
if "options" in x.keys():
362+
sub_group = x["name"]
363+
selected = base[sub_name][sub_group]
364+
args = await self.process_options(ctx.guild, x["options"], selected["auto_convert"]) \
365+
if "options" in x.keys() else []
366+
await selected["func"](ctx, *args)
367+
return
368+
selected = base[sub_name]
369+
args = await self.process_options(ctx.guild, sub_opts, selected["auto_convert"]) \
370+
if sub["options"] else []
371+
await selected["func"](ctx, *args)

0 commit comments

Comments
 (0)