Enhance automatic leaderboard sync process in Leaderboard cog by refining user credit aggregation and error handling. Implement detailed logging for successes and failures during sync, and ensure opted-out users are included in the sync process. Improve API verification after sync to confirm data integrity. Update user credit retrieval to streamline balance fetching across servers.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-26 06:06:05 -04:00
parent 9b8d4be301
commit 7eddd891bc

View file

@ -122,8 +122,8 @@ class Leaderboard(commands.Cog):
await self.bot.wait_until_ready()
while True:
try:
await self._perform_full_sync()
log.info("Completed automatic leaderboard sync")
success_count, fail_count = await self._perform_full_sync()
log.info(f"Completed automatic leaderboard sync: {success_count} successes, {fail_count} failures")
await asyncio.sleep(21600) # 6 hours in seconds
except asyncio.CancelledError:
break
@ -137,27 +137,69 @@ class Leaderboard(commands.Cog):
success_count = 0
fail_count = 0
processed_users = set()
users_to_sync = []
# First, collect all users and their credits
for guild in self.bot.guilds:
for member in guild.members:
if member.bot or member.id in processed_users:
continue
try:
# Get total credits across all guilds
credits = await self.get_user_credits(member)
opted_out = await self.config.user(member).opted_out()
# Update API
if await self._try_api_update(str(member.id), str(member), credits):
success_count += 1
else:
fail_count += 1
# Only include users with sufficient credits or who are opted out
# (we need to sync opted-out status even for users below threshold)
if credits >= 10000 or opted_out:
users_to_sync.append({
"member": member,
"credits": credits,
"opted_out": opted_out
})
processed_users.add(member.id)
except Exception as e:
log.error(f"Error syncing user {member} ({member.id}): {e}")
log.error(f"Error processing user {member} ({member.id}): {e}")
fail_count += 1
# Debug logging
log.info(f"Found {len(users_to_sync)} users to sync")
# Now perform the sync
for user_data in users_to_sync:
member = user_data["member"]
try:
if await self._try_api_update(
str(member.id),
str(member),
user_data["credits"]
):
success_count += 1
log.debug(f"Successfully synced {member} ({member.id})")
else:
fail_count += 1
log.error(f"Failed to sync {member} ({member.id})")
except Exception as e:
log.error(f"Error syncing user {member} ({member.id}): {e}")
fail_count += 1
# Verify the sync by checking the API
try:
async with self.session.get(
f"{self.api_base_url}/leaderboard",
headers={"Authorization": self.admin_secret["admin_secret"]}
) as resp:
if resp.status == 200:
data = await resp.json()
if "leaderboard" in data:
log.info(f"API verification: {len(data['leaderboard'])} users in leaderboard after sync")
else:
log.error("API verification failed: Invalid response format")
else:
log.error(f"API verification failed: Status {resp.status}")
except Exception as e:
log.error(f"API verification failed: {e}")
return success_count, fail_count
async def cog_load(self):
@ -166,38 +208,18 @@ class Leaderboard(commands.Cog):
async def get_user_credits(self, user: discord.Member) -> int:
"""Get a user's total credits from all servers."""
total_credits = 0
try:
# Get credits from the user's current server
try:
total_credits = await bank.get_balance(user)
except Exception as e:
log.error(f"Error getting balance for {user} in current guild: {e}")
# Then add credits from all other servers
for guild in self.bot.guilds:
if guild.id != user.guild.id: # Skip current guild
try:
member = guild.get_member(user.id)
if member: # If user is in this guild
guild_credits = await bank.get_balance(member)
total_credits += guild_credits
except Exception as e:
log.error(f"Error getting bank balance for {user} in {guild}: {e}")
continue
# Use Red's bank API to get the user's balance
# This automatically handles the global bank if enabled
return await bank.get_balance(user)
except Exception as e:
log.error(f"Error getting total credits for {user}: {e}")
return total_credits
log.error(f"Error getting credits for {user}: {e}")
return 0
async def get_all_balances(self) -> List[dict]:
"""Get all users' credit balances across all servers."""
all_users = {}
min_credits = 10000 # Minimum credits to show on leaderboard - matches API's MIN_CREDITS
# First get all unique members across all guilds
processed_users = set()
# Process each guild
@ -215,13 +237,16 @@ class Leaderboard(commands.Cog):
continue
try:
total_credits = await self.get_user_credits(member)
credits = await self.get_user_credits(member)
if total_credits >= min_credits:
# Debug logging for credit calculation
log.debug(f"Calculated credits for {member} ({member.id}): {credits}")
if credits >= min_credits:
all_users[member.id] = {
"userId": str(member.id),
"username": str(member),
"points": total_credits
"points": credits
}
processed_users.add(member.id)
@ -255,6 +280,9 @@ class Leaderboard(commands.Cog):
# Get opt-out status
opted_out = await self.config.user_from_id(int(user_id)).opted_out()
# Debug logging before update
log.debug(f"Sending API update for {username} ({user_id}): {credits} credits, opted_out: {opted_out}")
async with self.session.post(
f"{self.api_base_url}/leaderboard",
headers={
@ -409,7 +437,7 @@ class Leaderboard(commands.Cog):
credits = await self.get_user_credits(member)
# Debug logging
log.info(f"Credits check for {member}: {credits} credits")
log.info(f"Credits check for {member} ({member.id}): {credits} credits")
# Get user's rank
leaderboard_data = await self.get_all_balances()
@ -455,7 +483,8 @@ class Leaderboard(commands.Cog):
title="🔄 Global Leaderboard Resync Complete",
description=(
f"Successfully updated: **{success_count}** users\n"
f"Failed to update: **{fail_count}** users"
f"Failed to update: **{fail_count}** users\n\n"
"Note: Updates may take a few moments to appear on the website."
),
color=await ctx.embed_color(),
timestamp=datetime.now(timezone.utc)