mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
Add buttons to help (#5634)
Co-authored-by: Zephyrkul <23347632+Zephyrkul@users.noreply.github.com> Co-authored-by: Kowlin <10947836+Kowlin@users.noreply.github.com> Co-authored-by: Jakub Kuczys <me@jacken.men>
This commit is contained in:
parent
4158244117
commit
aaeb1b5daa
@ -120,7 +120,7 @@ class Red(
|
|||||||
help__page_char_limit=1000,
|
help__page_char_limit=1000,
|
||||||
help__max_pages_in_guild=2,
|
help__max_pages_in_guild=2,
|
||||||
help__delete_delay=0,
|
help__delete_delay=0,
|
||||||
help__use_menus=False,
|
help__use_menus=0,
|
||||||
help__show_hidden=False,
|
help__show_hidden=False,
|
||||||
help__show_aliases=True,
|
help__show_aliases=True,
|
||||||
help__verify_checks=True,
|
help__verify_checks=True,
|
||||||
@ -1045,6 +1045,16 @@ class Red(
|
|||||||
await self._schema_1_to_2()
|
await self._schema_1_to_2()
|
||||||
schema_version += 1
|
schema_version += 1
|
||||||
await self._config.schema_version.set(schema_version)
|
await self._config.schema_version.set(schema_version)
|
||||||
|
if schema_version == 2:
|
||||||
|
await self._schema_2_to_3()
|
||||||
|
schema_version += 1
|
||||||
|
await self._config.schema_version.set(schema_version)
|
||||||
|
|
||||||
|
async def _schema_2_to_3(self):
|
||||||
|
log.info("Migrating help menus to enum values")
|
||||||
|
old = await self._config.help__use_menus()
|
||||||
|
if old is not None:
|
||||||
|
await self._config.help__use_menus.set(int(old))
|
||||||
|
|
||||||
async def _schema_1_to_2(self):
|
async def _schema_1_to_2(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import abc
|
|||||||
import asyncio
|
import asyncio
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from dataclasses import dataclass, asdict as dc_asdict
|
from dataclasses import dataclass, asdict as dc_asdict
|
||||||
|
from enum import Enum
|
||||||
from typing import Union, List, AsyncIterator, Iterable, cast
|
from typing import Union, List, AsyncIterator, Iterable, cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@ -39,6 +40,7 @@ from discord.ext import commands as dpy_commands
|
|||||||
from . import commands
|
from . import commands
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from ..i18n import Translator
|
from ..i18n import Translator
|
||||||
|
from ..utils.views import SimpleMenu
|
||||||
from ..utils import can_user_react_in, menus
|
from ..utils import can_user_react_in, menus
|
||||||
from ..utils.mod import mass_purge
|
from ..utils.mod import mass_purge
|
||||||
from ..utils._internal_utils import fuzzy_command_search, format_fuzzy_results
|
from ..utils._internal_utils import fuzzy_command_search, format_fuzzy_results
|
||||||
@ -65,6 +67,14 @@ EmbedField = namedtuple("EmbedField", "name value inline")
|
|||||||
EMPTY_STRING = "\N{ZERO WIDTH SPACE}"
|
EMPTY_STRING = "\N{ZERO WIDTH SPACE}"
|
||||||
|
|
||||||
|
|
||||||
|
class HelpMenuSetting(Enum):
|
||||||
|
disabled = 0
|
||||||
|
reactions = 1
|
||||||
|
buttons = 2
|
||||||
|
select = 3
|
||||||
|
selectonly = 4
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class HelpSettings:
|
class HelpSettings:
|
||||||
"""
|
"""
|
||||||
@ -78,7 +88,7 @@ class HelpSettings:
|
|||||||
|
|
||||||
page_char_limit: int = 1000
|
page_char_limit: int = 1000
|
||||||
max_pages_in_guild: int = 2
|
max_pages_in_guild: int = 2
|
||||||
use_menus: bool = False
|
use_menus: HelpMenuSetting = HelpMenuSetting(0)
|
||||||
show_hidden: bool = False
|
show_hidden: bool = False
|
||||||
show_aliases: bool = True
|
show_aliases: bool = True
|
||||||
verify_checks: bool = True
|
verify_checks: bool = True
|
||||||
@ -103,7 +113,8 @@ class HelpSettings:
|
|||||||
Get the HelpSettings for the current context
|
Get the HelpSettings for the current context
|
||||||
"""
|
"""
|
||||||
settings = await context.bot._config.help.all()
|
settings = await context.bot._config.help.all()
|
||||||
return cls(**settings)
|
menus = settings.pop("use_menus", 0)
|
||||||
|
return cls(**settings, use_menus=HelpMenuSetting(menus))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pretty(self):
|
def pretty(self):
|
||||||
@ -129,6 +140,14 @@ class HelpSettings:
|
|||||||
tagline_info = ""
|
tagline_info = ""
|
||||||
|
|
||||||
data["tagline_info"] = tagline_info
|
data["tagline_info"] = tagline_info
|
||||||
|
menus_str = {
|
||||||
|
HelpMenuSetting.disabled: _("No"),
|
||||||
|
HelpMenuSetting.reactions: _("Yes, reactions"),
|
||||||
|
HelpMenuSetting.buttons: _("Yes, buttons"),
|
||||||
|
HelpMenuSetting.select: _("Yes, buttons with select menu"),
|
||||||
|
HelpMenuSetting.selectonly: _("Yes, select menu only"),
|
||||||
|
}
|
||||||
|
data["use_menus"] = menus_str[self.use_menus]
|
||||||
|
|
||||||
return _(
|
return _(
|
||||||
"Maximum characters per page: {page_char_limit}"
|
"Maximum characters per page: {page_char_limit}"
|
||||||
@ -813,7 +832,31 @@ class RedHelpFormatter(HelpFormatterABC):
|
|||||||
"""
|
"""
|
||||||
Sends pages based on settings.
|
Sends pages based on settings.
|
||||||
"""
|
"""
|
||||||
if not (can_user_react_in(ctx.me, ctx.channel) and help_settings.use_menus):
|
if help_settings.use_menus.value >= HelpMenuSetting.buttons.value:
|
||||||
|
use_select = help_settings.use_menus.value == 3
|
||||||
|
select_only = help_settings.use_menus.value == 4
|
||||||
|
await SimpleMenu(
|
||||||
|
pages,
|
||||||
|
timeout=help_settings.react_timeout,
|
||||||
|
use_select_menu=use_select,
|
||||||
|
use_select_only=select_only,
|
||||||
|
).start(ctx)
|
||||||
|
|
||||||
|
elif (
|
||||||
|
can_user_react_in(ctx.me, ctx.channel)
|
||||||
|
and help_settings.use_menus is HelpMenuSetting.reactions
|
||||||
|
):
|
||||||
|
# Specifically ensuring the menu's message is sent prior to returning
|
||||||
|
m = await (ctx.send(embed=pages[0]) if embed else ctx.send(pages[0]))
|
||||||
|
c = menus.DEFAULT_CONTROLS if len(pages) > 1 else {"\N{CROSS MARK}": menus.close_menu}
|
||||||
|
# Allow other things to happen during menu timeout/interaction.
|
||||||
|
asyncio.create_task(
|
||||||
|
menus.menu(ctx, pages, c, message=m, timeout=help_settings.react_timeout)
|
||||||
|
)
|
||||||
|
# menu needs reactions added manually since we fed it a message
|
||||||
|
menus.start_adding_reactions(m, c.keys())
|
||||||
|
|
||||||
|
else:
|
||||||
max_pages_in_guild = help_settings.max_pages_in_guild
|
max_pages_in_guild = help_settings.max_pages_in_guild
|
||||||
use_DMs = len(pages) > max_pages_in_guild
|
use_DMs = len(pages) > max_pages_in_guild
|
||||||
destination = ctx.author if use_DMs else ctx.channel
|
destination = ctx.author if use_DMs else ctx.channel
|
||||||
@ -855,16 +898,6 @@ class RedHelpFormatter(HelpFormatterABC):
|
|||||||
await mass_purge(messages, channel)
|
await mass_purge(messages, channel)
|
||||||
|
|
||||||
asyncio.create_task(_delete_delay_help(destination, messages, delete_delay))
|
asyncio.create_task(_delete_delay_help(destination, messages, delete_delay))
|
||||||
else:
|
|
||||||
# Specifically ensuring the menu's message is sent prior to returning
|
|
||||||
m = await (ctx.send(embed=pages[0]) if embed else ctx.send(pages[0]))
|
|
||||||
c = menus.DEFAULT_CONTROLS if len(pages) > 1 else {"\N{CROSS MARK}": menus.close_menu}
|
|
||||||
# Allow other things to happen during menu timeout/interaction.
|
|
||||||
asyncio.create_task(
|
|
||||||
menus.menu(ctx, pages, c, message=m, timeout=help_settings.react_timeout)
|
|
||||||
)
|
|
||||||
# menu needs reactions added manually since we fed it a message
|
|
||||||
menus.start_adding_reactions(m, c.keys())
|
|
||||||
|
|
||||||
|
|
||||||
@commands.command(name="help", hidden=True, i18n=_)
|
@commands.command(name="help", hidden=True, i18n=_)
|
||||||
|
|||||||
@ -22,7 +22,18 @@ from redbot.core.utils.menus import menu
|
|||||||
from redbot.core.utils.views import SetApiView
|
from redbot.core.utils.views import SetApiView
|
||||||
from redbot.core.commands import GuildConverter, RawUserIdConverter
|
from redbot.core.commands import GuildConverter, RawUserIdConverter
|
||||||
from string import ascii_letters, digits
|
from string import ascii_letters, digits
|
||||||
from typing import TYPE_CHECKING, Union, Tuple, List, Optional, Iterable, Sequence, Dict, Set
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Union,
|
||||||
|
Tuple,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Iterable,
|
||||||
|
Sequence,
|
||||||
|
Dict,
|
||||||
|
Set,
|
||||||
|
Literal,
|
||||||
|
)
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
@ -54,6 +65,7 @@ from .utils.chat_formatting import (
|
|||||||
)
|
)
|
||||||
from .commands import CommandConverter, CogConverter
|
from .commands import CommandConverter, CogConverter
|
||||||
from .commands.requires import PrivilegeLevel
|
from .commands.requires import PrivilegeLevel
|
||||||
|
from .commands.help import HelpMenuSetting
|
||||||
|
|
||||||
_entities = {
|
_entities = {
|
||||||
"*": "*",
|
"*": "*",
|
||||||
@ -3680,30 +3692,47 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@helpset.command(name="usemenus")
|
@helpset.command(name="usemenus")
|
||||||
async def helpset_usemenus(self, ctx: commands.Context, use_menus: bool = None):
|
async def helpset_usemenus(
|
||||||
|
self,
|
||||||
|
ctx: commands.Context,
|
||||||
|
use_menus: Literal["buttons", "reactions", "select", "selectonly", "disable"],
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Allows the help command to be sent as a paginated menu instead of separate
|
Allows the help command to be sent as a paginated menu instead of separate
|
||||||
messages.
|
messages.
|
||||||
|
|
||||||
When enabled, `[p]help` will only show one page at a time and will use reactions to navigate between pages.
|
When "reactions", "buttons", "select", or "selectonly" is passed,
|
||||||
|
`[p]help` will only show one page at a time
|
||||||
This defaults to False.
|
and will use the associated control scheme to navigate between pages.
|
||||||
Using this without a setting will toggle.
|
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
- `[p]helpset usemenus True` - Enables using menus.
|
- `[p]helpset usemenus reactions` - Enables using reaction menus.
|
||||||
- `[p]helpset usemenus` - Toggles the value.
|
- `[p]helpset usemenus buttons` - Enables using button menus.
|
||||||
|
- `[p]helpset usemenus select` - Enables buttons with a select menu.
|
||||||
|
- `[p]helpset usemenus selectonly` - Enables a select menu only on help.
|
||||||
|
- `[p]helpset usemenus disable` - Disables help menus.
|
||||||
|
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
- `[use_menus]` - Whether to use menus. Leave blank to toggle.
|
- `<"buttons"|"reactions"|"select"|"selectonly"|"disable">` - Whether to use `buttons`,
|
||||||
|
`reactions`, `select`, `selectonly`, or no menus.
|
||||||
"""
|
"""
|
||||||
if use_menus is None:
|
if use_menus == "selectonly":
|
||||||
use_menus = not await ctx.bot._config.help.use_menus()
|
msg = _("Help will use the select menu only.")
|
||||||
await ctx.bot._config.help.use_menus.set(use_menus)
|
await ctx.bot._config.help.use_menus.set(4)
|
||||||
if use_menus:
|
if use_menus == "select":
|
||||||
await ctx.send(_("Help will use menus."))
|
msg = _("Help will use button menus and add a select menu.")
|
||||||
else:
|
await ctx.bot._config.help.use_menus.set(3)
|
||||||
await ctx.send(_("Help will not use menus."))
|
if use_menus == "buttons":
|
||||||
|
msg = _("Help will use button menus.")
|
||||||
|
await ctx.bot._config.help.use_menus.set(2)
|
||||||
|
if use_menus == "reactions":
|
||||||
|
msg = _("Help will use reaction menus.")
|
||||||
|
await ctx.bot._config.help.use_menus.set(1)
|
||||||
|
if use_menus == "disabled":
|
||||||
|
msg = _("Help will not use menus.")
|
||||||
|
await ctx.bot._config.help.use_menus.set(0)
|
||||||
|
|
||||||
|
await ctx.send(msg)
|
||||||
|
|
||||||
@helpset.command(name="showhidden")
|
@helpset.command(name="showhidden")
|
||||||
async def helpset_showhidden(self, ctx: commands.Context, show_hidden: bool = None):
|
async def helpset_showhidden(self, ctx: commands.Context, show_hidden: bool = None):
|
||||||
|
|||||||
@ -3,12 +3,270 @@ from __future__ import annotations
|
|||||||
import discord
|
import discord
|
||||||
|
|
||||||
from discord.ext.commands import BadArgument
|
from discord.ext.commands import BadArgument
|
||||||
from typing import List, Dict, Union, Optional
|
from typing import TYPE_CHECKING, Any, List, Optional, Union, Dict
|
||||||
from redbot.core.commands.converter import get_dict_converter
|
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
|
from redbot.vendored.discord.ext import menus
|
||||||
|
from redbot.core.commands.converter import get_dict_converter
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from redbot.core.commands import Context
|
||||||
|
|
||||||
_ = Translator("UtilsViews", __file__)
|
_ = Translator("UtilsViews", __file__)
|
||||||
|
|
||||||
|
_ACCEPTABLE_PAGE_TYPES = Union[Dict[str, Union[str, discord.Embed]], discord.Embed, str]
|
||||||
|
|
||||||
|
|
||||||
|
class _SimplePageSource(menus.ListPageSource):
|
||||||
|
def __init__(self, items: List[_ACCEPTABLE_PAGE_TYPES]):
|
||||||
|
super().__init__(items, per_page=1)
|
||||||
|
|
||||||
|
async def format_page(
|
||||||
|
self, view: discord.ui.View, page: _ACCEPTABLE_PAGE_TYPES
|
||||||
|
) -> Union[str, discord.Embed]:
|
||||||
|
return page
|
||||||
|
|
||||||
|
|
||||||
|
class _SelectMenu(discord.ui.Select):
|
||||||
|
def __init__(self, options: List[discord.SelectOption]):
|
||||||
|
super().__init__(
|
||||||
|
placeholder=_("Select a Page"), min_values=1, max_values=1, options=options
|
||||||
|
)
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
index = int(self.values[0])
|
||||||
|
self.view.current_page = index
|
||||||
|
kwargs = await self.view.get_page(self.view.current_page)
|
||||||
|
await interaction.response.edit_message(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class _NavigateButton(discord.ui.Button):
|
||||||
|
def __init__(
|
||||||
|
self, style: discord.ButtonStyle, emoji: Union[str, discord.PartialEmoji], direction: int
|
||||||
|
):
|
||||||
|
super().__init__(style=style, emoji=emoji)
|
||||||
|
self.direction = direction
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
if self.direction == 0:
|
||||||
|
self.view.current_page = 0
|
||||||
|
elif self.direction == self.view.source.get_max_pages():
|
||||||
|
self.view.current_page = self.view.source.get_max_pages() - 1
|
||||||
|
else:
|
||||||
|
self.view.current_page += self.direction
|
||||||
|
kwargs = await self.view.get_page(self.view.current_page)
|
||||||
|
await interaction.response.edit_message(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class _StopButton(discord.ui.Button):
|
||||||
|
def __init__(self, style: discord.ButtonStyle, emoji: Union[str, discord.PartialEmoji]):
|
||||||
|
super().__init__(style=style, emoji=emoji)
|
||||||
|
|
||||||
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
self.view.stop()
|
||||||
|
if interaction.message.flags.ephemeral:
|
||||||
|
await interaction.response.edit_message(view=None)
|
||||||
|
return
|
||||||
|
await interaction.message.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleMenu(discord.ui.View):
|
||||||
|
"""
|
||||||
|
A simple Button menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pages: `list` of `str`, `discord.Embed`, or `dict`.
|
||||||
|
The pages of the menu.
|
||||||
|
if the page is a `dict` its keys must be valid messageable args.
|
||||||
|
e,g. "content", "embed", etc.
|
||||||
|
page_start: int
|
||||||
|
The page to start the menu at.
|
||||||
|
timeout: float
|
||||||
|
The time (in seconds) to wait for a reaction
|
||||||
|
defaults to 180 seconds.
|
||||||
|
delete_after_timeout: bool
|
||||||
|
Whether or not to delete the message after
|
||||||
|
the timeout has expired.
|
||||||
|
Defaults to False.
|
||||||
|
disable_after_timeout: bool
|
||||||
|
Whether to disable all components on the
|
||||||
|
menu after timeout has expired. By default
|
||||||
|
the view is removed from the message on timeout.
|
||||||
|
Defaults to False.
|
||||||
|
use_select_menu: bool
|
||||||
|
Whether or not to include a select menu
|
||||||
|
to jump specifically between pages.
|
||||||
|
Defaults to False.
|
||||||
|
use_select_only: bool
|
||||||
|
Whether the menu will only display the select
|
||||||
|
menu for paginating instead of the buttons.
|
||||||
|
The stop button will remain but is positioned
|
||||||
|
under the select menu in this instance.
|
||||||
|
Defaults to False.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
You can provide a list of strings::
|
||||||
|
|
||||||
|
from redbot.core.utils.views import SimpleMenu
|
||||||
|
|
||||||
|
pages = ["Hello", "Hi", "Bonjour", "Salut"]
|
||||||
|
await SimpleMenu(pages).start(ctx)
|
||||||
|
|
||||||
|
You can provide a list of dicts::
|
||||||
|
|
||||||
|
from redbot.core.utils.views import SimpleMenu
|
||||||
|
pages = [{"content": "My content", "embed": discord.Embed(description="hello")}]
|
||||||
|
await SimpleMenu(pages).start(ctx)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pages: List[_ACCEPTABLE_PAGE_TYPES],
|
||||||
|
timeout: float = 180.0,
|
||||||
|
page_start: int = 0,
|
||||||
|
delete_after_timeout: bool = False,
|
||||||
|
disable_after_timeout: bool = False,
|
||||||
|
use_select_menu: bool = False,
|
||||||
|
use_select_only: bool = False,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
self.author: Optional[discord.abc.User] = None
|
||||||
|
self.message: Optional[discord.Message] = None
|
||||||
|
self._source = _SimplePageSource(items=pages)
|
||||||
|
self.ctx: Optional[Context] = None
|
||||||
|
self.current_page = page_start
|
||||||
|
self.delete_after_timeout = delete_after_timeout
|
||||||
|
self.disable_after_timeout = disable_after_timeout
|
||||||
|
self.use_select_menu = use_select_menu or use_select_only
|
||||||
|
self.use_select_only = use_select_only
|
||||||
|
|
||||||
|
self.forward_button = _NavigateButton(
|
||||||
|
discord.ButtonStyle.grey,
|
||||||
|
"\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}",
|
||||||
|
direction=1,
|
||||||
|
)
|
||||||
|
self.backward_button = _NavigateButton(
|
||||||
|
discord.ButtonStyle.grey,
|
||||||
|
"\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}",
|
||||||
|
direction=-1,
|
||||||
|
)
|
||||||
|
self.first_button = _NavigateButton(
|
||||||
|
discord.ButtonStyle.grey,
|
||||||
|
"\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
|
||||||
|
direction=0,
|
||||||
|
)
|
||||||
|
self.last_button = _NavigateButton(
|
||||||
|
discord.ButtonStyle.grey,
|
||||||
|
"\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\N{VARIATION SELECTOR-16}",
|
||||||
|
direction=self.source.get_max_pages(),
|
||||||
|
)
|
||||||
|
self.select_options = [
|
||||||
|
discord.SelectOption(label=_("Page {num}").format(num=num + 1), value=num)
|
||||||
|
for num, x in enumerate(pages)
|
||||||
|
]
|
||||||
|
self.stop_button = _StopButton(
|
||||||
|
discord.ButtonStyle.red, "\N{HEAVY MULTIPLICATION X}\N{VARIATION SELECTOR-16}"
|
||||||
|
)
|
||||||
|
self.select_menu = self._get_select_menu()
|
||||||
|
self.add_item(self.stop_button)
|
||||||
|
if self.source.is_paginating() and not self.use_select_only:
|
||||||
|
self.add_item(self.first_button)
|
||||||
|
self.add_item(self.backward_button)
|
||||||
|
self.add_item(self.forward_button)
|
||||||
|
self.add_item(self.last_button)
|
||||||
|
if self.use_select_menu and self.source.is_paginating():
|
||||||
|
if self.use_select_only:
|
||||||
|
self.remove_item(self.stop_button)
|
||||||
|
self.add_item(self.select_menu)
|
||||||
|
self.add_item(self.stop_button)
|
||||||
|
else:
|
||||||
|
self.add_item(self.select_menu)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
async def on_timeout(self):
|
||||||
|
if self.delete_after_timeout and not self.message.flags.ephemeral:
|
||||||
|
await self.message.delete()
|
||||||
|
elif self.disable_after_timeout:
|
||||||
|
for child in self.children:
|
||||||
|
child.disabled = True
|
||||||
|
await self.message.edit(view=self)
|
||||||
|
else:
|
||||||
|
await self.message.edit(view=None)
|
||||||
|
|
||||||
|
def _get_select_menu(self):
|
||||||
|
# handles modifying the select menu if more than 25 pages are provided
|
||||||
|
# this will show the previous 12 and next 13 pages in the select menu
|
||||||
|
# based on the currently displayed page. Once you reach close to the max
|
||||||
|
# pages it will display the last 25 pages.
|
||||||
|
if len(self.select_options) > 25:
|
||||||
|
minus_diff = None
|
||||||
|
plus_diff = 25
|
||||||
|
if 12 < self.current_page < len(self.select_options) - 25:
|
||||||
|
minus_diff = self.current_page - 12
|
||||||
|
plus_diff = self.current_page + 13
|
||||||
|
elif self.current_page >= len(self.select_options) - 25:
|
||||||
|
minus_diff = len(self.select_options) - 25
|
||||||
|
plus_diff = None
|
||||||
|
options = self.select_options[minus_diff:plus_diff]
|
||||||
|
else:
|
||||||
|
options = self.select_options[:25]
|
||||||
|
return _SelectMenu(options)
|
||||||
|
|
||||||
|
async def start(self, ctx: Context, *, ephemeral: bool = False):
|
||||||
|
"""
|
||||||
|
Used to start the menu displaying the first page requested.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ctx: `commands.Context`
|
||||||
|
The context to start the menu in.
|
||||||
|
ephemeral: `bool`
|
||||||
|
Send the message ephemerally. This only works
|
||||||
|
if the context is from a slash command interaction.
|
||||||
|
"""
|
||||||
|
self.author = ctx.author
|
||||||
|
self.ctx = ctx
|
||||||
|
kwargs = await self.get_page(self.current_page)
|
||||||
|
self.message = await ctx.send(**kwargs, ephemeral=ephemeral)
|
||||||
|
|
||||||
|
async def get_page(self, page_num: int) -> Dict[str, Optional[Any]]:
|
||||||
|
try:
|
||||||
|
page = await self.source.get_page(page_num)
|
||||||
|
except IndexError:
|
||||||
|
self.current_page = 0
|
||||||
|
page = await self.source.get_page(self.current_page)
|
||||||
|
value = await self.source.format_page(self, page)
|
||||||
|
if self.use_select_menu and len(self.select_options) > 25 and self.source.is_paginating():
|
||||||
|
self.remove_item(self.select_menu)
|
||||||
|
self.select_menu = self._get_select_menu()
|
||||||
|
self.add_item(self.select_menu)
|
||||||
|
ret: Dict[str, Optional[Any]] = {"view": self}
|
||||||
|
if isinstance(value, dict):
|
||||||
|
ret.update(value)
|
||||||
|
elif isinstance(value, str):
|
||||||
|
ret.update({"content": value, "embed": None})
|
||||||
|
elif isinstance(value, discord.Embed):
|
||||||
|
ret.update({"embed": value, "content": None})
|
||||||
|
return ret
|
||||||
|
|
||||||
|
async def interaction_check(self, interaction: discord.Interaction):
|
||||||
|
"""Ensure only the author is allowed to interact with the menu."""
|
||||||
|
allowed_ids = (getattr(self.author, "id", None),)
|
||||||
|
if interaction.user.id not in allowed_ids:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
content=_("You are not authorized to interact with this."), ephemeral=True
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class SetApiModal(discord.ui.Modal):
|
class SetApiModal(discord.ui.Modal):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user