aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Izan <[email protected]>2022-07-23 14:28:36 +0100
committerGravatar Izan <[email protected]>2022-07-23 14:28:36 +0100
commitb84448734efbba0b892a0e8da6400315ee9bfc97 (patch)
tree4353a54d9c12854a9a1e2ca925c4c13cbff75b74
parentRaise error when referenced message has no content (diff)
parentMerge 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__.py3
-rw-r--r--botcore/utils/interactions.py98
-rw-r--r--docs/changelog.rst13
-rw-r--r--pyproject.toml2
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"