Skip to content

Commit

Permalink
Merge pull request #81 from therealr5/feature/perms-v2
Browse files Browse the repository at this point in the history
Perms v2
  • Loading branch information
breqdev authored Apr 28, 2022
2 parents 1ae2b96 + b8175f5 commit 5714137
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 32 deletions.
39 changes: 33 additions & 6 deletions docs/permissions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,35 @@ Discord allows defining permissions for each application command in a specific g
or at the global level. Flask-Discord-Interactions provides a Permission class
and two major ways of setting the permissions of a command.

Default member Permissions
--------------------------

This field represents a permission integer like it is used in channel overwrites and role permissions and
determines the permissions a guild member needs to have in order to execute that command.

Let's say you want to have a `/setup` command that is locked to members who have the permissions to manage channels and to manage roles.
The permission integer for this case would be ``268435472``.
A helping hand to calculate that permissions can be found `here <https://finitereality.github.io/permissions-calculator>`_.

By simply putting the number shown above into the ``default_member_permissions``, the command is locked.

.. code-block:: python
@discord.command(default_member_permissions=268435472)
def setup(ctx):
"Only members with channel- and role-managament permissions can run this"
return "You have the right permissions! Setting up now..."
Setting specific overwrites
---------------------------

.. warning::
The methods below will require an extra oauth scope granted by a server admin as of discord's rewrite of slash command permissions.
It's highly recommended to use the ``default_member_permissions`` field instead.

Permission class
----------------
^^^^^^^^^^^^^^^^

The :class:`.Permission` class accepts either a role ID or a user ID.

Expand All @@ -15,12 +42,12 @@ The :class:`.Permission` class accepts either a role ID or a user ID.


Command constructor
-------------------
^^^^^^^^^^^^^^^^^^^

You can define permissions when defining a command. These will be
registered immediately after your command is registered.

You can set the ``default_permission``, then use the ``permissions`` parameter
You can use the ``permissions`` parameter
to specify any overwrites.

.. code-block:: python
Expand All @@ -34,10 +61,10 @@ to specify any overwrites.
return "You have permissions!"
Subcommands and Command Groups
------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Discord only supports attaching permissions overwrites to top-level commands.
Thus, there are no ``default_permission`` or ``permissions`` parameters for the
Thus, there is no ``permissions`` parameter for the
:meth:`.SlashCommandGroup.command` decorator. However, you can still set
permissions for an entire tree of subcommands using the
:meth:`.DiscordInteractions.command_group` function.
Expand All @@ -55,7 +82,7 @@ permissions for an entire tree of subcommands using the
return "You have unlocked the secret subcommand!"
Context object
--------------
^^^^^^^^^^^^^^

You can also use the :meth:`.Context.overwrite_permissions` method to overwrite
the permissions for a command. By default, this is the command currently
Expand Down
8 changes: 7 additions & 1 deletion examples/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

sys.path.insert(1, ".")

from flask_discord_interactions import DiscordInteractions, Permission
from flask_discord_interactions import DiscordInteractions, Permission, Member


app = Flask(__name__)
Expand All @@ -18,6 +18,12 @@
discord.update_commands()


@discord.command(default_member_permissions=4)
def blacklist(ctx, user: Member):
"Only members with the 'Ban members' permission can use this command"
return f"{user.username} has been blacklisted!"


@discord.command(
default_permission=False, permissions=[Permission(role="786840072891662336")]
)
Expand Down
64 changes: 43 additions & 21 deletions flask_discord_interactions/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import enum
import inspect
import itertools
import warnings

