2023-10-27 13:18:59 +00:00
|
|
|
import discord
|
2024-07-14 17:57:48 +00:00
|
|
|
from discord.ext import bridge
|
|
|
|
from datetime import datetime
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-07-14 17:06:49 +00:00
|
|
|
from lib.embed_builder import EmbedBuilder
|
2024-06-20 10:34:03 +00:00
|
|
|
from services.currency_service import Currency
|
|
|
|
from services.daily_service import Dailies
|
2024-04-04 11:03:51 +00:00
|
|
|
from services.xp_service import XpService
|
2024-07-14 17:57:48 +00:00
|
|
|
from lib.constants import CONST
|
2023-10-27 13:18:59 +00:00
|
|
|
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
async def cmd(ctx: bridge.Context) -> None:
|
|
|
|
if not ctx.guild:
|
|
|
|
return
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-03-17 15:39:43 +00:00
|
|
|
options = LeaderboardCommandOptions()
|
|
|
|
view = LeaderboardCommandView(ctx, options)
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-03-17 15:39:43 +00:00
|
|
|
# default leaderboard
|
2024-07-14 17:06:49 +00:00
|
|
|
embed = EmbedBuilder.create_success_embed(
|
|
|
|
ctx=ctx,
|
2024-07-14 17:57:48 +00:00
|
|
|
thumbnail_url=CONST.FLOWERS_ART,
|
2024-07-14 17:06:49 +00:00
|
|
|
show_name=False,
|
2023-10-27 13:18:59 +00:00
|
|
|
)
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
icon = ctx.guild.icon.url if ctx.guild.icon else CONST.FLOWERS_ART
|
|
|
|
await view.populate_leaderboard("xp", embed, icon)
|
2024-03-15 22:44:27 +00:00
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
await ctx.respond(embed=embed, view=view)
|
2023-10-27 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LeaderboardCommandOptions(discord.ui.Select):
|
|
|
|
"""
|
|
|
|
This class specifies the options for the leaderboard command:
|
|
|
|
- XP
|
|
|
|
- Currency
|
|
|
|
- Daily streak
|
|
|
|
"""
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
def __init__(self) -> None:
|
2023-10-27 13:18:59 +00:00
|
|
|
super().__init__(
|
|
|
|
placeholder="Select a leaderboard",
|
|
|
|
min_values=1,
|
|
|
|
max_values=1,
|
|
|
|
options=[
|
|
|
|
discord.SelectOption(
|
|
|
|
label="Levels",
|
2024-03-15 22:44:27 +00:00
|
|
|
description="See the top chatters of this server!",
|
2023-10-27 13:18:59 +00:00
|
|
|
emoji="🆙",
|
2024-07-14 17:06:49 +00:00
|
|
|
value="xp",
|
2023-10-27 13:18:59 +00:00
|
|
|
),
|
|
|
|
discord.SelectOption(
|
|
|
|
label="Currency",
|
2024-06-15 22:45:24 +00:00
|
|
|
description="Who is the richest Lumi user?",
|
2023-10-27 13:18:59 +00:00
|
|
|
value="currency",
|
2024-07-14 17:06:49 +00:00
|
|
|
emoji="💸",
|
2023-10-27 13:18:59 +00:00
|
|
|
),
|
|
|
|
discord.SelectOption(
|
|
|
|
label="Dailies",
|
2024-03-15 22:44:27 +00:00
|
|
|
description="See who has the biggest streak!",
|
2023-10-27 13:18:59 +00:00
|
|
|
value="dailies",
|
2024-07-14 17:06:49 +00:00
|
|
|
emoji="📅",
|
|
|
|
),
|
|
|
|
],
|
2023-10-27 13:18:59 +00:00
|
|
|
)
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
async def callback(self, interaction: discord.Interaction) -> None:
|
|
|
|
if self.view:
|
|
|
|
await self.view.on_select(self.values[0], interaction)
|
2023-10-27 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LeaderboardCommandView(discord.ui.View):
|
|
|
|
"""
|
|
|
|
This view represents a dropdown menu to choose
|
|
|
|
what kind of leaderboard to show.
|
|
|
|
"""
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
def __init__(self, ctx: bridge.Context, options: LeaderboardCommandOptions) -> None:
|
2023-10-27 13:18:59 +00:00
|
|
|
self.ctx = ctx
|
|
|
|
self.options = options
|
|
|
|
|
|
|
|
super().__init__(timeout=180)
|
|
|
|
self.add_item(self.options)
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
async def on_timeout(self) -> None:
|
|
|
|
if self.message:
|
|
|
|
await self.message.edit(view=None)
|
2023-10-27 13:18:59 +00:00
|
|
|
self.stop()
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
|
|
|
if interaction.user and interaction.user != self.ctx.author:
|
|
|
|
embed = EmbedBuilder.create_error_embed(
|
|
|
|
ctx=self.ctx,
|
|
|
|
author_text=interaction.user.name,
|
|
|
|
description=CONST.STRINGS["xp_lb_cant_use_dropdown"],
|
|
|
|
show_name=False,
|
2024-07-14 17:06:49 +00:00
|
|
|
)
|
2024-07-14 17:57:48 +00:00
|
|
|
await interaction.response.send_message(embed=embed, ephemeral=True)
|
2023-10-27 13:18:59 +00:00
|
|
|
return False
|
2024-07-14 17:57:48 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
async def on_select(self, item: str, interaction: discord.Interaction) -> None:
|
|
|
|
if not self.ctx.guild:
|
|
|
|
return
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-07-14 17:06:49 +00:00
|
|
|
embed = EmbedBuilder.create_success_embed(
|
|
|
|
ctx=self.ctx,
|
2024-07-14 17:57:48 +00:00
|
|
|
thumbnail_url=CONST.FLOWERS_ART,
|
2024-07-14 17:06:49 +00:00
|
|
|
show_name=False,
|
2023-10-27 13:18:59 +00:00
|
|
|
)
|
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
icon = self.ctx.guild.icon.url if self.ctx.guild.icon else CONST.FLOWERS_ART
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-07-14 17:57:48 +00:00
|
|
|
await self.populate_leaderboard(item, embed, icon)
|
2023-10-27 13:18:59 +00:00
|
|
|
|
2024-07-14 17:06:49 +00:00
|
|
|
await interaction.response.edit_message(embed=embed)
|
2024-07-14 17:57:48 +00:00
|
|
|
|
|
|
|
async def populate_leaderboard(self, item: str, embed, icon):
|
|
|
|
leaderboard_methods = {
|
|
|
|
"xp": self._populate_xp_leaderboard,
|
|
|
|
"currency": self._populate_currency_leaderboard,
|
|
|
|
"dailies": self._populate_dailies_leaderboard,
|
|
|
|
}
|
|
|
|
await leaderboard_methods[item](embed, icon)
|
|
|
|
|
|
|
|
async def _populate_xp_leaderboard(self, embed, icon):
|
|
|
|
if not self.ctx.guild:
|
|
|
|
return
|
|
|
|
|
|
|
|
xp_lb = XpService.load_leaderboard(self.ctx.guild.id)
|
|
|
|
embed.set_author(name=CONST.STRINGS["xp_lb_author"], icon_url=icon)
|
|
|
|
|
|
|
|
for rank, (user_id, xp, level, xp_needed_for_next_level) in enumerate(
|
|
|
|
xp_lb[:5], start=1
|
|
|
|
):
|
|
|
|
try:
|
|
|
|
member = await self.ctx.guild.fetch_member(user_id)
|
|
|
|
except discord.HTTPException:
|
|
|
|
continue # skip user if not in guild
|
|
|
|
|
|
|
|
embed.add_field(
|
|
|
|
name=CONST.STRINGS["xp_lb_field_name"].format(rank, member.name),
|
|
|
|
value=CONST.STRINGS["xp_lb_field_value"].format(
|
|
|
|
level, xp, xp_needed_for_next_level
|
|
|
|
),
|
|
|
|
inline=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
async def _populate_currency_leaderboard(self, embed, icon):
|
|
|
|
if not self.ctx.guild:
|
|
|
|
return
|
|
|
|
|
|
|
|
cash_lb = Currency.load_leaderboard()
|
|
|
|
embed.set_author(name=CONST.STRINGS["xp_lb_currency_author"], icon_url=icon)
|
|
|
|
embed.set_thumbnail(url=CONST.TEAPOT_ART)
|
|
|
|
|
|
|
|
for user_id, balance, rank in cash_lb[:5]:
|
|
|
|
try:
|
|
|
|
member = await self.ctx.guild.fetch_member(user_id)
|
|
|
|
except discord.HTTPException:
|
|
|
|
member = None
|
|
|
|
|
|
|
|
name = member.name if member else str(user_id)
|
|
|
|
|
|
|
|
embed.add_field(
|
|
|
|
name=f"#{rank} - {name}",
|
|
|
|
value=CONST.STRINGS["xp_lb_currency_field_value"].format(
|
|
|
|
Currency.format(balance)
|
|
|
|
),
|
|
|
|
inline=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
async def _populate_dailies_leaderboard(self, embed, icon):
|
|
|
|
if not self.ctx.guild:
|
|
|
|
return
|
|
|
|
|
|
|
|
daily_lb = Dailies.load_leaderboard()
|
|
|
|
embed.set_author(name=CONST.STRINGS["xp_lb_dailies_author"], icon_url=icon)
|
|
|
|
embed.set_thumbnail(url=CONST.MUFFIN_ART)
|
|
|
|
|
|
|
|
for user_id, streak, claimed_at, rank in daily_lb[:5]:
|
|
|
|
try:
|
|
|
|
member = await self.ctx.guild.fetch_member(user_id)
|
|
|
|
except discord.HTTPException:
|
|
|
|
member = None
|
|
|
|
|
|
|
|
name = member.name if member else user_id
|
|
|
|
|
|
|
|
claimed_at = datetime.fromisoformat(claimed_at).date()
|
|
|
|
|
|
|
|
embed.add_field(
|
|
|
|
name=f"#{rank} - {name}",
|
|
|
|
value=CONST.STRINGS["xp_lb_dailies_field_value"].format(
|
|
|
|
streak, claimed_at
|
|
|
|
),
|
|
|
|
inline=False,
|
|
|
|
)
|