Refactor leaderboard sync process in Leaderboard cog to streamline user credit aggregation and improve logging. Remove unnecessary checks for minimum credits during sync, and enhance error handling for API updates. Update user credit retrieval to sum credits across all guilds, ensuring accurate leaderboard representation.
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
7cc05adc71
commit
76fd3bf053
1 changed files with 43 additions and 98 deletions
|
@ -122,8 +122,8 @@ class Leaderboard(commands.Cog):
|
|||
await self.bot.wait_until_ready()
|
||||
while True:
|
||||
try:
|
||||
success_count, fail_count = await self._perform_full_sync()
|
||||
log.info(f"Completed automatic leaderboard sync: {success_count} users with 10,000+ credits synced, {fail_count} failures")
|
||||
await self._perform_full_sync()
|
||||
log.info("Completed automatic leaderboard sync")
|
||||
await asyncio.sleep(21600) # 6 hours in seconds
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
@ -137,71 +137,27 @@ class Leaderboard(commands.Cog):
|
|||
success_count = 0
|
||||
fail_count = 0
|
||||
processed_users = set()
|
||||
users_to_sync = []
|
||||
min_credits = 10000 # Minimum credits to show on leaderboard
|
||||
|
||||
# First, collect all eligible 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)
|
||||
|
||||
# Only sync users with 10,000+ credits
|
||||
if credits >= min_credits:
|
||||
users_to_sync.append({
|
||||
"member": member,
|
||||
"credits": credits
|
||||
})
|
||||
log.debug(f"Queued for sync: {member} ({member.id}) with {credits} credits")
|
||||
|
||||
# Update API
|
||||
if await self._try_api_update(str(member.id), str(member), credits):
|
||||
success_count += 1
|
||||
else:
|
||||
fail_count += 1
|
||||
|
||||
processed_users.add(member.id)
|
||||
except Exception as e:
|
||||
log.error(f"Error processing user {member} ({member.id}): {e}")
|
||||
log.error(f"Error syncing user {member} ({member.id}): {e}")
|
||||
fail_count += 1
|
||||
|
||||
# Debug logging
|
||||
log.info(f"Found {len(users_to_sync)} users with {min_credits}+ credits to sync")
|
||||
|
||||
# Now perform the sync for eligible users
|
||||
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}) with {user_data['credits']} credits")
|
||||
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")
|
||||
if len(data['leaderboard']) != len(users_to_sync):
|
||||
log.warning(f"Mismatch in user count: {len(users_to_sync)} synced vs {len(data['leaderboard'])} in API")
|
||||
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):
|
||||
|
@ -210,26 +166,35 @@ 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 the user's balance
|
||||
balance = await bank.get_balance(user)
|
||||
log.info(f"Credit check for {user} ({user.id}): {balance} credits")
|
||||
return balance
|
||||
# Get credits from all servers the user is in
|
||||
for guild in self.bot.guilds:
|
||||
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
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"Error getting credits for {user}: {e}")
|
||||
return 0
|
||||
log.error(f"Error getting total credits for {user}: {e}")
|
||||
|
||||
return total_credits
|
||||
|
||||
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
|
||||
processed_users = set()
|
||||
|
||||
log.info("Starting to collect user balances...")
|
||||
# First get all unique members across all guilds
|
||||
processed_users = set()
|
||||
|
||||
# Process each guild
|
||||
for guild in self.bot.guilds:
|
||||
log.debug(f"Processing guild: {guild.name} ({guild.id})")
|
||||
for member in guild.members:
|
||||
if member.bot or member.id in processed_users:
|
||||
continue
|
||||
|
@ -237,25 +202,20 @@ class Leaderboard(commands.Cog):
|
|||
# Skip opted-out users
|
||||
try:
|
||||
if await self.config.user(member).opted_out():
|
||||
log.debug(f"User {member} ({member.id}) is opted out")
|
||||
continue
|
||||
except Exception as e:
|
||||
log.error(f"Error checking opt-out status for {member}: {e}")
|
||||
continue
|
||||
|
||||
try:
|
||||
credits = await bank.get_balance(member)
|
||||
total_credits = await self.get_user_credits(member)
|
||||
|
||||
# Debug logging for credit calculation
|
||||
log.info(f"User {member} ({member.id}) has {credits} credits (minimum: {min_credits})")
|
||||
|
||||
if credits >= min_credits:
|
||||
if total_credits >= min_credits:
|
||||
all_users[member.id] = {
|
||||
"userId": str(member.id),
|
||||
"username": str(member),
|
||||
"points": credits
|
||||
"points": total_credits
|
||||
}
|
||||
log.info(f"Added {member} to leaderboard with {credits} credits")
|
||||
|
||||
processed_users.add(member.id)
|
||||
except Exception as e:
|
||||
|
@ -270,14 +230,10 @@ class Leaderboard(commands.Cog):
|
|||
)
|
||||
|
||||
# Debug logging
|
||||
log.info(f"Found {len(all_users)} users with {min_credits}+ credits")
|
||||
if sorted_users:
|
||||
log.info("Top users on leaderboard:")
|
||||
for user in sorted_users[:10]:
|
||||
log.info(f"User {user['username']} ({user['userId']}) has {user['points']} credits")
|
||||
else:
|
||||
log.warning("No users found with sufficient credits!")
|
||||
|
||||
log.info(f"Found {len(sorted_users)} users with {min_credits}+ credits")
|
||||
for user in sorted_users[:10]: # Log top 10 for debugging
|
||||
log.info(f"User {user['username']} has {user['points']} credits")
|
||||
|
||||
return sorted_users
|
||||
|
||||
async def _try_api_update(self, user_id: str, username: str, credits: int) -> bool:
|
||||
|
@ -292,9 +248,6 @@ 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={
|
||||
|
@ -358,21 +311,17 @@ class Leaderboard(commands.Cog):
|
|||
async def show_leaderboard(self, ctx: commands.Context, page: int = 1):
|
||||
"""Show the global credits leaderboard."""
|
||||
async with ctx.typing():
|
||||
log.info(f"Leaderboard requested in guild {ctx.guild.name} ({ctx.guild.id})")
|
||||
leaderboard_data = await self.get_all_balances()
|
||||
|
||||
if not leaderboard_data:
|
||||
# Debug info in log
|
||||
log.warning(f"No users with 10,000+ credits found in guild {ctx.guild.id}")
|
||||
await ctx.send("No users have 10,000 or more credits! Check your balance with `[p]glb credits`")
|
||||
return
|
||||
return await ctx.send("No users have 10,000 or more credits!")
|
||||
|
||||
items_per_page = 10
|
||||
chunks = [leaderboard_data[i:i + items_per_page]
|
||||
for i in range(0, len(leaderboard_data), items_per_page)]
|
||||
|
||||
if not chunks:
|
||||
return await ctx.send("No users have 10,000 or more credits! Check your balance with `[p]glb credits`")
|
||||
return await ctx.send("No users have 10,000 or more credits!")
|
||||
|
||||
embeds = []
|
||||
for page_num, entries in enumerate(chunks, 1):
|
||||
|
@ -453,7 +402,7 @@ class Leaderboard(commands.Cog):
|
|||
credits = await self.get_user_credits(member)
|
||||
|
||||
# Debug logging
|
||||
log.info(f"Credits command used - User {member} ({member.id}) has {credits} credits")
|
||||
log.info(f"Credits check for {member}: {credits} credits")
|
||||
|
||||
# Get user's rank
|
||||
leaderboard_data = await self.get_all_balances()
|
||||
|
@ -476,10 +425,7 @@ class Leaderboard(commands.Cog):
|
|||
f"**ID:** {member.id}"
|
||||
)
|
||||
else:
|
||||
embed.description = (
|
||||
f"{member.mention} has **{humanize_number(credits)}** credits\n"
|
||||
f"*Need {humanize_number(10000 - credits)} more credits to appear on the leaderboard*"
|
||||
)
|
||||
embed.description = f"{member.mention} has less than 10,000 credits ({humanize_number(credits)} total)"
|
||||
|
||||
embed.set_thumbnail(url=member.display_avatar.url)
|
||||
await ctx.send(embed=embed)
|
||||
|
@ -487,13 +433,13 @@ class Leaderboard(commands.Cog):
|
|||
@globalboard.command(name="resync")
|
||||
@commands.is_owner()
|
||||
async def resync_leaderboard(self, ctx: commands.Context):
|
||||
"""Force a resync of all users with 10,000+ credits to the API."""
|
||||
"""Force a resync of all users' credits with the API (if configured)."""
|
||||
if not self.session or not self.admin_secret or not self.admin_secret.get("admin_secret"):
|
||||
await ctx.send("API is not configured.")
|
||||
return
|
||||
|
||||
async with ctx.typing():
|
||||
message = await ctx.send("🔄 Starting global leaderboard resync (10,000+ credits only)...")
|
||||
message = await ctx.send("🔄 Starting global leaderboard resync...")
|
||||
|
||||
try:
|
||||
success_count, fail_count = await self._perform_full_sync()
|
||||
|
@ -501,9 +447,8 @@ class Leaderboard(commands.Cog):
|
|||
embed = discord.Embed(
|
||||
title="🔄 Global Leaderboard Resync Complete",
|
||||
description=(
|
||||
f"Successfully updated: **{success_count}** users with 10,000+ credits\n"
|
||||
f"Failed to update: **{fail_count}** users\n\n"
|
||||
"Note: Updates may take a few moments to appear on the website."
|
||||
f"Successfully updated: **{success_count}** users\n"
|
||||
f"Failed to update: **{fail_count}** users"
|
||||
),
|
||||
color=await ctx.embed_color(),
|
||||
timestamp=datetime.now(timezone.utc)
|
||||
|
|
Loading…
Add table
Reference in a new issue