[V3 Context] Interactive sending of multiple messages (#1081)

* Interactive pages method for context

* Use new methods in dev

* Undo code formatting

* Rename method to be more generalised

* More general arg
This commit is contained in:
Tobotimus 2017-11-20 10:21:40 +11:00 committed by palmtree5
parent 8dcace4bfd
commit b94bad38e7
2 changed files with 87 additions and 27 deletions

View File

@ -1,12 +1,15 @@
"""Module for Red's Context class """
The purpose of this module is to allow for Red to further customise the command The purpose of this module is to allow for Red to further customise the command
invocation context provided by discord.py. invocation context provided by discord.py.
""" """
import asyncio
from typing import Iterable, List
import discord import discord
from discord.ext import commands from discord.ext import commands
from redbot.core.utils.chat_formatting import box
__all__ = ["RedContext"] __all__ = ["RedContext"]
TICK = "\N{WHITE HEAVY CHECK MARK}" TICK = "\N{WHITE HEAVY CHECK MARK}"
@ -20,9 +23,9 @@ class RedContext(commands.Context):
This class inherits from `commands.Context <discord.ext.commands.Context>`. This class inherits from `commands.Context <discord.ext.commands.Context>`.
""" """
async def send_help(self): async def send_help(self) -> List[discord.Message]:
"""Send the command help message. """Send the command help message.
Returns Returns
------- -------
`list` of `discord.Message` `list` of `discord.Message`
@ -36,7 +39,7 @@ class RedContext(commands.Context):
ret.append(await self.send(page)) ret.append(await self.send(page))
return ret return ret
async def tick(self): async def tick(self) -> bool:
"""Add a tick reaction to the command message. """Add a tick reaction to the command message.
Returns Returns
@ -51,3 +54,60 @@ class RedContext(commands.Context):
return False return False
else: else:
return True return True
async def send_interactive(self,
messages: Iterable[str],
box_lang: str=None,
timeout: int=15) -> 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
----------
messages : `iterable` of `str`
The messages to send.
box_lang : str
If specified, each message will be contained within a codeblock 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.
"""
messages = tuple(messages)
ret = []
more_check = lambda m: (m.author == self.author and
m.channel == self.channel and
m.content.lower() == "more")
for idx, page in enumerate(messages, 1):
if box_lang is None:
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."
"".format(is_are, n_remaining, plural))
try:
resp = await self.bot.wait_for(
'message', check=more_check, timeout=timeout)
except asyncio.TimeoutError:
await query.delete()
break
else:
await self.channel.delete_messages((query, resp))
return ret

View File

@ -51,6 +51,11 @@ class Dev:
''.format(e, '^', type(e).__name__), ''.format(e, '^', type(e).__name__),
lang="py") lang="py")
@staticmethod
def get_pages(msg: str):
"""Pagify the given message for output to the user."""
return pagify(msg, delims=["\n", " "], priority=True, shorten_by=10)
@staticmethod @staticmethod
def sanitize_output(ctx: commands.Context, input_: str) -> str: def sanitize_output(ctx: commands.Context, input_: str) -> str:
"""Hides the bot's token from a string.""" """Hides the bot's token from a string."""
@ -115,7 +120,7 @@ class Dev:
result = self.sanitize_output(ctx, str(result)) result = self.sanitize_output(ctx, str(result))
await ctx.send(box(result, lang="py")) await ctx.send_interactive(self.get_pages(result), box_lang="py")
@commands.command(name='eval') @commands.command(name='eval')
@checks.is_owner() @checks.is_owner()
@ -162,28 +167,24 @@ class Dev:
return await ctx.send(self.get_syntax_error(e)) return await ctx.send(self.get_syntax_error(e))
func = env['func'] func = env['func']
result = None
try: try:
with redirect_stdout(stdout): with redirect_stdout(stdout):
ret = await func() result = await func()
except: except:
value = stdout.getvalue() printed = "{}{}".format(stdout.getvalue(), traceback.format_exc())
await ctx.send(
box('\n{}{}'.format(value, traceback.format_exc()), lang="py"))
else: else:
value = stdout.getvalue() printed = stdout.getvalue()
try: await ctx.tick()
await ctx.message.add_reaction('\N{White Heavy Check Mark}')
except:
pass
if ret is None: if result is not None:
if value: self._last_result = result
value = self.sanitize_output(ctx, str(value)) msg = "{}{}".format(printed, result)
await ctx.send(box(value, lang="py")) else:
else: msg = printed
self._last_result = ret msg = self.sanitize_output(ctx, msg)
ret = self.sanitize_output(ctx, str(ret))
await ctx.send(box("{}{}".format(value, ret), lang="py")) await ctx.send_interactive(self.get_pages(msg), box_lang="py")
@commands.command() @commands.command()
@checks.is_owner() @checks.is_owner()
@ -260,7 +261,6 @@ class Dev:
result = await result result = await result
except: except:
value = stdout.getvalue() value = stdout.getvalue()
value = self.sanitize_output(ctx, value)
msg = "{}{}".format(value, traceback.format_exc()) msg = "{}{}".format(value, traceback.format_exc())
else: else:
value = stdout.getvalue() value = stdout.getvalue()
@ -270,10 +270,10 @@ class Dev:
elif value: elif value:
msg = "{}".format(value) msg = "{}".format(value)
msg = self.sanitize_output(ctx, msg)
try: try:
for page in pagify(str(msg), shorten_by=12): await ctx.send_interactive(self.get_pages(msg), box_lang="py")
page = self.sanitize_output(ctx, page)
await ctx.send(box(page, "py"))
except discord.Forbidden: except discord.Forbidden:
pass pass
except discord.HTTPException as e: except discord.HTTPException as e: