Compare commits

..

4 Commits

Author SHA1 Message Date
Stijn De Clercq a71232e292 Custom commands cleanup, remove train for my own sanity 2022-02-06 01:34:00 +01:00
Stijn De Clercq 81a0d90a12 Leaderboard cleanup 2022-02-06 01:11:40 +01:00
Stijn De Clercq ca687956f6 Rework last leaderboards 2022-02-05 23:21:47 +01:00
Stijn De Clercq 062d54722b Rework bc, rob & poke leaderboards 2022-02-05 22:55:09 +01:00
9 changed files with 354 additions and 440 deletions

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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))

View File

@ -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]

View File

@ -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"

View File

@ -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)

View File

@ -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)