mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
Add Traceback prompt (#5851)
Co-authored-by: Jakub Kuczys <me@jacken.men>
This commit is contained in:
parent
896d5e9200
commit
030607fb04
@ -52,9 +52,11 @@ from .settings_caches import (
|
|||||||
DisabledCogCache,
|
DisabledCogCache,
|
||||||
I18nManager,
|
I18nManager,
|
||||||
)
|
)
|
||||||
|
from .utils.predicates import MessagePredicate
|
||||||
from .rpc import RPCMixin
|
from .rpc import RPCMixin
|
||||||
from .tree import RedTree
|
from .tree import RedTree
|
||||||
from .utils import can_user_send_messages_in, common_filters, AsyncIter
|
from .utils import can_user_send_messages_in, common_filters, AsyncIter
|
||||||
|
from .utils.chat_formatting import box, text_to_file
|
||||||
from .utils._internal_utils import send_to_owners_with_prefix_replaced
|
from .utils._internal_utils import send_to_owners_with_prefix_replaced
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -81,6 +83,8 @@ PreInvokeCoroutine = Callable[[commands.Context], Awaitable[Any]]
|
|||||||
T_BIC = TypeVar("T_BIC", bound=PreInvokeCoroutine)
|
T_BIC = TypeVar("T_BIC", bound=PreInvokeCoroutine)
|
||||||
UserOrRole = Union[int, discord.Role, discord.Member, discord.User]
|
UserOrRole = Union[int, discord.Role, discord.Member, discord.User]
|
||||||
|
|
||||||
|
_ = i18n.Translator("Core", __file__)
|
||||||
|
|
||||||
|
|
||||||
def _is_submodule(parent, child):
|
def _is_submodule(parent, child):
|
||||||
return parent == child or child.startswith(parent + ".")
|
return parent == child or child.startswith(parent + ".")
|
||||||
@ -2289,3 +2293,102 @@ class Red(
|
|||||||
failed_cogs=failures["cog"],
|
failed_cogs=failures["cog"],
|
||||||
unhandled=failures["unhandled"],
|
unhandled=failures["unhandled"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def send_interactive(
|
||||||
|
self,
|
||||||
|
channel: discord.abc.Messageable,
|
||||||
|
messages: Iterable[str],
|
||||||
|
*,
|
||||||
|
user: Optional[discord.User] = None,
|
||||||
|
box_lang: Optional[str] = None,
|
||||||
|
timeout: int = 15,
|
||||||
|
join_character: str = "",
|
||||||
|
) -> List[discord.Message]:
|
||||||
|
"""
|
||||||
|
Send multiple messages interactively.
|
||||||
|
|
||||||
|
The user will be prompted for whether or not they would like to view
|
||||||
|
the next message, one at a time. They will also be notified of how
|
||||||
|
many messages are remaining on each prompt.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
channel : discord.abc.Messageable
|
||||||
|
The channel to send the messages to.
|
||||||
|
messages : `iterable` of `str`
|
||||||
|
The messages to send.
|
||||||
|
user : discord.User
|
||||||
|
The user that can respond to the prompt.
|
||||||
|
When this is ``None``, any user can respond.
|
||||||
|
box_lang : Optional[str]
|
||||||
|
If specified, each message will be contained within a code block of
|
||||||
|
this language.
|
||||||
|
timeout : int
|
||||||
|
How long the user has to respond to the prompt before it times out.
|
||||||
|
After timing out, the bot deletes its prompt message.
|
||||||
|
join_character : str
|
||||||
|
The character used to join all the messages when the file output
|
||||||
|
is selected.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[discord.Message]
|
||||||
|
A list of sent messages.
|
||||||
|
"""
|
||||||
|
messages = tuple(messages)
|
||||||
|
ret = []
|
||||||
|
# using dpy_commands.Context to keep the Messageable contract in full
|
||||||
|
if isinstance(channel, dpy_commands.Context):
|
||||||
|
# this is only necessary to ensure that `channel.delete_messages()` works
|
||||||
|
# when `ctx.channel` has that method
|
||||||
|
channel = channel.channel
|
||||||
|
|
||||||
|
for idx, page in enumerate(messages, 1):
|
||||||
|
if box_lang is None:
|
||||||
|
msg = await channel.send(page)
|
||||||
|
else:
|
||||||
|
msg = await channel.send(box(page, lang=box_lang))
|
||||||
|
ret.append(msg)
|
||||||
|
n_remaining = len(messages) - idx
|
||||||
|
if n_remaining > 0:
|
||||||
|
if n_remaining == 1:
|
||||||
|
prompt_text = _(
|
||||||
|
"There is still one message remaining. Type {command_1} to continue"
|
||||||
|
" or {command_2} to upload all contents as a file."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
prompt_text = _(
|
||||||
|
"There are still {count} messages remaining. Type {command_1} to continue"
|
||||||
|
" or {command_2} to upload all contents as a file."
|
||||||
|
)
|
||||||
|
query = await channel.send(
|
||||||
|
prompt_text.format(count=n_remaining, command_1="`more`", command_2="`file`")
|
||||||
|
)
|
||||||
|
pred = MessagePredicate.lower_contained_in(
|
||||||
|
("more", "file"), channel=channel, user=user
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
resp = await self.wait_for(
|
||||||
|
"message",
|
||||||
|
check=pred,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
with contextlib.suppress(discord.HTTPException):
|
||||||
|
await query.delete()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
await channel.delete_messages((query, resp))
|
||||||
|
except (discord.HTTPException, AttributeError):
|
||||||
|
# In case the bot can't delete other users' messages,
|
||||||
|
# or is not a bot account
|
||||||
|
# or channel is a DM
|
||||||
|
with contextlib.suppress(discord.HTTPException):
|
||||||
|
await query.delete()
|
||||||
|
if pred.result == 1:
|
||||||
|
ret.append(
|
||||||
|
await channel.send(file=text_to_file(join_character.join(messages)))
|
||||||
|
)
|
||||||
|
break
|
||||||
|
return ret
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import discord
|
|||||||
from discord.ext.commands import Context as DPYContext
|
from discord.ext.commands import Context as DPYContext
|
||||||
|
|
||||||
from .requires import PermState
|
from .requires import PermState
|
||||||
from ..utils.chat_formatting import box, text_to_file
|
|
||||||
from ..utils.predicates import MessagePredicate
|
|
||||||
from ..utils import can_user_react_in
|
from ..utils import can_user_react_in
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -152,11 +150,12 @@ class Context(DPYContext):
|
|||||||
async def send_interactive(
|
async def send_interactive(
|
||||||
self,
|
self,
|
||||||
messages: Iterable[str],
|
messages: Iterable[str],
|
||||||
box_lang: str = None,
|
box_lang: Optional[str] = None,
|
||||||
timeout: int = 15,
|
timeout: int = 15,
|
||||||
join_character: str = "",
|
join_character: str = "",
|
||||||
) -> List[discord.Message]:
|
) -> List[discord.Message]:
|
||||||
"""Send multiple messages interactively.
|
"""
|
||||||
|
Send multiple messages interactively.
|
||||||
|
|
||||||
The user will be prompted for whether or not they would like to view
|
The user will be prompted for whether or not they would like to view
|
||||||
the next message, one at a time. They will also be notified of how
|
the next message, one at a time. They will also be notified of how
|
||||||
@ -176,53 +175,19 @@ class Context(DPYContext):
|
|||||||
The character used to join all the messages when the file output
|
The character used to join all the messages when the file output
|
||||||
is selected.
|
is selected.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
List[discord.Message]
|
||||||
|
A list of sent messages.
|
||||||
"""
|
"""
|
||||||
messages = tuple(messages)
|
return await self.bot.send_interactive(
|
||||||
ret = []
|
channel=self.channel,
|
||||||
|
messages=messages,
|
||||||
for idx, page in enumerate(messages, 1):
|
user=self.author,
|
||||||
if box_lang is None:
|
box_lang=box_lang,
|
||||||
msg = await self.send(page)
|
|
||||||
else:
|
|
||||||
msg = await self.send(box(page, lang=box_lang))
|
|
||||||
ret.append(msg)
|
|
||||||
n_remaining = len(messages) - idx
|
|
||||||
if n_remaining > 0:
|
|
||||||
if n_remaining == 1:
|
|
||||||
plural = ""
|
|
||||||
is_are = "is"
|
|
||||||
else:
|
|
||||||
plural = "s"
|
|
||||||
is_are = "are"
|
|
||||||
query = await self.send(
|
|
||||||
"There {} still {} message{} remaining. "
|
|
||||||
"Type `more` to continue or `file` to upload all contents as a file."
|
|
||||||
"".format(is_are, n_remaining, plural)
|
|
||||||
)
|
|
||||||
pred = MessagePredicate.lower_contained_in(("more", "file"), self)
|
|
||||||
try:
|
|
||||||
resp = await self.bot.wait_for(
|
|
||||||
"message",
|
|
||||||
check=pred,
|
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
|
join_character=join_character,
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
|
||||||
with contextlib.suppress(discord.HTTPException):
|
|
||||||
await query.delete()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await self.channel.delete_messages((query, resp))
|
|
||||||
except (discord.HTTPException, AttributeError):
|
|
||||||
# In case the bot can't delete other users' messages,
|
|
||||||
# or is not a bot account
|
|
||||||
# or channel is a DM
|
|
||||||
with contextlib.suppress(discord.HTTPException):
|
|
||||||
await query.delete()
|
|
||||||
if pred.result == 1:
|
|
||||||
await self.send(file=text_to_file(join_character.join(messages)))
|
|
||||||
break
|
|
||||||
return ret
|
|
||||||
|
|
||||||
async def embed_colour(self):
|
async def embed_colour(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1446,15 +1446,16 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
|||||||
**Arguments:**
|
**Arguments:**
|
||||||
- `[public]` - Whether to send the traceback to the current context. Leave blank to send to your DMs.
|
- `[public]` - Whether to send the traceback to the current context. Leave blank to send to your DMs.
|
||||||
"""
|
"""
|
||||||
if not public:
|
channel = ctx.channel if public else ctx.author
|
||||||
destination = ctx.author
|
|
||||||
else:
|
|
||||||
destination = ctx.channel
|
|
||||||
|
|
||||||
if self.bot._last_exception:
|
if self.bot._last_exception:
|
||||||
for page in pagify(self.bot._last_exception, shorten_by=10):
|
|
||||||
try:
|
try:
|
||||||
await destination.send(box(page, lang="py"))
|
await self.bot.send_interactive(
|
||||||
|
channel,
|
||||||
|
pagify(self.bot._last_exception, shorten_by=10),
|
||||||
|
user=ctx.author,
|
||||||
|
box_lang="py",
|
||||||
|
)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
await ctx.channel.send(
|
await ctx.channel.send(
|
||||||
"I couldn't send the traceback message to you in DM. "
|
"I couldn't send the traceback message to you in DM. "
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user