mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
Make controls in menu() optional (#5678)
* Make `controls` in `menu()` optional You might wonder, shouldn't we pass `None` to functions from controls? No, we shouldn't because when `None` is passed, only DEFAULT_CONTROLS can be used and that means that the length of pages list won't change. * Update usage in core and core cogs * Add missing docstrings to `redbot.core.utils.menus` module
This commit is contained in:
parent
955b40ac6d
commit
27bed5010f
@ -9,7 +9,7 @@ import discord
|
||||
from redbot.core import Config, commands, checks
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils.chat_formatting import box, pagify
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
from redbot.core.utils.menus import menu
|
||||
|
||||
from redbot.core.bot import Red
|
||||
from .alias_entry import AliasEntry, AliasCache, ArgParseError
|
||||
@ -185,7 +185,7 @@ class Alias(commands.Cog):
|
||||
if len(alias_list) == 1:
|
||||
await ctx.send(alias_list[0])
|
||||
return
|
||||
await menu(ctx, alias_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, alias_list)
|
||||
|
||||
@commands.group()
|
||||
async def alias(self, ctx: commands.Context):
|
||||
|
||||
@ -14,7 +14,7 @@ from redbot.core import bank, commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import box, humanize_number
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu, start_adding_reactions
|
||||
from redbot.core.utils.menus import menu, start_adding_reactions
|
||||
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
|
||||
|
||||
from ...audio_dataclasses import LocalPath
|
||||
@ -102,7 +102,7 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
discord.Embed(title=_("Global Whitelist"), description=page, colour=embed_colour)
|
||||
for page in pages
|
||||
)
|
||||
await menu(ctx, pages, DEFAULT_CONTROLS)
|
||||
await menu(ctx, pages)
|
||||
|
||||
@command_audioset_perms_global_whitelist.command(name="clear")
|
||||
async def command_audioset_perms_global_whitelist_clear(self, ctx: commands.Context):
|
||||
@ -196,7 +196,7 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
discord.Embed(title=_("Global Blacklist"), description=page, colour=embed_colour)
|
||||
for page in pages
|
||||
)
|
||||
await menu(ctx, pages, DEFAULT_CONTROLS)
|
||||
await menu(ctx, pages)
|
||||
|
||||
@command_audioset_perms_global_blacklist.command(name="clear")
|
||||
async def command_audioset_perms_global_blacklist_clear(self, ctx: commands.Context):
|
||||
@ -292,7 +292,7 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
discord.Embed(title=_("Whitelist"), description=page, colour=embed_colour)
|
||||
for page in pages
|
||||
)
|
||||
await menu(ctx, pages, DEFAULT_CONTROLS)
|
||||
await menu(ctx, pages)
|
||||
|
||||
@command_audioset_perms_whitelist.command(name="clear")
|
||||
async def command_audioset_perms_whitelist_clear(self, ctx: commands.Context):
|
||||
@ -385,7 +385,7 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
discord.Embed(title=_("Blacklist"), description=page, colour=embed_colour)
|
||||
for page in pages
|
||||
)
|
||||
await menu(ctx, pages, DEFAULT_CONTROLS)
|
||||
await menu(ctx, pages)
|
||||
|
||||
@command_audioset_perms_blacklist.command(name="clear")
|
||||
async def command_audioset_perms_blacklist_clear(self, ctx: commands.Context):
|
||||
|
||||
@ -10,7 +10,7 @@ from red_commons.logging import getLogger
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import box, humanize_number, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu, start_adding_reactions
|
||||
from redbot.core.utils.menus import menu, start_adding_reactions
|
||||
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
|
||||
|
||||
from ...equalizer import Equalizer
|
||||
@ -141,7 +141,7 @@ class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
text=_("{num} preset(s)").format(num=humanize_number(len(list(eq_presets.keys()))))
|
||||
)
|
||||
page_list.append(embed)
|
||||
await menu(ctx, page_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, page_list)
|
||||
|
||||
@command_equalizer.command(name="load")
|
||||
async def command_equalizer_load(self, ctx: commands.Context, eq_preset: str):
|
||||
|
||||
@ -8,7 +8,7 @@ from red_commons.logging import getLogger
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, close_menu, menu, next_page, prev_page
|
||||
from redbot.core.utils.menus import close_menu, menu, next_page, prev_page
|
||||
|
||||
from ...audio_dataclasses import LocalPath, Query
|
||||
from ..abc import MixinMeta
|
||||
@ -113,7 +113,7 @@ class LocalTrackCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||
if dj_enabled and not await self._can_instaskip(ctx, ctx.author):
|
||||
return await menu(ctx, folder_page_list, DEFAULT_CONTROLS)
|
||||
return await menu(ctx, folder_page_list)
|
||||
else:
|
||||
await menu(ctx, folder_page_list, local_folder_controls)
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import humanize_number, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
from redbot.core.utils.menus import menu
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
@ -92,7 +92,7 @@ class MiscellaneousCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
pages += 1
|
||||
servers_embed.append(em)
|
||||
|
||||
await menu(ctx, servers_embed, DEFAULT_CONTROLS)
|
||||
await menu(ctx, servers_embed)
|
||||
|
||||
@commands.command(name="percent")
|
||||
@commands.guild_only()
|
||||
|
||||
@ -15,7 +15,7 @@ from redbot.core import commands
|
||||
from redbot.core.commands import UserInputOptional
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, close_menu, menu, next_page, prev_page
|
||||
from redbot.core.utils.menus import close_menu, menu, next_page, prev_page
|
||||
|
||||
from ...audio_dataclasses import _PARTIALLY_SUPPORTED_MUSIC_EXT, Query
|
||||
from ...errors import (
|
||||
@ -930,6 +930,6 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
search_page_list.append(embed)
|
||||
|
||||
if dj_enabled and not can_skip:
|
||||
return await menu(ctx, search_page_list, DEFAULT_CONTROLS)
|
||||
return await menu(ctx, search_page_list)
|
||||
|
||||
await menu(ctx, search_page_list, search_controls)
|
||||
|
||||
@ -19,7 +19,7 @@ from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import bold, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
from redbot.core.utils.menus import menu
|
||||
from redbot.core.utils.predicates import MessagePredicate
|
||||
|
||||
from ...apis.api_utils import FakePlaylist
|
||||
@ -899,7 +899,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
)
|
||||
)
|
||||
page_list.append(embed)
|
||||
await menu(ctx, page_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, page_list)
|
||||
|
||||
@commands.cooldown(1, 15, commands.BucketType.guild)
|
||||
@command_playlist.command(name="list", usage="[args]", cooldown_after_parsing=True)
|
||||
@ -1052,7 +1052,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async for page_num in AsyncIter(range(1, len_playlist_list_pages + 1)):
|
||||
embed = await self._build_playlist_list_page(ctx, page_num, abc_names, name)
|
||||
playlist_embeds.append(embed)
|
||||
await menu(ctx, playlist_embeds, DEFAULT_CONTROLS)
|
||||
await menu(ctx, playlist_embeds)
|
||||
|
||||
@command_playlist.command(name="queue", usage="<name> [args]", cooldown_after_parsing=True)
|
||||
@commands.cooldown(1, 300, commands.BucketType.member)
|
||||
@ -1742,7 +1742,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
),
|
||||
)
|
||||
if embeds:
|
||||
await menu(ctx, embeds, DEFAULT_CONTROLS)
|
||||
await menu(ctx, embeds)
|
||||
|
||||
@command_playlist.command(name="upload", usage="[args]")
|
||||
@commands.is_owner()
|
||||
|
||||
@ -14,7 +14,6 @@ from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.menus import (
|
||||
DEFAULT_CONTROLS,
|
||||
close_menu,
|
||||
menu,
|
||||
next_page,
|
||||
@ -304,7 +303,7 @@ class QueueCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async for page_num in AsyncIter(range(1, len_search_pages + 1)):
|
||||
embed = await self._build_queue_search_page(ctx, page_num, search_list)
|
||||
search_page_list.append(embed)
|
||||
await menu(ctx, search_page_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, search_page_list)
|
||||
|
||||
@command_queue.command(name="shuffle")
|
||||
@commands.cooldown(1, 30, commands.BucketType.guild)
|
||||
|
||||
@ -309,7 +309,7 @@ class CustomCommands(commands.Cog):
|
||||
if len(msg) > 2000:
|
||||
msg = f"{msg[:1997]}..."
|
||||
msglist.append(msg)
|
||||
await menus.menu(ctx, msglist, menus.DEFAULT_CONTROLS)
|
||||
await menus.menu(ctx, msglist)
|
||||
|
||||
@customcom.command(name="search")
|
||||
@commands.guild_only()
|
||||
@ -572,11 +572,11 @@ class CustomCommands(commands.Cog):
|
||||
)
|
||||
embed.set_footer(text=_("Page {num}/{total}").format(num=idx, total=len(pages)))
|
||||
embed_pages.append(embed)
|
||||
await menus.menu(ctx, embed_pages, menus.DEFAULT_CONTROLS)
|
||||
await menus.menu(ctx, embed_pages)
|
||||
else:
|
||||
content = "\n".join(map("{0[0]:<12} : {0[1]}".format, results))
|
||||
pages = list(map(box, pagify(content, page_length=2000, shorten_by=10)))
|
||||
await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS)
|
||||
await menus.menu(ctx, pages)
|
||||
|
||||
@customcom.command(name="show")
|
||||
async def cc_show(self, ctx, command_name: str):
|
||||
|
||||
@ -14,7 +14,7 @@ from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import box, humanize_number
|
||||
from redbot.core.utils.menus import close_menu, menu, DEFAULT_CONTROLS
|
||||
from redbot.core.utils.menus import close_menu, menu
|
||||
from .converters import positive_int
|
||||
|
||||
T_ = Translator("Economy", __file__)
|
||||
@ -516,11 +516,7 @@ class Economy(commands.Cog):
|
||||
highscores.append(box(temp_msg, lang="md"))
|
||||
|
||||
if highscores:
|
||||
await menu(
|
||||
ctx,
|
||||
highscores,
|
||||
DEFAULT_CONTROLS if len(highscores) > 1 else {"\N{CROSS MARK}": close_menu},
|
||||
)
|
||||
await menu(ctx, highscores)
|
||||
else:
|
||||
await ctx.send(_("No balances found."))
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import discord
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
from redbot.core.utils.menus import menu
|
||||
from redbot.core.utils.chat_formatting import (
|
||||
bold,
|
||||
escape,
|
||||
@ -511,7 +511,6 @@ class General(commands.Cog):
|
||||
await menu(
|
||||
ctx,
|
||||
pages=embeds,
|
||||
controls=DEFAULT_CONTROLS,
|
||||
message=None,
|
||||
page=0,
|
||||
timeout=30,
|
||||
@ -537,7 +536,6 @@ class General(commands.Cog):
|
||||
await menu(
|
||||
ctx,
|
||||
pages=messages,
|
||||
controls=DEFAULT_CONTROLS,
|
||||
message=None,
|
||||
page=0,
|
||||
timeout=30,
|
||||
|
||||
@ -9,7 +9,7 @@ from redbot.core import checks, commands, modlog
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils.chat_formatting import bold, box, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
from redbot.core.utils.menus import menu
|
||||
from redbot.core.utils.predicates import MessagePredicate
|
||||
|
||||
_ = Translator("ModLog", __file__)
|
||||
@ -84,7 +84,7 @@ class ModLog(commands.Cog):
|
||||
)
|
||||
rendered_cases.append(message)
|
||||
|
||||
await menu(ctx, rendered_cases, DEFAULT_CONTROLS)
|
||||
await menu(ctx, rendered_cases)
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@ -119,7 +119,7 @@ class ModLog(commands.Cog):
|
||||
)
|
||||
for page in pagify(message, ["\n\n", "\n"], priority=True):
|
||||
rendered_cases.append(page)
|
||||
await menu(ctx, rendered_cases, DEFAULT_CONTROLS)
|
||||
await menu(ctx, rendered_cases)
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
|
||||
@ -19,7 +19,7 @@ from redbot.core.commands import UserInputOptional
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import warning, pagify
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
from redbot.core.utils.menus import menu
|
||||
|
||||
|
||||
_ = Translator("Warnings", __file__)
|
||||
@ -324,7 +324,7 @@ class Warnings(commands.Cog):
|
||||
).format(reason_name=r, **v)
|
||||
)
|
||||
if msg_list:
|
||||
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, msg_list)
|
||||
else:
|
||||
await ctx.send(_("There are no reasons configured!"))
|
||||
|
||||
@ -359,7 +359,7 @@ class Warnings(commands.Cog):
|
||||
).format(**r)
|
||||
)
|
||||
if msg_list:
|
||||
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
||||
await menu(ctx, msg_list)
|
||||
else:
|
||||
await ctx.send(_("There are no actions configured!"))
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import pip
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from redbot.core import data_manager
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||
from redbot.core.utils.menus import menu
|
||||
from redbot.core.commands import GuildConverter, RawUserIdConverter
|
||||
from string import ascii_letters, digits
|
||||
from typing import TYPE_CHECKING, Union, Tuple, List, Optional, Iterable, Sequence, Dict, Set
|
||||
@ -1659,7 +1659,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
if len(pages) == 1:
|
||||
await ctx.send(pages[0])
|
||||
else:
|
||||
await menu(ctx, pages, DEFAULT_CONTROLS)
|
||||
await menu(ctx, pages)
|
||||
|
||||
@commands.command(require_var_positional=True)
|
||||
@checks.is_owner()
|
||||
|
||||
@ -6,7 +6,7 @@ import asyncio
|
||||
import contextlib
|
||||
import functools
|
||||
from types import MappingProxyType
|
||||
from typing import Callable, Iterable, List, Mapping, TypeVar, Union
|
||||
from typing import Callable, Iterable, List, Mapping, Optional, TypeVar, Union
|
||||
|
||||
import discord
|
||||
|
||||
@ -22,7 +22,7 @@ _ControlCallable = Callable[[commands.Context, _PageList, discord.Message, int,
|
||||
async def menu(
|
||||
ctx: commands.Context,
|
||||
pages: _PageList,
|
||||
controls: Mapping[str, _ControlCallable],
|
||||
controls: Optional[Mapping[str, _ControlCallable]] = None,
|
||||
message: discord.Message = None,
|
||||
page: int = 0,
|
||||
timeout: float = 30.0,
|
||||
@ -45,10 +45,12 @@ async def menu(
|
||||
The command context
|
||||
pages: `list` of `str` or `discord.Embed`
|
||||
The pages of the menu.
|
||||
controls: Mapping[str, Callable],
|
||||
controls: Optional[Mapping[str, Callable]]
|
||||
A mapping of emoji to the function which handles the action for the
|
||||
emoji. The signature of the function should be the same as of this function
|
||||
and should additionally accept an ``emoji`` parameter of type `str`.
|
||||
If not passed, `DEFAULT_CONTROLS` is used *or*
|
||||
only a close menu control is shown when ``pages`` is of length 1.
|
||||
message: discord.Message
|
||||
The message representing the menu. Usually :code:`None` when first opening
|
||||
the menu
|
||||
@ -68,6 +70,11 @@ async def menu(
|
||||
isinstance(x, str) for x in pages
|
||||
):
|
||||
raise RuntimeError("All pages must be of the same type")
|
||||
if controls is None:
|
||||
if len(pages) == 1:
|
||||
controls = {"\N{CROSS MARK}": close_menu}
|
||||
else:
|
||||
controls = DEFAULT_CONTROLS
|
||||
for key, value in controls.items():
|
||||
maybe_coro = value
|
||||
if isinstance(value, functools.partial):
|
||||
@ -144,6 +151,10 @@ async def next_page(
|
||||
timeout: float,
|
||||
emoji: str,
|
||||
) -> _T:
|
||||
"""
|
||||
Function for showing next page which is suitable
|
||||
for use in ``controls`` mapping that is passed to `menu()`.
|
||||
"""
|
||||
if page == len(pages) - 1:
|
||||
page = 0 # Loop around to the first item
|
||||
else:
|
||||
@ -160,6 +171,10 @@ async def prev_page(
|
||||
timeout: float,
|
||||
emoji: str,
|
||||
) -> _T:
|
||||
"""
|
||||
Function for showing previous page which is suitable
|
||||
for use in ``controls`` mapping that is passed to `menu()`.
|
||||
"""
|
||||
if page == 0:
|
||||
page = len(pages) - 1 # Loop around to the last item
|
||||
else:
|
||||
@ -176,6 +191,10 @@ async def close_menu(
|
||||
timeout: float,
|
||||
emoji: str,
|
||||
) -> None:
|
||||
"""
|
||||
Function for closing (deleting) menu which is suitable
|
||||
for use in ``controls`` mapping that is passed to `menu()`.
|
||||
"""
|
||||
with contextlib.suppress(discord.NotFound):
|
||||
await message.delete()
|
||||
|
||||
@ -191,7 +210,7 @@ def start_adding_reactions(
|
||||
|
||||
This is particularly useful if you wish to start waiting for a
|
||||
reaction whilst the reactions are still being added - in fact,
|
||||
this is exactly what `menu` uses to do that.
|
||||
this is exactly what `menu()` uses to do that.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@ -216,6 +235,8 @@ def start_adding_reactions(
|
||||
return asyncio.create_task(task())
|
||||
|
||||
|
||||
#: Default controls for `menu()` that contain controls for
|
||||
#: previous page, closing menu, and next page.
|
||||
DEFAULT_CONTROLS: Mapping[str, _ControlCallable] = MappingProxyType(
|
||||
{
|
||||
"\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}": prev_page,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user