mirror of
https://github.com/wlinator/luminara.git
synced 2024-10-02 18:23:12 +00:00
Add leaderboard command
This commit is contained in:
parent
bdbc5d0e73
commit
3c6bc711d6
5 changed files with 239 additions and 2 deletions
40
modules/levels/leaderboard.py
Normal file
40
modules/levels/leaderboard.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
from typing import Optional
|
||||
from discord.ext import commands
|
||||
from discord import Embed, Guild
|
||||
from lib.const import CONST
|
||||
from ui.embeds import builder
|
||||
from ui.views.leaderboard import LeaderboardCommandOptions, LeaderboardCommandView
|
||||
|
||||
|
||||
class Leaderboard(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot) -> None:
|
||||
self.bot: commands.Bot = bot
|
||||
|
||||
@commands.hybrid_command(
|
||||
name="leaderboard",
|
||||
aliases=["lb"],
|
||||
usage="leaderboard",
|
||||
)
|
||||
async def leaderboard(self, ctx: commands.Context[commands.Bot]) -> None:
|
||||
guild: Optional[Guild] = ctx.guild
|
||||
if not guild:
|
||||
return
|
||||
|
||||
options: LeaderboardCommandOptions = LeaderboardCommandOptions()
|
||||
view: LeaderboardCommandView = LeaderboardCommandView(ctx, options)
|
||||
|
||||
embed: Embed = builder.create_embed(
|
||||
theme="info",
|
||||
user_name=ctx.author.name,
|
||||
thumbnail_url=ctx.author.display_avatar.url,
|
||||
hide_name_in_description=True,
|
||||
)
|
||||
|
||||
icon: str = guild.icon.url if guild.icon else CONST.FLOWERS_ART
|
||||
await view.populate_leaderboard("xp", embed, icon)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
|
||||
async def setup(bot: commands.Bot) -> None:
|
||||
await bot.add_cog(Leaderboard(bot))
|
13
poetry.lock
generated
13
poetry.lock
generated
|
@ -838,6 +838,17 @@ nodeenv = ">=1.6.0"
|
|||
all = ["twine (>=3.4.1)"]
|
||||
dev = ["twine (>=3.4.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2024.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
|
||||
{file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
|
@ -1154,4 +1165,4 @@ multidict = ">=4.0"
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "189f79c9e4eaaae2cfdde9b29509d1dd15030bdf8c82f972f5883d87c74365ae"
|
||||
content-hash = "1bec4428d16328dd4054cda20654e446c54aa0463b79ef32ae4cfa10de7c0dfd"
|
||||
|
|
|
@ -23,6 +23,7 @@ aiocache = "^0.12.2"
|
|||
aioconsole = "^0.7.1"
|
||||
psutil = "^6.0.0"
|
||||
dropbox = "^12.0.2"
|
||||
pytz = "^2024.1"
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
@ -14,7 +14,7 @@ art:
|
|||
money_coins: lumi_money_coins.png
|
||||
question: lumi_question.png
|
||||
streak: lumi_streak.png
|
||||
streak_bronze: lumi_streak_bronze.png
|
||||
streak_bronze: lumi_streak_bronze.png\
|
||||
streak_gold: lumi_streak_gold.png
|
||||
streak_silver: lumi_streak_silver.png
|
||||
warning: lumi_warning.png
|
||||
|
|
185
ui/views/leaderboard.py
Normal file
185
ui/views/leaderboard.py
Normal file
|
@ -0,0 +1,185 @@
|
|||
from datetime import datetime
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
from lib.const import CONST
|
||||
from ui.embeds import builder
|
||||
from services.currency_service import Currency
|
||||
from services.daily_service import Dailies
|
||||
from services.xp_service import XpService
|
||||
|
||||
|
||||
class LeaderboardCommandOptions(discord.ui.Select):
|
||||
"""
|
||||
This class specifies the options for the leaderboard command:
|
||||
- XP
|
||||
- Currency
|
||||
- Daily streak
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
placeholder="Select a leaderboard",
|
||||
min_values=1,
|
||||
max_values=1,
|
||||
options=[
|
||||
discord.SelectOption(
|
||||
label="Levels",
|
||||
description="See the top chatters of this server!",
|
||||
emoji="🆙",
|
||||
value="xp",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="Currency",
|
||||
description="Who is the richest Lumi user?",
|
||||
value="currency",
|
||||
emoji="💸",
|
||||
),
|
||||
discord.SelectOption(
|
||||
label="Dailies",
|
||||
description="See who has the biggest streak!",
|
||||
value="dailies",
|
||||
emoji="📅",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
async def callback(self, interaction: discord.Interaction) -> None:
|
||||
if self.view:
|
||||
await self.view.on_select(self.values[0], interaction)
|
||||
|
||||
|
||||
class LeaderboardCommandView(discord.ui.View):
|
||||
"""
|
||||
This view represents a dropdown menu to choose
|
||||
what kind of leaderboard to show.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ctx: commands.Context[commands.Bot],
|
||||
options: LeaderboardCommandOptions,
|
||||
) -> None:
|
||||
self.ctx = ctx
|
||||
self.options = options
|
||||
|
||||
super().__init__(timeout=180)
|
||||
self.add_item(self.options)
|
||||
|
||||
async def on_timeout(self) -> None:
|
||||
self.stop()
|
||||
|
||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||
if interaction.user and interaction.user != self.ctx.author:
|
||||
embed = builder.create_embed(
|
||||
theme="error",
|
||||
user_name=interaction.user.name,
|
||||
description=CONST.STRINGS["xp_lb_cant_use_dropdown"],
|
||||
hide_name_in_description=True,
|
||||
)
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def on_select(self, item: str, interaction: discord.Interaction) -> None:
|
||||
if not self.ctx.guild:
|
||||
return
|
||||
|
||||
embed = builder.create_embed(
|
||||
theme="success",
|
||||
user_name=interaction.user.name,
|
||||
thumbnail_url=CONST.FLOWERS_ART,
|
||||
hide_name_in_description=True,
|
||||
)
|
||||
|
||||
icon = self.ctx.guild.icon.url if self.ctx.guild.icon else CONST.FLOWERS_ART
|
||||
|
||||
await self.populate_leaderboard(item, embed, icon)
|
||||
|
||||
await interaction.response.edit_message(embed=embed)
|
||||
|
||||
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,
|
||||
)
|
Loading…
Reference in a new issue