Lacie/xp/leaderboard.py
2026-02-03 17:28:43 +01:00

145 lines
5.3 KiB
Python

import discord
from discord.ext import commands
from discord import app_commands
from discord.ui import View, Button
from .database import get_db
from .groups import xp_group
import math
class LeaderboardView(View):
def __init__(self, embed_pages):
super().__init__(timeout=60)
self.embed_pages = embed_pages
self.current_page = 0
self.update_button_states()
async def update_message(self, interaction: discord.Interaction):
self.update_button_states()
embed = self.embed_pages[self.current_page]
await interaction.response.edit_message(embed=embed, view=self)
def update_button_states(self):
self.previous.disabled = self.current_page == 0
self.next.disabled = self.current_page == len(self.embed_pages) - 1
@discord.ui.button(label="⬅️ Previous", style=discord.ButtonStyle.secondary)
async def previous(self, interaction: discord.Interaction, button: Button):
if self.current_page > 0:
self.current_page -= 1
await self.update_message(interaction)
@discord.ui.button(label="➡️ Next", style=discord.ButtonStyle.secondary)
async def next(self, interaction: discord.Interaction, button: Button):
if self.current_page < len(self.embed_pages) - 1:
self.current_page += 1
await self.update_message(interaction)
async def on_timeout(self):
for child in self.children:
if isinstance(child, Button):
child.disabled = True
if hasattr(self, "message") and self.message:
try:
await self.message.edit(view=self)
except discord.NotFound:
pass
class Leaderboard(commands.Cog):
def __init__(self, bot):
self.bot = bot
# Register this command onto the shared xp group
xp_group.add_command(app_commands.command(name="top", description="Show the server leaderboard")(self.leaderboard))
@app_commands.describe(
board_type="Choose which leaderboard to view",
show_absent="Include members who are no longer in the server (default: False)"
)
@app_commands.choices(
board_type=[
app_commands.Choice(name="Lifetime XP", value="lifetime"),
app_commands.Choice(name="Annual XP", value="annual"),
app_commands.Choice(name="Monthly XP", value="monthly"),
app_commands.Choice(name="Weekly XP", value="weekly"),
app_commands.Choice(name="Daily XP", value="daily")
]
)
async def leaderboard(
self,
interaction: discord.Interaction,
board_type: app_commands.Choice[str] = None,
show_absent: bool = False
):
await interaction.response.defer(thinking=True)
board_type_value = board_type.value if board_type else "annual"
board_display_name = board_type.name if board_type else "Annual XP"
conn, cur = get_db(board_type_value)
cur.execute("SELECT user_id, xp, level FROM xp ORDER BY xp DESC")
all_rows = cur.fetchall()
conn.close()
if not all_rows:
return await interaction.followup.send("No leaderboard data yet!", ephemeral=True)
user_id = str(interaction.user.id)
# Filter out absent users UNLESS show_absent is True
if not show_absent:
all_rows = [
row for row in all_rows
if interaction.guild.get_member(int(row[0])) is not None
]
if not all_rows:
return await interaction.followup.send(
"No visible leaderboard entries with the current settings.", ephemeral=True
)
per_page = 10
total_pages = math.ceil(len(all_rows) / per_page)
embeds = []
for page_num in range(total_pages):
start_idx = page_num * per_page
end_idx = start_idx + per_page
page_rows = all_rows[start_idx:end_idx]
embed = discord.Embed(
title=f"{board_display_name} Leaderboard (Page {page_num + 1}/{total_pages})",
color=self.bot.get_cog("EmbedColor").get_user_color(interaction.user)
)
if page_num == 0 and page_rows:
top_member = interaction.guild.get_member(int(page_rows[0][0]))
if top_member:
embed.set_thumbnail(url=top_member.display_avatar.url)
description_lines = []
for idx, (uid, xp, level) in enumerate(page_rows, start=start_idx + 1):
member = interaction.guild.get_member(int(uid))
if member:
line = f"{idx}. {member.mention} · Level {level} · {xp:,} XP"
elif show_absent:
# Only show absent users if show_absent is True
line = f"{idx}. User {uid} · Level {level} · {xp:,} XP"
else:
continue
description_lines.append(line)
embed.description = "\n".join(description_lines)
embeds.append(embed)
view = LeaderboardView(embeds)
await interaction.followup.send(embed=embeds[0], view=view)
view.message = await interaction.original_response()
def cog_unload(self):
xp_group.remove_command("top")
async def setup(bot):
await bot.add_cog(Leaderboard(bot))