From 889164c1fb7c131e0b135d7dd0de9d051e0605ff Mon Sep 17 00:00:00 2001 From: Vein05 <72024472+Vein05@users.noreply.github.com> Date: Sat, 29 May 2021 01:16:50 +0545 Subject: [PATCH 1/8] fixed the timeout thing --- disputils/pagination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/disputils/pagination.py b/disputils/pagination.py index 6070dcd..11cd034 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -56,7 +56,7 @@ def formatted_pages(self) -> List[discord.Embed]: ) return pages - async def run(self, users: List[discord.User], channel: discord.TextChannel = None): + async def run(self, users: List[discord.User], channel: discord.TextChannel = None, timeout = 100): """ Runs the paginator. @@ -101,7 +101,7 @@ def check(r: discord.Reaction, u: discord.User): while True: try: reaction, user = await self._client.wait_for( - "reaction_add", check=check, timeout=100 + "reaction_add", check=check, timeout=timeout ) except asyncio.TimeoutError: if not isinstance( From dfbc7f87c05ae11af5a16577b66a4fe68731d534 Mon Sep 17 00:00:00 2001 From: Vein05 <72024472+Vein05@users.noreply.github.com> Date: Sat, 29 May 2021 01:50:41 +0545 Subject: [PATCH 2/8] Fixedtimeout, added new para timeout --- disputils/pagination.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/disputils/pagination.py b/disputils/pagination.py index 11cd034..1b2b3ef 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -56,7 +56,7 @@ def formatted_pages(self) -> List[discord.Embed]: ) return pages - async def run(self, users: List[discord.User], channel: discord.TextChannel = None, timeout = 100): + async def run(self, users: List[discord.User], channel: discord.TextChannel = None, timeout: int = 100): """ Runs the paginator. @@ -215,7 +215,7 @@ def __init__( ) async def run( - self, channel: discord.TextChannel = None, users: List[discord.User] = None + self, channel: discord.TextChannel = None, users: List[discord.User] = None, timeout: int = 100 ): """ Runs the paginator. @@ -240,4 +240,4 @@ async def run( if self.message is None and channel is None: channel = self._ctx.channel - await super().run(users, channel) + await super().run(users, channel, timeout) From 08fc370ac6b8d269370283802542444e8f7c08e9 Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Fri, 28 May 2021 21:50:52 +0200 Subject: [PATCH 3/8] add timout to docstings and reformat with black --- disputils/pagination.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/disputils/pagination.py b/disputils/pagination.py index 1b2b3ef..2ed2eba 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -56,7 +56,12 @@ def formatted_pages(self) -> List[discord.Embed]: ) return pages - async def run(self, users: List[discord.User], channel: discord.TextChannel = None, timeout: int = 100): + async def run( + self, + users: List[discord.User], + channel: discord.TextChannel = None, + timeout: int = 100, + ): """ Runs the paginator. @@ -70,6 +75,10 @@ async def run(self, users: List[discord.User], channel: discord.TextChannel = No The text channel to send the embed to. Must only be specified if `self.message` is `None`. + :type timeout: int + :param timeout: + Seconds to wait until stopping to listen for user interaction. + :return: None """ @@ -215,7 +224,10 @@ def __init__( ) async def run( - self, channel: discord.TextChannel = None, users: List[discord.User] = None, timeout: int = 100 + self, + channel: discord.TextChannel = None, + users: List[discord.User] = None, + timeout: int = 100, ): """ Runs the paginator. @@ -231,6 +243,10 @@ async def run( Default is the context author. Passing an empty list will grant access to all users. (Not recommended.) + :type timeout: int + :param timeout: + Seconds to wait until stopping to listen for user interaction. + :return: None """ From d0e52a87144a3048d691c51776f448c0ac8f1118 Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Fri, 28 May 2021 21:55:15 +0200 Subject: [PATCH 4/8] add timeout parameter for confirmation --- disputils/confirmation.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/disputils/confirmation.py b/disputils/confirmation.py index 06c4c68..cddfbea 100644 --- a/disputils/confirmation.py +++ b/disputils/confirmation.py @@ -35,6 +35,7 @@ async def confirm( user: discord.User, channel: discord.TextChannel = None, hide_author: bool = False, + timeout: int = 20 ) -> bool or None: """ Run the confirmation. @@ -52,6 +53,10 @@ async def confirm( :param hide_author: Whether or not the ``user`` should be set as embed author. :type hide_author: bool, optional + :type timeout: int + :param timeout: + Seconds to wait until stopping to listen for user interaction. + :return: True when it's been confirmed, otherwise False. Will return None when a timeout occurs. :rtype: :class:`bool`, optional @@ -80,7 +85,7 @@ async def confirm( check=lambda r, u: (r.message.id == msg.id) and (u.id == user.id) and (r.emoji in self.emojis), - timeout=20, + timeout=timeout, ) except asyncio.TimeoutError: self._confirmed = None @@ -114,6 +119,7 @@ async def confirm( user: discord.User = None, channel: discord.TextChannel = None, hide_author: bool = False, + timeout: int = 20 ) -> bool or None: if user is None: @@ -122,4 +128,4 @@ async def confirm( if self.message is None and channel is None: channel = self._ctx.channel - return await super().confirm(text, user, channel, hide_author=hide_author) + return await super().confirm(text, user, channel, hide_author, timeout) From 427bd244b6953cdfbf34c7d7351bc3d2dbac075a Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Sat, 29 May 2021 19:34:22 +0200 Subject: [PATCH 5/8] fix message editing; add kwargs - fixes #18, fixes #20, should also close #32 - add kwargs , and for pagination and multiple choice - other minor fixes and code formatting --- disputils/abc.py | 27 ++++++++++++++++++++++++--- disputils/confirmation.py | 21 +++++++-------------- disputils/multiple_choice.py | 22 +++++++++++----------- disputils/pagination.py | 32 ++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/disputils/abc.py b/disputils/abc.py index 6d3e530..f5660fa 100644 --- a/disputils/abc.py +++ b/disputils/abc.py @@ -1,5 +1,5 @@ from abc import ABC -from discord import Message, Embed +from discord import Message, Embed, TextChannel, errors from typing import Optional @@ -11,6 +11,23 @@ def __init__(self, *args, **kwargs): self.message: Optional[Message] = None self.color: hex = kwargs.get("color") or kwargs.get("colour") or 0x000000 + async def _publish(self, channel: Optional[TextChannel], **kwargs) -> TextChannel: + if channel is None and self.message is None: + raise TypeError( + "Missing argument. You need to specify a target channel or message." + ) + + if channel is None: + try: + await self.message.edit(**kwargs) + except errors.NotFound: + self.message = None + + if self.message is None: + self.message = await channel.send(**kwargs) + + return self.message.channel + async def quit(self, text: str = None): """ Quit the dialog. @@ -23,9 +40,13 @@ async def quit(self, text: str = None): if text is None: await self.message.delete() + self.message = None else: - await self.message.edit(content=text, embed=None) - await self.message.clear_reactions() + await self.display(text) + try: + await self.message.clear_reactions() + except errors.Forbidden: + pass async def update(self, text: str, color: hex = None, hide_author: bool = False): """ diff --git a/disputils/confirmation.py b/disputils/confirmation.py index cddfbea..84a9303 100644 --- a/disputils/confirmation.py +++ b/disputils/confirmation.py @@ -35,7 +35,7 @@ async def confirm( user: discord.User, channel: discord.TextChannel = None, hide_author: bool = False, - timeout: int = 20 + timeout: int = 20, ) -> bool or None: """ Run the confirmation. @@ -68,13 +68,8 @@ async def confirm( self._embed = emb - if channel is None and self.message is not None: - channel = self.message.channel - elif channel is None: - raise TypeError("Missing argument. You need to specify a target channel.") - - msg = await channel.send(embed=emb) - self.message = msg + await self._publish(channel, embed=emb) + msg = self.message for emoji in self.emojis: await msg.add_reaction(emoji) @@ -90,17 +85,15 @@ async def confirm( except asyncio.TimeoutError: self._confirmed = None return + else: + self._confirmed = self.emojis[reaction.emoji] + return self._confirmed finally: try: await msg.clear_reactions() except discord.Forbidden: pass - confirmed = self.emojis[reaction.emoji] - - self._confirmed = confirmed - return confirmed - class BotConfirmation(Confirmation): def __init__( @@ -119,7 +112,7 @@ async def confirm( user: discord.User = None, channel: discord.TextChannel = None, hide_author: bool = False, - timeout: int = 20 + timeout: int = 20, ) -> bool or None: if user is None: diff --git a/disputils/multiple_choice.py b/disputils/multiple_choice.py index b6f887f..c9ad348 100644 --- a/disputils/multiple_choice.py +++ b/disputils/multiple_choice.py @@ -122,6 +122,9 @@ async def run( - message :class:`discord.Message` - timeout :class:`int` (seconds, default: ``60``), - closable :class:`bool` (default: ``True``) + - text :class:`str`: Text to appear in the message. + - timeout_msg :class:`str`: Text to appear when dialog times out. + - quit_msg :class:`str`: Text to appear when user quits the dialog. :return: selected option and used :class:`discord.Message` :rtype: tuple[:class:`str`, :class:`discord.Message`] @@ -134,18 +137,11 @@ async def run( timeout = kwargs.get("timeout", 60) closable: bool = kwargs.get("closable", True) - config_embed = self.embed + publish_kwargs = {"embed": self.embed} + if "text" in kwargs: + publish_kwargs["content"] = kwargs["text"] - if channel is not None: - self.message = await channel.send(embed=config_embed) - elif self.message is not None: - await self.message.clear_reactions() - await self.message.edit(content=self.message.content, embed=config_embed) - else: - raise TypeError( - "Missing argument. " - + "You need to specify either 'channel' or 'message' as a target." - ) + await self._publish(channel, **publish_kwargs) for emoji in self._emojis: await self.message.add_reaction(emoji) @@ -173,10 +169,14 @@ def check(r, u): ) except asyncio.TimeoutError: self._choice = None + if "timeout_msg" in kwargs: + await self.quit(kwargs["timeout_msg"]) return None, self.message if reaction.emoji == self.close_emoji: self._choice = None + if "quit_msg" in kwargs: + await self.quit(kwargs["quit_msg"]) return None, self.message index = self._emojis.index(reaction.emoji) diff --git a/disputils/pagination.py b/disputils/pagination.py index 2ed2eba..6c29218 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -61,6 +61,7 @@ async def run( users: List[discord.User], channel: discord.TextChannel = None, timeout: int = 100, + **kwargs, ): """ Runs the paginator. @@ -79,21 +80,24 @@ async def run( :param timeout: Seconds to wait until stopping to listen for user interaction. + :param kwargs: + - text :class:`str`: Text to appear in the pagination message. + - timeout_msg :class:`str`: Text to appear when pagination times out. + - quit_msg :class:`str`: Text to appear when user quits the dialog. + :return: None """ - if channel is None and self.message is not None: - channel = self.message.channel - elif channel is None: - raise TypeError("Missing argument. You need to specify a target channel.") - self._embed = self.pages[0] + text = kwargs.get("text") if len(self.pages) == 1: # no pagination needed in this case - self.message = await channel.send(embed=self._embed) + await self._publish(channel, content=text, embed=self._embed) return - self.message = await channel.send(embed=self.formatted_pages[0]) + channel = await self._publish( + channel, content=text, embed=self.formatted_pages[0] + ) current_page_index = 0 for emoji in self.control_emojis: @@ -120,6 +124,8 @@ def check(r: discord.Reaction, u: discord.User): await self.message.clear_reactions() except discord.Forbidden: pass + if "timeout_msg" in kwargs: + await self.display(kwargs["timeout_msg"]) return emoji = reaction.emoji @@ -146,10 +152,10 @@ def check(r: discord.Reaction, u: discord.User): load_page_index = max_index else: - await self.message.delete() + await self.quit(kwargs.get("quit_msg")) return - await self.message.edit(embed=self.formatted_pages[load_page_index]) + await self.display(text, self.formatted_pages[load_page_index]) if not isinstance(channel, discord.channel.DMChannel) and not isinstance( channel, discord.channel.GroupChannel ): @@ -228,6 +234,7 @@ async def run( channel: discord.TextChannel = None, users: List[discord.User] = None, timeout: int = 100, + **kwargs, ): """ Runs the paginator. @@ -247,6 +254,11 @@ async def run( :param timeout: Seconds to wait until stopping to listen for user interaction. + :param kwargs: + - text :class:`str`: Text to appear in the pagination message. + - timeout_msg :class:`str`: Text to appear when pagination times out. + - quit_msg :class:`str`: Text to appear when user quits the dialog. + :return: None """ @@ -256,4 +268,4 @@ async def run( if self.message is None and channel is None: channel = self._ctx.channel - await super().run(users, channel, timeout) + await super().run(users, channel, timeout, **kwargs) From 5f5e0df9df4ffd0c916989d3ad8419526a2cc6c7 Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Sat, 29 May 2021 20:40:34 +0200 Subject: [PATCH 6/8] change: listen to instead of This resolves #21: Dialogs becoming unresponsive after a while, when message is not in cache anymore. --- disputils/confirmation.py | 12 ++++++------ disputils/multiple_choice.py | 20 +++++++++++--------- disputils/pagination.py | 20 ++++++++++---------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/disputils/confirmation.py b/disputils/confirmation.py index 84a9303..023ca10 100644 --- a/disputils/confirmation.py +++ b/disputils/confirmation.py @@ -75,18 +75,18 @@ async def confirm( await msg.add_reaction(emoji) try: - reaction, user = await self._client.wait_for( - "reaction_add", - check=lambda r, u: (r.message.id == msg.id) - and (u.id == user.id) - and (r.emoji in self.emojis), + reaction = await self._client.wait_for( + "raw_reaction_add", + check=lambda r: (r.message_id == msg.id) + and (r.user_id == user.id) + and (str(r.emoji) in self.emojis), timeout=timeout, ) except asyncio.TimeoutError: self._confirmed = None return else: - self._confirmed = self.emojis[reaction.emoji] + self._confirmed = self.emojis[str(reaction.emoji)] return self._confirmed finally: try: diff --git a/disputils/multiple_choice.py b/disputils/multiple_choice.py index c9ad348..24141b5 100644 --- a/disputils/multiple_choice.py +++ b/disputils/multiple_choice.py @@ -149,23 +149,25 @@ async def run( if closable: await self.message.add_reaction(self.close_emoji) - def check(r, u): - res = (r.message.id == self.message.id) and u.id != self._client.user.id + def check(r: discord.RawReactionActionEvent): + res = ( + r.message_id == self.message.id + ) and r.user_id != self._client.user.id if users is not None: - res = res and (u.id in [_u.id for _u in users]) + res = res and (r.user_id in [_u.id for _u in users]) - is_valid_emoji = r.emoji in self._emojis + is_valid_emoji = str(r.emoji) in self._emojis if closable: - is_valid_emoji = is_valid_emoji or r.emoji == self.close_emoji + is_valid_emoji = is_valid_emoji or str(r.emoji) == self.close_emoji res = res and is_valid_emoji return res try: - reaction, user = await self._client.wait_for( - "reaction_add", check=check, timeout=timeout + reaction = await self._client.wait_for( + "raw_reaction_add", check=check, timeout=timeout ) except asyncio.TimeoutError: self._choice = None @@ -173,13 +175,13 @@ def check(r, u): await self.quit(kwargs["timeout_msg"]) return None, self.message - if reaction.emoji == self.close_emoji: + if str(reaction.emoji) == self.close_emoji: self._choice = None if "quit_msg" in kwargs: await self.quit(kwargs["quit_msg"]) return None, self.message - index = self._emojis.index(reaction.emoji) + index = self._emojis.index(str(reaction.emoji)) self._choice = self.options[index] return self._choice, self.message diff --git a/disputils/pagination.py b/disputils/pagination.py index 6c29218..bc77576 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -103,18 +103,20 @@ async def run( for emoji in self.control_emojis: await self.message.add_reaction(emoji) - def check(r: discord.Reaction, u: discord.User): - res = (r.message.id == self.message.id) and (r.emoji in self.control_emojis) + def check(r: discord.RawReactionActionEvent): + res = (r.message_id == self.message.id) and ( + str(r.emoji) in self.control_emojis + ) if len(users) > 0: - res = res and u.id in [u1.id for u1 in users] + res = res and r.user_id in [u1.id for u1 in users] return res while True: try: - reaction, user = await self._client.wait_for( - "reaction_add", check=check, timeout=timeout + reaction = await self._client.wait_for( + "raw_reaction_add", check=check, timeout=timeout ) except asyncio.TimeoutError: if not isinstance( @@ -128,7 +130,7 @@ def check(r: discord.Reaction, u: discord.User): await self.display(kwargs["timeout_msg"]) return - emoji = reaction.emoji + emoji = str(reaction.emoji) max_index = len(self.pages) - 1 # index for the last page if emoji == self.control_emojis[0]: @@ -156,11 +158,9 @@ def check(r: discord.Reaction, u: discord.User): return await self.display(text, self.formatted_pages[load_page_index]) - if not isinstance(channel, discord.channel.DMChannel) and not isinstance( - channel, discord.channel.GroupChannel - ): + if reaction.member: try: - await self.message.remove_reaction(reaction, user) + await self.message.remove_reaction(emoji, reaction.member) except discord.Forbidden: pass From c0efe8b3ec1139cc21a8adc51871ae7df7a449e5 Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Sat, 29 May 2021 22:43:43 +0200 Subject: [PATCH 7/8] improve control emoji customization resolves #31 --- disputils/__init__.py | 2 +- disputils/pagination.py | 49 +++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/disputils/__init__.py b/disputils/__init__.py index d4647a6..a35debf 100644 --- a/disputils/__init__.py +++ b/disputils/__init__.py @@ -1,4 +1,4 @@ -from .pagination import EmbedPaginator, BotEmbedPaginator +from .pagination import EmbedPaginator, BotEmbedPaginator, ControlEmojis from .confirmation import Confirmation, BotConfirmation from .multiple_choice import MultipleChoice, BotMultipleChoice from . import abc diff --git a/disputils/pagination.py b/disputils/pagination.py index bc77576..7487382 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -2,10 +2,18 @@ from discord.ext import commands import asyncio from copy import deepcopy -from typing import List, Tuple +from typing import List, Union +from collections import namedtuple from .abc import Dialog +ControlEmojis = namedtuple( + "ControlEmojis", + ("first", "previous", "next", "last", "close"), + defaults=("⏮", "◀", "▶", "⏭", "⏹"), +) + + class EmbedPaginator(Dialog): """ Represents an interactive menu containing multiple embeds. """ @@ -15,7 +23,7 @@ def __init__( pages: [discord.Embed], message: discord.Message = None, *, - control_emojis: Tuple[str, str, str, str, str] = None, + control_emojis: Union[ControlEmojis, tuple, list] = ControlEmojis(), ): """ Initialize a new EmbedPaginator. @@ -24,8 +32,9 @@ def __init__( :param pages: A list of :class:`discord.Embed` to paginate through. :param message: An optional :class:`discord.Message` to edit. Otherwise a new message will be sent. - :param control_emojis: An option :class:`typing.Tuple` of control emojis to use, - otherwise the default will be used + :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` + containing control emojis to use, otherwise the default will be used. + A value of `None` causes a reaction to be left out. """ super().__init__() @@ -33,7 +42,29 @@ def __init__( self.pages = pages self.message = message - self.control_emojis = control_emojis or ("⏮", "◀", "▶", "⏭", "⏹") + if isinstance(control_emojis, ControlEmojis): + self.control_emojis = control_emojis + else: + control_emojis = tuple(control_emojis) + if len(control_emojis) == 1: + self.control_emojis = ControlEmojis( + None, None, None, None, control_emojis[0] + ) + elif len(control_emojis) == 2: + self.control_emojis = ControlEmojis( + None, control_emojis[0], control_emojis[1], None, None + ) + elif len(control_emojis) == 3: + self.control_emojis = ControlEmojis( + None, + control_emojis[0], + control_emojis[1], + None, + control_emojis[2], + ) + else: + control_emojis += (None,) * (5 - len(control_emojis)) + self.control_emojis = ControlEmojis(*control_emojis) @property def formatted_pages(self) -> List[discord.Embed]: @@ -101,7 +132,8 @@ async def run( current_page_index = 0 for emoji in self.control_emojis: - await self.message.add_reaction(emoji) + if emoji is not None: + await self.message.add_reaction(emoji) def check(r: discord.RawReactionActionEvent): res = (r.message_id == self.message.id) and ( @@ -213,7 +245,7 @@ def __init__( pages: [discord.Embed], message: discord.Message = None, *, - control_emojis: Tuple[str, str, str, str, str] = None, + control_emojis: Union[ControlEmojis, tuple, list] = ControlEmojis(), ): """ Initialize a new EmbedPaginator. @@ -222,6 +254,9 @@ def __init__( :param pages: A list of :class:`discord.Embed` to paginate through. :param message: An optional :class:`discord.Message` to edit. Otherwise a new message will be sent. + :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` + containing control emojis to use, otherwise the default will be used. + A value of `None` causes a reaction to be left out. """ self._ctx = ctx From 87fb7718ade79ea13914429e25ab446c2c3e9822 Mon Sep 17 00:00:00 2001 From: Linus Bartsch Date: Sat, 29 May 2021 23:06:14 +0200 Subject: [PATCH 8/8] fix paginator docstring --- disputils/pagination.py | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/disputils/pagination.py b/disputils/pagination.py index 7487382..ccd81bf 100644 --- a/disputils/pagination.py +++ b/disputils/pagination.py @@ -15,7 +15,17 @@ class EmbedPaginator(Dialog): - """ Represents an interactive menu containing multiple embeds. """ + """ + Represents an interactive menu containing multiple embeds. + + :param client: The :class:`discord.Client` to use. + :param pages: A list of :class:`discord.Embed` to paginate through. + :param message: An optional :class:`discord.Message` to edit. + Otherwise a new message will be sent. + :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` + containing control emojis to use, otherwise the default will be used. + A value of `None` causes a reaction to be left out. + """ def __init__( self, @@ -25,17 +35,6 @@ def __init__( *, control_emojis: Union[ControlEmojis, tuple, list] = ControlEmojis(), ): - """ - Initialize a new EmbedPaginator. - - :param client: The :class:`discord.Client` to use. - :param pages: A list of :class:`discord.Embed` to paginate through. - :param message: An optional :class:`discord.Message` to edit. - Otherwise a new message will be sent. - :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` - containing control emojis to use, otherwise the default will be used. - A value of `None` causes a reaction to be left out. - """ super().__init__() self._client = client @@ -239,6 +238,18 @@ def generate_sub_lists(origin_list: list, max_len: int = 25) -> List[list]: class BotEmbedPaginator(EmbedPaginator): + """ + Same as :class:`EmbedPaginator`, except for the discord.py commands extension. + + :param ctx: The :class:`discord.ext.commands.Context` to use. + :param pages: A list of :class:`discord.Embed` to paginate through. + :param message: An optional :class:`discord.Message` to edit. + Otherwise a new message will be sent. + :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` + containing control emojis to use, otherwise the default will be used. + A value of `None` causes a reaction to be left out. + """ + def __init__( self, ctx: commands.Context, @@ -247,17 +258,6 @@ def __init__( *, control_emojis: Union[ControlEmojis, tuple, list] = ControlEmojis(), ): - """ - Initialize a new EmbedPaginator. - - :param ctx: The :class:`discord.ext.commands.Context` to use. - :param pages: A list of :class:`discord.Embed` to paginate through. - :param message: An optional :class:`discord.Message` to edit. - Otherwise a new message will be sent. - :param control_emojis: :class:`ControlEmojis`, `tuple` or `list` - containing control emojis to use, otherwise the default will be used. - A value of `None` causes a reaction to be left out. - """ self._ctx = ctx super(BotEmbedPaginator, self).__init__(