tuxbot-bot/tuxbot/core/utils/paginator.py

172 lines
5.5 KiB
Python

"""
Credits to Rapptz/RoboDanny
https://github.com/Rapptz/RoboDanny/blob/0dfa21599da76e84c2f8e7fde0c132ec93c840a8/cogs/utils/paginator.py
"""
import asyncio
import discord
from discord.ext.commands import Paginator as CommandPaginator
from discord.ext import menus
class RoboPages(menus.MenuPages):
def __init__(self, source):
super().__init__(source=source, check_embeds=True)
self.input_lock = asyncio.Lock()
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{INFORMATION SOURCE}\ufe0f", position=menus.Last(3))
async def show_help(self):
"""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,
)
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(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), lock=False
)
async def numbered_page(self, payload):
"""lets you type a page number to go to"""
if self.input_lock.locked():
return
async with self.input_lock:
channel = self.message.channel
author_id = payload.user_id
to_delete = []
to_delete.append(
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 FieldPageSource(menus.ListPageSource):
"""A page source that requires (field_name, field_value) tuple items."""
def __init__(self, entries, *, per_page=12):
super().__init__(entries, per_page=per_page)
self.embed = discord.Embed(colour=discord.Colour.blurple())
# pylint: disable=arguments-differ
async def format_page(self, menu, entries):
self.embed.clear_fields()
self.embed.description = discord.Embed.Empty
for key, value in entries:
self.embed.add_field(name=key, value=value, inline=False)
maximum = self.get_max_pages()
if maximum > 1:
text = f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} entries)"
self.embed.set_footer(text=text)
return self.embed
class TextPageSource(menus.ListPageSource):
def __init__(self, text, *, prefix="```", suffix="```", max_size=2000):
pages = CommandPaginator(
prefix=prefix, suffix=suffix, max_size=max_size - 200
)
for line in text.split("\n"):
pages.add_line(line)
super().__init__(entries=pages.pages, per_page=1)
# pylint: disable=arguments-differ
async def format_page(self, menu, content):
maximum = self.get_max_pages()
if maximum > 1:
return f"{content}\nPage {menu.current_page + 1}/{maximum}"
return content
class SimplePageSource(menus.ListPageSource):
def __init__(self, entries, *, per_page=12):
super().__init__(entries, per_page=per_page)
self.initial_page = True
# pylint: disable=arguments-differ
async def format_page(self, menu, entries):
pages = []
for index, entry in enumerate(
entries, start=menu.current_page * self.per_page
):
pages.append(f"{index + 1}. {entry}")
maximum = self.get_max_pages()
if maximum > 1:
footer = f"Page {menu.current_page + 1}/{maximum} ({len(self.entries)} entries)"
menu.embed.set_footer(text=footer)
if self.initial_page and self.is_paginating():
pages.append("")
pages.append(
"Confused? React with \N{INFORMATION SOURCE} for more info."
)
self.initial_page = False
menu.embed.description = "\n".join(pages)
return menu.embed
class SimplePages(RoboPages):
"""A simple pagination session reminiscent of the old Pages interface.
Basically an embed with some normal formatting.
"""
def __init__(self, entries, *, per_page=12):
super().__init__(SimplePageSource(entries, per_page=per_page))
self.embed = discord.Embed(colour=discord.Colour.blurple())