mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
Add ConfirmView utility (#6176)
Co-authored-by: Jakub Kuczys <me@jacken.men>
This commit is contained in:
parent
49bf103891
commit
7dff136937
@ -84,6 +84,65 @@ Utility UI
|
|||||||
|
|
||||||
.. automodule:: redbot.core.utils.views
|
.. automodule:: redbot.core.utils.views
|
||||||
:members:
|
:members:
|
||||||
|
:exclude-members: ConfirmView
|
||||||
|
|
||||||
|
.. autoclass:: ConfirmView
|
||||||
|
:members:
|
||||||
|
:exclude-members: confirm_button, dismiss_button
|
||||||
|
|
||||||
|
.. autoattribute:: confirm_button
|
||||||
|
:no-value:
|
||||||
|
|
||||||
|
A `discord.ui.Button` to confirm the message.
|
||||||
|
|
||||||
|
The button's callback will set `result` to ``True``, defer the response,
|
||||||
|
and call `on_timeout()` to clean up the view.
|
||||||
|
|
||||||
|
.. rubric:: Example
|
||||||
|
|
||||||
|
Changing the style and label of this `discord.ui.Button`::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author)
|
||||||
|
view.confirm_button.style = discord.ButtonStyle.red
|
||||||
|
view.confirm_button.label = "Delete"
|
||||||
|
view.dismiss_button.label = "Cancel"
|
||||||
|
view.message = await ctx.send(
|
||||||
|
"Are you sure you want to remove #very-important-channel?", view=view
|
||||||
|
)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Channel #very-important-channel deleted.")
|
||||||
|
else:
|
||||||
|
await ctx.send("Canceled.")
|
||||||
|
|
||||||
|
:type: discord.ui.Button
|
||||||
|
|
||||||
|
.. autoattribute:: dismiss_button
|
||||||
|
:no-value:
|
||||||
|
|
||||||
|
A `discord.ui.Button` to dismiss the message.
|
||||||
|
|
||||||
|
The button's callback will set `result` to ``False``, defer the response,
|
||||||
|
and call `on_timeout()` to clean up the view.
|
||||||
|
|
||||||
|
.. rubric:: Example
|
||||||
|
|
||||||
|
Changing the style and label of this `discord.ui.Button`::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author)
|
||||||
|
view.confirm_button.style = discord.ButtonStyle.red
|
||||||
|
view.confirm_button.label = "Delete"
|
||||||
|
view.dismiss_button.label = "Cancel"
|
||||||
|
view.message = await ctx.send(
|
||||||
|
"Are you sure you want to remove #very-important-channel?", view=view
|
||||||
|
)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Channel #very-important-channel deleted.")
|
||||||
|
else:
|
||||||
|
await ctx.send("Canceled.")
|
||||||
|
|
||||||
|
:type: discord.ui.Button
|
||||||
|
|
||||||
AntiSpam
|
AntiSpam
|
||||||
========
|
========
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from redbot.core.commands.converter import get_dict_converter
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from redbot.core.commands import Context
|
from redbot.core.commands import Context
|
||||||
|
|
||||||
__all__ = ("SimpleMenu", "SetApiModal", "SetApiView")
|
__all__ = ("SimpleMenu", "SetApiModal", "SetApiView", "ConfirmView")
|
||||||
|
|
||||||
_ = Translator("UtilsViews", __file__)
|
_ = Translator("UtilsViews", __file__)
|
||||||
|
|
||||||
@ -431,3 +431,190 @@ class SetApiView(discord.ui.View):
|
|||||||
return await interaction.response.send_modal(
|
return await interaction.response.send_modal(
|
||||||
SetApiModal(self.default_service, self.default_keys)
|
SetApiModal(self.default_service, self.default_keys)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmView(discord.ui.View):
|
||||||
|
"""
|
||||||
|
A simple `discord.ui.View` used for confirming something.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
author: Optional[discord.abc.User]
|
||||||
|
The user who you want to be interacting with the confirmation.
|
||||||
|
If this is omitted anyone can click yes or no.
|
||||||
|
timeout: float
|
||||||
|
The timeout of the view in seconds. Defaults to ``180`` seconds.
|
||||||
|
disable_buttons: bool
|
||||||
|
Whether to disable the buttons instead of removing them from the message after the timeout.
|
||||||
|
Defaults to ``False``.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Using the view::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author)
|
||||||
|
# attach the message to the view after sending it.
|
||||||
|
# This way, the view will be automatically removed
|
||||||
|
# from the message after the timeout.
|
||||||
|
view.message = await ctx.send("Are you sure you about that?", view=view)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Okay I will do that.")
|
||||||
|
else:
|
||||||
|
await ctx.send("I will not be doing that then.")
|
||||||
|
|
||||||
|
Auto-disable the buttons after timeout if nothing is pressed::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author, disable_buttons=True)
|
||||||
|
view.message = await ctx.send("Are you sure you about that?", view=view)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Okay I will do that.")
|
||||||
|
else:
|
||||||
|
await ctx.send("I will not be doing that then.")
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
result: Optional[bool]
|
||||||
|
The result of the confirm view.
|
||||||
|
author: Optional[discord.abc.User]
|
||||||
|
The author of the message who is allowed to press the buttons.
|
||||||
|
message: Optional[discord.Message]
|
||||||
|
The message the confirm view is sent on. This can be set while
|
||||||
|
sending the message. This can also be left as ``None`` in which case
|
||||||
|
nothing will happen in `on_timeout()`, if the view is never interacted with.
|
||||||
|
disable_buttons: bool
|
||||||
|
Whether to disable the buttons isntead of removing them on timeout
|
||||||
|
(if the `message` attribute has been set on the view).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
author: Optional[discord.abc.User] = None,
|
||||||
|
*,
|
||||||
|
timeout: float = 180.0,
|
||||||
|
disable_buttons: bool = False,
|
||||||
|
):
|
||||||
|
if timeout is None:
|
||||||
|
raise TypeError("This view should not be used as a persistent view.")
|
||||||
|
super().__init__(timeout=timeout)
|
||||||
|
self.result: Optional[bool] = None
|
||||||
|
self.author: Optional[discord.abc.User] = author
|
||||||
|
self.message: Optional[discord.Message] = None
|
||||||
|
self.disable_buttons = disable_buttons
|
||||||
|
|
||||||
|
async def on_timeout(self):
|
||||||
|
"""
|
||||||
|
A callback that is called by the provided (default) callbacks for `confirm_button`
|
||||||
|
and `dismiss_button` as well as when a view’s timeout elapses without being
|
||||||
|
explicitly stopped.
|
||||||
|
|
||||||
|
The default implementation will either disable the buttons
|
||||||
|
when `disable_buttons` is ``True``, or remove the view from the message otherwise.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This will not do anything if `message` is ``None``.
|
||||||
|
"""
|
||||||
|
if self.message is None:
|
||||||
|
# we can't do anything here if message is none
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.disable_buttons:
|
||||||
|
self.confirm_button.disabled = True
|
||||||
|
self.dismiss_button.disabled = True
|
||||||
|
await self.message.edit(view=self)
|
||||||
|
else:
|
||||||
|
await self.message.edit(view=None)
|
||||||
|
|
||||||
|
@discord.ui.button(label=_("Yes"), style=discord.ButtonStyle.green)
|
||||||
|
async def confirm_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
# Warning: The Sphinx documentation for this method/attribute does not use this docstring.
|
||||||
|
"""
|
||||||
|
A `discord.ui.Button` to confirm the message.
|
||||||
|
|
||||||
|
The button's callback will set `result` to ``True``, defer the response,
|
||||||
|
and call `on_timeout()` to clean up the view.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
Changing the style and label of this `discord.ui.Button`::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author)
|
||||||
|
view.confirm_button.style = discord.ButtonStyle.red
|
||||||
|
view.confirm_button.label = "Delete"
|
||||||
|
view.dismiss_button.label = "Cancel"
|
||||||
|
view.message = await ctx.send(
|
||||||
|
"Are you sure you want to remove #very-important-channel?", view=view
|
||||||
|
)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Channel #very-important-channel deleted.")
|
||||||
|
else:
|
||||||
|
await ctx.send("Canceled.")
|
||||||
|
"""
|
||||||
|
self.result = True
|
||||||
|
self.stop()
|
||||||
|
# respond to the interaction so the user does not see "interaction failed".
|
||||||
|
await interaction.response.defer()
|
||||||
|
# call `on_timeout` explicitly here since it's not called when `stop()` is called.
|
||||||
|
await self.on_timeout()
|
||||||
|
|
||||||
|
@discord.ui.button(label=_("No"), style=discord.ButtonStyle.secondary)
|
||||||
|
async def dismiss_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
# Warning: The Sphinx documentation for this method/attribute does not use this docstring.
|
||||||
|
"""
|
||||||
|
A `discord.ui.Button` to dismiss the message.
|
||||||
|
|
||||||
|
The button's callback will set `result` to ``False``, defer the response,
|
||||||
|
and call `on_timeout()` to clean up the view.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
Changing the style and label of this `discord.ui.Button`::
|
||||||
|
|
||||||
|
view = ConfirmView(ctx.author)
|
||||||
|
view.confirm_button.style = discord.ButtonStyle.red
|
||||||
|
view.confirm_button.label = "Delete"
|
||||||
|
view.dismiss_button.label = "Cancel"
|
||||||
|
view.message = await ctx.send(
|
||||||
|
"Are you sure you want to remove #very-important-channel?", view=view
|
||||||
|
)
|
||||||
|
await view.wait()
|
||||||
|
if view.result:
|
||||||
|
await ctx.send("Channel #very-important-channel deleted.")
|
||||||
|
else:
|
||||||
|
await ctx.send("Canceled.")
|
||||||
|
"""
|
||||||
|
self.result = False
|
||||||
|
self.stop()
|
||||||
|
# respond to the interaction so the user does not see "interaction failed".
|
||||||
|
await interaction.response.defer()
|
||||||
|
# call `on_timeout` explicitly here since it's not called when `stop()` is called.
|
||||||
|
await self.on_timeout()
|
||||||
|
|
||||||
|
async def interaction_check(self, interaction: discord.Interaction):
|
||||||
|
"""
|
||||||
|
A callback that is called when an interaction happens within the view
|
||||||
|
that checks whether the view should process item callbacks for the interaction.
|
||||||
|
|
||||||
|
The default implementation of this will assign value of `discord.Interaction.message`
|
||||||
|
to the `message` attribute and either:
|
||||||
|
|
||||||
|
- send an ephemeral failure message and return ``False``,
|
||||||
|
if `author` is set and isn't the same as the interaction user, or
|
||||||
|
- return ``True``
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
The documentation of the callback in the base class:
|
||||||
|
:meth:`discord.ui.View.interaction_check()`
|
||||||
|
"""
|
||||||
|
if self.message is None:
|
||||||
|
self.message = interaction.message
|
||||||
|
if self.author and interaction.user.id != self.author.id:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
content=_("You are not authorized to interact with this."), ephemeral=True
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user