Skip to content

Commit

Permalink
[ext.bridge] Permission decorators and invoke function (#1642)
Browse files Browse the repository at this point in the history
Co-authored-by: Dorukyum <[email protected]>
Co-authored-by: BobDotCom <[email protected]>
Co-authored-by: Lala Sabathil <[email protected]>
  • Loading branch information
4 people authored Oct 2, 2022
1 parent 4c12462 commit 085b45a
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#1655](https://github.com/Pycord-Development/pycord/pull/1655))
- `delete_message_seconds` parameter in ban methods. ([#1557](https://github.com/Pycord-Development/pycord/pull/1557))
- New `View.get_item()` method. ([#1659](https://github.com/Pycord-Development/pycord/pull/1659))
- Permissions support for bridge commands. ([#1642](https://github.com/Pycord-Development/pycord/pull/1642))
- New `BridgeCommand.invoke()` method. ([#1642](https://github.com/Pycord-Development/pycord/pull/1642))

### Deprecated
- The `delete_message_days` parameter in ban methods is now deprecated. Please use `delete_message_seconds` instead.
Expand Down
18 changes: 14 additions & 4 deletions discord/cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ def __new__(cls: Type[CogMeta], *args: Any, **kwargs: Any) -> CogMeta:
raise TypeError(no_bot_cog.format(base, elem))

commands[f"ext_{elem}"] = value.ext_variant
commands[f"application_{elem}"] = value.slash_variant
commands[f"app_{elem}"] = value.slash_variant
for cmd in getattr(value, "subcommands", []):
commands[f"ext_{cmd.ext_variant.qualified_name}"] = cmd.ext_variant

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

lookup = {cmd.qualified_name: cmd for cmd in new_cls.__cog_commands__}
name_filter = lambda c: 'app' if isinstance(c, ApplicationCommand) else 'ext'

lookup = {f"{name_filter(cmd)}_{cmd.qualified_name}": cmd for cmd in new_cls.__cog_commands__}

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

if not isinstance(command, SlashCommandGroup):
setattr(new_cls, command.callback.__name__, command)
# ignore bridge commands
cmd = getattr(new_cls, command.callback.__name__, None)
if hasattr(cmd, "add_to"):
setattr(cmd, f"{name_filter(command).replace('app', 'slash')}_variant", command)
else:
setattr(new_cls, command.callback.__name__, command)

parent = command.parent
if parent is not None:
# Get the latest parent reference
parent = lookup[parent.qualified_name] # type: ignore
parent = lookup[f"{name_filter(command)}_{parent.qualified_name}"] # type: ignore

# Update our parent's reference to our self
parent.remove_command(command.name) # type: ignore
Expand Down
1 change: 1 addition & 0 deletions discord/commands/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def inner(command: Callable):
command.guild_only = True
else:
command.__guild_only__ = True

return command

return inner
12 changes: 11 additions & 1 deletion discord/ext/bridge/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Any, overload, Optional, Union

from discord.commands import ApplicationContext
from discord.interactions import Interaction, InteractionMessage
Expand All @@ -32,6 +34,10 @@

from ..commands import Context

if TYPE_CHECKING:
from .core import BridgeSlashCommand, BridgeExtCommand


__all__ = ("BridgeContext", "BridgeExtContext", "BridgeApplicationContext")


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

@overload
async def invoke(self, command: Union[BridgeSlashCommand, BridgeExtCommand], *args, **kwargs) -> None:
...

async def respond(
self, *args, **kwargs
) -> Union[Union[Interaction, WebhookMessage], Message]:
Expand Down
75 changes: 71 additions & 4 deletions discord/ext/bridge/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,21 @@
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
from __future__ import annotations

import inspect
from typing import Any, List, Union, Optional
from typing import TYPE_CHECKING, Any, List, Union, Optional, Callable, Dict

import discord.commands.options
from discord import SlashCommandOptionType, Attachment, Option, SlashCommand, SlashCommandGroup
from .context import BridgeApplicationContext
from discord import (
ApplicationCommand,
SlashCommand,
SlashCommandGroup,
Permissions,
SlashCommandOptionType,
Attachment,
Option,
)
from ..commands.converter import _convert_to_bool, run_converters
from ..commands import (
Command,
Expand All @@ -43,6 +51,9 @@
)
from ...utils import get, filter_params, find

if TYPE_CHECKING:
from .context import BridgeApplicationContext, BridgeExtContext


__all__ = (
"BridgeCommand",
Expand All @@ -54,6 +65,8 @@
"BridgeExtGroup",
"BridgeSlashGroup",
"map_to",
"guild_only",
"has_permissions",
)


Expand Down Expand Up @@ -183,6 +196,11 @@ def add_to(self, bot: ExtBot) -> None:
bot.add_application_command(self.slash_variant)
bot.add_command(self.ext_variant)

async def invoke(self, ctx: Union[BridgeExtContext, BridgeApplicationContext], /, *args, **kwargs):
if ctx.is_app:
return await self.slash_variant.invoke(ctx)
return await self.ext_variant.invoke(ctx)

def error(self, coro):
"""A decorator that registers a coroutine as a local error handler.
Expand Down Expand Up @@ -329,7 +347,7 @@ def bridge_group(**kwargs):
Parameters
----------
kwargs: Optional[Dict[:class:`str`, Any]]
Keyword arguments that are directly passed to the respective command constructors. (:class:`.SlashCommandGroup` and :class:`.ext.commands.Group`)
Keyword arguments that are directly passed to the respective command constructors (:class:`.SlashCommandGroup` and :class:`.ext.commands.Group`).
"""
def decorator(callback):
return BridgeCommandGroup(callback, **kwargs)
Expand Down Expand Up @@ -376,6 +394,54 @@ def decorator(callback):
return decorator


def guild_only():
"""Intended to work with :class:`.ApplicationCommand` and :class:`BridgeCommand`, adds a :func:`~ext.commands.check`
that locks the command to only run in guilds, and also registers the command as guild only client-side (on discord).
Basically a utility function that wraps both :func:`discord.ext.commands.guild_only` and :func:`discord.commands.guild_only`.
"""
def predicate(func: Union[Callable, ApplicationCommand]):
if isinstance(func, ApplicationCommand):
func.guild_only = True
else:
func.__guild_only__ = True

from ..commands import guild_only

return guild_only()(func)

return predicate


def has_permissions(**perms: Dict[str, bool]):
"""Intended to work with :class:`.SlashCommand` and :class:`BridgeCommand`, adds a
:func:`~ext.commands.check` that locks the command to be run by people with certain
permissions inside guilds, and also registers the command as locked behind said permissions.
Basically a utility function that wraps both :func:`discord.ext.commands.has_permissions`
and :func:`discord.commands.default_permissions`.
Parameters
----------
\*\*perms: Dict[:class:`str`, :class:`bool`]
An argument list of permissions to check for.
"""

def predicate(func: Union[Callable, ApplicationCommand]):
from ..commands import has_permissions

func = has_permissions(**perms)(func)
_perms = Permissions(**perms)
if isinstance(func, ApplicationCommand):
func.default_member_permissions = perms
else:
func.__default_member_permissions__ = perms

return perms

return predicate


class MentionableConverter(Converter):
"""A converter that can convert a mention to a user or a role."""

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


class AttachmentConverter(Converter):
async def convert(self, ctx: Context, arg: str):
try:
Expand Down
2 changes: 1 addition & 1 deletion discord/ext/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2096,7 +2096,7 @@ def has_permissions(**perms: bool) -> Callable[[T], T]:
Parameters
------------
perms
\*\*perms: Dict[:class:`str`, :class:`bool`]
An argument list of permissions to check for.
Example
Expand Down
14 changes: 11 additions & 3 deletions docs/ext/bridge/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ BridgeCommand
.. autoclass:: discord.ext.bridge.BridgeCommand
:members:

.. automethod:: discord.ext.bridge.bridge_command()
:decorator:

BridgeCommandGroup
~~~~~~~~~~~~~~~~~~~

Expand All @@ -62,12 +59,23 @@ BridgeCommandGroup
.. autoclass:: discord.ext.bridge.BridgeCommandGroup
:members:

Decorators
~~~~~~~~~~~
.. automethod:: discord.ext.bridge.bridge_command()
:decorator:

.. automethod:: discord.ext.bridge.bridge_group()
:decorator:

.. automethod:: discord.ext.bridge.map_to()
:decorator:

.. automethod:: discord.ext.bridge.guild_only()
:decorator:

.. automethod:: discord.ext.bridge.has_permissions()
:decorator:

Command Subclasses
~~~~~~~~~~~~~~~~~~~

Expand Down

0 comments on commit 085b45a

Please sign in to comment.