class HelpPages(MenuPages): def __init__(self, source): super().__init__(source, delete_message_after=True, timeout=60.0) @menus.button('\U00002754', position=menus.Last(4)) async def show_bot_help(self, payload): """ Shows the bot help message """ embed = discord.Embed( title="Bot help", description="Hello! Welcome to the bot help page.") embed.add_field(name="[arg]", value="Optional argument!", inline=False) embed.add_field(name="<arg>", value="Required argument!", inline=False) embed.add_field( name="What to do?", value= r"Use `{self.lmao.prefix}help [cmd]` for command help and `{self.lmao.prefix}help [module]` for module help." ) await self.message.edit(content=None, embed=embed) @menus.button('\N{INFORMATION SOURCE}\ufe0f', position=menus.Last(3)) async def show_help(self, payload): """Shows this message""" embed = discord.Embed(title='Paginator help', description='Hello! Welcome to the help page.') messages = [] for (emoji, button) in self.buttons.items(): messages.append(f'{emoji}: {button.action.__doc__}') embed.add_field(name='What are these reactions for?', value='\n'.join(messages), inline=False) await self.message.edit(content=None, embed=embed)
class Pages(menus.MenuPages): __slots__ = () def __init__(self, source): super().__init__(source=source, check_embeds=1) async def finalize(self, timed_out): with suppress(discord.HTTPException): if timed_out: await self.message.clear_reactions() else: await self.message.delete() @menus.button('\N{INFORMATION SOURCE}\ufe0f', position=menus.Last(3)) async def show_help(self, _): """shows this message""" embed = Embed(title='Помощь от Батиной книги') messages = [] for emoji, button in self.buttons.items(): messages.append(f'{emoji}: {button.action.__doc__}') embed.add_field(name=__('Для чего это кнопкочка?'), value='\n'.join(messages), inline=0) embed.set_footer(text=__('Ты на странице - {current_page}.').format( current_page=self.current_page + 1)) await self.message.edit(content=None, embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page()) @menus.button('\N{INPUT SYMBOL FOR NUMBERS}', position=menus.Last(1.5)) async def numbered_page(self, payload): """lets you type a page number to go to""" channel = self.message.channel author_id = payload.user_id to_delete = [ await channel.send('Полистай побольше, может найдешь что то годное') ] def message_check(m): # check out, for author_id and channel, and content is it digit() return (m.author.id == author_id and channel == m.channel and m.content.isdigit()) try: msg = await self.bot.wait_for('message', check=message_check, timeout=30.0) except asyncio.TimeoutError: to_delete.append( await channel.send('Ты проебался, ты проебал страницу.')) await asyncio.sleep(5) else: page = int(msg.content) to_delete.append(msg) await self.show_checked_page(page - 1) with suppress(Exception): await channel.delete_messages(to_delete)
class MenuPagesBase(menus.MenuPages, inherit_buttons=False): async def update(self, payload: discord.RawReactionActionEvent): if self._can_remove_reactions: if payload.event_type != 'REACTION_ADD': return await self.message.remove_reaction(payload.emoji, payload.member) await super().update(payload) async def send_initial_message(self, ctx: ErisContext, channel: discord.TextChannel): page = await self._source.get_page(0) kwargs = await self._get_kwargs_from_page(page) return await ctx.reply(**kwargs) async def show_page(self, page_number: int): page = await self._source.get_page(page_number) self.current_page = page_number kwargs = await self._get_kwargs_from_page(page) mentions = discord.AllowedMentions(replied_user=False) await self.message.edit(**kwargs, allowed_mentions=mentions) def _skip_double_arrows(self) -> bool: max_pages = self._source.get_max_pages() if max_pages is None: return True return max_pages <= 2 @menus.button(DOUBLE_LEFT_EMOJI, position=menus.First(0), skip_if=_skip_double_arrows) async def go_to_first_page(self, payload: discord.RawReactionActionEvent): '''Vai para a primeira página.''' await self.show_page(0) @menus.button(LEFT_EMOJI, position=menus.First(0)) async def go_to_previous_page(self, payload: discord.RawReactionActionEvent): '''Vai para a página anterior.''' await self.show_checked_page(self.current_page - 1) @menus.button(STOP_EMOJI) async def stop_pages(self, payload: discord.RawReactionActionEvent): '''Para a sessão de paginamento.''' self.stop() @menus.button(RIGHT_EMOJI, position=menus.Last(0)) async def go_to_next_page(self, payload: discord.RawReactionActionEvent): '''Vai para a próxima página.''' await self.show_checked_page(self.current_page + 1) @menus.button(DOUBLE_RIGHT_EMOJI, position=menus.Last(1), skip_if=_skip_double_arrows) async def go_to_last_page(self, payload: discord.RawReactionActionEvent): '''Vai para a última página.''' await self.show_page(self._source.get_max_pages() - 1)
class NavMenuPages(menus.MenuPages): def __init__(self, source: menus.PageSource, **kwargs): super().__init__(source, **kwargs) self._in_info = False async def go_back_to_current_page(self): await asyncio.sleep(30.0) await self.show_current_page() @menus.button('\N{INPUT SYMBOL FOR NUMBERS}', position=menus.Last(1.5)) async def pick_page(self, payload: discord.RawReactionActionEvent): """lets you type a page number to go to""" my_msg = await self.ctx.send('What page do you want to go to?') try: msg = await self.bot.wait_for( 'message', check=lambda m: m.author == self.ctx.author and m.channel == self.ctx.channel, timeout=30.0) except asyncio.TimeoutError: await self.ctx.send('Took too long.') return finally: await my_msg.delete() if msg.content.isdigit(): page = int(msg.content) await self.show_checked_page(page - 1) @menus.button('\N{INFORMATION SOURCE}', position=menus.Last(3)) async def info(self, payload: discord.RawReactionActionEvent): """shows this message""" self._in_info = not self._in_info if self._in_info: embed = discord.Embed( title='Paginator help', description='Hello! Welcome to the help page.') value = '\n'.join(f'{emoji}: {button.action.__doc__}' for emoji, button in self.buttons.items()) embed.add_field(name='What are these reactions for?', value=value) embed.set_footer( text= f'We were on page {self.current_page + 1} before this message.' ) await self.message.edit(embed=embed) task = asyncio.create_task(self.go_back_to_current_page()) def on_done(fut): self._in_info = False task.add_done_callback(on_done) else: await self.show_current_page()
class MainMenu(menus.MenuPages): def __init__(self, source, **kwargs): super().__init__(source=source, check_embeds=True, **kwargs) @menus.button('\N{INPUT SYMBOL FOR NUMBERS}', position=menus.Last(1.5)) async def numbered_page(self, payload): """lets you type a page number to go to""" channel = self.message.channel author_id = payload.user_id to_delete = [await channel.send('What page do you want to go to?')] def message_check(m): return m.author.id == author_id and channel == m.channel and m.content.isdigit( ) try: msg = await self.bot.wait_for('message', check=message_check, timeout=30.0) except asyncio.TimeoutError: to_delete.append(await channel.send('Took too long.')) await asyncio.sleep(5) else: page = int(msg.content) to_delete.append(msg) await self.show_checked_page(page - 1) try: await channel.delete_messages(to_delete) except Exception: pass
class HelpMenu(MenuPages): def __init__(self, src): super().__init__(src) @menus.button("\N{WHITE QUESTION MARK ORNAMENT}", position=menus.Last(5)) async def show_signature_help(self, _): embed = discord.Embed(color=core.COLOR, timestamp=self.ctx.now) embed.set_author(name=f"{self.bot.user.name} - Help", icon_url=self.bot.avatar) embed.set_footer(text=f"You were on page {self.current_page+1}.") for name, value in ( ('<argument>', 'This is a required argument.'), ('[argument]', 'This is an optional argument.'), ('<A | B>', 'This means that this argument can either be A or B.'), ('[A | B]', 'Similar to the previous one, but the argument is optional.'), ('<\'argument\'>', 'This argument should be typed exactly as shown.'), ('<argument...>', 'You can use this argument multiple times.'), ('<argument=X>', 'This means that X is the default value for the argument if none is passed.' )): embed.add_field(name=f'`{name}`', value=value, inline=False) no_mention = discord.AllowedMentions.none() await self.ctx.maybe_edit(self.message, embed=embed, allowed_mentions=no_mention) async def go_back(): await asyncio.sleep(45) await self.show_page(self.current_page) self.bot.loop.create_task(go_back())
class HelpMenu(menus.Menu): def __init__(self, cogs, main_embed, instance): super().__init__(clear_reactions_after=True) self.cogs = cogs self.main_embed = main_embed self.instance = instance for cog in cogs: self.add_button(menus.Button(cog.emoji, self.cog_embed)) async def send_initial_message(self, ctx, channel): return await ctx.send(embed=self.main_embed) @menus.button('↩️') async def main(self, payload): await self.message.edit(embed=self.main_embed) @menus.button('⏹️', position=menus.Last()) async def end(self, payload): self.stop() async def cog_embed(self, payload): for cog in self.cogs: if payload.emoji.name == cog.emoji: await self.message.edit( embed=await self.instance.cog_embed(cog))
class KalPages(menus.MenuPages): def __init__(self, source, **kwargs): super().__init__(source=source, check_embeds=True, **kwargs) async def finalize(self, timed_out): try: if timed_out: await self.message.clear_reactions() else: await self.message.delete() except discord.HTTPException: pass @menus.button("\N{PUBLIC ADDRESS LOUDSPEAKER}", position=menus.Last(6)) async def announcements(self, payload): """Announcements go here""" embed = self.bot.embed(self.ctx) embed.title = self.bot.announcement["title"] embed.description = self.bot.announcement["message"] await self.message.edit(content=None, embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page())
class BaseChoiceMenu(menus.Menu, Generic[T]): options: list[T] def __init__(self, options: list[T]) -> None: super().__init__(delete_message_after=True) if len(options) > 9: raise RuntimeError("Too many options for choice menu.") self.options = options self.selection: Optional[T] = None for i, _ in enumerate(self.options, 1): emoji = f"{i}\ufe0f\N{COMBINING ENCLOSING KEYCAP}" self.add_button(menus.Button(emoji, self.choose)) async def send_initial_message(self, ctx: Context, channel: discord.TextChannel) -> discord.Message: raise NotImplementedError async def choose(self, payload: discord.RawReactionActionEvent): self.selection = self.options[int(str(payload.emoji)[0]) - 1] self.stop() async def start(self, ctx, *, channel=None) -> Optional[T]: await super().start(ctx, channel=channel, wait=True) return self.selection @menus.button("\N{BLACK SQUARE FOR STOP}\ufe0f", position=menus.Last(0)) async def cancel(self, payload): self.stop()
class Game(menus.Menu): name: Optional[str] = "Foggle" footer: Optional[str] = None def __init__(self, *, size: int = ORIGINAL, base: int = 10, **kwargs): self.board = Board(size=size, base=base) self.setup() super().__init__(**kwargs) @property def state(self): state = "" for row in range(self.board.size): emoji = [] for column in range(self.board.size): emoji.append(NUMBER_EMOJI[self.board.columns[column][row]]) state = " ".join(emoji) + "\n" + state number = self.board.number if self.board.base == 2: number = bin(number) elif self.board.base == 16: number = hex(number) state += f"\n\n The magic number is **{number}**!" return discord.Embed(title=self.name, description=state).set_footer(text=self.footer) def setup(self): raise NotImplementedError async def send_initial_message(self, ctx, channel): return await channel.send(content="Foggle game started, you have 3 minutes!", embed=self.state) async def start(self, *args, **kwargs): await super().start(*args, **kwargs) # await self.bot.loop.run_in_executor(None, lambda: self.board.legal_equations) async def finalize(self, timed_out): self.bot.dispatch("foggle_game_complete", self.message.channel) def get_points(self, equations: Iterable[str]) -> int: return self.board.total_points(equations) def get_correct(self, equations: Iterable[str]): return sum(self.board.is_legal(equation) for equation in equations) def check_equation(self, equation: str) -> bool: return self.board.is_legal(equation) async def check_message(self, message: discord.Message): raise NotImplementedError @menus.button("\N{BLACK SQUARE FOR STOP}\ufe0f", position=menus.Last(0)) async def cancel(self, payload): await self.message.edit(content="Game Cancelled.") self.stop()
class PaginatedHelp(ViewMenuPages, inherit_buttons=False): # type: ignore ## mypy doesn't like metaclasses. def __init__(self, source, **kwargs): super().__init__(source, delete_message_after=True, timeout=30, **kwargs) @menus.button(EMOJIS["information"], position=menus.Last(4)) async def on_questionmark(self, payload): embed = CustomEmbed( title="Help Info", description=( "```diff\n" "- <arg> Required \n" "- [arg] Optional \n" "- [arg..] Multiple Args\n" "```\n" ), ) embed.add_field( name="What do the emojis do?", value=( f"{EMOJIS['arrow_left']} - Goes one page backward.\n" f"{EMOJIS['double_backward']} - Goes to the first page.\n" f"{EMOJIS['stop']} - Stops the menu.\n" f"{EMOJIS['double_forward']} - Goes to the last page.\n" f"{EMOJIS['arrow_right']} - Goes one page forward\n" f"{EMOJIS['information']} - Shows this page.\n" ), ) if self.source.get_max_pages() == 1: embed.set_footer(text="To get back to the page, use the double arrows.") await self.message.edit(embed=embed) def should_add_reactions(self): return True @menus.button(EMOJIS["arrow_left"], position=menus.First(2)) async def left_arrow(self, payload): await self.go_to_previous_page(payload) @menus.button(EMOJIS["stop"], position=menus.First(3)) async def stop_emoji(self, payload): self.stop() @menus.button(EMOJIS["arrow_right"], position=menus.First(4)) async def right_arrow(self, payload): await self.go_to_next_page(payload) @menus.button(EMOJIS["double_forward"], position=menus.First(5)) async def double_forward(self, payload): await self.go_to_last_page(payload) @menus.button(EMOJIS["double_backward"], position=menus.First(1)) async def double_backward(self, payload): await self.go_to_first_page(payload)
def __init__(self, source): super().__init__(source=source, check_embeds=False) EmojiB = namedtuple("EmojiB", "emoji position explain") def_dict_emoji = { "\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\ufe0f": EmojiB( "<:backward2:816457785167314987>", menus.First(0), "Goes to the first page.", ), "\N{BLACK LEFT-POINTING TRIANGLE}\ufe0f": EmojiB( "<:backward:816458218145579049>", menus.First(1), "Goes to the previous page.", ), "\N{BLACK RIGHT-POINTING TRIANGLE}\ufe0f": EmojiB("<:forward:816458167835820093>", menus.Last(0), "Goes to the next page."), "\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}\ufe0f": EmojiB( "<:forward2:816457685905440850>", menus.Last(1), "Goes to the last page.", ), "\N{BLACK SQUARE FOR STOP}\ufe0f": EmojiB("<:trash:816463111958560819>", menus.Last(4), "Remove this message."), } self.dict_emoji = def_dict_emoji for emoji in self.buttons: callback = self.buttons[emoji].action if emoji.name not in self.dict_emoji: continue new_but = self.dict_emoji[emoji.name] new_button = menus.Button(new_but.emoji, callback, position=new_but.position) del self.dict_emoji[emoji.name] self.dict_emoji[new_but.emoji] = new_but self.add_button(new_button) self.remove_button(emoji)
class PaginatedHelpCommand(menus.MenuPages): """ Paginated help command. """ @menus.button('\U00002139', position=menus.Last(2)) async def on_info(self, _): embed = discord.Embed(title="How to Use the Paginator", color=self.ctx.bot.embed_colour) embed.add_field( name="Add and Remove Reactions to Navigate the Help Menu:", value="➡️ next page\n" "⬅️ previous page\n" "⏮️ first page\n" "⏭️ last page\n" "ℹ️ shows this message\n" "❔ shows how to read the bot's signature\n" "⏹️ closes the paginator") embed.set_thumbnail(url=self.ctx.bot.user.avatar_url) embed.set_footer( text= f"You were on page {self.current_page + 1} before this message.") await self.message.edit(embed=embed) @menus.button('\U00002754', position=menus.Last(3)) async def on_question_mark(self, _): embed = discord.Embed( title="How to read the Bot's Signature", description="`<argument>`: required argument\n" "`[argument]`: optional argument (These arguments will usually have an '=' followed by their default value.)\n" "`argument...`: multiple arguments can be provided", color=self.ctx.bot.embed_colour) embed.set_thumbnail(url=self.ctx.bot.user.avatar_url) embed.set_footer( text= f"You were on page {self.current_page + 1} before this message.") await self.message.edit(embed=embed) @menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f', position=menus.Last(4)) async def end_menu(self, _): self.stop()
class Game(menus.Menu): name = 'Boggle' footer = None def __init__(self, *, size=ORIGINAL, **kwargs): self.board = Board(size=size) self.setup() super().__init__(**kwargs) @property def state(self): state = '' for row in range(self.board.size): emoji = [] for column in range(self.board.size): emoji.append(LETTERS_EMOJI[self.board.columns[column][row]]) state = ' '.join(emoji) + '\n' + state return discord.Embed(title=self.name, description=state).set_footer(text=self.footer) def setup(self): raise NotImplementedError async def send_initial_message(self, ctx, channel): return await channel.send( content='Boggle game started, you have 3 minutes!', embed=self.state) async def start(self, *args, **kwargs): await super().start(*args, **kwargs) # await self.bot.loop.run_in_executor(None, lambda: self.board.legal_words) async def finalize(self, timed_out): self.bot.dispatch('boggle_game_complete', self.message.channel) def get_points(self, words: List[str]) -> int: return self.board.total_points(words) def check_word(self, word: str) -> bool: return self.board.is_legal(word) async def check_message(self, message: discord.Message): raise NotImplementedError @menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f', position=menus.Last(0)) async def cancel(self, payload): await self.message.edit(content='Game Cancelled.') self.stop()
class HelpPages(MenuPages): def __init__(self, source): super().__init__(source, delete_message_after=True, timeout=60.0) @menus.button('\N{INFORMATION SOURCE}\ufe0f', position=menus.Last(3)) async def show_help(self, payload): """Shows this message""" embed = discord.Embed(title='Paginator help', description='Hello! Welcome to the help page.') messages = [] for (emoji, button) in self.buttons.items(): messages.append(f'{emoji}: {button.action.__doc__}') embed.add_field(name='What are these reactions for?', value='\n'.join(messages), inline=False) await self.message.edit(content=None, embed=embed)
class GelbooruMenuPages(dmenus.MenuPages): def __init__(self, source, **kwargs): super().__init__(source, **kwargs) @dmenus.button("\N{LABEL}", position=dmenus.Last(3)) async def show_tags(self, payload) -> None: """Shows all tags for this post""" embed = self.message.embeds[0] if "See all tags" in embed.description: return all_tags = "\n".join(self.source.entries[self.current_page].tags) link = await helpers.haste(self.ctx.bot.aiosession, f"All Tags:\n{all_tags}") embed.description = ( f"{embed.description[:-22]} [See all tags]({link})") await self.message.edit(embed=embed)
class DeleteNotes(menus.MenuPages): def __init__(self, source): super().__init__(source=source, timeout=60, delete_message_after=True) async def finalize(self, timed_out): try: if timed_out: await self.message.clear_reactions() else: await self.message.delete() except discord.HTTPException: pass @menus.button('<:cancel:851278899270909993>', position=menus.Last(0)) async def on_cancel(self, payload): self.stop()
def __init__(self, **kwargs): super().__init__(**kwargs) self.selection = None def action_maker(selection_num): async def action(_, payload): page = await self.source.get_page(self.current_page) self.selection = page[selection_num - 1] self.stop() return action for i in range(1, self.source.per_page + 1): button = menus.Button(NUMBER_TO_EMOJI[i], action_maker(i), position=menus.Last(1)) self.add_button(button)
class HelpPaginatorMenu(InfoMenuPages): def __init__(self, help_command, ctx, source, **kwargs): super().__init__(source, clear_reactions_after=True, check_embeds=True, **kwargs) self.help_command = help_command self.total = len(source.entries) self.ctx = ctx self.is_bot = False @menus.button("\N{WHITE QUESTION MARK ORNAMENT}", position=menus.Last(5)) async def show_bot_help(self, payload) -> None: """shows how to use the bot""" embed = discord.Embed(color=discord.Color.blurple()) embed.title = 'Using the bot' embed.description = 'Hello! Welcome to the help page.' entries = ( ('<argument>', 'This means the argument is __**required**__.'), ('[argument]', 'This means the argument is __**optional**__.'), ('[A|B]', 'This means it can be __**either A or B**__.'), ('[argument...]', 'This means you can have multiple arguments.\n' 'Now that you know the basics, it should be noted that...\n' '__**You do not type in the brackets!**__')) embed.add_field(name='How do I use this bot?', value='Reading the bot signature is pretty simple.') for name, value in entries: embed.add_field(name=name, value=value, inline=False) embed.set_footer( text=f'We were on page {self.current_page + 1} before this message.' ) await self.message.edit(embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page()) async def paginate(self, **kwargs) -> None: await super().start(self.ctx, **kwargs)
class ListItem(menus.MenuPages): def __init__(self, data, ctx, desc): super().__init__(source=MySource(data), clear_reactions_after=True) self.data = data self.sort = "time" self.ctx = ctx self.desc = desc @menus.button('\U0001f503', position=menus.Last(1.2)) async def sort_by(self, _): """Lets you sort the results """ sort = ['time', 'quantity', 'price'] sort_id = sort.index(self.sort) if sort_id == 2: self.sort = 'time' else: self.sort = sort[sort_id + 1] data = sorted(self.data, key=lambda x: x[self.sort]) await self.change_source(source=MySource(data))
class HelpMenu(Pages): def __init__(self, source): super().__init__(source) @menus.button("\N{WHITE QUESTION MARK ORNAMENT}", position=menus.Last(5)) async def show_bot_help(self, payload): """shows how to use the bot""" embed = discord.Embed(title="Using the bot", colour=color.invis(self)) embed.title = "Using the bot" embed.description = "Hello! Welcome to the help page." entries = ( ("<argument>", "This means the argument is __**required**__."), ("[argument]", "This means the argument is __**optional**__."), ("[A|B]", "This means that it can be __**either A or B**__."), ( "[argument...]", "This means you can have multiple arguments.\n" "Now that you know the basics, it should be noted that...\n" "__**You do not type in the brackets!**__", ), ) embed.add_field( name="How do I use this bot?", value="Reading the bot signature is pretty simple.", ) for name, value in entries: embed.add_field(name=name, value=value, inline=False) embed.set_footer( text=f"We were on page {self.current_page + 1} before this message." ) await self.message.edit(embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page())
class HelpMenu(ViewMenuPages): @menus.button("🔢", position=menus.Last(1)) async def select_page(self, payload): channel = self.bot.get_channel(payload.channel_id) i = await channel.send("Please enter the page you want to skip to:", delete_after=30) def check(msg): return (msg.channel.id == payload.channel_id and msg.author.id == self.ctx.author.id) try: message = await self.bot.wait_for("message", check=check, timeout=30) except Exception: # timeout return else: await i.delete() await message.delete() try: msg_int = int(message.content) await self.show_checked_page(msg_int - 1) except Exception: await self.ctx.send("Please enter a valid number...", delete_after=30) async def show_checked_page(self, page_number): max_pages = self._source.get_max_pages() try: if max_pages is None: # If it doesn't give maximum pages, it cannot be checked await self.show_page(page_number) elif max_pages > page_number >= 0: await self.show_page(page_number) except IndexError: # An error happened that can be handled, so ignore it. await self.ctx.send("This page doesn't exist...", delete_after=30)
class HelpMenu(RoboPages): def __init__(self, source): super().__init__(source) @menus.button("\N{WHITE QUESTION MARK ORNAMENT}", position=menus.Last(5)) async def show_bot_help(self, payload): """Shows how to use the bot.""" embed = discord.Embed(colour=discord.Colour.blurple()) embed.title = "How Interpret The Help Pages" entries = ( ("<argument>", "This means the argument is __**required**__."), ("[argument]", "This means the argument is __**optional**__."), ("[A|B]", "This means that it can be __**either A or B**__."), ( "[argument...]", "This means you can have multiple arguments.\n" "Now that you know the basics, it should be noted that...\n" "__**You do not type in the brackets!**__", ), ) for name, value in entries: embed.add_field(name=name, value=value, inline=False) embed.set_footer( text=f"We were on page {self.current_page + 1} before this message." ) await self.message.edit(embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) try: await self.show_page(self.current_page) except discord.errors.NotFound: return self.bot.loop.create_task(go_back_to_current_page())
def __init__( self, *, main_source: menus.ListPageSource, extra_sources: Union[Tuple[menus.ListPageSource], tuple] = (), **options, ): self.initial_source = main_source super().__init__(self.initial_source, delete_message_after=True, timeout=60, **options) self.extra_sources = {} for index, source in enumerate(extra_sources, 3): self.extra_sources[source.emoji] = source position = menus.Last(index) button = menus.Button(source.emoji, self._extra_source_button, position=position) self.add_button(button)
class HelpMenu(Pages): def __init__(self, source): super().__init__(source) @menus.button('\N{WHITE QUESTION MARK ORNAMENT}', position=menus.Last(5)) async def show_bot_help(self, payload): """shows how to use the bot""" embed = discord.Embed(title='Comment utiliser le bot', colour=discord.Colour.blurple()) embed.title = 'Comment utiliser le bot' embed.description = "Salut ! Bienvenue sur l'aide du bot." entries = ( ('<argument>', "Cela signifie que l'argument est __**requis**__."), ('[argument]', "Cela signifie que l'argument est __**optionel**__."), ('[A|B]', "Cela signifie qu'il peut s'agir de __**A ou B**__."), ('[argument...]', 'Cela signifie que vous pouvez avoir plusieurs arguments.\n' \ 'Maintenant que vous connaissez les bases, il faut noter que ...\n' \ '__**Vous ne tapez pas entre parenthèses!**__') ) embed.add_field(name="Comment j'utilise ce bot ?", value='Comprendre le bot est assez simple.') for name, value in entries: embed.add_field(name=name, value=value, inline=False) embed.set_footer( text=f'Nous somme à la page {self.current_page + 1} .') await self.message.edit(embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page())
class HelpMenu(RoboPages): def __init__(self, source): super().__init__(source) @menus.button('\N{WHITE QUESTION MARK ORNAMENT}', position=menus.Last(5)) async def show_bot_help(self, payload): """shows how to use the bot""" embed = discord.Embed(title='Using the bot', colour=discord.Colour.from_rgb(48, 162, 242)) embed.title = 'Using the bot' embed.description = 'Hello! Welcome to the help page.' entries = ( ('<argument>', 'This means the argument is __**required**__.'), ('[argument]', 'This means the argument is __**optional**__.'), ('[A|B]', 'This means that it can be __**either A or B**__.'), ('[argument...]', 'This means you can have multiple arguments.\n' 'Now that you know the basics, it should be noted that...\n' '__**You do not type in the brackets!**__')) embed.add_field(name='How do I use this bot?', value='Reading the bot signature is pretty simple.') for name, value in entries: embed.add_field(name=name, value=value, inline=False) embed.set_footer( text=f'We were on page {self.current_page + 1} before this message.' ) await self.message.edit(embed=embed) async def go_back_to_current_page(): await asyncio.sleep(30.0) await self.show_page(self.current_page) self.bot.loop.create_task(go_back_to_current_page())
class HelpMenu(NavMenuPages): @menus.button('\N{WHITE QUESTION MARK ORNAMENT}', position=menus.Last(5)) async def using_the_bot(self, payload): """shows how to use the bot""" embed = discord.Embed( title='Using the bot', description='Hello! Welcome to the help page' ).add_field( name='How do I use the bot?', value='Reading the bot signature is pretty simple.', inline=False ).add_field( name='<argument>', value='This means the argument is required.', inline=False ).add_field( name='[argument]', value='This means the argument is optional.', inline=False ).add_field( name='[A|B]', value='This means that it can be either A or B.', inline=False ).add_field( name='[argument...]', value='This means you can have multiple arguments.', inline=False ).add_field( name='Now that you know the basics, it should be noted that...', value='You do not type in the brackets!', inline=False ).set_footer( text=f'We were on page {self.current_page + 1} before this message.' ) await self.message.edit(embed=embed) asyncio.create_task(self.go_back_to_current_page())
class InfoMenuPages(ViewMenuPages): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @menus.button("\N{INFORMATION SOURCE}\ufe0f", position=menus.Last(3)) async def info_page(self, payload) -> None: """shows you this message""" messages = ['Welcome to the interactive paginator!\n'] messages.append('This interactively allows you to see pages of text by navigating with ' 'reactions. They are as follows:\n') for emoji, button in self.buttons.items(): messages.append(f'{str(emoji)} {button.action.__doc__}') embed = discord.Embed(color=discord.Color.blurple()) embed.clear_fields() embed.description = '\n'.join(messages) embed.set_footer(text=f"We were on page {self.current_page + 1} before this message.") await self.message.edit(content=None, embed=embed) async def go_back_to_current_page(): await asyncio.sleep(60.0) await self.show_page(self.current_page) self.ctx.bot.loop.create_task(go_back_to_current_page())
class MafiaMenu(menus.MenuPages): amount_of_players = 0 amount_of_mafia = 0 @property def allowed_mafia(self): """The amount of mafia roles allowed to add""" # Subtract an extra one, because one of them HAS to be Godfather return (self.amount_of_mafia - sum([v for k, v in self.source.entries if k.is_mafia]) - 1) @property def allowed_non_mafia(self): """The amount of non-mafia roles allowed to add""" return (self.amount_of_players - self.amount_of_mafia - sum([v for k, v in self.source.entries if not k.is_mafia])) async def finalize(self, timed_out): if timed_out: raise asyncio.TimeoutError def should_add_reactions(self): return True def _should_not_paginate(self): return not self._source.is_paginating() def _should_skip_0(self): return len(self._get_pages(self.current_page)) < 1 def _should_skip_1(self): return len(self._get_pages(self.current_page)) < 2 def _should_skip_2(self): return len(self._get_pages(self.current_page)) < 3 def _should_skip_3(self): return len(self._get_pages(self.current_page)) < 4 def _should_skip_4(self): return len(self._get_pages(self.current_page)) < 5 def _get_pages(self, page_number): base = page_number * self.source.per_page return self.source.entries[base:base + self.source.per_page] @menus.button( "0\N{variation selector-16}\N{combining enclosing keycap}", skip_if=_should_skip_0, ) async def _0_click_passthrough(self, payload): await self.handle_click(payload) @menus.button( "1\N{variation selector-16}\N{combining enclosing keycap}", skip_if=_should_skip_1, ) async def _1_click_passthrough(self, payload): await self.handle_click(payload) @menus.button( "2\N{variation selector-16}\N{combining enclosing keycap}", skip_if=_should_skip_2, ) async def _2_click_passthrough(self, payload): await self.handle_click(payload) @menus.button( "3\N{variation selector-16}\N{combining enclosing keycap}", skip_if=_should_skip_3, ) async def _3_click_passthrough(self, payload): await self.handle_click(payload) @menus.button( "4\N{variation selector-16}\N{combining enclosing keycap}", skip_if=_should_skip_4, ) async def _4_click_passthrough(self, payload): await self.handle_click(payload) async def handle_click(self, payload): # Get the number that was clicked num = int(str(payload.emoji)[0]) index = num + self.source.per_page * self.current_page role, current_num = self.source.entries[index] # Only allow up to how many members can remain if role.is_mafia: amt_allowed = self.allowed_mafia + current_num else: amt_allowed = self.allowed_non_mafia + current_num # Make sure to limit to the role's limit if role.limit: amt_allowed = min(role.limit, amt_allowed) # No need to get answer if can't add any anymore if not amt_allowed: await self.ctx.send("Cannot add any more of that role", delete_after=5) return msg = await self.ctx.send( f"{role.__name__}: How many? 0 - {amt_allowed}") answer = await self.ctx.bot.wait_for("message", check=self.ctx.bot.min_max_check( self.ctx, 0, amt_allowed)) # Delete and set answer self.source.entries[index] = (role, int(answer.content)) await self.ctx.channel.delete_messages([msg, answer]) # Refresh await self.show_page(self.current_page) @menus.button( "\N{BLACK LEFT-POINTING TRIANGLE}\ufe0f", position=menus.First(1), skip_if=_should_not_paginate, ) async def go_to_previous_page(self, payload): """go to the previous page""" await self.show_checked_page(self.current_page - 1) @menus.button( "\N{BLACK RIGHT-POINTING TRIANGLE}\ufe0f", position=menus.Last(0), skip_if=_should_not_paginate, ) async def go_to_next_page(self, payload): """go to the next page""" await self.show_checked_page(self.current_page + 1) @menus.button( "\N{BLACK SQUARE FOR STOP}\ufe0f", position=menus.Last(2), skip_if=lambda x: True, ) async def stop_pages(self, payload): pass @menus.button("\N{WHITE HEAVY CHECK MARK}", position=menus.Last(2)) async def accept_setings(self, payload): self.stop()
class Game(menus.Menu): ranked = True async def start(self, ctx, opponent, *, channel=None, wait=False): if random.random() < 0.5: self.players = (ctx.author, opponent) else: self.players = (opponent, ctx.author) for player in self.players: async with ctx.db as connection: await Ranking.insert(connection, ignore_on_conflict=True, user_id=player.id) self.board = Board() self.current_player = 0 # Setup buttons for emoji in REGIONAL_INDICATOR_EMOJI: self.add_button(menus.Button(emoji, self.place)) await super().start(ctx, channel=channel, wait=wait) async def send_initial_message(self, ctx, channel): current_player = self.current_player return await channel.send( content= f"{self.players[current_player].mention}'s ({DISCS[current_player]}) turn!", embed=self.state, ) def reaction_check(self, payload): if payload.message_id != self.message.id: return False current_player = self.current_player if payload.user_id != self.players[current_player].id: return False return payload.emoji in self.buttons @property def state(self): return discord.Embed(description=self.board.state) async def _end_game(self, resignation: int = None): winner: Optional[int] if resignation is not None: winner = not resignation content = f"Game cancelled by {self.players[resignation].mention} ({DISCS[resignation]})!" elif self.board.winner is not None: winner = self.board.winner content = f"{self.players[self.board.winner].mention} ({DISCS[self.board.winner]}) Wins!" else: winner = None content = "Draw!" await self.message.edit(content=f"Game Over! {content}", embed=self.state) self.stop() if not self.ranked: return # Calulate new ELO async with self.ctx.db as connection: await Games.insert( connection=connection, players=[p.id for p in self.players], winner=winner, finished=resignation is None, ) record_1 = await Ranking.fetch_row(connection, user_id=self.players[0].id) record_2 = await Ranking.fetch_row(connection, user_id=self.players[1].id) R1 = 10**(record_1["ranking"] / 400) R2 = 10**(record_2["ranking"] / 400) E1 = R1 / (R1 + R2) E2 = R2 / (R1 + R2) S1 = self.board.winner == 0 if self.board.winner is not None else 0.5 S2 = self.board.winner == 1 if self.board.winner is not None else 0.5 r1 = record_1["ranking"] + K * (S1 - E1) r2 = record_2["ranking"] + K * (S2 - E2) await Ranking.update_record( connection, record_1, ranking=round(r1), games=record_1["games"] + 1, wins=record_1["wins"] + self.board.winner == 0, losses=record_1["losses"] + self.board.winner != 0, ) await Ranking.update_record( connection, record_2, ranking=round(r2), games=record_2["games"] + 1, wins=record_2["wins"] + self.board.winner == 1, losses=record_2["losses"] + self.board.winner != 1, ) async def place(self, payload): column = REGIONAL_INDICATOR_EMOJI.index(str(payload.emoji)) if column not in self.board.legal_moves: return ... self.board = self.board.move(column, DISCS[self.current_player]) if self.board.game_over: return await self._end_game() self.current_player = not self.current_player await self.message.edit( content= f"{self.players[self.current_player].mention}'s ({DISCS[self.current_player]}) turn!", embed=self.state, ) @menus.button("\N{BLACK SQUARE FOR STOP}\ufe0f", position=menus.Last(0)) async def cancel(self, payload): await self._end_game(resignation=self.current_player)