mirror of
https://github.com/wlinator/luminara.git
synced 2024-10-02 22:23:13 +00:00
333 lines
14 KiB
Python
333 lines
14 KiB
Python
|
"""
|
||
|
This type stub file was generated by pyright.
|
||
|
"""
|
||
|
|
||
|
import discord
|
||
|
from typing import ClassVar, List, Literal, Optional, Sequence, TYPE_CHECKING, Union, overload
|
||
|
from discord.ext.commands import Context
|
||
|
from .abc import MenuType, _BaseMenu
|
||
|
from .buttons import ReactionButton
|
||
|
from .decorators import ensure_not_primed
|
||
|
from .errors import *
|
||
|
|
||
|
"""
|
||
|
MIT License
|
||
|
|
||
|
Copyright (c) 2021-present @defxult
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
copy of this software and associated documentation files (the "Software"),
|
||
|
to deal in the Software without restriction, including without limitation
|
||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||
|
Software is furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
DEALINGS IN THE SOFTWARE.
|
||
|
"""
|
||
|
if TYPE_CHECKING:
|
||
|
...
|
||
|
class ReactionMenu(_BaseMenu):
|
||
|
"""A class to create a discord pagination menu using reactions
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
method: Union[:class:`discord.ext.commands.Context`, :class:`discord.Interaction`]
|
||
|
The Context object. You can get this using a command or if you're in a `discord.on_message` event. Also accepts interactions, typically received when using slash commands
|
||
|
|
||
|
menu_type: :class:`MenuType`
|
||
|
The configuration of the menu. Class variables :attr:`ReactionMenu.TypeEmbed`, :attr:`ReactionMenu.TypeEmbedDynamic`, or :attr:`ReactionMenu.TypeText`
|
||
|
|
||
|
Kwargs
|
||
|
------
|
||
|
all_can_click: :class:`bool`
|
||
|
Sets if everyone is allowed to control when pages are 'turned' when buttons are pressed (defaults to `False`)
|
||
|
|
||
|
allowed_mentions: :class:`discord.AllowedMentions`
|
||
|
Controls the mentions being processed in the menu message (defaults to :class:`discord.AllowedMentions(everyone=False, users=True, roles=False, replied_user=True)`)
|
||
|
|
||
|
clear_reactions_after: :class:`bool`
|
||
|
If the menu times out, remove all reactions (defaults to `True`)
|
||
|
|
||
|
custom_embed: :class:`discord.Embed`
|
||
|
Embed object to use when adding data with :meth:`ReactionMenu.add_row()`. Used for styling purposes (:attr:`ReactionMenu.TypeEmbedDynamic` only/defaults to :class:`None`)
|
||
|
|
||
|
delete_interactions: :class:`bool`
|
||
|
Delete the prompt message by the bot and response message by the user when asked what page they would like to go to when using :attr:`ReactionButton.Type.GO_TO_PAGE` (defaults to `True`)
|
||
|
|
||
|
delete_on_timeout: :class:`bool`
|
||
|
When the menu times out, delete the menu message. This overrides :attr:`clear_reactions_after` (defaults to `False`)
|
||
|
|
||
|
name: :class:`str`
|
||
|
A name you can set for the menu (defaults to :class:`None`)
|
||
|
|
||
|
navigation_speed: :class:`str`
|
||
|
Sets if the user needs to wait for the reaction to be removed by the bot before "turning" the page. Setting the speed to :attr:`ReactionMenu.FAST` makes it so that there is no need to wait (reactions are not removed on each press) and can
|
||
|
navigate lengthy menu's more quickly (defaults to :attr:`ReactionMenu.NORMAL`)
|
||
|
|
||
|
only_roles: List[:class:`discord.Role`]
|
||
|
Members with any of the provided roles are the only ones allowed to control the menu. The member who started the menu will always be able to control it. This overrides :attr:`all_can_click` (defaults to :class:`None`)
|
||
|
|
||
|
remove_extra_reactions: :class:`bool`
|
||
|
If `True`, all emojis (reactions) added to the menu message that were not originally added to the menu will be removed (defaults to `False`)
|
||
|
|
||
|
rows_requested: :class:`int`
|
||
|
The amount of information per :meth:`ReactionMenu.add_row()` you would like applied to each embed page (:attr:`ReactionMenu.TypeEmbedDynamic` only/defaults to :class:`None`)
|
||
|
|
||
|
show_page_director: :class:`bool`
|
||
|
Shown at the bottom of each embed page. "Page 1/20" (defaults to `True`)
|
||
|
|
||
|
style: :class:`str`
|
||
|
A custom page director style you can select. "$" represents the current page, "&" represents the total amount of pages (defaults to "Page $/&") Example: `ReactionMenu(ctx, ..., style='On $ out of &')`
|
||
|
|
||
|
timeout: Union[:class:`float`, :class:`int`, :class:`None`]
|
||
|
Timer for when the menu should end. Can be :class:`None` for no timeout (defaults to 60.0)
|
||
|
|
||
|
wrap_in_codeblock: :class:`str`
|
||
|
The discord codeblock language identifier (:attr:`ReactionMenu.TypeEmbedDynamic` only/defaults to :class:`None`). Example: `ReactionMenu(ctx, ..., wrap_in_codeblock='py')`
|
||
|
"""
|
||
|
NORMAL: ClassVar[str] = ...
|
||
|
FAST: ClassVar[str] = ...
|
||
|
_active_sessions: List[ReactionMenu] = ...
|
||
|
def __init__(self, method: Union[Context, discord.Interaction], /, *, menu_type: MenuType, **kwargs) -> None:
|
||
|
...
|
||
|
|
||
|
def __repr__(self): # -> str:
|
||
|
...
|
||
|
|
||
|
@property
|
||
|
def navigation_speed(self) -> str:
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
:class:`str`: The current :attr:`navigation_speed` that is set for the menu
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@property
|
||
|
def buttons(self) -> List[ReactionButton]:
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
List[:class:`ReactionButton`]: A list of all the buttons that have been added to the menu
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@property
|
||
|
def buttons_most_clicked(self) -> List[ReactionButton]:
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
List[:class:`ReactionButton`]: The list of buttons on the menu ordered from highest (button with the most clicks) to lowest (button with the least clicks). Can be an empty list if there are no buttons registered to the menu
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@classmethod
|
||
|
async def quick_start(cls, method: Union[Context, discord.Interaction], /, pages: Sequence[Union[discord.Embed, str]], buttons: Optional[Sequence[ReactionButton]] = ...) -> ReactionMenu:
|
||
|
"""|coro class method|
|
||
|
|
||
|
Start a menu with default settings either with a `menu_type` of `ReactionMenu.TypeEmbed` (all values in `pages` are of type `discord.Embed`) or `ReactionMenu.TypeText` (all values in `pages` are of type `str`)
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
method: Union[:class:`discord.ext.commands.Context`, :class:`discord.Interaction`]
|
||
|
The Context object. You can get this using a command or if you're in a `discord.on_message` event. Also accepts interactions, typically received when using slash commands
|
||
|
|
||
|
pages: Sequence[Union[:class:`discord.Embed`, :class:`str`]]
|
||
|
The pages to add
|
||
|
|
||
|
buttons: Optional[Sequence[:class:`ReactionButton`]]
|
||
|
The buttons to add. If left as `DEFAULT_BUTTONS`, that is equivalent to `ReactionButton.all()`
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
:class:`ReactionMenu`: The menu that was started
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||
|
- `NoPages`: The menu was started when no pages have been added
|
||
|
- `NoButtons`: Attempted to start the menu when no Buttons have been registered
|
||
|
- `IncorrectType`: All items in :param:`pages` were not of type :class:`discord.Embed` or :class:`str`
|
||
|
|
||
|
.. added v3.1.0
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
def get_button(self, identity: str, *, search_by: Literal['name', 'emoji', 'type'] = ...) -> List[ReactionButton]:
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
def get_button(self, identity: int, *, search_by: Literal['name', 'emoji', 'type'] = ...) -> List[ReactionButton]:
|
||
|
...
|
||
|
|
||
|
def get_button(self, identity: Union[str, int], *, search_by: Literal['name', 'emoji', 'type'] = ...) -> List[ReactionButton]:
|
||
|
"""Get a button that has been registered to the menu by name, emoji, or type
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
identity: :class:`str`
|
||
|
The button name, emoji, or type
|
||
|
|
||
|
search_by: :class:`str`
|
||
|
How to search for the button. If "name", it's searched by button names. If "emoji", it's searched by it's emojis.
|
||
|
If "type", it's searched by :attr:`ReactionMenu.Type`, aka the `linked_to` of the button
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
List[:class:`ReactionButton`]: The button(s) matching the given identity
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `ReactionMenuException`: Parameter :param:`search_by` was not "name", "emoji", or "type"
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@ensure_not_primed
|
||
|
def add_button(self, button: ReactionButton) -> None:
|
||
|
"""Add a button to the menu
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
button: :class:`ReactionButton`
|
||
|
The button to add
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call this method after the menu has started
|
||
|
- `MissingSetting`: Set the buttons `linked_to` as :attr:`ReactionButton.Type.CALLER` but did not assign the :class:`ReactionButton` kwarg "details" a value
|
||
|
- `DuplicateButton`: The emoji used is already registered as a button
|
||
|
- `TooManyButtons`: More than 20 buttons were added. Discord has a reaction limit of 20
|
||
|
- `IncorrectType`: Parameter :param:`button` was not of type :class:`ReactionButton`
|
||
|
- `ReactionMenuException`: When attempting to add a button with the type `ReactionButton.Type.SKIP`, the "skip" kwarg was not set
|
||
|
- `MenuSettingsMismatch`: A :class:`ReactionButton` with a `linked_to` of :attr:`ReactionButton.Type.CUSTOM_EMBED` cannot be used when the `menu_type` is `TypeText`
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@ensure_not_primed
|
||
|
def add_buttons(self, buttons: Sequence[ReactionButton]) -> None:
|
||
|
"""Add multiple buttons to the menu at once
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
buttons: Sequence[:class:`ReactionButton`]
|
||
|
The buttons to add
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call this method after the menu has started
|
||
|
- `MissingSetting`: Set the buttons `linked_to` as :attr:`ReactionButton.Type.CALLER` but did not assign the :class:`ReactionButton` kwarg "details" a value
|
||
|
- `DuplicateButton`: The emoji used is already registered as a button
|
||
|
- `TooManyButtons`: More than 20 buttons were added. Discord has a reaction limit of 20
|
||
|
- `IncorrectType`: Parameter :param:`button` was not of type :class:`ReactionButton`
|
||
|
- `ReactionMenuException`: When attempting to add a button with the type `ReactionButton.Type.SKIP`, the "skip" kwarg was not set
|
||
|
- `MenuSettingsMismatch`: A :class:`ReactionButton` with a `linked_to` of :attr:`ReactionButton.Type.CUSTOM_EMBED` cannot be used when the `menu_type` is `TypeText`
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@ensure_not_primed
|
||
|
def remove_button(self, button: ReactionButton) -> None:
|
||
|
"""Remove a button from the menu
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
button: :class:`ReactionButton`
|
||
|
The button to remove
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call this method after the menu has started
|
||
|
- `ButtonNotFound`: The provided button was not found in the list of buttons on the menu
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@ensure_not_primed
|
||
|
def remove_all_buttons(self) -> None:
|
||
|
"""Remove all buttons from the menu
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call this method after the menu has started
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
async def stop(self, *, delete_menu_message: bool = ..., clear_reactions: bool = ...) -> None:
|
||
|
"""|coro|
|
||
|
|
||
|
Stops the process of the menu with the option of deleting the menu's message or clearing reactions upon stop
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
delete_menu_message: :class:`bool`
|
||
|
Delete the menu message
|
||
|
|
||
|
clear_reactions: :class:`bool`
|
||
|
Remove all reactions
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `discord.DiscordException`: Any exception that can be raised when deleting a message or removing a reaction from a message
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
async def start(self, *, send_to: Optional[str] = ..., reply: bool = ...) -> None:
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
async def start(self, *, send_to: Optional[int] = ..., reply: bool = ...) -> None:
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
async def start(self, *, send_to: Optional[discord.TextChannel] = ..., reply: bool = ...) -> None:
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
async def start(self, *, send_to: Optional[discord.VoiceChannel] = ..., reply: bool = ...) -> None:
|
||
|
...
|
||
|
|
||
|
@overload
|
||
|
async def start(self, *, send_to: Optional[discord.Thread] = ..., reply: bool = ...) -> None:
|
||
|
...
|
||
|
|
||
|
@ensure_not_primed
|
||
|
async def start(self, *, send_to: Optional[Union[str, int, discord.TextChannel, discord.VoiceChannel, discord.Thread]] = ..., reply: bool = ...) -> None:
|
||
|
"""|coro|
|
||
|
|
||
|
Start the menu
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
send_to: Optional[Union[:class:`str`, :class:`int`, :class:`discord.TextChannel`, :class:`discord.VoiceChannel`, :class:`discord.Thread`]]
|
||
|
The channel/thread you'd like the menu to start in. Use the channel/threads name, ID, or it's object. Please note that if you intend to use a channel/thread object, using
|
||
|
method :meth:`discord.Client.get_channel()` (or any other related methods), that channel should be in the same list as if you were to use `ctx.guild.text_channels`
|
||
|
or `ctx.guild.threads`. This only works on a context guild channel basis. That means a menu instance cannot be created in one guild and the menu itself (:param:`send_to`)
|
||
|
be sent to another. Whichever guild context the menu was instantiated in, the channels/threads of that guild are the only options for :param:`send_to`
|
||
|
|
||
|
reply: :class:`bool`
|
||
|
Enables the menu message to reply to the message that triggered it. Parameter :param:`send_to` must be :class:`None` if this is `True`. This only pertains to a non-interaction based menu.
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||
|
- `NoPages`: The menu was started when no pages have been added
|
||
|
- `NoButtons`: Attempted to start the menu when no Buttons have been registered
|
||
|
- `ReactionMenuException`: The :class:`ReactionMenu`'s `menu_type` was not recognized
|
||
|
- `DescriptionOversized`: When using a `menu_type` of :attr:`ReactionMenu.TypeEmbedDynamic`, the embed description was over discords size limit
|
||
|
- `IncorrectType`: Parameter :param:`send_to` was not of the expected type
|
||
|
- `MenuException`: The channel set in :param:`send_to` was not found
|
||
|
"""
|
||
|
...
|
||
|
|
||
|
|
||
|
|