diff --git a/cogs/events.py b/cogs/events.py index 6696d4c..67abe79 100644 --- a/cogs/events.py +++ b/cogs/events.py @@ -85,7 +85,6 @@ class Events(commands.Cog): Logs commands in your terminal. :param ctx: Discord Context """ - print("a") print(stringFormatters.format_command_usage(ctx)) command_stats.invoked(command_stats.InvocationType.TextCommand) diff --git a/cogs/fun.py b/cogs/fun.py index affd2c9..cb938bb 100644 --- a/cogs/fun.py +++ b/cogs/fun.py @@ -1,5 +1,5 @@ from data.embeds.xkcd import XKCDEmbed -from data.menus import paginated_leaderboard +from data.menus import leaderboards from decorators import help import discord from discord.ext import commands diff --git a/cogs/leaderboards.py b/cogs/leaderboards.py index 6eb9edf..9f41c5b 100644 --- a/cogs/leaderboards.py +++ b/cogs/leaderboards.py @@ -1,16 +1,12 @@ -from data.menus import paginated_leaderboard -from decorators import help import discord from discord.ext import commands + +from data.menus import leaderboards +from decorators import help from enums.help_categories import Category -from enums.numbers import Numbers -from functions import checks, xp -from functions.database import currency, stats, poke, muttn -import math -import requests +from functions import checks -# TODO some sort of general leaderboard because all of them are the same class Leaderboards(commands.Cog): def __init__(self, client): @@ -18,7 +14,7 @@ class Leaderboards(commands.Cog): self.utilsCog = self.client.get_cog("Utils") # Don't allow any commands to work when locked - def cog_check(self, ctx): + def cog_check(self, _): return not self.client.locked @commands.group(name="Leaderboard", aliases=["Lb", "Leaderboards"], case_insensitive=True, usage="[Categorie]*", @@ -34,179 +30,48 @@ class Leaderboards(commands.Cog): @leaderboard.command(name="Dinks", aliases=["Cash"], hidden=True) async def dinks(self, ctx): - entries = currency.getAllRows() - platDinks = currency.getAllPlatDinks() - - # Take platinum dinks into account - for i, user in enumerate(entries): - if str(user[0]) in platDinks: - # Tuples don't support assignment, cast to list - user = list(user) - user[1] += platDinks[str(user[0])] * Numbers.q.value - entries[i] = user - - data = [] - for i, user in enumerate(sorted(entries, key=lambda x: (float(x[1]) + float(x[3])), reverse=True)): - if i == 0 and float(user[1]) + float(user[3]) == 0.0: - return await self.empty_leaderboard(ctx, "Dinks Leaderboard", - "Er zijn nog geen personen met Didier Dinks.") - elif float(user[1]) + float(user[3]) > 0.0: - total_dinks = math.floor(float(user[1]) + float(user[3])) - data.append((user[0], total_dinks,)) - - lb = paginated_leaderboard.Leaderboard( - ctx=ctx, title="Dinks Leaderboard", data=data, fetch_names=True - ) - - await lb.send(ctx) + lb = leaderboards.DinksLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Corona", hidden=True) async def corona(self, ctx): - result = requests.get("https://disease.sh/v3/covid-19/countries").json() - result.sort(key=lambda x: int(x["cases"]), reverse=True) - - data = [] - for country in result: - data.append((country["country"], f"{country['cases']:,}",)) - - lb = paginated_leaderboard.Leaderboard( - ctx=ctx, title="Corona Leaderboard", data=data, highlight="Belgium", - colour=discord.Colour.red() - ) - - await lb.send(ctx) + lb = leaderboards.CoronaLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Bitcoin", aliases=["Bc"], hidden=True) async def bitcoin(self, ctx): - users = currency.getAllRows() - boardTop = [] - for i, user in enumerate(sorted(users, key=lambda x: x[8], reverse=True)): - # Don't create an empty leaderboard - if i == 0 and float(user[8]) == 0.0: - return await self.empty_leaderboard(ctx, "Bitcoin Leaderboard", - "Er zijn nog geen personen met Bitcoins.") - elif float(user[8]) > 0.0: - # Only add people with more than 0 - # Get the username in this guild - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} ({:,})**".format(name, round(user[8], 8))) - else: - boardTop.append("{} ({:,})".format(name, round(user[8], 8))) - - await self.startPaginated(ctx, boardTop, "Bitcoin Leaderboard") + lb = leaderboards.BitcoinLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Rob", hidden=True) async def rob(self, ctx): - users = list(stats.getAllRows()) - boardTop = [] - for i, user in enumerate(sorted(users, key=lambda x: x[4], reverse=True)): - # Don't create an empty leaderboard - if i == 0 and float(user[4]) == 0.0: - return await self.empty_leaderboard(ctx, "Rob Leaderboard", - "Er heeft nog niemand Didier Dinks gestolen.") - elif float(user[4]) > 0.0: - # Only add people with more than 0 - # Get the username in this guild - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} ({:,})**".format(name, math.floor(float(user[4])))) - else: - boardTop.append("{} ({:,})".format(name, math.floor(float(user[4])))) - await self.startPaginated(ctx, boardTop, "Rob Leaderboard") + lb = leaderboards.RobLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Poke", hidden=True) async def poke(self, ctx): - s = stats.getAllRows() - blacklist = poke.getAllBlacklistedUsers() - boardTop = [] - for i, user in enumerate(sorted(s, key=lambda x: x[1], reverse=True)): - if i == 0 and int(user[1]) == 0: - return await self.empty_leaderboard(ctx, "Poke Leaderboard", "Er is nog niemand getikt.") - - elif int(user[1]) == 0: - break - # Don't include blacklisted users - elif str(user[0]) not in blacklist: - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} ({:,})**".format(name, round(int(user[1])))) - else: - boardTop.append("{} ({:,})".format(name, round(int(user[1])))) - await self.startPaginated(ctx, boardTop, "Poke Leaderboard") + lb = leaderboards.PokeLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Xp", aliases=["Level"], hidden=True) async def xp(self, ctx): - s = stats.getAllRows() - boardTop = [] - for i, user in enumerate(sorted(s, key=lambda x: x[12], reverse=True)): - if int(user[12]) == 0: - break - - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} (Level {:,} | {:,} XP)**".format(name, - xp.calculate_level(round(int(user[12]))), - round(int(user[12])))) - else: - boardTop.append("{} (Level {:,} | {:,} XP)".format(name, - xp.calculate_level(round(int(user[12]))), - round(int(user[12])))) - await self.startPaginated(ctx, boardTop, "XP Leaderboard") + lb = leaderboards.XPLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Messages", aliases=["Mc", "Mess"], hidden=True) async def messages(self, ctx): - s = stats.getAllRows() - boardTop = [] - - message_count = stats.getTotalMessageCount() - - for i, user in enumerate(sorted(s, key=lambda x: x[11], reverse=True)): - if int(user[11]) == 0: - break - - perc = round(int(user[11]) * 100 / message_count, 2) - - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} ({:,} | {}%)**".format(name, round(int(user[11])), perc)) - else: - boardTop.append("{} ({:,} | {}%)".format(name, round(int(user[11])), perc)) - await self.startPaginated(ctx, boardTop, "Messages Leaderboard") + lb = leaderboards.MessageLeaderboard(ctx=ctx) + await lb.send() @leaderboard.command(name="Muttn", aliases=["M", "Mutn", "Mutten"], hidden=True) async def muttn(self, ctx): - users = muttn.getAllRows() - boardTop = [] - for i, user in enumerate(sorted(users, key=lambda x: x[1], reverse=True)): - if i == 0 and int(user[1]) == 0: - return await self.empty_leaderboard(ctx, "Muttn Leaderboard", "Der zittn nog geen muttns in de server.") - - if float(user[1]) == 0: - break - - name = self.utilsCog.getDisplayName(ctx, user[0]) - if int(user[0]) == int(ctx.author.id): - boardTop.append("**{} ({})%**".format(name, round(float(user[1]), 2))) - else: - boardTop.append("{} ({}%)".format(name, round(float(user[1]), 2))) - await self.startPaginated(ctx, boardTop, "Muttn Leaderboard") + lb = leaderboards.MuttnLeaderboard(ctx=ctx) + await lb.send() async def callLeaderboard(self, name, ctx): command = [command for command in self.leaderboard.commands if command.name.lower() == name.lower()][0] await command(ctx) - async def startPaginated(self, ctx, source, name, colour=discord.Colour.blue()): - pages = paginated_leaderboard.Pages(source=paginated_leaderboard.Source(source, name, colour), - clear_reactions_after=True) - await pages.start(ctx) - - async def empty_leaderboard(self, ctx, name, message, colour=discord.Colour.blue()): - embed = discord.Embed(colour=colour) - embed.set_author(name=name) - embed.description = message - await ctx.send(embed=embed) - def setup(client): client.add_cog(Leaderboards(client)) diff --git a/cogs/other.py b/cogs/other.py index 0c62b8d..859c55d 100644 --- a/cogs/other.py +++ b/cogs/other.py @@ -5,8 +5,6 @@ from data.menus import custom_commands from data.snipe import Action, Snipe from decorators import help from enums.help_categories import Category -from functions.database.custom_commands import get_all -from functions.stringFormatters import capitalize from startup.didier import Didier @@ -14,10 +12,9 @@ class Other(commands.Cog): def __init__(self, client: Didier): self.client: Didier = client - # TODO add locked field to Didier instead of client - # # Don't allow any commands to work when locked - # def cog_check(self, ctx): - # return not self.client.locked + # Don't allow any commands to work when locked + def cog_check(self, _): + return not self.client.locked @commands.command(name="Custom") @help.Category(category=Category.Didier) @@ -25,10 +22,7 @@ class Other(commands.Cog): """ Get a list of all custom commands """ - all_commands = get_all() - formatted = list(sorted(map(lambda x: capitalize(x["name"]), all_commands))) - src = custom_commands.CommandsList(formatted) - await custom_commands.Pages(source=src, clear_reactions_after=True).start(ctx) + await custom_commands.CommandsList(ctx).send() @commands.command(name="Snipe") @help.Category(category=Category.Other) diff --git a/cogs/train.py b/cogs/train.py deleted file mode 100644 index 5865bf8..0000000 --- a/cogs/train.py +++ /dev/null @@ -1,127 +0,0 @@ -from data.menus import paginated_leaderboard -from decorators import help -import discord -from discord.ext import commands, menus -from enums.help_categories import Category -from functions import checks, timeFormatters -import requests - - -class Train(commands.Cog): - - def __init__(self, client): - self.client = client - - # Don't allow any commands to work when locked - def cog_check(self, ctx): - return not self.client.locked - - @commands.command(name="Train", aliases=["Trein"], usage="[Vertrek]* [Bestemming]") - @help.Category(category=Category.School) - async def train(self, ctx, *args): - if not args or len(args) > 2: - await ctx.send("Controleer je argumenten.") - return - destination = args[-1] - departure = args[0] if len(args) > 1 else "Gent Sint-Pieters" - - req = requests.get( - "http://api.irail.be/connections/?from={}&to={}&alerts=true&lang=nl&format=json".format(departure, - destination)).json() - if "error" in req: - embed = discord.Embed(colour=discord.Colour.red()) - embed.set_author(name="Treinen van {} naar {}".format( - self.formatCity(departure), self.formatCity(destination))) - embed.add_field(name="Error", value="Er ging iets fout, probeer het later opnieuw.", inline=False) - await self.sendEmbed(ctx, embed) - return - - pages = paginated_leaderboard.Pages(source=TrainPagination(self.formatConnections(req["connection"]), - self.formatCity(departure), - self.formatCity(destination)), - clear_reactions_after=True) - await pages.start(ctx) - - def formatConnections(self, connections): - response = [] - for connection in sorted(connections, key=lambda con: con["departure"]["time"]): - conn = {} - if connection["departure"]["canceled"] != "0" or connection["arrival"]["canceled"] != "0": - conn = {"Canceled": "Afgeschaft"} - dep = connection["departure"] - arr = connection["arrival"] - conn["depStation"] = self.formatCity(dep["station"]) - conn["depTime"] = self.formatTime(dep["time"]) - conn["delay"] = self.formatDelay(dep["delay"]) - conn["track"] = dep["platform"] - conn["arrStation"] = self.formatCity(arr["station"]) - conn["direction"] = self.formatCity(dep["direction"]["name"]) - conn["arrTime"] = self.formatTime(arr["time"]) - conn["duration"] = self.formatTime(connection["duration"]) - response.append(conn) - return response - - def formatTime(self, timestamp): - if int(timestamp) <= 86400: - minutes = int(timestamp) // 60 - if minutes < 60: - return str(minutes) + "m" - return "{}h{:02}m".format(minutes // 60, minutes % 60) - else: - return timeFormatters.epochToDate(int(timestamp), "%H:%M")["date"] - - def formatDelay(self, seconds): - seconds = int(seconds) - return self.sign(seconds) + self.formatTime(abs(seconds)) if seconds != 0 else "" - - def sign(self, number): - return "-" if int(number) < 0 else "+" - - def formatCity(self, city): - city = city[0].upper() + city[1:] - arr = [] - for i, letter in enumerate(city): - if (i > 0 and (city[i - 1] == " " or city[i - 1] == "-")) or i == 0: - arr.append(letter.upper()) - else: - arr.append(letter.lower()) - return "".join(arr) - - async def sendEmbed(self, ctx, embed): - if await checks.allowedChannels(ctx): - await ctx.send(embed=embed) - else: - await ctx.author.send(embed=embed) - - -class TrainPagination(menus.ListPageSource): - def __init__(self, data, departure, destination): - super().__init__(data, per_page=3) - self.departure = departure - self.destination = destination - - async def format_page(self, menu: menus.MenuPages, entries): - offset = menu.current_page * self.per_page - embed = discord.Embed(colour=discord.Colour.blue()) - embed.set_author(name="Treinen van {} naar {}".format(self.departure, self.destination)) - embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages())) - - for i, connection in enumerate(entries, start=offset): - afgeschaft = "Canceled" in connection - embed.add_field(name="Van", value=str(connection["depStation"]), inline=True) - embed.add_field(name="Om", value=str(connection["depTime"]), inline=True) - embed.add_field(name="Spoor", value=str(connection["track"]), inline=True) - embed.add_field(name="Richting", value=str(connection["direction"]), inline=True) - embed.add_field(name="Aankomst", value=(str(connection["arrTime"]) - if not afgeschaft else "**AFGESCHAFT**"), inline=True) - embed.add_field(name="Vertraging", value=str(connection["delay"]) if connection["delay"] != "" else "0", - inline=True) - - # White space - if i - offset < 2: - embed.add_field(name="\u200b", value="\u200b", inline=False) - return embed - - -def setup(client): - client.add_cog(Train(client)) diff --git a/data/menus/custom_commands.py b/data/menus/custom_commands.py index 68c79c9..cb5261b 100644 --- a/data/menus/custom_commands.py +++ b/data/menus/custom_commands.py @@ -1,21 +1,18 @@ -import discord -from discord.ext import menus +from typing import Union + +from discord import ApplicationContext +from discord.ext.commands import Context + +from data.menus.paginated import Paginated +from functions.database.custom_commands import get_all +from functions.stringFormatters import capitalize -class CommandsList(menus.ListPageSource): - def __init__(self, data, colour=discord.Colour.blue()): - super().__init__(data, per_page=15) - self.colour = colour +class CommandsList(Paginated): + def __init__(self, ctx: Union[ApplicationContext, Context]): + all_commands = get_all() + commands_sorted = list(sorted(map(lambda x: (capitalize(x["name"]),), all_commands))) + super().__init__(ctx=ctx, title="Custom Commands", data=commands_sorted, per_page=15) - async def format_page(self, menu: menus.MenuPages, entries): - embed = discord.Embed(colour=self.colour) - embed.set_author(name="Custom Commands") - embed.description = "\n".join(entries) - embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages())) - - return embed - - -class Pages(menus.MenuPages): - def __init__(self, source, clear_reactions_after, timeout=30.0): - super().__init__(source, timeout=timeout, delete_message_after=True, clear_reactions_after=clear_reactions_after) + def format_entry(self, index: int, value: tuple) -> str: + return value[0] diff --git a/data/menus/leaderboards.py b/data/menus/leaderboards.py new file mode 100644 index 0000000..f35652f --- /dev/null +++ b/data/menus/leaderboards.py @@ -0,0 +1,246 @@ +import math +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from typing import Union, Optional + +import discord +import requests +from discord import ApplicationContext +from discord.ext.commands import Context + +from data.menus.paginated import Paginated +from enums.numbers import Numbers +from functions import xp +from functions.database import currency, stats, poke, muttn +from functions.utils import get_display_name + + +@dataclass +class Leaderboard(Paginated, ABC): + highlight: str = None + colour: discord.Colour = discord.Colour.blue() + fetch_names: bool = True + ignore_non_pos: bool = True + + def __post_init__(self): + self.data = self.process_data(self.get_data()) + + @abstractmethod + def get_data(self) -> list[tuple]: + pass + + def process_data(self, entries: list[tuple]) -> Optional[list[tuple]]: + data = [] + for i, v in enumerate(sorted(entries, key=self.get_value, reverse=True)): + entry_data = self.get_value(v) + + # Leaderboard is empty + if i == 0 and entry_data == 0 and self.ignore_non_pos: + return None + + # Ignore entries with no data + if self.ignore_non_pos and entry_data <= 0: + continue + + data.append((self.get_key(v), f"{entry_data:,}", entry_data,)) + + return data + + def get_key(self, data: tuple): + return data[0] + + def get_value(self, data: tuple): + return data[1] + + def _should_highlight(self, data) -> bool: + """Check if an entry should be highlighted""" + if self.fetch_names: + return data == self.ctx.author.id + + return data == self.highlight + + def format_entry_data(self, data: tuple) -> str: + return str(data[1]) + + def format_entry(self, index: int, data: tuple) -> str: + name = data[0] + + if self.fetch_names: + name = get_display_name(self.ctx, int(data[0])) + + s = f"{index + 1}: {name} ({self.format_entry_data(data)})" + + if self._should_highlight(data[0]): + return f"**{s}**" + + return s + + @property + def empty_description(self) -> str: + return "" + + async def empty_leaderboard(self, ctx: Union[ApplicationContext, Context], **kwargs): + embed = discord.Embed(colour=self.colour) + embed.set_author(name=self.title) + embed.description = self.empty_description + + if isinstance(ctx, ApplicationContext): + return await ctx.respond(embed=embed) + + return await ctx.reply(embed=embed, **kwargs) + + async def respond(self, **kwargs) -> discord.Message: + if self.data is None: + return await self.empty_leaderboard(self.ctx, **kwargs) + + return await super().respond(**kwargs) + + async def send(self, **kwargs) -> discord.Message: + if self.data is None: + return await self.empty_leaderboard(self.ctx, mention_author=False, **kwargs) + + return await super().send(mention_author=False, **kwargs) + + +@dataclass +class BitcoinLeaderboard(Leaderboard): + title: str = field(default="Bitcoin Leaderboard") + + def get_data(self) -> list[tuple]: + return currency.getAllRows() + + def get_value(self, data: tuple): + return round(float(data[8]), 8) + + @property + def empty_description(self) -> str: + return "Er zijn nog geen personen met Bitcoins." + + +@dataclass +class CoronaLeaderboard(Leaderboard): + colour: discord.Colour = field(default=discord.Colour.red()) + fetch_names: bool = field(default=False) + highlight: str = field(default="Belgium") + title: str = field(default="Corona Leaderboard") + + def get_data(self) -> list[tuple]: + result = requests.get("https://disease.sh/v3/covid-19/countries").json() + result.sort(key=lambda x: int(x["cases"]), reverse=True) + + data = [] + for country in result: + data.append((country["country"], f"{country['cases']:,}", country["cases"])) + + return data + + def get_value(self, data: tuple): + return data[2] + + +@dataclass +class DinksLeaderboard(Leaderboard): + title: str = field(default="Dinks Leaderboard") + + def get_data(self) -> list[tuple]: + entries = currency.getAllRows() + platDinks = currency.getAllPlatDinks() + + # Take platinum dinks into account + for i, user in enumerate(entries): + if str(user[0]) in platDinks: + # Tuples don't support assignment, cast to list + user = list(user) + user[1] += platDinks[str(user[0])] * Numbers.q.value + entries[i] = user + + return entries + + def get_value(self, data: tuple): + return float(data[1]) + float(data[3]) + + @property + def empty_description(self) -> str: + return "Er zijn nog geen personen met Didier Dinks." + + +@dataclass +class MessageLeaderboard(Leaderboard): + title: str = field(default="Message Leaderboard") + message_count: int = field(init=False) + + def get_data(self) -> list[tuple]: + entries = stats.getAllRows() + self.message_count = stats.getTotalMessageCount() + return entries + + def get_value(self, data: tuple): + return round(int(data[11])) + + def format_entry_data(self, data: tuple) -> str: + perc = round(data[2] * 100 / self.message_count, 2) + return f"{data[2]:,} | {perc}%" + + +@dataclass +class MuttnLeaderboard(Leaderboard): + title: str = field(default="Muttn Leaderboard") + + def get_data(self) -> list[tuple]: + return muttn.getAllRows() + + def get_value(self, data: tuple): + return round(float(data[1]), 2) + + def format_entry_data(self, data: tuple) -> str: + return f"{data[2]}%" + + def empty_description(self) -> str: + return "Der zittn nog geen muttns in de server." + + +@dataclass +class PokeLeaderboard(Leaderboard): + title: str = field(default="Poke Leaderboard") + + def get_data(self) -> list[tuple]: + data = stats.getAllRows() + blacklist = poke.getAllBlacklistedUsers() + return list(filter(lambda x: x[0] not in blacklist, data)) + + def get_value(self, data: tuple): + return round(int(data[1])) + + @property + def empty_description(self) -> str: + return "Er is nog niemand getikt." + + +@dataclass +class RobLeaderboard(Leaderboard): + title: str = field(default="Rob Leaderboard") + + def get_data(self) -> list[tuple]: + return list(stats.getAllRows()) + + def get_value(self, data: tuple): + return math.floor(float(data[4])) + + @property + def empty_description(self) -> str: + return "Er heeft nog niemand Didier Dinks gestolen." + + +@dataclass +class XPLeaderboard(Leaderboard): + title: str = field(default="XP Leaderboard") + + def get_data(self) -> list[tuple]: + return stats.getAllRows() + + def get_value(self, data: tuple): + return round(int(data[12])) + + def format_entry_data(self, data: tuple) -> str: + entry = data[2] + return f"Level {xp.calculate_level(entry):,} | {entry:,} XP" diff --git a/data/menus/paginated.py b/data/menus/paginated.py new file mode 100644 index 0000000..7a06cc3 --- /dev/null +++ b/data/menus/paginated.py @@ -0,0 +1,67 @@ +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Union + +import discord +from discord import ApplicationContext +from discord.ext import pages +from discord.ext.commands import Context + + +@dataclass +class Paginated(ABC): + """Abstract class to support paginated menus easily""" + ctx: Union[ApplicationContext, Context] + title: str + data: list[tuple] = None + per_page: int = 10 + colour: discord.Colour = discord.Colour.blue() + + def create_embed(self, description: str) -> discord.Embed: + embed = discord.Embed(colour=self.colour) + embed.set_author(name=self.title) + embed.description = description + + return embed + + @abstractmethod + def format_entry(self, index: int, value: tuple) -> str: + pass + + def create_pages(self, data: list[tuple]) -> list[discord.Embed]: + # Amount of entries added to this page + added = 0 + page_list = [] + + description = "" + for i, v in enumerate(data): + s = self.format_entry(i, v) + + description += s + "\n" + added += 1 + + # Page full, create an embed & change counters + if added == self.per_page: + embed = self.create_embed(description) + + description = "" + added = 0 + page_list.append(embed) + + # Add final embed if necessary + if added != 0: + embed = self.create_embed(description) + page_list.append(embed) + + return page_list + + def create_paginator(self) -> pages.Paginator: + return pages.Paginator(pages=self.create_pages(self.data), show_disabled=False, disable_on_timeout=True, timeout=30) + + async def respond(self, **kwargs) -> discord.Message: + paginator = self.create_paginator() + return await paginator.respond(self.ctx.interaction, **kwargs) + + async def send(self, **kwargs) -> discord.Message: + paginator = self.create_paginator() + return await paginator.send(self.ctx, **kwargs) diff --git a/data/menus/paginated_leaderboard.py b/data/menus/paginated_leaderboard.py deleted file mode 100644 index dd9bec7..0000000 --- a/data/menus/paginated_leaderboard.py +++ /dev/null @@ -1,127 +0,0 @@ -from typing import Callable - -import discord -from discord import ApplicationContext -from discord.ext import menus, pages -from dataclasses import dataclass - -from discord.ext.commands import Context - -from functions.utils import get_display_name - - -@dataclass -class Leaderboard: - ctx: Context - title: str - data: list - highlight: str = None - format_f: Callable = None - per_page: int = 10 - colour: discord.Colour = discord.Colour.blue() - fetch_names: bool = False - - def __post_init__(self): - if self.format_f is None: - self.format_f = self._format - - def _should_highlight(self, data) -> bool: - """Check if an entry should be highlighted""" - if self.fetch_names: - return data == self.ctx.author.id - - return data == self.highlight - - def _format(self, index: int, data: tuple) -> str: - name = data[0] - - if self.fetch_names: - name = get_display_name(self.ctx, int(data[0])) - - s = f"{index + 1}: {name} ({data[1]})" - - return s - - def _get_page_count(self) -> int: - """Get the amount of pages required to represent this data""" - count = len(self.data) // self.per_page - if len(self.data) % self.per_page != 0: - count += 1 - - return count - - def _create_embed(self, description: str) -> discord.Embed: - embed = discord.Embed(colour=self.colour) - embed.set_author(name=self.title) - embed.description = description - - return embed - - def create_pages(self) -> list[discord.Embed]: - # Amount of entries added to this page - added = 0 - page_list = [] - - description = "" - for i, v in enumerate(self.data): - s = self.format_f(i, v) - - if self._should_highlight(v[0]): - s = f"**{s}**" - - description += s + "\n" - added += 1 - - # Page full, create an embed & change counters - if added == self.per_page: - embed = self._create_embed(description) - - description = "" - added = 0 - page_list.append(embed) - - # Add final embed - if added != 0: - embed = self._create_embed(description) - page_list.append(embed) - - return page_list - - def create_paginator(self) -> pages.Paginator: - return pages.Paginator(pages=self.create_pages(), show_disabled=False, disable_on_timeout=True, timeout=30) - - async def respond(self, ctx: ApplicationContext, **kwargs) -> discord.Message: - paginator = self.create_paginator() - return await paginator.respond(ctx.interaction, **kwargs) - - async def send(self, ctx: Context, **kwargs) -> discord.Message: - paginator = self.create_paginator() - return await paginator.send(ctx, **kwargs) - - -class Source(menus.ListPageSource): - def __init__(self, data, name, colour=discord.Colour.blue()): - super().__init__(data, per_page=10) - self.name = name - self.colour = colour - - async def format_page(self, menu: menus.MenuPages, entries): - offset = menu.current_page * self.per_page - - description = "" - for i, v in enumerate(entries, start=offset): - # Check if the person's name has to be highlighted - if v.startswith("**") and v.endswith("**"): - description += "**" - v = v[2:] - description += "{}: {}\n".format(i + 1, v) - embed = discord.Embed(colour=self.colour) - embed.set_author(name=self.name) - embed.description = description - embed.set_footer(text="{}/{}".format(menu.current_page + 1, self.get_max_pages())) - return embed - - -class Pages(menus.MenuPages): - def __init__(self, source, clear_reactions_after, timeout=30.0): - super().__init__(source, timeout=timeout, delete_message_after=True, clear_reactions_after=clear_reactions_after)