Skip to content
This repository was archived by the owner on Mar 8, 2022. It is now read-only.

Commit 5b32f92

Browse files
committed
added alternative options + fixed remove_cog
1 parent 31f8313 commit 5b32f92

File tree

9 files changed

+238
-66
lines changed

9 files changed

+238
-66
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,37 @@ You can find more (and better) examples [here](https://github.com/discord-py-ui/
174174

175175
# Changelog
176176

177+
- <details>
178+
<summary>4.2.0</summary>
179+
180+
## **Added**
181+
- cog_remove sync
182+
> when you remove a cog the slash commands will now get deleted if you set `delete_unused` to True and set `sync_on_cog` to True
183+
- alternativ slash options
184+
> you don't have to specify options in one of the slash decorators anymore. Instead, you can set them in your callback function
185+
> Example
186+
```py
187+
@ui.slash.command()
188+
async def greet(ctx, user): # This will add an required option with the name "user" of type "user"
189+
"""Greets a user
190+
191+
You can use multiline docstrings, because only the first line will be used for the description
192+
"""
193+
...
194+
195+
@ui.slash.command()
196+
async def tag(ctx, target: discord.User = None): # This will add an optional option with the name "target" of type "user"
197+
# Note: you could also use target: "user" = None or anything else you would use in SlashOption for the type
198+
...
199+
200+
```
201+
202+
## **Fixed**
203+
- sync_commands
204+
> if you would sync the commands after the first sync, it threw an error
205+
206+
</details>
207+
177208
- <details>
178209
<summary>4.1.4</summary>
179210

discord_ui/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@
4444

4545

4646
__title__ = "discord-ui"
47-
__version__ = "4.1.4"
47+
__version__ = "4.2.0"

discord_ui/client.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ async def sync_commands(self, delete_unused=False):
298298

299299

300300
#region gather commands
301-
commands = self.commands
301+
commands = self.commands.copy()
302302
for _base in self.subcommands:
303303
# get first base
304304
for _sub in self.subcommands[_base]:
@@ -340,6 +340,7 @@ async def sync_commands(self, delete_unused=False):
340340
commands[_base] = SlashCommand(None, _base, options=[sub.to_dict()], guild_ids=sub.guild_ids, default_permission=sub.default_permission, guild_permissions=sub.guild_permissions)
341341
#endregion
342342

343+
343344
async def guild_stuff(command, guild_ids):
344345
"""Adds the command to the guilds"""
345346
for x in guild_ids:
@@ -506,8 +507,6 @@ async def add_guild_command(self, base, guild_id):
506507
await delete_global_command(self._discord, global_command["id"])
507508
await create_guild_command(base.to_dict(), self._discord, target_guild, base.permissions.to_dict())
508509
elif api_command != base:
509-
# for some reason, if you sync commands with cogs, it will duplicate the options of a subgroup command
510-
# print("api_command", api_command, "\n", "base", base.to_dict())
511510
await edit_guild_command(api_command["id"], self._discord, target_guild, base.to_dict(), base.permissions.to_dict())
512511
elif api_permissions != base.permissions:
513512
await update_command_permissions(self._discord.user.id, self._discord.http.token, guild_id, api_command["id"], base.permissions.to_dict())
@@ -605,7 +604,7 @@ async def edit_command(self, old_name, typ: Literal["slash", 1, "user", 2, "mess
605604
if name is not None:
606605
command.name = name
607606
if description is not None:
608-
command.description = _or(description, inspect.getdoc(callback) if callback is not None else None, name, old_name)
607+
command.description = _or(description, inspect.getdoc(callback).split("\n")[0] if callback is not None and inspect.getdoc(callback) is not None else None, name, old_name)
609608
if options is not None:
610609
command.options = options
611610
if default_permission is not None:
@@ -683,6 +682,30 @@ def _add_to_cache(self, base: Union[SlashCommand, SlashSubcommand]):
683682
# context message command
684683
elif base.command_type == CommandType.Message:
685684
self.context_commands["message"][base.name] = base
685+
def _remove_from_cache(self, base: Union[SlashCommand, SlashSubcommand]):
686+
# slash command
687+
if base.command_type is CommandType.Slash:
688+
# basic slash command
689+
if type(base) in [SlashCommand, CogCommand]:
690+
del self.commands[base.name]
691+
# subcommand or subgroup
692+
else:
693+
# subgroup
694+
if len(base.base_names) > 1:
695+
# remove from internal cache
696+
del self.subcommands[base.base_names[0]][base.base_names[1]][base.name]
697+
# subcommand
698+
else:
699+
# remove from cache
700+
del self.subcommands[base.base_names[0]][base.name]
701+
# context user command
702+
elif base.command_type == CommandType.User:
703+
# remove from cache
704+
del self.context_commands["user"][base.name]
705+
# context message command
706+
elif base.command_type == CommandType.Message:
707+
# remove from cache
708+
del self.context_commands["message"][base.name]
686709

687710
async def delete_global_commands(self):
688711
"""**Deletes all global commands**"""
@@ -1048,7 +1071,7 @@ class Components():
10481071
10491072
...
10501073
# Your bot declaration should be here
1051-
components = components(client)
1074+
components = Components(client)
10521075
10531076
10541077
for listening to button presses, use

discord_ui/receive.py

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,6 @@ async def respond(self, content=MISSING, *, tts=False, embed=MISSING, embeds=MIS
133133
-------
134134
:return: Returns the sent message
135135
:type: :class:`~Message` | :class:`EphemeralMessage`
136-
137-
.. note::
138-
139-
If the response is hidden, a EphemeralMessage will be returned, which is an empty class
140-
141136
"""
142137
if ninja_mode is True or all(y in [MISSING, False] for x, y in locals().items() if x not in ["self"]):
143138
try:
@@ -212,7 +207,7 @@ async def send(self, content=None, *, tts=False, embed=MISSING, embeds=MISSING,
212207
----------
213208
content: :class:`str`, optional
214209
The raw message content
215-
tts: `bool`
210+
tts: :class:`bool`
216211
Whether the message should be send with text-to-speech
217212
embed: :class:`discord.Embed`
218213
Embed rich content
@@ -239,9 +234,6 @@ async def send(self, content=None, *, tts=False, embed=MISSING, embeds=MISSING,
239234
-------
240235
:return: Returns the sent message
241236
:type: :class:`~Message` | :class:`EphemeralMessage`
242-
243-
.. note::
244-
If the response is hidden, a EphemeralMessage will be returned, which is an empty class
245237
"""
246238
if self.responded is False:
247239
return await self.respond(content=content, tts=tts, embed=embed, embeds=embeds, file=file, files=files, nonce=nonce, allowed_mentions=allowed_mentions, mention_author=mention_author, components=components, hidden=hidden)
@@ -536,34 +528,49 @@ async def wait_for(self, event_name: Literal["select", "button"], client, custom
536528
537529
Parameters
538530
-----------
539-
event_name: :class:`str`
540-
The name of the event which will be awaited [``"select"`` | ``"button"``]
541-
542-
.. note::
543-
544-
``event_name`` must be ``select`` for a select menu selection and ``button`` for a button press
545-
546-
client: :class:`discord.ext.commands.Bot`
547-
The discord client
548-
custom_id: :class:`str`, Optional
549-
Filters the waiting for a custom_id
550-
by: :class:`discord.User` | :class:`discord.Member` | :class:`int` | :class:`str`, Optional
551-
The user or the user id by that has to create the component interaction
552-
check: :class:`function`, Optional
553-
A check that has to return True in order to break from the event and return the received component
554-
The function takes the received component as the parameter
555-
timeout: :class:`float`, Optional
556-
After how many seconds the waiting should be canceled.
557-
Throws an :class:`asyncio.TimeoutError` Exception
531+
event_name: :class:`str`
532+
The name of the event which will be awaited [``"select"`` | ``"button"``]
533+
534+
.. note::
535+
536+
``event_name`` must be ``select`` for a select menu selection and ``button`` for a button press
537+
538+
client: :class:`discord.ext.commands.Bot`
539+
The discord client
540+
custom_id: :class:`str`, Optional
541+
Filters the waiting for a custom_id
542+
by: :class:`discord.User` | :class:`discord.Member` | :class:`int` | :class:`str`, Optional
543+
The user or the user id by that has to create the component interaction
544+
check: :class:`function`, Optional
545+
A check that has to return True in order to break from the event and return the received component
546+
The function takes the received component as the parameter
547+
timeout: :class:`float`, Optional
548+
After how many seconds the waiting should be canceled.
549+
Throws an :class:`asyncio.TimeoutError` Exception
558550
559551
Raises
560552
------
561553
:raises: :class:`discord_ui.errors.InvalidEvent` : The event name passed was invalid
562554
563555
Returns
564556
----------
565-
:returns: The component that was waited for
566-
:type: :class:`~PressedButton` | :class:`~SelectedMenu`
557+
:returns: The component that was waited for
558+
:type: :class:`~PressedButton` | :class:`~SelectedMenu`
559+
560+
Example
561+
---------
562+
563+
.. code-block::
564+
565+
# send a message with comoponents
566+
msg = await ctx.send("okayy", components=[Button("a_custom_id", ...)])
567+
try:
568+
# wait for the button
569+
btn = await msg.wait_for("button", client, "a_custom_id", by=ctx.author, timeout=20)
570+
# send response
571+
btn.respond()
572+
except asyncio.TimeoutError:
573+
# no button press was received in 20 seconds timespan
567574
"""
568575
if event_name.lower() in ["button", "select"]:
569576
def _check(com):

discord_ui/slash/types.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from discord.enums import InteractionResponseType
12
from ..tools import MISSING, _or, _default, _none
23
from ..errors import InvalidLength, WrongType
34
from .errors import CallbackMissingContextCommandParameters, MissingOptionParameter, NoAsyncCallback, OptionalOptionParameter
@@ -374,7 +375,7 @@ class SlashCommand():
374375
375376
376377
"""
377-
def __init__(self, callback, name=MISSING, description=MISSING, options=[], guild_ids=MISSING, default_permission=MISSING, guild_permissions=MISSING) -> None:
378+
def __init__(self, callback, name=MISSING, description=MISSING, options=MISSING, guild_ids=MISSING, default_permission=MISSING, guild_permissions=MISSING) -> None:
378379
"""
379380
Creates a new base slash command
380381
@@ -398,8 +399,9 @@ async def my_function(command, parameter=None):
398399
}
399400

400401
# Check options before callback because callback makes an option check
401-
self.options: typing.List[SlashOption] = options
402-
402+
if options is MISSING:
403+
options = []
404+
self.options = options
403405
if callback is not None:
404406
if not inspect.iscoroutinefunction(callback):
405407
raise NoAsyncCallback()
@@ -412,10 +414,26 @@ async def my_function(command, parameter=None):
412414
param = callback_params[op.name]
413415
if not op.required and param.default is param.empty:
414416
raise OptionalOptionParameter(param.name)
415-
417+
if _none(options, empty_array=True):
418+
_ops = []
419+
has_self = False
420+
for _i, _name in enumerate(callback_params):
421+
# ignore context parameter
422+
if _name == "self":
423+
has_self = True
424+
continue
425+
if _i == [0, 1][has_self]:
426+
continue
427+
_val = callback_params.get(_name)
428+
op_type = [_val.annotation, _name][_val.annotation == _val.empty]
429+
if OptionType.any_to_type(op_type) is None:
430+
raise InvalidArgument("Could not find a matching option type for parameter '" + str(op_type) + "'")
431+
_ops.append(SlashOption(op_type, _name, required=_val.default is not None))
432+
self.options = _ops
433+
416434
self.callback: function = callback
417435
self.name = _or(name, self.callback.__name__ if not _none(self.callback) else None)
418-
self.description = _or(description, (inspect.getdoc(self.callback) if not _none(self.callback) else None), self.name)
436+
self.description = _or(description, inspect.getdoc(callback).split("\n")[0] if not _none(callback) and inspect.getdoc(callback) is not None else None, self.name)
419437
if default_permission is MISSING:
420438
default_permission = True
421439
self.default_permission: bool = default_permission

discord_ui/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def get(self, *args):
1818
MISSING = _MISSING()
1919

2020
def _none(*args, empty_array=False):
21-
return all(x in [None, MISSING] + ([[]] if empty_array is True else []) for x in args)
21+
return all(x in [None, MISSING] + [[], [[]]][empty_array is True] for x in args)
2222
def _or(*args, default=None):
2323
for i in range(len(args)):
2424
if not _none(args[i]):

docs/source/_static/js/override_page.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ window.onload = function() {
4747

4848
// async, def, await
4949
change_color("k", "#eb52ff")
50+
// in keyword
51+
change_color("ow", "#eb52ff")
5052
// function name
5153
change_color("nf", "#5c9aff")
5254
// Decorators
@@ -102,5 +104,14 @@ window.onload = function() {
102104
if (result.length > 0)
103105
result[0].style["color"] = "white";
104106

105-
setTimeout(() => document.getElementsByClassName("rst-content")[0].innerHTML += "\n<p style='color: gray;'>Modified by <a href='https://github.com/404kuso' target='_blank'>404kuso</a></p>", 100)
107+
// remove the discord_ui. ... prefix
108+
result = document.getElementsByClassName("reference internal")
109+
for(let i = 0; i < result.length; i++) {
110+
if (result[i].text != null && result[i].text.match("discord_ui\.[\w]*\."))
111+
result[i].text = (result[i].text.split(".").at(-1))
112+
}
113+
114+
// Add author
115+
document.getElementsByClassName("rst-content")[0].innerHTML += "\n<p style='color: gray;'>Modified by <a href='https://github.com/404kuso' target='_blank'>404kuso</a></p>"
116+
106117
}

0 commit comments

Comments
 (0)