Skip to content

Commit 085b45a

Browse files
MiddledotDorukyumBobDotComLulalaby
authored
[ext.bridge] Permission decorators and invoke function (#1642)
Co-authored-by: Dorukyum <[email protected]> Co-authored-by: BobDotCom <[email protected]> Co-authored-by: Lala Sabathil <[email protected]>
1 parent 4c12462 commit 085b45a

File tree

7 files changed

+111
-13
lines changed

7 files changed

+111
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
([#1655](https://github.com/Pycord-Development/pycord/pull/1655))
1414
- `delete_message_seconds` parameter in ban methods. ([#1557](https://github.com/Pycord-Development/pycord/pull/1557))
1515
- New `View.get_item()` method. ([#1659](https://github.com/Pycord-Development/pycord/pull/1659))
16+
- Permissions support for bridge commands. ([#1642](https://github.com/Pycord-Development/pycord/pull/1642))
17+
- New `BridgeCommand.invoke()` method. ([#1642](https://github.com/Pycord-Development/pycord/pull/1642))
1618

1719
### Deprecated
1820
- The `delete_message_days` parameter in ban methods is now deprecated. Please use `delete_message_seconds` instead.

discord/cog.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ def __new__(cls: Type[CogMeta], *args: Any, **kwargs: Any) -> CogMeta:
200200
raise TypeError(no_bot_cog.format(base, elem))
201201

202202
commands[f"ext_{elem}"] = value.ext_variant
203-
commands[f"application_{elem}"] = value.slash_variant
203+
commands[f"app_{elem}"] = value.slash_variant
204+
for cmd in getattr(value, "subcommands", []):
205+
commands[f"ext_{cmd.ext_variant.qualified_name}"] = cmd.ext_variant
204206

205207
if inspect.iscoroutinefunction(value):
206208
try:
@@ -229,19 +231,27 @@ def __new__(cls: Type[CogMeta], *args: Any, **kwargs: Any) -> CogMeta:
229231
# r.e type ignore, type-checker complains about overriding a ClassVar
230232
new_cls.__cog_commands__ = tuple(c._update_copy(cmd_attrs) for c in new_cls.__cog_commands__) # type: ignore
231233

232-
lookup = {cmd.qualified_name: cmd for cmd in new_cls.__cog_commands__}
234+
name_filter = lambda c: 'app' if isinstance(c, ApplicationCommand) else 'ext'
235+
236+
lookup = {f"{name_filter(cmd)}_{cmd.qualified_name}": cmd for cmd in new_cls.__cog_commands__}
233237

234238
# Update the Command instances dynamically as well
235239
for command in new_cls.__cog_commands__:
236240
if isinstance(command, ApplicationCommand) and not command.guild_ids and new_cls.__cog_guild_ids__:
237241
command.guild_ids = new_cls.__cog_guild_ids__
238242

239243
if not isinstance(command, SlashCommandGroup):
240-
setattr(new_cls, command.callback.__name__, command)
244+
# ignore bridge commands
245+
cmd = getattr(new_cls, command.callback.__name__, None)
246+
if hasattr(cmd, "add_to"):
247+
setattr(cmd, f"{name_filter(command).replace('app', 'slash')}_variant", command)
248+
else:
249+
setattr(new_cls, command.callback.__name__, command)
250+
241251
parent = command.parent
242252
if parent is not None:
243253
# Get the latest parent reference
244-
parent = lookup[parent.qualified_name] # type: ignore
254+
parent = lookup[f"{name_filter(command)}_{parent.qualified_name}"] # type: ignore
245255

246256
# Update our parent's reference to our self
247257
parent.remove_command(command.name) # type: ignore

discord/commands/permissions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def inner(command: Callable):
104104
command.guild_only = True
105105
else:
106106
command.__guild_only__ = True
107+
107108
return command
108109

109110
return inner

discord/ext/bridge/context.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2323
DEALINGS IN THE SOFTWARE.
2424
"""
25+
from __future__ import annotations
26+
2527
from abc import ABC, abstractmethod
26-
from typing import Any, Optional, Union
28+
from typing import TYPE_CHECKING, Any, overload, Optional, Union
2729

2830
from discord.commands import ApplicationContext
2931
from discord.interactions import Interaction, InteractionMessage
@@ -32,6 +34,10 @@
3234

3335
from ..commands import Context
3436

37+
if TYPE_CHECKING:
38+
from .core import BridgeSlashCommand, BridgeExtCommand
39+
40+
3541
__all__ = ("BridgeContext", "BridgeExtContext", "BridgeApplicationContext")
3642

3743

@@ -73,6 +79,10 @@ async def _defer(self, *args, **kwargs) -> None:
7379
async def _edit(self, *args, **kwargs) -> Union[InteractionMessage, Message]:
7480
...
7581

82+
@overload
83+
async def invoke(self, command: Union[BridgeSlashCommand, BridgeExtCommand], *args, **kwargs) -> None:
84+
...
85+
7686
async def respond(
7787
self, *args, **kwargs
7888
) -> Union[Union[Interaction, WebhookMessage], Message]:

discord/ext/bridge/core.py

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,21 @@
2222
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2323
DEALINGS IN THE SOFTWARE.
2424
"""
25+
from __future__ import annotations
2526

2627
import inspect
27-
from typing import Any, List, Union, Optional
28+
from typing import TYPE_CHECKING, Any, List, Union, Optional, Callable, Dict
2829

2930
import discord.commands.options
30-
from discord import SlashCommandOptionType, Attachment, Option, SlashCommand, SlashCommandGroup
31-
from .context import BridgeApplicationContext
31+
from discord import (
32+
ApplicationCommand,
33+
SlashCommand,
34+
SlashCommandGroup,
35+
Permissions,
36+
SlashCommandOptionType,
37+
Attachment,
38+
Option,
39+
)
3240
from ..commands.converter import _convert_to_bool, run_converters
3341
from ..commands import (
3442
Command,
@@ -43,6 +51,9 @@
4351
)
4452
from ...utils import get, filter_params, find
4553

54+
if TYPE_CHECKING:
55+
from .context import BridgeApplicationContext, BridgeExtContext
56+
4657

4758
__all__ = (
4859
"BridgeCommand",
@@ -54,6 +65,8 @@
5465
"BridgeExtGroup",
5566
"BridgeSlashGroup",
5667
"map_to",
68+
"guild_only",
69+
"has_permissions",
5770
)
5871

5972

@@ -183,6 +196,11 @@ def add_to(self, bot: ExtBot) -> None:
183196
bot.add_application_command(self.slash_variant)
184197
bot.add_command(self.ext_variant)
185198

199+
async def invoke(self, ctx: Union[BridgeExtContext, BridgeApplicationContext], /, *args, **kwargs):
200+
if ctx.is_app:
201+
return await self.slash_variant.invoke(ctx)
202+
return await self.ext_variant.invoke(ctx)
203+
186204
def error(self, coro):
187205
"""A decorator that registers a coroutine as a local error handler.
188206
@@ -329,7 +347,7 @@ def bridge_group(**kwargs):
329347
Parameters
330348
----------
331349
kwargs: Optional[Dict[:class:`str`, Any]]
332-
Keyword arguments that are directly passed to the respective command constructors. (:class:`.SlashCommandGroup` and :class:`.ext.commands.Group`)
350+
Keyword arguments that are directly passed to the respective command constructors (:class:`.SlashCommandGroup` and :class:`.ext.commands.Group`).
333351
"""
334352
def decorator(callback):
335353
return BridgeCommandGroup(callback, **kwargs)
@@ -376,6 +394,54 @@ def decorator(callback):
376394
return decorator
377395

378396

397+
def guild_only():
398+
"""Intended to work with :class:`.ApplicationCommand` and :class:`BridgeCommand`, adds a :func:`~ext.commands.check`
399+
that locks the command to only run in guilds, and also registers the command as guild only client-side (on discord).
400+
401+
Basically a utility function that wraps both :func:`discord.ext.commands.guild_only` and :func:`discord.commands.guild_only`.
402+
"""
403+
def predicate(func: Union[Callable, ApplicationCommand]):
404+
if isinstance(func, ApplicationCommand):
405+
func.guild_only = True
406+
else:
407+
func.__guild_only__ = True
408+
409+
from ..commands import guild_only
410+
411+
return guild_only()(func)
412+
413+
return predicate
414+
415+
416+
def has_permissions(**perms: Dict[str, bool]):
417+
"""Intended to work with :class:`.SlashCommand` and :class:`BridgeCommand`, adds a
418+
:func:`~ext.commands.check` that locks the command to be run by people with certain
419+
permissions inside guilds, and also registers the command as locked behind said permissions.
420+
421+
Basically a utility function that wraps both :func:`discord.ext.commands.has_permissions`
422+
and :func:`discord.commands.default_permissions`.
423+
424+
Parameters
425+
----------
426+
\*\*perms: Dict[:class:`str`, :class:`bool`]
427+
An argument list of permissions to check for.
428+
"""
429+
430+
def predicate(func: Union[Callable, ApplicationCommand]):
431+
from ..commands import has_permissions
432+
433+
func = has_permissions(**perms)(func)
434+
_perms = Permissions(**perms)
435+
if isinstance(func, ApplicationCommand):
436+
func.default_member_permissions = perms
437+
else:
438+
func.__default_member_permissions__ = perms
439+
440+
return perms
441+
442+
return predicate
443+
444+
379445
class MentionableConverter(Converter):
380446
"""A converter that can convert a mention to a user or a role."""
381447

@@ -385,6 +451,7 @@ async def convert(self, ctx, argument):
385451
except BadArgument:
386452
return await UserConverter().convert(ctx, argument)
387453

454+
388455
class AttachmentConverter(Converter):
389456
async def convert(self, ctx: Context, arg: str):
390457
try:

discord/ext/commands/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,7 @@ def has_permissions(**perms: bool) -> Callable[[T], T]:
20962096
20972097
Parameters
20982098
------------
2099-
perms
2099+
\*\*perms: Dict[:class:`str`, :class:`bool`]
21002100
An argument list of permissions to check for.
21012101
21022102
Example

docs/ext/bridge/api.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ BridgeCommand
5151
.. autoclass:: discord.ext.bridge.BridgeCommand
5252
:members:
5353

54-
.. automethod:: discord.ext.bridge.bridge_command()
55-
:decorator:
56-
5754
BridgeCommandGroup
5855
~~~~~~~~~~~~~~~~~~~
5956

@@ -62,12 +59,23 @@ BridgeCommandGroup
6259
.. autoclass:: discord.ext.bridge.BridgeCommandGroup
6360
:members:
6461

62+
Decorators
63+
~~~~~~~~~~~
64+
.. automethod:: discord.ext.bridge.bridge_command()
65+
:decorator:
66+
6567
.. automethod:: discord.ext.bridge.bridge_group()
6668
:decorator:
6769

6870
.. automethod:: discord.ext.bridge.map_to()
6971
:decorator:
7072

73+
.. automethod:: discord.ext.bridge.guild_only()
74+
:decorator:
75+
76+
.. automethod:: discord.ext.bridge.has_permissions()
77+
:decorator:
78+
7179
Command Subclasses
7280
~~~~~~~~~~~~~~~~~~~
7381

0 commit comments

Comments
 (0)