Refactor Leaderboard cog to replace points with credits, updating related methods and commands for consistency. Enhance user experience by renaming commands and improving error handling during credit updates. Introduce a resync command for global leaderboard updates.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
57c7b1aaa3
commit
8622d7e223
1 changed files with 68 additions and 73 deletions
|
@ -85,17 +85,11 @@ class Leaderboard(commands.Cog):
|
||||||
|
|
||||||
# Default settings
|
# Default settings
|
||||||
default_guild = {
|
default_guild = {
|
||||||
"points_per_message": 1,
|
|
||||||
"points_decay": 0.5, # Points lost per day of inactivity
|
|
||||||
"min_message_length": 5,
|
"min_message_length": 5,
|
||||||
"cooldown": 60, # Seconds between point gains
|
"cooldown": 60, # Seconds between updates
|
||||||
}
|
}
|
||||||
|
|
||||||
self.config.register_guild(**default_guild)
|
self.config.register_guild(**default_guild)
|
||||||
|
|
||||||
def cog_unload(self):
|
|
||||||
if self.session:
|
|
||||||
asyncio.create_task(self.session.close())
|
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
"""Load the admin secret from bot config and initialize session."""
|
"""Load the admin secret from bot config and initialize session."""
|
||||||
|
@ -109,31 +103,17 @@ class Leaderboard(commands.Cog):
|
||||||
self.session = aiohttp.ClientSession()
|
self.session = aiohttp.ClientSession()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_user_points(self, user: discord.Member) -> int:
|
async def get_user_credits(self, user: discord.Member) -> int:
|
||||||
"""Get a user's total points combining bank balance and levelup XP."""
|
"""Get a user's total credits from Red's bank system."""
|
||||||
total_points = 0
|
|
||||||
|
|
||||||
# Get bank balance (credits)
|
|
||||||
try:
|
try:
|
||||||
if await bank.is_global():
|
if await bank.is_global():
|
||||||
balance = await bank.get_balance(user)
|
credits = await bank.get_balance(user)
|
||||||
else:
|
else:
|
||||||
balance = await bank.get_balance(user, _forced=True)
|
credits = await bank.get_balance(user, _forced=True)
|
||||||
total_points += balance
|
return credits
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Error getting bank balance for {user}: {e}")
|
log.error(f"Error getting bank balance for {user}: {e}")
|
||||||
|
return 0
|
||||||
# Get LevelUp XP if cog is loaded
|
|
||||||
levelup = self.bot.get_cog("LevelUp")
|
|
||||||
if levelup:
|
|
||||||
try:
|
|
||||||
profile = await levelup.get_user_profile(user)
|
|
||||||
if profile:
|
|
||||||
total_points += int(profile.total_xp)
|
|
||||||
except Exception as e:
|
|
||||||
log.error(f"Error getting LevelUp XP for {user}: {e}")
|
|
||||||
|
|
||||||
return total_points
|
|
||||||
|
|
||||||
async def _get_leaderboard(self) -> Optional[list]:
|
async def _get_leaderboard(self) -> Optional[list]:
|
||||||
"""Fetch the global leaderboard from the API."""
|
"""Fetch the global leaderboard from the API."""
|
||||||
|
@ -174,8 +154,8 @@ class Leaderboard(commands.Cog):
|
||||||
log.error(f"Error fetching leaderboard: {e}")
|
log.error(f"Error fetching leaderboard: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _update_points(self, user_id: str, username: str, points: int = None) -> bool:
|
async def _update_credits(self, user_id: str, username: str, credits: int = None) -> bool:
|
||||||
"""Update a user's points in the global leaderboard."""
|
"""Update a user's credits in the global leaderboard."""
|
||||||
if not self.admin_secret or not self.admin_secret.get("admin_secret"):
|
if not self.admin_secret or not self.admin_secret.get("admin_secret"):
|
||||||
log.error("Admin secret not configured")
|
log.error("Admin secret not configured")
|
||||||
return False
|
return False
|
||||||
|
@ -185,8 +165,8 @@ class Leaderboard(commands.Cog):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# If points not provided, calculate from Red's systems
|
# If credits not provided, get from Red's bank
|
||||||
if points is None:
|
if credits is None:
|
||||||
member = None
|
member = None
|
||||||
for guild in self.bot.guilds:
|
for guild in self.bot.guilds:
|
||||||
member = guild.get_member(int(user_id))
|
member = guild.get_member(int(user_id))
|
||||||
|
@ -196,7 +176,7 @@ class Leaderboard(commands.Cog):
|
||||||
if not member:
|
if not member:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
points = await self.get_user_points(member)
|
credits = await self.get_user_credits(member)
|
||||||
|
|
||||||
async with self.session.post(
|
async with self.session.post(
|
||||||
f"{self.api_base_url}/leaderboard",
|
f"{self.api_base_url}/leaderboard",
|
||||||
|
@ -207,7 +187,7 @@ class Leaderboard(commands.Cog):
|
||||||
json={
|
json={
|
||||||
"userId": user_id,
|
"userId": user_id,
|
||||||
"username": username,
|
"username": username,
|
||||||
"points": points
|
"points": credits # API still uses "points" field
|
||||||
}
|
}
|
||||||
) as resp:
|
) as resp:
|
||||||
if resp.status == 200:
|
if resp.status == 200:
|
||||||
|
@ -218,10 +198,10 @@ class Leaderboard(commands.Cog):
|
||||||
log.error("Unauthorized: Invalid admin secret")
|
log.error("Unauthorized: Invalid admin secret")
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
log.error(f"Failed to update points: Status {resp.status}")
|
log.error(f"Failed to update credits: Status {resp.status}")
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Error updating points: {e}")
|
log.error(f"Error updating credits: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@commands.group(name="globalboard", aliases=["glb"])
|
@commands.group(name="globalboard", aliases=["glb"])
|
||||||
|
@ -233,7 +213,7 @@ class Leaderboard(commands.Cog):
|
||||||
|
|
||||||
@globalboard.command(name="show")
|
@globalboard.command(name="show")
|
||||||
async def show_leaderboard(self, ctx: commands.Context, page: int = 1):
|
async def show_leaderboard(self, ctx: commands.Context, page: int = 1):
|
||||||
"""Show the global leaderboard."""
|
"""Show the global credits leaderboard."""
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
leaderboard_data = await self._get_leaderboard()
|
leaderboard_data = await self._get_leaderboard()
|
||||||
|
|
||||||
|
@ -255,7 +235,7 @@ class Leaderboard(commands.Cog):
|
||||||
embeds = []
|
embeds = []
|
||||||
for page_num, entries in enumerate(chunks, 1):
|
for page_num, entries in enumerate(chunks, 1):
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="🏆 Global Leaderboard",
|
title="🏆 Global Credits Leaderboard",
|
||||||
color=await ctx.embed_color()
|
color=await ctx.embed_color()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -266,11 +246,11 @@ class Leaderboard(commands.Cog):
|
||||||
for i, entry in enumerate(entries, start=start_pos + 1):
|
for i, entry in enumerate(entries, start=start_pos + 1):
|
||||||
username = entry.get("username", "Unknown User")
|
username = entry.get("username", "Unknown User")
|
||||||
user_id = entry.get("userId", "0")
|
user_id = entry.get("userId", "0")
|
||||||
points = entry.get("points", 0)
|
credits = entry.get("points", 0) # API returns as "points"
|
||||||
|
|
||||||
# Format each entry with position, name, points, and user ID
|
# Format each entry with position, name, credits, and user ID
|
||||||
description.append(
|
description.append(
|
||||||
f"`{i}.` <@{user_id}> • **{humanize_number(points)}** points"
|
f"`{i}.` <@{user_id}> • **{humanize_number(credits)}** credits"
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.description = "\n".join(description)
|
embed.description = "\n".join(description)
|
||||||
|
@ -280,9 +260,9 @@ class Leaderboard(commands.Cog):
|
||||||
view = LeaderboardView(self, ctx, embeds)
|
view = LeaderboardView(self, ctx, embeds)
|
||||||
view.message = await ctx.send(embed=embeds[0], view=view)
|
view.message = await ctx.send(embed=embeds[0], view=view)
|
||||||
|
|
||||||
@globalboard.command(name="points")
|
@globalboard.command(name="credits")
|
||||||
async def check_points(self, ctx: commands.Context, member: commands.MemberConverter = None):
|
async def check_credits(self, ctx: commands.Context, member: commands.MemberConverter = None):
|
||||||
"""Check your points or another member's points."""
|
"""Check your credits or another member's credits."""
|
||||||
member = member or ctx.author
|
member = member or ctx.author
|
||||||
leaderboard_data = await self._get_leaderboard()
|
leaderboard_data = await self._get_leaderboard()
|
||||||
|
|
||||||
|
@ -304,12 +284,12 @@ class Leaderboard(commands.Cog):
|
||||||
)
|
)
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="🏆 Global Points",
|
title="🏆 Global Credits",
|
||||||
color=await ctx.embed_color()
|
color=await ctx.embed_color()
|
||||||
)
|
)
|
||||||
embed.description = (
|
embed.description = (
|
||||||
f"**User:** <@{member.id}>\n"
|
f"**User:** <@{member.id}>\n"
|
||||||
f"**Points:** {humanize_number(user_data.get('points', 0))}\n"
|
f"**Credits:** {humanize_number(user_data.get('points', 0))}\n"
|
||||||
f"**Rank:** #{humanize_number(rank)}\n"
|
f"**Rank:** #{humanize_number(rank)}\n"
|
||||||
f"**ID:** {member.id}"
|
f"**ID:** {member.id}"
|
||||||
)
|
)
|
||||||
|
@ -318,45 +298,60 @@ class Leaderboard(commands.Cog):
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="No Points Found",
|
title="No Credits Found",
|
||||||
description=f"<@{member.id}> has no points yet!",
|
description=f"<@{member.id}> has no credits yet!",
|
||||||
color=await ctx.embed_color()
|
color=await ctx.embed_color()
|
||||||
)
|
)
|
||||||
embed.set_footer(text=f"ID: {member.id}")
|
embed.set_footer(text=f"ID: {member.id}")
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@globalboard.command(name="resync")
|
||||||
|
@commands.is_owner()
|
||||||
|
async def resync_leaderboard(self, ctx: commands.Context):
|
||||||
|
"""Force a resync of all users' credits with the global leaderboard."""
|
||||||
|
async with ctx.typing():
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
# Clear cache
|
||||||
|
self._cache.clear()
|
||||||
|
self._last_update.clear()
|
||||||
|
|
||||||
|
# Update all members across all guilds
|
||||||
|
for guild in self.bot.guilds:
|
||||||
|
for member in guild.members:
|
||||||
|
if member.bot:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if await self._update_credits(str(member.id), str(member)):
|
||||||
|
success_count += 1
|
||||||
|
else:
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🔄 Global Leaderboard Resync Complete",
|
||||||
|
description=(
|
||||||
|
f"Successfully updated: **{success_count}** users\n"
|
||||||
|
f"Failed to update: **{fail_count}** users"
|
||||||
|
),
|
||||||
|
color=await ctx.embed_color()
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||||
"""Update points when member's data changes."""
|
"""Update credits when member's data changes."""
|
||||||
if before.bot:
|
if before.bot:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Update points in API when bank balance or XP changes
|
# Update credits in API
|
||||||
await self._update_points(str(after.id), str(after))
|
await self._update_credits(str(after.id), str(after))
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_message(self, message: discord.Message):
|
async def on_bank_update(self, member: discord.Member, before: int, after: int):
|
||||||
"""Update points periodically during user activity."""
|
"""Update credits when a member's bank balance changes."""
|
||||||
if message.author.bot or not message.guild:
|
if member.bot:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get guild settings
|
# Update credits in API with the new balance
|
||||||
guild_settings = await self.config.guild(message.guild).all()
|
await self._update_credits(str(member.id), str(member), after)
|
||||||
min_length = guild_settings["min_message_length"]
|
|
||||||
cooldown = guild_settings["cooldown"]
|
|
||||||
|
|
||||||
# Check message length
|
|
||||||
if len(message.content) < min_length:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check cooldown
|
|
||||||
now = datetime.now(timezone.utc).timestamp()
|
|
||||||
last_msg_time = self._last_update.get(f"msg_{message.author.id}", 0)
|
|
||||||
|
|
||||||
if now - last_msg_time < cooldown:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._last_update[f"msg_{message.author.id}"] = now
|
|
||||||
|
|
||||||
# Update points in API
|
|
||||||
await self._update_points(str(message.author.id), str(message.author))
|
|
Loading…
Add table
Reference in a new issue