[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
invocation context provided by discord.py.
"""
import asyncio
from typing import Iterable, List
import discord
from discord.ext import commands
from redbot.core.utils.chat_formatting import box
__all__ = ["RedContext"]
TICK = "\N{WHITE HEAVY CHECK MARK}"
@ -20,7 +23,7 @@ class RedContext(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.
Returns
@ -36,7 +39,7 @@ class RedContext(commands.Context):
ret.append(await self.send(page))
return ret
async def tick(self):
async def tick(self) -> bool:
"""Add a tick reaction to the command message.
Returns
@ -51,3 +54,60 @@ class RedContext(commands.Context):
return False
else:
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__),
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
def sanitize_output(ctx: commands.Context, input_: str) -> str:
"""Hides the bot's token from a string."""
@ -115,7 +120,7 @@ class Dev:
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')
@checks.is_owner()
@ -162,28 +167,24 @@ class Dev:
return await ctx.send(self.get_syntax_error(e))
func = env['func']
result = None
try:
with redirect_stdout(stdout):
ret = await func()
result = await func()
except:
value = stdout.getvalue()
await ctx.send(
box('\n{}{}'.format(value, traceback.format_exc()), lang="py"))
printed = "{}{}".format(stdout.getvalue(), traceback.format_exc())
else:
value = stdout.getvalue()
try:
await ctx.message.add_reaction('\N{White Heavy Check Mark}')
except:
pass
printed = stdout.getvalue()
await ctx.tick()
if ret is None:
if value:
value = self.sanitize_output(ctx, str(value))
await ctx.send(box(value, lang="py"))
if result is not None:
self._last_result = result
msg = "{}{}".format(printed, result)
else:
self._last_result = ret
ret = self.sanitize_output(ctx, str(ret))
await ctx.send(box("{}{}".format(value, ret), lang="py"))
msg = printed
msg = self.sanitize_output(ctx, msg)
await ctx.send_interactive(self.get_pages(msg), box_lang="py")
@commands.command()
@checks.is_owner()
@ -260,7 +261,6 @@ class Dev:
result = await result
except:
value = stdout.getvalue()
value = self.sanitize_output(ctx, value)
msg = "{}{}".format(value, traceback.format_exc())
else:
value = stdout.getvalue()
@ -270,10 +270,10 @@ class Dev:
elif value:
msg = "{}".format(value)
msg = self.sanitize_output(ctx, msg)
try:
for page in pagify(str(msg), shorten_by=12):
page = self.sanitize_output(ctx, page)
await ctx.send(box(page, "py"))
await ctx.send_interactive(self.get_pages(msg), box_lang="py")
except discord.Forbidden:
pass
except discord.HTTPException as e: