diff options
| author | 2022-07-23 14:28:36 +0100 | |
|---|---|---|
| committer | 2022-07-23 14:28:36 +0100 | |
| commit | b84448734efbba0b892a0e8da6400315ee9bfc97 (patch) | |
| tree | 4353a54d9c12854a9a1e2ca925c4c13cbff75b74 | |
| parent | Raise error when referenced message has no content (diff) | |
| parent | Merge branch 'main' into bot-core-100 (diff) | |
Merge remote-tracking branch 'origin/bot-core-100' into bot-core-100
| -rw-r--r-- | botcore/utils/__init__.py | 3 | ||||
| -rw-r--r-- | botcore/utils/interactions.py | 98 | ||||
| -rw-r--r-- | docs/changelog.rst | 13 | ||||
| -rw-r--r-- | pyproject.toml | 2 | 
4 files changed, 114 insertions, 2 deletions
| diff --git a/botcore/utils/__init__.py b/botcore/utils/__init__.py index 2e493374..95e89d20 100644 --- a/botcore/utils/__init__.py +++ b/botcore/utils/__init__.py @@ -1,6 +1,6 @@  """Useful utilities and tools for Discord bot development.""" -from botcore.utils import _monkey_patches, caching, channel, commands, logging, members, regex, scheduling +from botcore.utils import _monkey_patches, caching, channel, commands, interactions, logging, members, regex, scheduling  from botcore.utils._extensions import unqualify @@ -25,6 +25,7 @@ __all__ = [      caching,      channel,      commands, +    interactions,      logging,      members,      regex, diff --git a/botcore/utils/interactions.py b/botcore/utils/interactions.py new file mode 100644 index 00000000..26bd92f2 --- /dev/null +++ b/botcore/utils/interactions.py @@ -0,0 +1,98 @@ +import contextlib +from typing import Optional, Sequence + +from discord import ButtonStyle, Interaction, Message, NotFound, ui + +from botcore.utils.logging import get_logger + +log = get_logger(__name__) + + +class ViewWithUserAndRoleCheck(ui.View): +    """ +    A view that allows the original invoker and moderators to interact with it. + +    Args: +        allowed_users: A sequence of user's ids who are allowed to interact with the view. +        allowed_roles: A sequence of role ids that are allowed to interact with the view. +        timeout: Timeout in seconds from last interaction with the UI before no longer accepting input. +            If ``None`` then there is no timeout. +        message: The message to remove the view from on timeout. This can also be set with +            ``view.message = await ctx.send( ... )``` , or similar, after the view is instantiated. +    """ + +    def __init__( +        self, +        *, +        allowed_users: Sequence[int], +        allowed_roles: Sequence[int], +        timeout: Optional[float] = 180.0, +        message: Optional[Message] = None +    ) -> None: +        super().__init__(timeout=timeout) +        self.allowed_users = allowed_users +        self.allowed_roles = allowed_roles +        self.message = message + +    async def interaction_check(self, interaction: Interaction) -> bool: +        """ +        Ensure the user clicking the button is the view invoker, or a moderator. + +        Args: +            interaction: The interaction that occurred. +        """ +        if interaction.user.id in self.allowed_users: +            log.trace( +                "Allowed interaction by %s (%d) on %d as they are an allowed user.", +                interaction.user, +                interaction.user.id, +                interaction.message.id, +            ) +            return True + +        if any(role.id in self.allowed_roles for role in getattr(interaction.user, "roles", [])): +            log.trace( +                "Allowed interaction by %s (%d)on %d as they have an allowed role.", +                interaction.user, +                interaction.user.id, +                interaction.message.id, +            ) +            return True + +        await interaction.response.send_message("This is not your button to click!", ephemeral=True) +        return False + +    async def on_timeout(self) -> None: +        """Remove the view from ``self.message`` if set.""" +        if self.message: +            with contextlib.suppress(NotFound): +                # Cover the case where this message has already been deleted by external means +                await self.message.edit(view=None) + + +class DeleteMessageButton(ui.Button): +    """ +    A button that can be added to a view to delete the message containing the view on click. + +    This button itself carries out no interaction checks, these should be done by the parent view. + +    See :obj:`botcore.utils.interactions.ViewWithUserAndRoleCheck` for a view that implements basic checks. + +    Args: +        style (:literal-url:`ButtonStyle <https://discordpy.readthedocs.io/en/latest/interactions/api.html#discord.ButtonStyle>`): +            The style of the button, set to ``ButtonStyle.secondary`` if not specified. +        label: The label of the button, set to "Delete" if not specified. +    """  # noqa: E501 + +    def __init__( +        self, +        *, +        style: ButtonStyle = ButtonStyle.secondary, +        label: str = "Delete", +        **kwargs +    ): +        super().__init__(style=style, label=label, **kwargs) + +    async def callback(self, interaction: Interaction) -> None: +        """Delete the original message on button click.""" +        await interaction.message.delete() diff --git a/docs/changelog.rst b/docs/changelog.rst index 9d74eee8..e35e75d0 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,19 @@  Changelog  ========= +- :release:`7.4.0 <17th July 2022>` +- :feature:`106` Add an optional ``message`` attr to :obj:`botcore.utils.interactions.ViewWithUserAndRoleCheck`. On view timeout, this message has its view removed if set. + + +- :release:`7.3.1 <16th July 2022>` +- :bug:`104` Fix :obj:`botcore.utils.interactions.DeleteMessageButton` not working due to using wrong delete method. + + +- :release:`7.3.0 <16th July 2022>` +- :feature:`103` Add a generic view :obj:`botcore.utils.interactions.ViewWithUserAndRoleCheck` that only allows specified users and roles to interaction with it +- :feature:`103` Add a button :obj:`botcore.utils.interactions.DeleteMessageButton` that deletes the message attached to its parent view. + +  - :release:`7.2.2 <9th July 2022>`  - :bug:`98` Only close ``BotBase.stats._transport`` if ``BotBase.stats`` was created diff --git a/pyproject.toml b/pyproject.toml index ccb1a39d..6c91dde3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@  [tool.poetry]  name = "bot-core" -version = "7.2.2" +version = "7.4.0"  description = "Bot-Core provides the core functionality and utilities for the bots of the Python Discord community."  authors = ["Python Discord <[email protected]>"]  license = "MIT" | 