from flask_discord_interactions.context import Context, AsyncContext
from flask_discord_interactions.models import (
Expand Down Expand Up @@ -49,7 +50,11 @@ class Command:
The value is in ``ApplicationCommandType``. If omitted, set the default
value to ``ApplicationCommandType.CHAT_INPUT``.
default_permission
Whether the command is enabled by default. Default is True.
Deprecated as of v1.5! Whether the command is enabled by default.
default_member_permissions
A permission integer defining the required permissions a user must have to run the command
dm_permission
Indicates whether the command can be used in DMs
permissions
List of permission overwrites.
discord
Expand All @@ -65,7 +70,9 @@ def __init__(
options,
annotations,
command_type=ApplicationCommandType.CHAT_INPUT,
default_permission=True,
default_permission=None,
default_member_permissions=None,
dm_permission=None,
permissions=None,
discord=None,
):
Expand All @@ -76,17 +83,16 @@ def __init__(
self.annotations = annotations or {}
self.type = command_type
self.default_permission = default_permission
self.default_member_permissions = default_member_permissions
self.dm_permission = dm_permission
self.permissions = permissions or []
self.discord = discord

if self.name is None:
self.name = command.__name__

if not 1 <= len(self.name) <= 32:
raise ValueError(
f"Error adding command {self.name}: "
"Command name must be between 1 and 32 characters."
)
raise ValueError(f"Error adding command {self.name}: " "Command name must be between 1 and 32 characters.")
if self.type is ApplicationCommandType.CHAT_INPUT:
if self.description is None:
self.description = command.__doc__ or "No description"
Expand All @@ -104,18 +110,15 @@ def __init__(
)
if not 1 <= len(self.description) <= 100:
raise ValueError(
f"Error adding command {self.name}: "
"Command description must be between 1 and 100 characters."
f"Error adding command {self.name}: " "Command description must be between 1 and 100 characters."
)
else:
self.description = None

self.is_async = inspect.iscoroutinefunction(self.command)

if self.options:
self.options = [
(o.dump() if isinstance(o, Option) else o) for o in self.options
]
self.options = [(o.dump() if isinstance(o, Option) else o) for o in self.options]

if self.type is ApplicationCommandType.CHAT_INPUT and self.options is None:
sig = inspect.signature(self.command)
Expand Down Expand Up @@ -159,9 +162,7 @@ def __init__(

option = {
"name": parameter.name,
"description": self.annotations.get(
parameter.name, "No description"
),
"description": self.annotations.get(parameter.name, "No description"),
"type": ptype,
"required": (parameter.default == parameter.empty),
"autocomplete": autocomplete,
Expand All @@ -176,9 +177,7 @@ def __init__(
value_type = str

for name, member in annotation.__members__.items():
choices.append(
{"name": name, "value": value_type(member.value)}
)
choices.append({"name": name, "value": value_type(member.value)})

option["choices"] = choices

Expand Down Expand Up @@ -237,14 +236,27 @@ def run(self, context, *args, **kwargs):
def dump(self):
"Returns this command as a dict for registration with the Discord API."
data = {
"type": self.type,
"name": self.name,
"description": self.description,
"options": self.options,
"default_permission": self.default_permission,
}

if hasattr(self, "type"):
data["type"] = self.type
# Keeping this here not to break any bots using the old system
if self.default_permission is not None:
data["default_permission"] = self.default_permission
warnings.warn(
"Deprecated! As of v1.5, the old default_permission is deprecated in favor of "
"the new default_member_permissions",
DeprecationWarning,
stacklevel=2,
)

if self.default_member_permissions:
data["default_member_permissions"] = str(self.default_member_permissions)

if self.dm_permission:
data["dm_permission"] = self.dm_permission

return data

Expand Down Expand Up @@ -297,6 +309,8 @@ def __init__(self, name, description, is_async=False):
self.type = ApplicationCommandType.CHAT_INPUT

self.default_permission = None
self.default_member_permissions = None
self.dm_permission = None
self.permissions = []

self.is_async = is_async
Expand Down Expand Up @@ -375,6 +389,10 @@ class SlashCommandGroup(SlashCommandSubgroup):
get an :class:`AsyncContext` instead of a :class:`Context`.)
default_permission
Whether the subgroup is enabled by default. Default is True.
default_member_permissions:
Permission integer setting permission defaults for a command
dm_permission
Indicates whether the command can be used in DMs
permissions
List of permission overwrites. These apply to all subcommands of this
group.
Expand All @@ -385,7 +403,9 @@ def __init__(
name,
description,
is_async=False,
default_permission=True,
default_permission=None,
default_member_permissions=None,
dm_permission=None,
permissions=None,
):
self.name = name
Expand All @@ -394,6 +414,8 @@ def __init__(
self.type = ApplicationCommandType.CHAT_INPUT

self.default_permission = default_permission
self.default_member_permissions = default_member_permissions
self.dm_permission = dm_permission
self.permissions = permissions or []

self.is_async = is_async
Expand Down
36 changes: 32 additions & 4 deletions flask_discord_interactions/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ def add_command(
annotations=None,
type=ApplicationCommandType.CHAT_INPUT,
default_permission=None,
default_member_permissions=None,
dm_permission=None,
permissions=None,
):
"""
Expand All @@ -75,7 +77,11 @@ def add_command(
type
The ``ApplicationCommandType`` of the command.
default_permission
Whether the command is enabled by default. Default is True.
Deprecated as of v1.5! Whether the command is enabled by default.
default_member_permissions
A permission integer defining the required permissions a user must have to run the command
dm_permission
Indicates whether the command can be used in DMs
permissions
List of permission overwrites.
"""
Expand All @@ -87,6 +93,8 @@ def add_command(
annotations,
type,
default_permission,
default_member_permissions,
dm_permission,
permissions,
self,
)
Expand Down Expand Up @@ -114,6 +122,8 @@ def command(
annotations=None,
type=ApplicationCommandType.CHAT_INPUT,
default_permission=None,
default_member_permissions=None,
dm_permission=None,
permissions=None,
):
"""
Expand All @@ -134,7 +144,11 @@ def command(
type
The ``ApplicationCommandType`` of the command.
default_permission
Whether the command is enabled by default. Default is True.
Deprecated as of v1.5! Whether the command is enabled by default.
default_member_permissions
A permission integer defining the required permissions a user must have to run the command
dm_permission
Indicates whether the command can be used in DMs
permissions
List of permission overwrites.
"""
Expand All @@ -149,6 +163,8 @@ def decorator(func):
annotations,
type,
default_permission,
default_member_permissions,
dm_permission,
permissions,
)
return command
Expand All @@ -161,6 +177,8 @@ def command_group(
description="No description",
is_async=False,
default_permission=None,
default_member_permissions=None,
dm_permission=None,
permissions=None,
):
"""
Expand All @@ -177,13 +195,23 @@ def command_group(
Whether the subgroup should be considered async (if subcommands
get an :class:`.AsyncContext` instead of a :class:`Context`.)
default_permission
Whether the command group is enabled by default.
Deprecated as of v1.5! Whether the command is enabled by default.
default_member_permissions
A permission integer defining the required permissions a user must have to run the command
dm_permission
Indicates whether the command canbe used in DMs
permissions
List of permission overwrites. These apply to the entire group.
"""

group = SlashCommandGroup(
name, description, is_async, default_permission, permissions
name,
description,
is_async,
default_permission,
default_member_permissions,
dm_permission,
permissions,
)
self.discord_commands[name] = group
return group
Expand Down

0 comments on commit 5714137

Please sign in to comment.