diff --git a/leaderboard/leaderboard.py b/leaderboard/leaderboard.py index 864a629..f43dad9 100644 --- a/leaderboard/leaderboard.py +++ b/leaderboard/leaderboard.py @@ -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)