mirror of
https://github.com/wlinator/luminara.git
synced 2024-10-02 18:23:12 +00:00
Add case commands
This commit is contained in:
parent
d68368a30e
commit
84759ab294
10 changed files with 2769 additions and 1 deletions
168
modules/moderation/cases.py
Normal file
168
modules/moderation/cases.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
from reactionmenu import ViewButton, ViewMenu
|
||||||
|
|
||||||
|
from lib.case_handler import edit_case_modlog
|
||||||
|
from lib.const import CONST
|
||||||
|
from lib.exceptions import LumiException
|
||||||
|
from lib.format import format_case_number
|
||||||
|
from services.case_service import CaseService
|
||||||
|
from ui.cases import (
|
||||||
|
create_case_embed,
|
||||||
|
create_case_list_embed,
|
||||||
|
)
|
||||||
|
from ui.embeds import Builder
|
||||||
|
|
||||||
|
case_service = CaseService()
|
||||||
|
|
||||||
|
|
||||||
|
class Cases(commands.Cog):
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.hybrid_command(name="case", aliases=["c", "ca"], description="View a specific case by number")
|
||||||
|
@commands.has_permissions(manage_messages=True)
|
||||||
|
async def view_case_by_number(self, ctx: commands.Context[commands.Bot], case_number: int) -> None:
|
||||||
|
guild_id = ctx.guild.id if ctx.guild else 0
|
||||||
|
case = case_service.fetch_case_by_guild_and_number(guild_id, case_number)
|
||||||
|
|
||||||
|
if not case:
|
||||||
|
embed = Builder.create_embed(
|
||||||
|
user_name=ctx.author.name,
|
||||||
|
author_text=CONST.STRINGS["error_no_case_found_author"],
|
||||||
|
description=CONST.STRINGS["error_no_case_found_description"],
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
target = await commands.UserConverter().convert(ctx, str(case["target_id"]))
|
||||||
|
embed: discord.Embed = create_case_embed(
|
||||||
|
ctx=ctx,
|
||||||
|
target=target,
|
||||||
|
case_number=case["case_number"],
|
||||||
|
action_type=case["action_type"],
|
||||||
|
reason=case["reason"],
|
||||||
|
timestamp=case["created_at"],
|
||||||
|
duration=case["duration"] or None,
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@commands.hybrid_command(name="cases", description="View all cases in the guild")
|
||||||
|
@commands.has_permissions(manage_messages=True)
|
||||||
|
async def view_all_cases_in_guild(self, ctx: commands.Context[commands.Bot]) -> None:
|
||||||
|
if not ctx.guild:
|
||||||
|
raise LumiException(CONST.STRINGS["error_not_in_guild"])
|
||||||
|
|
||||||
|
guild_id = ctx.guild.id
|
||||||
|
cases = case_service.fetch_cases_by_guild(guild_id)
|
||||||
|
|
||||||
|
if not cases:
|
||||||
|
embed = Builder.create_embed(
|
||||||
|
user_name=ctx.author.name,
|
||||||
|
author_text=CONST.STRINGS["case_guild_no_cases_author"],
|
||||||
|
description=CONST.STRINGS["case_guild_no_cases"],
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed, all_can_click=True, delete_on_timeout=True)
|
||||||
|
|
||||||
|
for i in range(0, len(cases), 10):
|
||||||
|
chunk = cases[i : i + 10]
|
||||||
|
embed = create_case_list_embed(
|
||||||
|
ctx,
|
||||||
|
chunk,
|
||||||
|
CONST.STRINGS["case_guild_cases_author"],
|
||||||
|
)
|
||||||
|
menu.add_page(embed)
|
||||||
|
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_GO_TO_FIRST_PAGE, emoji="⏮️"),
|
||||||
|
)
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_PREVIOUS_PAGE, emoji="⏪"),
|
||||||
|
)
|
||||||
|
menu.add_button(ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_NEXT_PAGE, emoji="⏩"))
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_GO_TO_LAST_PAGE, emoji="⏭️"),
|
||||||
|
)
|
||||||
|
|
||||||
|
await menu.start()
|
||||||
|
|
||||||
|
@commands.hybrid_command(name="modcases", aliases=["mc", "modc"], description="View all cases in the guild")
|
||||||
|
@commands.has_permissions(manage_messages=True)
|
||||||
|
async def view_all_cases_by_mod(self, ctx: commands.Context[commands.Bot], moderator: discord.Member) -> None:
|
||||||
|
if not ctx.guild:
|
||||||
|
raise LumiException(CONST.STRINGS["error_not_in_guild"])
|
||||||
|
|
||||||
|
guild_id = ctx.guild.id
|
||||||
|
cases = case_service.fetch_cases_by_moderator(guild_id, moderator.id)
|
||||||
|
|
||||||
|
menu = ViewMenu(ctx, menu_type=ViewMenu.TypeEmbed, all_can_click=True, delete_on_timeout=True)
|
||||||
|
|
||||||
|
if not cases:
|
||||||
|
embed = Builder.create_embed(
|
||||||
|
user_name=ctx.author.name,
|
||||||
|
author_text=CONST.STRINGS["case_mod_no_cases_author"],
|
||||||
|
description=CONST.STRINGS["case_mod_no_cases"],
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in range(0, len(cases), 10):
|
||||||
|
chunk = cases[i : i + 10]
|
||||||
|
embed = create_case_list_embed(
|
||||||
|
ctx,
|
||||||
|
chunk,
|
||||||
|
CONST.STRINGS["case_mod_cases_author"].format(moderator.name),
|
||||||
|
)
|
||||||
|
menu.add_page(embed)
|
||||||
|
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_GO_TO_FIRST_PAGE, emoji="⏮️"),
|
||||||
|
)
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_PREVIOUS_PAGE, emoji="⏪"),
|
||||||
|
)
|
||||||
|
menu.add_button(ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_NEXT_PAGE, emoji="⏩"))
|
||||||
|
menu.add_button(
|
||||||
|
ViewButton(style=discord.ButtonStyle.secondary, custom_id=ViewButton.ID_GO_TO_LAST_PAGE, emoji="⏭️"),
|
||||||
|
)
|
||||||
|
|
||||||
|
await menu.start()
|
||||||
|
|
||||||
|
@commands.hybrid_command(name="editcase", aliases=["ec"], description="Edit the reason for a case")
|
||||||
|
@commands.has_permissions(manage_messages=True)
|
||||||
|
async def edit_case_reason(self, ctx: commands.Context[commands.Bot], case_number: int, *, new_reason: str):
|
||||||
|
if not ctx.guild:
|
||||||
|
raise LumiException(CONST.STRINGS["error_not_in_guild"])
|
||||||
|
|
||||||
|
guild_id = ctx.guild.id
|
||||||
|
|
||||||
|
case_service.edit_case_reason(
|
||||||
|
guild_id,
|
||||||
|
case_number,
|
||||||
|
new_reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
embed = Builder.create_embed(
|
||||||
|
user_name=ctx.author.name,
|
||||||
|
author_text=CONST.STRINGS["case_reason_update_author"],
|
||||||
|
description=CONST.STRINGS["case_reason_update_description"].format(
|
||||||
|
format_case_number(case_number),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def update_tasks():
|
||||||
|
await asyncio.gather(
|
||||||
|
ctx.send(embed=embed),
|
||||||
|
edit_case_modlog(ctx, guild_id, case_number, new_reason),
|
||||||
|
)
|
||||||
|
|
||||||
|
await update_tasks()
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot) -> None:
|
||||||
|
await bot.add_cog(Cases(bot))
|
16
poetry.lock
generated
16
poetry.lock
generated
|
@ -1056,6 +1056,20 @@ files = [
|
||||||
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
|
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reactionmenu"
|
||||||
|
version = "3.1.7"
|
||||||
|
description = "A library to create a discord.py 2.0+ paginator. Supports pagination with buttons, reactions, and category selection using selects."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "reactionmenu-3.1.7-py3-none-any.whl", hash = "sha256:51a217c920382dfecbb2f05d60bd20b79ed9895e9f5663f6c0edb75e806f863a"},
|
||||||
|
{file = "reactionmenu-3.1.7.tar.gz", hash = "sha256:10da3c1966de2b6264fcdf72537348923c5e151501644375c25f430bfd870463"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
"discord.py" = ">=2.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.32.3"
|
version = "2.32.3"
|
||||||
|
@ -1310,4 +1324,4 @@ multidict = ">=4.0"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "4a7a75036f4de7e0126a8f6b058eb3deb52f710becf44e4da6bac4a0ea0a1a2f"
|
content-hash = "122c3bd137956c87143ead4ae71a7800669a9f379a62c93da21a7ef61231c0f5"
|
||||||
|
|
|
@ -26,6 +26,7 @@ ruff = "^0.6.2"
|
||||||
typing-extensions = "^4.12.2"
|
typing-extensions = "^4.12.2"
|
||||||
pydantic = "^2.8.2"
|
pydantic = "^2.8.2"
|
||||||
pytimeparse = "^1.1.8"
|
pytimeparse = "^1.1.8"
|
||||||
|
reactionmenu = "^3.1.7"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
21
stubs/reactionmenu/__init__.pyi
Normal file
21
stubs/reactionmenu/__init__.pyi
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .buttons import ReactionButton, ViewButton
|
||||||
|
from .core import ReactionMenu
|
||||||
|
from .views_menu import ViewMenu, ViewSelect
|
||||||
|
from .abc import Page
|
||||||
|
|
||||||
|
"""
|
||||||
|
reactionmenu • discord pagination
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A library to create a discord.py 2.0+ paginator. Supports pagination with buttons, reactions, and category selection using selects.
|
||||||
|
|
||||||
|
:copyright: (c) 2021-present @defxult
|
||||||
|
:license: MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
__source__ = ...
|
||||||
|
__all__ = ('ReactionMenu', 'ReactionButton', 'ViewMenu', 'ViewButton', 'ViewSelect', 'Page')
|
816
stubs/reactionmenu/abc.pyi
Normal file
816
stubs/reactionmenu/abc.pyi
Normal file
|
@ -0,0 +1,816 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import discord
|
||||||
|
from typing import Any, Callable, ClassVar, Final, Generic, List, Literal, NamedTuple, Optional, Set, TYPE_CHECKING, Tuple, TypeVar, Union, overload
|
||||||
|
from datetime import datetime
|
||||||
|
from typing_extensions import Self
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from enum import Enum
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
from discord.utils import MISSING
|
||||||
|
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:
|
||||||
|
...
|
||||||
|
_DYNAMIC_EMBED_LIMIT: Final[int] = ...
|
||||||
|
_DEFAULT_STYLE: Final[str] = ...
|
||||||
|
DEFAULT_BUTTONS = ...
|
||||||
|
DEFAULT = MISSING
|
||||||
|
GB = TypeVar('GB', bound='_BaseButton')
|
||||||
|
M = TypeVar('M', bound='_BaseMenu')
|
||||||
|
class Page:
|
||||||
|
"""Represents a single "page" in the pagination process
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
__slots__ = ...
|
||||||
|
def __init__(self, *, content: Optional[str] = ..., embed: Optional[discord.Embed] = ..., files: Optional[List[discord.File]] = ...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_embeds(embeds: Sequence[discord.Embed]) -> List[Page]:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Converts a sequence of embeds into a list of :class:`Page`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PaginationEmojis:
|
||||||
|
"""A set of basic emojis for your convenience to use for your buttons emoji
|
||||||
|
- ◀️ as `BACK_BUTTON`
|
||||||
|
- ▶️ as `NEXT_BUTTON`
|
||||||
|
- ⏪ as `FIRST_PAGE`
|
||||||
|
- ⏩ as `LAST_PAGE`
|
||||||
|
- 🔢 as `GO_TO_PAGE`
|
||||||
|
- ⏹️ as `END_SESSION`
|
||||||
|
"""
|
||||||
|
BACK_BUTTON: ClassVar[str] = ...
|
||||||
|
NEXT_BUTTON: ClassVar[str] = ...
|
||||||
|
FIRST_PAGE: ClassVar[str] = ...
|
||||||
|
LAST_PAGE: ClassVar[str] = ...
|
||||||
|
GO_TO_PAGE: ClassVar[str] = ...
|
||||||
|
END_SESSION: ClassVar[str] = ...
|
||||||
|
|
||||||
|
|
||||||
|
class _PageController:
|
||||||
|
def __init__(self, pages: List[Page]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_page(self) -> Page:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_pages(self) -> int:
|
||||||
|
"""Return the total amount of pages registered to the menu"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def validate_index(self) -> Page:
|
||||||
|
"""If the index is out of bounds, assign the appropriate values so the pagination process can continue and return the associated page"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def skip_loop(self, action: str, amount: int) -> None:
|
||||||
|
"""Using `self.index += amount` does not work because this library is used to operating on a +-1 basis. This loop
|
||||||
|
provides a simple way to still operate on the +-1 standard.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def skip(self, skip: _BaseButton.Skip) -> Page:
|
||||||
|
"""Return the page that the skip value was set to"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def next(self) -> Page:
|
||||||
|
"""Return the next page in the pagination process"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def prev(self) -> Page:
|
||||||
|
"""Return the previous page in the pagination process"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def first_page(self) -> Page:
|
||||||
|
"""Return the first page in the pagination process"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def last_page(self) -> Page:
|
||||||
|
"""Return the last page in the pagination process"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _MenuType(Enum):
|
||||||
|
TypeEmbed = ...
|
||||||
|
TypeEmbedDynamic = ...
|
||||||
|
TypeText = ...
|
||||||
|
|
||||||
|
|
||||||
|
MenuType = _MenuType
|
||||||
|
class _LimitDetails(NamedTuple):
|
||||||
|
limit: int
|
||||||
|
per: str
|
||||||
|
message: str
|
||||||
|
set_by_user: bool = ...
|
||||||
|
@classmethod
|
||||||
|
def default(cls) -> Self:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseButton(Generic[GB], metaclass=abc.ABCMeta):
|
||||||
|
Emojis: ClassVar[PaginationEmojis] = ...
|
||||||
|
def __init__(self, name: str, event: Optional[_BaseButton.Event], skip: _BaseButton.Skip) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def menu(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clicked_by(self) -> Set[discord.Member]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Set[:class:`discord.Member`]: The members who clicked the button
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_clicks(self) -> int:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`int`: The amount of clicks on the button
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_clicked(self) -> Optional[datetime]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`datetime.datetime`]: The time in UTC for when the button was last clicked. Can be :class:`None` if the button has not been clicked
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
class Skip:
|
||||||
|
"""Initialize a skip button with the appropriate values
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
action: :class:`str`
|
||||||
|
Whether to go forward in the pagination process ("+") or backwards ("-")
|
||||||
|
|
||||||
|
amount: :class:`int`
|
||||||
|
The amount of pages to skip. Must be >= 1. If value is <= 0, it is implicitly set to 2
|
||||||
|
"""
|
||||||
|
def __init__(self, action: Literal['+', '-'], amount: int) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Event:
|
||||||
|
"""Set a button to be disabled or removed when it has been pressed a certain amount of times. If the button is a :class:`ReactionButton`, only the "remove" event is available
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
event: :class:`str`
|
||||||
|
The action to take. Can either be "disable" or "remove"
|
||||||
|
|
||||||
|
value: :class:`int`
|
||||||
|
The amount set for the specified event. Must be >= 1. If value is <= 0, it is implicitly set to 1"""
|
||||||
|
_DISABLE = ...
|
||||||
|
_REMOVE = ...
|
||||||
|
def __init__(self, event_type: Literal['disable', 'remove'], value: int) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseMenu(metaclass=abc.ABCMeta):
|
||||||
|
TypeEmbed: Final[_MenuType] = ...
|
||||||
|
TypeEmbedDynamic: Final[_MenuType] = ...
|
||||||
|
TypeText: Final[_MenuType] = ...
|
||||||
|
_sessions_limit_details = ...
|
||||||
|
_active_sessions: List[Self]
|
||||||
|
def __init__(self, method: Union[Context, discord.Interaction], /, menu_type: _MenuType, **kwargs) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def remove_all_buttons(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_button(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def remove_button(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def add_button(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def add_buttons(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def stop(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def start(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def quick_start(cls):
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def separate(values: Sequence[Any]) -> Tuple[List[discord.Embed], List[str]]:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Sorts all embeds and strings into a single tuple
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
values: Sequence[`Any`]
|
||||||
|
The values to separate
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Tuple[List[:class:`discord.Embed`], List[:class:`str`]]
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> embeds, strings = .separate([...])
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def all_embeds(values: Sequence[Any]) -> bool:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Tests to see if all items in the sequence are of type :class:`discord.Embed`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
values: Sequence[`Any`]
|
||||||
|
The values to test
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`bool`: Can return `False` if the sequence is empty
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def all_strings(values: Sequence[Any]) -> bool:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Tests to see if all items in the sequence are of type :class:`str`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
values: Sequence[`Any`]
|
||||||
|
The values to test
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`bool`: Can return `False` if the sequence is empty
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_limit(cls) -> None:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Remove the limits currently set for menu's
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_dm_sessions(cls) -> List[Self]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Retrieve all active DM menu sessions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
A :class:`list` of active DM menu sessions that are currently running. Can be an empty list if there are no active DM sessions
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_sessions(cls) -> List[Self]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Retrieve all active menu sessions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
A :class:`list` of menu sessions that are currently running. Can be an empty list if there are no active sessions
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_session(cls, name: str) -> List[Self]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Get a menu instance by it's name
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
The name of the menu to return
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
A :class:`list` of menu sessions that are currently running that match the supplied :param:`name`. Can be an empty list if there are no active sessions that matched the :param:`name`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_sessions_count(cls) -> int:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Returns the number of active sessions
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`int`: The amount of menu sessions that are active
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_sessions_limit(cls, limit: int, per: Literal['channel', 'guild', 'member'] = ..., message: str = ...) -> None:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Sets the amount of menu sessions that can be active at the same time per guild, channel, or member. This applies to both :class:`ReactionMenu` & :class:`ViewMenu`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
limit: :class:`int`
|
||||||
|
The amount of menu sessions allowed
|
||||||
|
|
||||||
|
per: :class:`str`
|
||||||
|
How menu sessions should be limited. Options: "channel", "guild", or "member"
|
||||||
|
|
||||||
|
message: :class:`str`
|
||||||
|
Message that will be sent informing users about the menu limit when the limit is reached. Can be :class:`None` for no message
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: The :param:`limit` parameter was not of type :class:`int`
|
||||||
|
- `MenuException`: The value of :param:`per` was not valid or the limit was not greater than or equal to one
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def stop_session(cls, name: str, include_all: bool = ...) -> None:
|
||||||
|
"""|coro class method|
|
||||||
|
|
||||||
|
Stop a specific menu with the supplied name
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
The menus name
|
||||||
|
|
||||||
|
include_all: :class:`bool`
|
||||||
|
If set to `True`, it stops all menu sessions with the supplied :param:`name`. If `False`, stops only the most recently started menu with the supplied :param:`name`
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuException`: The session with the supplied name was not found
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def stop_all_sessions(cls) -> None:
|
||||||
|
"""|coro class method|
|
||||||
|
|
||||||
|
Stops all menu sessions that are currently running
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_menu_from_message(cls, message_id: int, /) -> Optional[Self]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
Return the menu object associated with the message with the given ID
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
message_id: :class:`int`
|
||||||
|
The `discord.Message.id` from the menu message
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
The menu object. Can be :class:`None` if the menu was not found in the list of active menu sessions
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rows(self) -> Optional[List[str]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[List[:class:`str`]]: All rows that's been added to the menu. Can return `None` if the menu has not started or the `menu_type` is not `TypeEmbedDynamic`
|
||||||
|
|
||||||
|
.. added: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu_type(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`str`: The `menu_type` you set via the constructor. This will either be `TypeEmbed`, `TypeEmbedDynamic`, or `TypeText`
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_viewed(self) -> Optional[Page]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`Page`]: The last page that was viewed in the pagination process. Can be :class:`None` if the menu has not been started
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def owner(self) -> Union[discord.Member, discord.User]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Union[:class:`discord.Member`, :class:`discord.User`]: The owner of the menu (the person that started the menu). If the menu was started in a DM, this will return :class:`discord.User`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_pages(self) -> int:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`int`: The amount of pages that have been added to the menu. If the `menu_type` is :attr:`TypeEmbedDynamic`, the amount of pages is not known until AFTER the menu has started.
|
||||||
|
If attempted to retrieve the value before a dynamic menu has started, this will return a value of -1
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pages(self) -> Optional[List[Page]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[List[:class:`Page`]]: The pages currently applied to the menu. Can return :class:`None` if there are no pages
|
||||||
|
|
||||||
|
Note: If the `menu_type` is :attr:`TypeEmbedDynamic`, the pages aren't known until after the menu has started
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self) -> Optional[Union[discord.Message, discord.InteractionMessage]]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[Union[:class:`discord.Message`, :class:`discord.InteractionMessage`]]: The menu's message object. Can be :class:`None` if the menu has not been started
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_running(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`bool`: `True` if the menu is currently running, `False` otherwise
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def in_dms(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`bool`: If the menu was started in a DM
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def randomize_embed_colors(self) -> None:
|
||||||
|
"""Randomize the color of all the embeds that have been added to the menu
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuException`: The `menu_type` was not of `TypeEmbed`
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_page_director_style(self, style_id: int, separator: str = ...) -> None:
|
||||||
|
"""Set how the page numbers dictating what page you are on (in the footer of an embed/regular message) are displayed
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
style_id: :class:`int`
|
||||||
|
Varying formats of how the page director can be presented. The following ID's are available:
|
||||||
|
|
||||||
|
- `1` = Page 1/10
|
||||||
|
- `2` = Page 1 out of 10
|
||||||
|
- `3` = 1 out of 10
|
||||||
|
- `4` = 1 • 10
|
||||||
|
- `5` = 1 » 10
|
||||||
|
- `6` = 1 | 10
|
||||||
|
- `7` = 1 : 10
|
||||||
|
- `8` = 1 - 10
|
||||||
|
- `9` = 1 / 10
|
||||||
|
- `10` = 1 🔹 10
|
||||||
|
- `11` = 1 🔸 10
|
||||||
|
|
||||||
|
separator: :class:`str`
|
||||||
|
The separator between the page director and any text you may have in the embed footer. The default separator is ":". It should be noted that whichever separator you assign,
|
||||||
|
if you wish to have spacing between the page director and the separator, you must place the space inside the string yourself as such: " :"
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuException`: The :param:`style_id` value was not valid
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def wait_until_closed(self) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Waits until the menu session ends using `.stop()` or when the menu times out. This should not be used inside relays
|
||||||
|
|
||||||
|
.. added:: v3.0.1
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_from_messages(self, messages: Sequence[discord.Message]) -> None:
|
||||||
|
"""Add pages to the menu using the message object itself
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
messages: Sequence[:class:`discord.Message`]
|
||||||
|
A sequence of discord message objects
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: The messages provided did not have the correct values. For example, the `menu_type` was set to `TypeEmbed`, but the messages you've provided only contains text. If the `menu_type` is `TypeEmbed`, only messages with embeds should be provided
|
||||||
|
- `IncorrectType`: All messages were not of type :class:`discord.Message`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
async def add_from_ids(self, messageable: discord.abc.Messageable, message_ids: Sequence[int]) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Add pages to the menu using the IDs of messages. This only grabs embeds (if the `menu_type` is :attr:`TypeEmbed`) or the content (if the `menu_type` is :attr:`TypeText`) from the message
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
messageable: :class:`discord.abc.Messageable`
|
||||||
|
A discord :class:`Messageable` object (:class:`discord.TextChannel`, :class:`commands.Context`, etc.)
|
||||||
|
|
||||||
|
message_ids: Sequence[:class:`int`]
|
||||||
|
The messages to fetch
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: The message IDs provided did not have the correct values when fetched. For example, the `menu_type` was set to `TypeEmbed`, but the messages you've provided for the library to fetch only contains text. If the `menu_type` is `TypeEmbed`, only messages with embeds should be provided
|
||||||
|
- `MenuException`: An error occurred when attempting to fetch a message or not all :param:`message_ids` were of type int
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def clear_all_row_data(self) -> None:
|
||||||
|
"""Delete all the data thats been added using :meth:`add_row()`
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: This method was called but the menus `menu_type` was not :attr:`TypeEmbedDynamic`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_row(self, data: str) -> None:
|
||||||
|
"""Add text to the embed page by rows of data
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data: :class:`str`
|
||||||
|
The data to add
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: This method was called but the menus `menu_type` was not :attr:`TypeEmbedDynamic`
|
||||||
|
- `MissingSetting`: The kwarg "rows_requested" (int) has not been set for the menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def set_main_pages(self, *embeds: discord.Embed) -> None:
|
||||||
|
"""On a menu with a `menu_type` of :attr:`TypeEmbedDynamic`, set the pages you would like to show first. These embeds will be shown before the embeds that contain your data
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
*embeds: :class:`discord.Embed`
|
||||||
|
An argument list of :class:`discord.Embed` objects
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuSettingsMismatch`: Tried to use method on a menu that was not of `menu_type` :attr:`TypeEmbedDynamic`
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuException`: The "embeds" parameter was empty. At least one value is needed
|
||||||
|
- `IncorrectType`: All values in the argument list were not of type :class:`discord.Embed`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def set_last_pages(self, *embeds: discord.Embed) -> None:
|
||||||
|
"""On a menu with a `menu_type` of :attr:`TypeEmbedDynamic`, set the pages you would like to show last. These embeds will be shown after the embeds that contain your data
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
*embeds: :class:`discord.Embed`
|
||||||
|
An argument list of :class:`discord.Embed` objects
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuSettingsMismatch`: Tried to use method on a menu that was not of `menu_type` :attr:`TypeEmbedDynamic`
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuException`: The "embeds" parameter was empty. At least one value is needed
|
||||||
|
- `IncorrectType`: All values in the argument list were not of type :class:`discord.Embed`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_page(self, embed: Optional[discord.Embed] = ..., content: Optional[str] = ..., files: Optional[List[discord.File]] = ...) -> None:
|
||||||
|
"""Add a page to the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
embed: Optional[:class:`discord.Embed`]
|
||||||
|
The embed of the page
|
||||||
|
|
||||||
|
content: Optional[:class:`str`]
|
||||||
|
The text that appears above an embed in a message
|
||||||
|
|
||||||
|
files: Optional[Sequence[:class:`discord.File`]]
|
||||||
|
Files you'd like to attach to the page
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuException`: Attempted to add a page with no parameters
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: The page being added does not match the menus `menu_type`
|
||||||
|
|
||||||
|
.. changes::
|
||||||
|
v3.1.0
|
||||||
|
Added parameter content
|
||||||
|
Added parameter embed
|
||||||
|
Added parameter files
|
||||||
|
Removed parameter "page"
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def add_pages(self, pages: Sequence[discord.Embed]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def add_pages(self, pages: Sequence[str]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_pages(self, pages: Sequence[Union[discord.Embed, str]]) -> None:
|
||||||
|
"""Add multiple pages to the menu at once
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pages: Sequence[Union[:class:`discord.Embed`, :class:`str`]]
|
||||||
|
The pages to add. Can only be used when the menus `menu_type` is :attr:`TypeEmbed` (adding a :class:`discord.Embed`)
|
||||||
|
or :attr:`TypeText` (adding a :class:`str`)
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: The page being added does not match the menus `menu_type`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def remove_all_pages(self) -> None:
|
||||||
|
"""Remove all pages from the menu
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def remove_page(self, page_number: int) -> None:
|
||||||
|
"""Remove a page from the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
page_number: :class:`int`
|
||||||
|
The page to remove
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `InvalidPage`: The page associated with the given page number was not valid
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_on_timeout(self, func: Callable[[M], None]) -> None:
|
||||||
|
"""Set the function to be called when the menu times out
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
func: Callable[[:type:`M`]], :class:`None`]
|
||||||
|
The function object that will be called when the menu times out. The function should contain a single positional argument
|
||||||
|
and should not return anything. The argument passed to that function is an instance of the menu.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: Parameter "func" was not a callable object
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_on_timeout(self) -> None:
|
||||||
|
"""Remove the timeout call to the function you have set when the menu times out"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_relay(self, func: Callable[[NamedTuple], None], *, only: Optional[List[GB]] = ...) -> None:
|
||||||
|
"""Set a function to be called with a given set of information when a button is pressed on the menu. The information passed is `RelayPayload`, a named tuple.
|
||||||
|
The named tuple contains the following attributes:
|
||||||
|
|
||||||
|
- `member`: The :class:`discord.Member` object of the person who pressed the button. Could be :class:`discord.User` if the menu was started in a DM
|
||||||
|
- `button`: Depending on the menu instance, the :class:`ReactionButton` or :class:`ViewButton` object of the button that was pressed
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
func: Callable[[:class:`NamedTuple`], :class:`None`]
|
||||||
|
The function should only contain a single positional argument. Command functions (`@bot.command()`) not supported
|
||||||
|
|
||||||
|
only: Optional[List[:generic:`GB`]]
|
||||||
|
A list of buttons (`GB`) associated with the current menu instance. If the menu instance is :class:`ReactionMenu`, this should be a list of :class:`ReactionButton`
|
||||||
|
and vice-versa for :class:`ViewMenu` instances. If this is :class:`None`, all buttons on the menu will be relayed. If set, only button presses from those specified buttons will be relayed
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: The :param:`func` argument provided was not callable
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_relay(self) -> None:
|
||||||
|
"""Remove the relay that's been set"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
549
stubs/reactionmenu/buttons.pyi
Normal file
549
stubs/reactionmenu/buttons.pyi
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from typing import Any, Callable, Dict, Final, Iterable, List, Literal, NamedTuple, Optional, TYPE_CHECKING, Union
|
||||||
|
from . import ReactionButton, ReactionMenu, ViewMenu
|
||||||
|
from enum import Enum
|
||||||
|
from .abc import _BaseButton
|
||||||
|
|
||||||
|
"""
|
||||||
|
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 _Details(NamedTuple):
|
||||||
|
"""Used for buttons with a `custom_id` of `ID_CALLER`"""
|
||||||
|
func: Callable[..., None]
|
||||||
|
args: Iterable[Any]
|
||||||
|
kwargs: Dict[str, Any]
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Details = _Details
|
||||||
|
class ViewButton(discord.ui.Button, _BaseButton):
|
||||||
|
"""A helper class for :class:`ViewMenu`. Represents a UI button.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
style: :class:`discord.ButtonStyle`
|
||||||
|
The style of the button
|
||||||
|
|
||||||
|
label: Optional[:class:`str`]
|
||||||
|
The button label, if any
|
||||||
|
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the button is disabled or not
|
||||||
|
|
||||||
|
custom_id: Optional[:class:`str`]
|
||||||
|
The ID of the button that gets received during an interaction. If this button is for a URL, it does not have a custom ID
|
||||||
|
|
||||||
|
url: Optional[:class:`str`]
|
||||||
|
The URL this button sends you to
|
||||||
|
|
||||||
|
emoji: Optional[Union[:class:`str`, :class:`discord.PartialEmoji`]]
|
||||||
|
The emoji of the button, if available
|
||||||
|
|
||||||
|
followup: Optional[:class:`ViewButton.Follow`]
|
||||||
|
Used with buttons with custom_id :attr:`ViewButton.ID_CALLER`, :attr:`ViewButton.ID_SEND_MESSAGE`, :attr:`ViewButton.ID_CUSTOM_EMBED`
|
||||||
|
|
||||||
|
event: Optional[:class:`ViewButton.Event`]
|
||||||
|
Set the button to be disabled or removed when it has been pressed a certain amount of times
|
||||||
|
|
||||||
|
Kwargs
|
||||||
|
------
|
||||||
|
name: :class:`str`
|
||||||
|
An optional name for the button. Can be set to retrieve it later via :meth:`ViewMenu.get_button()`
|
||||||
|
|
||||||
|
skip: :class:`ViewButton.Skip`
|
||||||
|
Set the action and the amount of pages to skip when using a `custom_id` of `ViewButton.ID_SKIP`
|
||||||
|
|
||||||
|
persist: :class:`bool`
|
||||||
|
Available only when using link buttons. This prevents link buttons from being disabled/removed when the menu times out or is stopped so they can remain clickable
|
||||||
|
|
||||||
|
.. added v3.1.0
|
||||||
|
:param:`persist`
|
||||||
|
"""
|
||||||
|
ID_NEXT_PAGE: Final[str] = ...
|
||||||
|
ID_PREVIOUS_PAGE: Final[str] = ...
|
||||||
|
ID_GO_TO_FIRST_PAGE: Final[str] = ...
|
||||||
|
ID_GO_TO_LAST_PAGE: Final[str] = ...
|
||||||
|
ID_GO_TO_PAGE: Final[str] = ...
|
||||||
|
ID_END_SESSION: Final[str] = ...
|
||||||
|
ID_CALLER: Final[str] = ...
|
||||||
|
ID_SEND_MESSAGE: Final[str] = ...
|
||||||
|
ID_CUSTOM_EMBED: Final[str] = ...
|
||||||
|
ID_SKIP: Final[str] = ...
|
||||||
|
_RE_IDs = ...
|
||||||
|
_RE_UNIQUE_ID_SET = ...
|
||||||
|
def __init__(self, *, style: discord.ButtonStyle = ..., label: Optional[str] = ..., disabled: bool = ..., custom_id: Optional[str] = ..., url: Optional[str] = ..., emoji: Optional[Union[str, discord.PartialEmoji]] = ..., followup: Optional[ViewButton.Followup] = ..., event: Optional[ViewButton.Event] = ..., **kwargs) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self): # -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction) -> None:
|
||||||
|
"""*INTERNAL USE ONLY* - The callback function from the button interaction. This should not be manually called"""
|
||||||
|
...
|
||||||
|
|
||||||
|
class Followup:
|
||||||
|
"""A class that represents the message sent using a :class:`ViewButton`. Contains parameters similar to method `discord.abc.Messageable.send`. Only to be used with :class:`ViewButton` kwarg "followup".
|
||||||
|
It is to be noted that this should not be used with :class:`ViewButton` with a "style" of `discord.ButtonStyle.link` because link buttons do not send interaction events.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
content: Optional[:class:`str`]
|
||||||
|
Message to send
|
||||||
|
|
||||||
|
embed: Optional[:class:`discord.Embed`]
|
||||||
|
Embed to send. Can also bet set for buttons with a custom_id of :attr:`ViewButton.ID_CUSTOM_EMBED`
|
||||||
|
|
||||||
|
file: Optional[:class:`discord.File`]
|
||||||
|
File to send. If the :class:`ViewButton` custom_id is :attr:`ViewButton.ID_SEND_MESSAGE`, the file will be ignored because of discord API limitations
|
||||||
|
|
||||||
|
tts: :class:`bool`
|
||||||
|
If discord should read the message aloud. Not valid for `ephemeral` messages
|
||||||
|
|
||||||
|
allowed_mentions: Optional[:class:`discord.AllowedMentions`]
|
||||||
|
Controls the mentions being processed in the menu message. Not valid for `ephemeral` messages
|
||||||
|
|
||||||
|
delete_after: Optional[Union[:class:`int`, :class:`float`]]
|
||||||
|
Amount of time to wait before the message is deleted. Not valid for `ephemeral` messages
|
||||||
|
|
||||||
|
ephemeral: :class:`bool`
|
||||||
|
If the message will be hidden from everyone except the person that pressed the button. This is only valid for a :class:`ViewButton` with custom_id :attr:`ViewButton.ID_SEND_MESSAGE`
|
||||||
|
|
||||||
|
Kwargs
|
||||||
|
------
|
||||||
|
details: :meth:`ViewButton.Followup.set_caller_details()`
|
||||||
|
The information that will be used when a `ViewButton.ID_CALLER` button is pressed (defaults to :class:`None`)
|
||||||
|
"""
|
||||||
|
__slots__ = ...
|
||||||
|
def __repr__(self): # -> LiteralString:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __init__(self, content: Optional[str] = ..., *, embed: Optional[discord.Embed] = ..., file: Optional[discord.File] = ..., tts: bool = ..., allowed_mentions: Optional[discord.AllowedMentions] = ..., delete_after: Optional[Union[int, float]] = ..., ephemeral: bool = ..., **kwargs) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_caller_details(func: Callable[..., None], *args, **kwargs) -> Details:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Set the parameters for the function you set for a :class:`ViewButton` with the custom_id :attr:`ViewButton.ID_CALLER`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
func: Callable[..., :class:`None`]
|
||||||
|
The function object that will be called when the associated button is pressed
|
||||||
|
|
||||||
|
*args: `Any`
|
||||||
|
An argument list that represents the parameters of that function
|
||||||
|
|
||||||
|
**kwargs: `Any`
|
||||||
|
An argument list that represents the kwarg parameters of that function
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`Details`: The :class:`NamedTuple` containing the values needed to internally call the function you have set
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: Parameter "func" was not a callable object
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu(self) -> Optional[ViewMenu]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`ViewMenu`]: The menu instance this button is attached to. Could be :class:`None` if the button is not attached to a menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_skip(cls, label: str, action: Literal['+', '-'], amount: int) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: `<label>`
|
||||||
|
- custom_id: :attr:`ViewButton.ID_SKIP`
|
||||||
|
- skip: `ViewButton.Skip(<action>, <amount>)`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def link(cls, label: str, url: str) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.link`
|
||||||
|
- label: `<label>`
|
||||||
|
- url: `<url>`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def back(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Back"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_PREVIOUS_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def next(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Next"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_NEXT_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_first_page(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "First Page"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_FIRST_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_last_page(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Last Page"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_LAST_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_page(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Page Selection"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def end_session(cls) -> ViewButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ViewButton` with the following parameters set:
|
||||||
|
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Close"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_END_SESSION`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all(cls) -> List[ViewButton]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a list of all base navigation buttons. The following buttons are returned with pre-set values:
|
||||||
|
|
||||||
|
- Button 1
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "First Page"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_FIRST_PAGE`
|
||||||
|
- Button 2
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Back"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_PREVIOUS_PAGE`
|
||||||
|
- Button 3
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Next"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_NEXT_PAGE`
|
||||||
|
- Button 4
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Last Page"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_LAST_PAGE`
|
||||||
|
- Button 5
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Page Selection"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_PAGE`
|
||||||
|
- Button 6
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- label: "Close"
|
||||||
|
- custom_id: :attr:`ViewButton.ID_END_SESSION`
|
||||||
|
|
||||||
|
They are returned in that order
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewButton`]
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_with_emojis(cls) -> List[ViewButton]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a list of all base navigation buttons with emojis assigned instead of labels. The following buttons are returned with pre-set values:
|
||||||
|
|
||||||
|
- Button 1
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: ⏪
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_FIRST_PAGE`
|
||||||
|
- Button 2
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: ◀️
|
||||||
|
- custom_id: :attr:`ViewButton.ID_PREVIOUS_PAGE`
|
||||||
|
- Button 3
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: ▶️
|
||||||
|
- custom_id: :attr:`ViewButton.ID_NEXT_PAGE`
|
||||||
|
- Button 4
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: ⏩
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_LAST_PAGE`
|
||||||
|
- Button 5
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: 🔢
|
||||||
|
- custom_id: :attr:`ViewButton.ID_GO_TO_PAGE`
|
||||||
|
- Button 6
|
||||||
|
- style: `discord.ButtonStyle.gray`
|
||||||
|
- emoji: ⏹️
|
||||||
|
- custom_id: :attr:`ViewButton.ID_END_SESSION`
|
||||||
|
|
||||||
|
They are returned in that order
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewButton`]
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonType(Enum):
|
||||||
|
"""A helper class for :class:`ReactionMenu`. Determines the generic action a button can perform."""
|
||||||
|
NEXT_PAGE = ...
|
||||||
|
PREVIOUS_PAGE = ...
|
||||||
|
GO_TO_FIRST_PAGE = ...
|
||||||
|
GO_TO_LAST_PAGE = ...
|
||||||
|
GO_TO_PAGE = ...
|
||||||
|
END_SESSION = ...
|
||||||
|
CUSTOM_EMBED = ...
|
||||||
|
CALLER = ...
|
||||||
|
SKIP = ...
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionButton(_BaseButton):
|
||||||
|
"""A helper class for :class:`ReactionMenu`. Represents a reaction.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
emoji: :class:`str`
|
||||||
|
The discord reaction that will be used
|
||||||
|
|
||||||
|
linked_to: :class:`ReactionButton.Type`
|
||||||
|
A generic action a button can perform
|
||||||
|
|
||||||
|
Kwargs
|
||||||
|
------
|
||||||
|
embed: :class:`discord.Embed`
|
||||||
|
Only used when :param:`linked_to` is set as :attr:`ReactionButton.Type.CUSTOM_EMBED`. This is the embed that can be selected separately from the menu (`TypeEmbed` menu's only)
|
||||||
|
|
||||||
|
name: :class:`str`
|
||||||
|
An optional name for the button. Can be set to retrieve it later via :meth:`ReactionMenu.get_button()`
|
||||||
|
|
||||||
|
details: :meth:`ReactionButton.set_caller_details()`
|
||||||
|
The class method used to set the function and it's arguments to be called when the button is pressed
|
||||||
|
|
||||||
|
event: :class:`ReactionButton.Event`
|
||||||
|
Determine when a button should be removed depending on how many times it has been pressed
|
||||||
|
|
||||||
|
skip: :class:`ReactionButton.Skip`
|
||||||
|
Set the action and the amount of pages to skip when using a `linked_to` of `ReactionButton.Type.SKIP`
|
||||||
|
"""
|
||||||
|
Type = ButtonType
|
||||||
|
def __init__(self, *, emoji: str, linked_to: ReactionButton.Type, **kwargs) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self): # -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu(self) -> Optional[ReactionMenu]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`ReactionMenu`]: The menu the button is currently operating under. Can be :class:`None` if the button is not registered to a menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_caller_details(func: Callable[..., None], *args, **kwargs) -> Details:
|
||||||
|
"""|static method|
|
||||||
|
|
||||||
|
Set the parameters for the function you set for a :class:`ReactionButton` with a `linked_to` of :attr:`ReactionButton.Type.CALLER`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
func: Callable[..., :class:`None`]
|
||||||
|
The function object that will be called when the associated button is pressed
|
||||||
|
|
||||||
|
*args: `Any`
|
||||||
|
An argument list that represents the parameters of that function
|
||||||
|
|
||||||
|
**kwargs: `Any`
|
||||||
|
An argument list that represents the kwarg parameters of that function
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`Details`: The :class:`NamedTuple` containing the values needed to internally call the function you have set
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: Parameter "func" was not a callable object
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_skip(cls, emoji: str, action: Literal['+', '-'], amount: int) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: `<emoji>`
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.SKIP`
|
||||||
|
- skip: `ReactionButton.Skip(<action>, <amount>)`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def back(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: ◀️
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.PREVIOUS_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def next(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: ▶️
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.NEXT_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_first_page(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: ⏪
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.GO_TO_FIRST_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_last_page(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: ⏩
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.GO_TO_LAST_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def go_to_page(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: 🔢
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.GO_TO_PAGE`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def end_session(cls) -> ReactionButton:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a :class:`ReactionButton` with the following parameters set:
|
||||||
|
|
||||||
|
- emoji: ⏹️
|
||||||
|
- linked_to: :attr:`ReactionButton.Type.END_SESSION`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all(cls) -> List[ReactionButton]:
|
||||||
|
"""|class method|
|
||||||
|
|
||||||
|
A factory method that returns a `list` of all base navigation buttons. Base navigation buttons are :class:`ReactionButton` with a `linked_to` of:
|
||||||
|
|
||||||
|
- :attr:`ReactionButton.Type.GO_TO_FIRST_PAGE`
|
||||||
|
- :attr:`ReactionButton.Type.PREVIOUS_PAGE`
|
||||||
|
- :attr:`ReactionButton.Type.NEXT_PAGE`
|
||||||
|
- :attr:`ReactionButton.Type.GO_TO_LAST_PAGE`
|
||||||
|
- :attr:`ReactionButton.Type.GO_TO_PAGE`
|
||||||
|
- :attr:`ReactionButton.Type.END_SESSION`
|
||||||
|
|
||||||
|
They are returned in that order
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
332
stubs/reactionmenu/core.pyi
Normal file
332
stubs/reactionmenu/core.pyi
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
31
stubs/reactionmenu/decorators.pyi
Normal file
31
stubs/reactionmenu/decorators.pyi
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
def ensure_not_primed(func): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Coroutine[Any, Any, Any]] | _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]:
|
||||||
|
"""Check to make sure certain methods cannot be ran once the menu has been fully started"""
|
||||||
|
...
|
||||||
|
|
122
stubs/reactionmenu/errors.pyi
Normal file
122
stubs/reactionmenu/errors.pyi
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
class MenuException(Exception):
|
||||||
|
"""Base exception for all menu's"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ViewMenuException(MenuException):
|
||||||
|
"""Base :class:`ViewMenu` exception"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ReactionMenuException(MenuException):
|
||||||
|
"""Base :class:`ReactionMenu` exception"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class IncorrectType(MenuException):
|
||||||
|
"""Raised when the expected type was not given"""
|
||||||
|
def __init__(self, message: str) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NoButtons(MenuException):
|
||||||
|
"""Raised when the menu was started but no buttons were registered or the action initiated requires buttons to be registered"""
|
||||||
|
def __init__(self, message: str = ...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidPage(MenuException):
|
||||||
|
"""Raised when the page selected to remove does not exist"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class DescriptionOversized(MenuException):
|
||||||
|
"""Used for `TypeEmbedDynamic` menus. The embed description of the menu has a character count > 4096"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MenuSettingsMismatch(MenuException):
|
||||||
|
"""Used settings for a specific `menu_type` but different/unrecognized values for those settings were given"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateButton(MenuException):
|
||||||
|
"""The emoji selected as a button has already been registered"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ImproperStyleFormat(MenuException):
|
||||||
|
"""The custom style selected by the user did not meet formatting requirements"""
|
||||||
|
def __init__(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TooManyButtons(MenuException):
|
||||||
|
"""The amount of buttons registered is > 20 (over discords reaction limit)"""
|
||||||
|
def __init__(self, message: str = ...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MissingSetting(MenuException):
|
||||||
|
"""Raised when an action requires specific input from the user"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class NoPages(MenuException):
|
||||||
|
"""Tried to start the menu when they haven't added any pages"""
|
||||||
|
def __init__(self, message: str = ...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MenuAlreadyRunning(MenuException):
|
||||||
|
"""Called a method that is not allowed to be called after the menu has started"""
|
||||||
|
def __init__(self, message: str) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonNotFound(MenuException):
|
||||||
|
"""Raised when :meth:`.remove_button()` did not find any matching buttons"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class SelectNotFound(MenuException):
|
||||||
|
"""Raised when :meth:`.remove_select()` did not find any matching selects
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
714
stubs/reactionmenu/views_menu.pyi
Normal file
714
stubs/reactionmenu/views_menu.pyi
Normal file
|
@ -0,0 +1,714 @@
|
||||||
|
"""
|
||||||
|
This type stub file was generated by pyright.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from typing import Callable, Dict, List, Literal, NamedTuple, Optional, Sequence, TYPE_CHECKING, Union, overload
|
||||||
|
from .abc import MenuType, Page, _BaseMenu
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
from . import ViewButton
|
||||||
|
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:
|
||||||
|
...
|
||||||
|
_SelectOptionRelayPayload = ...
|
||||||
|
class ViewSelect(discord.ui.Select):
|
||||||
|
"""A class to assist in the process of categorizing information on a :class:`ViewMenu`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
title: Union[:class:`str`, `None`]
|
||||||
|
The category name. If `None`, the category name defaults to "Select a category"
|
||||||
|
|
||||||
|
options: Dict[:class:`discord.SelectOption`, List[:class:`Page`]]
|
||||||
|
A key/value pair associating the category options with pages to navigate
|
||||||
|
|
||||||
|
disabled: :class:`bool`
|
||||||
|
If the select should start enabled or disabled
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
def __init__(self, *, title: Union[str, None], options: Dict[discord.SelectOption, List[Page]], disabled: bool = ...) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu(self) -> Optional[ViewMenu]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`ViewMenu`]: The menu this select is attached to. Can be `None` if not attached to any menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction) -> None:
|
||||||
|
"""*INTERNAL USE ONLY* - The callback function from the select interaction. This should not be manually called"""
|
||||||
|
...
|
||||||
|
|
||||||
|
class GoTo(discord.ui.Select):
|
||||||
|
"""Represents a UI based version of a :class:`ViewButton` with the ID `ViewButton.ID_GO_TO_PAGE`
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
title: Union[:class:`str`, `None`]
|
||||||
|
The selects name. If `None`, the name defaults to "Navigate to page..."
|
||||||
|
|
||||||
|
page_numbers: This parameter accepts 3 different types and are explained below
|
||||||
|
- List[:class:`int`]
|
||||||
|
- If set to a list of integers, those specified values are the only options that are available when the select is clicked
|
||||||
|
|
||||||
|
- Dict[:class:`int`, Union[:class:`str`, :class:`discord.Emoji`, :class:`discord.PartialEmoji`]]
|
||||||
|
- You can use this type if you'd like to utilize emojis in your select options
|
||||||
|
|
||||||
|
- `ellipsis`
|
||||||
|
- Set an ellipsis to have the library automatically assign all page numbers to the amount of pages that you've added to the menu.
|
||||||
|
|
||||||
|
NOTE: Setting the `page_numbers` parameter to an ellipsis (...) only works as intended if you've added the go to select AFTER you've added pages to the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
def __init__(self, *, title: Union[str, None], page_numbers: Union[List[int], Dict[int, Union[str, discord.Emoji, discord.PartialEmoji]], ellipsis]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu(self) -> Optional[ViewMenu]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Optional[:class:`ViewMenu`]: The menu this select is attached to. Can be `None` if not attached to any menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ViewMenu(_BaseMenu):
|
||||||
|
"""A class to create a discord pagination menu using :class:`discord.ui.View`
|
||||||
|
|
||||||
|
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:`ViewMenu.TypeEmbed`, :attr:`ViewMenu.TypeEmbedDynamic`, or :attr:`ViewMenu.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)`).
|
||||||
|
Not valid for `ViewMenu` with a `menu_type` of `TypeText`
|
||||||
|
|
||||||
|
custom_embed: :class:`discord.Embed`
|
||||||
|
Embed object to use when adding data with :meth:`ViewMenu.add_row()`. Used for styling purposes only (:attr:`ViewMenu.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:`ViewButton.ID_GO_TO_PAGE` (defaults to `True`)
|
||||||
|
|
||||||
|
delete_on_timeout: :class:`bool`
|
||||||
|
Delete the menu when it times out (defaults to `False`) If `True`, :attr:`disable_items_on_timeout` and :attr:`remove_items_on_timeout` will not execute regardless of if they are `True`. This takes priority over those actions
|
||||||
|
|
||||||
|
disable_items_on_timeout: :class:`bool`
|
||||||
|
Disable the buttons on the menu when the menu times out (defaults to `True`) If :attr:`delete_on_timeout` is `True`, this will be overridden
|
||||||
|
|
||||||
|
name: :class:`str`
|
||||||
|
A name you can set for the menu (defaults to :class:`None`)
|
||||||
|
|
||||||
|
only_roles: List[:class:`discord.Role`]
|
||||||
|
If set, only members with any of the given roles are allowed to control the menu. The menu owner can always control the menu (defaults to :class:`None`)
|
||||||
|
|
||||||
|
remove_items_on_timeout: :class:`bool`
|
||||||
|
Remove the buttons on the menu when the menu times out (defaults to `False`) If :attr:`disable_items_on_timeout` is `True`, this will be overridden
|
||||||
|
|
||||||
|
rows_requested: :class:`int`
|
||||||
|
The amount of information per :meth:`ViewMenu.add_row()` you would like applied to each embed page (:attr:`ViewMenu.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: `ViewMenu(ctx, ..., style='On $ out of &')`
|
||||||
|
|
||||||
|
timeout: Union[:class:`int`, :class:`float`, :class:`None`]
|
||||||
|
The timer for when the menu times out. Can be :class:`None` for no timeout (defaults to `60.0`)
|
||||||
|
|
||||||
|
wrap_in_codeblock: :class:`str`
|
||||||
|
The discord codeblock language identifier to wrap your data in (:attr:`ViewMenu.TypeEmbedDynamic` only/defaults to :class:`None`). Example: `ViewMenu(ctx, ..., wrap_in_codeblock='py')`
|
||||||
|
"""
|
||||||
|
_active_sessions: List[ViewMenu] = ...
|
||||||
|
def __init__(self, method: Union[Context, discord.Interaction], /, *, menu_type: MenuType, **kwargs) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __repr__(self): # -> str:
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selects(self) -> List[ViewSelect]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewSelect`]: All selects that have been added to the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def go_to_selects(self) -> List[ViewSelect.GoTo]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewSelect.GoTo`]: All go to selects that have been added to the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def timeout(self): # -> int | float | None:
|
||||||
|
...
|
||||||
|
|
||||||
|
@timeout.setter
|
||||||
|
def timeout(self, value) -> Union[int, float, None]:
|
||||||
|
"""A property getter/setter for kwarg `timeout`"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def buttons(self) -> List[ViewButton]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewButton`]: All buttons that have been added to the menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def buttons_most_clicked(self) -> List[ViewButton]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewButton`]: 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[ViewButton]] = ...) -> ViewMenu:
|
||||||
|
"""|coro class method|
|
||||||
|
|
||||||
|
Start a menu with default settings either with a `menu_type` of `ViewMenu.TypeEmbed` (all values in `pages` are of type `discord.Embed`) or `ViewMenu.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:`ViewButton`]]
|
||||||
|
The buttons to add. If left as `DEFAULT_BUTTONS`, that is equivalent to `ViewButton.all()`
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`ViewMenu`: 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
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_select_option_relay(self, func: Callable[[NamedTuple], None], *, only: Optional[Sequence[str]] = ...) -> None:
|
||||||
|
"""Set a function to be called with a given set of information when a select option is pressed on the menu. The information passed is `SelectOptionRelayPayload`, a named tuple.
|
||||||
|
The named tuple contains the following attributes:
|
||||||
|
|
||||||
|
- `member`: The :class:`discord.Member` object of the person who pressed the option. Could be :class:`discord.User` if the menu was started in a DM
|
||||||
|
- `option`: The :class:`discord.SelectOption` that was pressed
|
||||||
|
- `menu`: An instance of :class:`ViewMenu` that the select option is operating under
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
func: Callable[[:class:`NamedTuple`], :class:`None`]
|
||||||
|
The function should only contain a single positional argument. Command functions (`@bot.command()`) not supported
|
||||||
|
|
||||||
|
only: Optional[Sequence[:class:`str`]]
|
||||||
|
A sequence of :class:`discord.SelectOption` labels associated with the current menu instance. If this is :class:`None`, all select options on the menu will be relayed.
|
||||||
|
If set, only presses from the options matching the given labels specified will be relayed
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `IncorrectType`: The :param:`func` argument provided was not callable
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_select_option_relay(self) -> None:
|
||||||
|
"""Remove the select option relay that's been set
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_select(self, select: ViewSelect) -> None:
|
||||||
|
"""Add a select category to the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
select: :class:`ViewSelect`
|
||||||
|
The category listing to add
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `ViewMenuException`: The `menu_type` was not of :attr:`TypeEmbed`. The "embed" parameter in a :class:`Page` was not set. Or both :class:`ViewSelect` and a :class:`ViewSelect.GoTo` were being used
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_select(self, select: ViewSelect) -> None:
|
||||||
|
"""Remove a select from the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
select: :class:`ViewSelect`
|
||||||
|
The select to remove
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `SelectNotFound`: The provided select was not found in the list of selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_all_selects(self) -> None:
|
||||||
|
"""Remove all selects from the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_select(self, select: ViewSelect) -> None:
|
||||||
|
"""Disable a select on the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
select: :class:`ViewSelect`
|
||||||
|
The select to disable
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `SelectNotFound`: The provided select was not found in the list of selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_all_selects(self) -> None:
|
||||||
|
"""Disable all selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_select(self, select: ViewSelect) -> None:
|
||||||
|
"""Enable the specified select
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
select: :class:`ViewSelect`
|
||||||
|
The select to enable
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `SelectNotFound`: The provided select was not found in the list of selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_all_selects(self) -> None:
|
||||||
|
"""Enable all selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_select(self, title: str) -> List[ViewSelect]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_select(self, title: None) -> List[ViewSelect]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_select(self, title: Union[str, None]) -> List[ViewSelect]:
|
||||||
|
"""Get a select by it's title (category name) that has been registered to the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
title: Union[:class:`str`, `None`]
|
||||||
|
Title of the category
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewSelect`]: All selects matching the given title
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_go_to_select(self, goto: ViewSelect.GoTo) -> None:
|
||||||
|
"""Add a select where the user can choose which page they'd like to navigate to.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
goto: :class:`ViewSelect.GoTo`
|
||||||
|
The navigation listing
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `ViewMenuException`: A :class:`ViewSelect` was already added to the menu. A :class:`ViewSelect` and a :class:`ViewSelect.GoTo` cannot both be used on a single menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_go_to_select(self, goto: ViewSelect.GoTo) -> None:
|
||||||
|
"""Enable the specified go to select
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
goto: :class:`ViewSelect.GoTo`
|
||||||
|
The go to select to enable
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_all_go_to_selects(self) -> None:
|
||||||
|
"""Enable all go to selects
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_go_to_select(self, goto: ViewSelect.GoTo) -> None:
|
||||||
|
"""Disable the specified go to select
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
goto: :class:`ViewSelect.GoTo`
|
||||||
|
The go to select to disable
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_all_go_to_selects(self) -> None:
|
||||||
|
"""Disable all go to selects
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_go_to_select(self, goto: ViewSelect.GoTo) -> None:
|
||||||
|
"""Remove a go to select from the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
goto: :class:`ViewSelect.GoTo`
|
||||||
|
The go to select to remove
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `SelectNotFound`: The provided go to select was not found in the list of selects on the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_all_go_to_selects(self) -> None:
|
||||||
|
"""Remove all go to selects from the menu
|
||||||
|
|
||||||
|
.. added:: v3.1.0
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def update(self, *, new_pages: Union[List[Union[discord.Embed, str]], None], new_buttons: Union[List[ViewButton], None]) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
When the menu is running, update the pages or buttons. It should be noted that `ViewSelect`s are not supported here, and they will automatically be removed
|
||||||
|
once the menu is updated. This method only supports pages and buttons.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
new_pages: List[Union[:class:`discord.Embed`, :class:`str`]]
|
||||||
|
Pages to *replace* the current pages with. If the menus current `menu_type` is :attr:`ViewMenu.TypeEmbed`, only :class:`discord.Embed` can be used. If :attr:`ViewMenu.TypeText`, only :class:`str` can be used. If you
|
||||||
|
don't want to replace any pages, set this to :class:`None`
|
||||||
|
|
||||||
|
new_buttons: List[:class:`ViewButton`]
|
||||||
|
Buttons to *replace* the current buttons with. Can be an empty list if you want the updated menu to have no buttons. Can also be set to :class:`None` if you don't want to replace any buttons
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `ViewMenuException`: The :class:`ViewButton` custom_id was not recognized or a :class:`ViewButton` with that ID has already been added
|
||||||
|
- `TooManyButtons`: There are already 25 buttons on the menu
|
||||||
|
- `IncorrectType`: The values in :param:`new_pages` did not match the :class:`ViewMenu`'s `menu_type`. An attempt to use this method when the `menu_type` is :attr:`ViewMenu.TypeEmbedDynamic` which is not allowed. Or
|
||||||
|
all :param:`new_buttons` values were not of type :class:`ViewButton`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def randomize_button_styles(self) -> None:
|
||||||
|
"""Set all buttons currently registered to the menu to a random :class:`discord.ButtonStyle` excluding link buttons"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def set_button_styles(self, style: discord.ButtonStyle) -> None:
|
||||||
|
"""Set all buttons currently registered to the menu to the specified :class:`discord.ButtonStyle` excluding link buttons
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
style: :class:`discord.ButtonStyle`
|
||||||
|
The button style to set
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def refresh_menu_items(self) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
When the menu is running, update the message to reflect the buttons/selects that were removed, enabled, or disabled
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_button(self, button: ViewButton) -> None:
|
||||||
|
"""Remove a button from the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
button: :class:`ViewButton`
|
||||||
|
The button to remove
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `ButtonNotFound`: The provided button was not found in the list of buttons on the menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove_all_buttons(self) -> None:
|
||||||
|
"""Remove all buttons from the menu"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_button(self, button: ViewButton) -> None:
|
||||||
|
"""Disable a button on the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
button: :class:`ViewButton`
|
||||||
|
The button to disable
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `ButtonNotFound`: The provided button was not found in the list of buttons on the menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def disable_all_buttons(self) -> None:
|
||||||
|
"""Disable all buttons on the menu"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_button(self, button: ViewButton) -> None:
|
||||||
|
"""Enable the specified button
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
button: :class:`ViewButton`
|
||||||
|
The button to enable
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `ButtonNotFound`: The provided button was not found in the list of buttons on the menu
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def enable_all_buttons(self) -> None:
|
||||||
|
"""Enable all buttons on the menu"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_button(self, button: ViewButton) -> None:
|
||||||
|
"""Add a button to the menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
button: :class:`ViewButton`
|
||||||
|
The button to add
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: The buttons custom_id was set as :attr:`ViewButton.ID_CUSTOM_EMBED` but the `menu_type` is :attr:`ViewMenu.TypeText`
|
||||||
|
- `ViewMenuException`: The custom_id for the button was not recognized or a button with that custom_id has already been added
|
||||||
|
- `TooManyButtons`: There are already 25 buttons on the menu
|
||||||
|
- `IncorrectType`: Parameter :param:`button` was not of type :class:`ViewButton`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@ensure_not_primed
|
||||||
|
def add_buttons(self, buttons: Sequence[ViewButton]) -> None:
|
||||||
|
"""Add multiple buttons to the menu at once
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
buttons: Sequence[:class:`ViewButton`]
|
||||||
|
The buttons to add
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `MenuAlreadyRunning`: Attempted to call method after the menu has already started
|
||||||
|
- `MenuSettingsMismatch`: One of the buttons `custom_id` was set as :attr:`ViewButton.ID_CUSTOM_EMBED` but the `menu_type` is :attr:`ViewMenu.TypeText`
|
||||||
|
- `ViewMenuException`: The `custom_id` for a button was not recognized or a button with that `custom_id` has already been added
|
||||||
|
- `TooManyButtons`: There are already 25 buttons on the menu
|
||||||
|
- `IncorrectType`: One or more values supplied in parameter :param:`buttons` was not of type :class:`ViewButton`
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_button(self, identity: str, *, search_by: Literal['label', 'id', 'name'] = ...) -> List[ViewButton]:
|
||||||
|
"""Get a button that has been registered to the menu by it's label, custom_id, or name
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
identity: :class:`str`
|
||||||
|
The button label, custom_id, or name
|
||||||
|
|
||||||
|
search_by: :class:`str`
|
||||||
|
How to search for the button. If "label", it's searched by button labels. If "id", it's searched by it's custom_id.
|
||||||
|
If "name", it's searched by button names
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[:class:`ViewButton`]: The button(s) matching the given identity
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `ViewMenuException`: Parameter :param:`search_by` was not "label", "id", or "name"
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def stop(self, *, delete_menu_message: bool = ..., disable_items: bool = ..., remove_items: bool = ...) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Stops the process of the menu with the option of deleting the menu's message, removing the buttons, or disabling the buttons upon stop
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
delete_menu_message: :class:`bool`
|
||||||
|
Delete the message the menu is operating from
|
||||||
|
|
||||||
|
disable_items: :class:`bool`
|
||||||
|
Disable the buttons & selects on the menu
|
||||||
|
|
||||||
|
remove_items: :class:`bool`
|
||||||
|
Remove the buttons & selects from the menu
|
||||||
|
|
||||||
|
Parameter Hierarchy
|
||||||
|
-------------------
|
||||||
|
Only one option is available when stopping the menu. If you have multiple parameters as `True`, only one will execute
|
||||||
|
- `disable_items` > `remove_items`
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
- `discord.DiscordException`: Any exception that can be raised when deleting or editing 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`
|
||||||
|
|
||||||
|
Note: This parameter is not available if your `method` is a :class:`discord.Interaction`, aka a slash command
|
||||||
|
|
||||||
|
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
|
||||||
|
- `ViewMenuException`: The :class:`ViewMenu`'s `menu_type` was not recognized. There were more than one base navigation buttons. Or a :attr:`ViewButton.ID_CUSTOM_EMBED` button was not correctly formatted
|
||||||
|
- `DescriptionOversized`: When using a `menu_type` of :attr:`ViewMenu.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
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue