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()
|
await self.bot.wait_until_ready()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
success_count, fail_count = await self._perform_full_sync()
|
await self._perform_full_sync()
|
||||||
log.info(f"Completed automatic leaderboard sync: {success_count} users with 10,000+ credits synced, {fail_count} failures")
|
log.info("Completed automatic leaderboard sync")
|
||||||
await asyncio.sleep(21600) # 6 hours in seconds
|
await asyncio.sleep(21600) # 6 hours in seconds
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
break
|
break
|
||||||
|
@ -137,71 +137,27 @@ class Leaderboard(commands.Cog):
|
||||||
success_count = 0
|
success_count = 0
|
||||||
fail_count = 0
|
fail_count = 0
|
||||||
processed_users = set()
|
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 guild in self.bot.guilds:
|
||||||
for member in guild.members:
|
for member in guild.members:
|
||||||
if member.bot or member.id in processed_users:
|
if member.bot or member.id in processed_users:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Get total credits across all guilds
|
||||||
credits = await self.get_user_credits(member)
|
credits = await self.get_user_credits(member)
|
||||||
|
|
||||||
# Only sync users with 10,000+ credits
|
# Update API
|
||||||
if credits >= min_credits:
|
if await self._try_api_update(str(member.id), str(member), credits):
|
||||||
users_to_sync.append({
|
success_count += 1
|
||||||
"member": member,
|
else:
|
||||||
"credits": credits
|
fail_count += 1
|
||||||
})
|
|
||||||
log.debug(f"Queued for sync: {member} ({member.id}) with {credits} credits")
|
|
||||||
|
|
||||||
processed_users.add(member.id)
|
processed_users.add(member.id)
|
||||||
except Exception as e:
|
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
|
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
|
return success_count, fail_count
|
||||||
|
|
||||||
async def cog_load(self):
|
async def cog_load(self):
|
||||||
|
@ -210,26 +166,35 @@ class Leaderboard(commands.Cog):
|
||||||
|
|
||||||
async def get_user_credits(self, user: discord.Member) -> int:
|
async def get_user_credits(self, user: discord.Member) -> int:
|
||||||
"""Get a user's total credits from all servers."""
|
"""Get a user's total credits from all servers."""
|
||||||
|
total_credits = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the user's balance
|
# Get credits from all servers the user is in
|
||||||
balance = await bank.get_balance(user)
|
for guild in self.bot.guilds:
|
||||||
log.info(f"Credit check for {user} ({user.id}): {balance} credits")
|
try:
|
||||||
return balance
|
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:
|
except Exception as e:
|
||||||
log.error(f"Error getting credits for {user}: {e}")
|
log.error(f"Error getting total credits for {user}: {e}")
|
||||||
return 0
|
|
||||||
|
return total_credits
|
||||||
|
|
||||||
async def get_all_balances(self) -> List[dict]:
|
async def get_all_balances(self) -> List[dict]:
|
||||||
"""Get all users' credit balances across all servers."""
|
"""Get all users' credit balances across all servers."""
|
||||||
all_users = {}
|
all_users = {}
|
||||||
min_credits = 10000 # Minimum credits to show on leaderboard - matches API's MIN_CREDITS
|
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
|
# Process each guild
|
||||||
for guild in self.bot.guilds:
|
for guild in self.bot.guilds:
|
||||||
log.debug(f"Processing guild: {guild.name} ({guild.id})")
|
|
||||||
for member in guild.members:
|
for member in guild.members:
|
||||||
if member.bot or member.id in processed_users:
|
if member.bot or member.id in processed_users:
|
||||||
continue
|
continue
|
||||||
|
@ -237,25 +202,20 @@ class Leaderboard(commands.Cog):
|
||||||
# Skip opted-out users
|
# Skip opted-out users
|
||||||
try:
|
try:
|
||||||
if await self.config.user(member).opted_out():
|
if await self.config.user(member).opted_out():
|
||||||
log.debug(f"User {member} ({member.id}) is opted out")
|
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Error checking opt-out status for {member}: {e}")
|
log.error(f"Error checking opt-out status for {member}: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
credits = await bank.get_balance(member)
|
total_credits = await self.get_user_credits(member)
|
||||||
|
|
||||||
# Debug logging for credit calculation
|
if total_credits >= min_credits:
|
||||||
log.info(f"User {member} ({member.id}) has {credits} credits (minimum: {min_credits})")
|
|
||||||
|
|
||||||
if credits >= min_credits:
|
|
||||||
all_users[member.id] = {
|
all_users[member.id] = {
|
||||||
"userId": str(member.id),
|
"userId": str(member.id),
|
||||||
"username": str(member),
|
"username": str(member),
|
||||||
"points": credits
|
"points": total_credits
|
||||||
}
|
}
|
||||||
log.info(f"Added {member} to leaderboard with {credits} credits")
|
|
||||||
|
|
||||||
processed_users.add(member.id)
|
processed_users.add(member.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -270,14 +230,10 @@ class Leaderboard(commands.Cog):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Debug logging
|
# Debug logging
|
||||||
log.info(f"Found {len(all_users)} users with {min_credits}+ credits")
|
log.info(f"Found {len(sorted_users)} users with {min_credits}+ credits")
|
||||||
if sorted_users:
|
for user in sorted_users[:10]: # Log top 10 for debugging
|
||||||
log.info("Top users on leaderboard:")
|
log.info(f"User {user['username']} has {user['points']} credits")
|
||||||
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!")
|
|
||||||
|
|
||||||
return sorted_users
|
return sorted_users
|
||||||
|
|
||||||
async def _try_api_update(self, user_id: str, username: str, credits: int) -> bool:
|
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
|
# Get opt-out status
|
||||||
opted_out = await self.config.user_from_id(int(user_id)).opted_out()
|
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(
|
async with self.session.post(
|
||||||
f"{self.api_base_url}/leaderboard",
|
f"{self.api_base_url}/leaderboard",
|
||||||
headers={
|
headers={
|
||||||
|
@ -358,21 +311,17 @@ class Leaderboard(commands.Cog):
|
||||||
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 credits leaderboard."""
|
"""Show the global credits leaderboard."""
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
log.info(f"Leaderboard requested in guild {ctx.guild.name} ({ctx.guild.id})")
|
|
||||||
leaderboard_data = await self.get_all_balances()
|
leaderboard_data = await self.get_all_balances()
|
||||||
|
|
||||||
if not leaderboard_data:
|
if not leaderboard_data:
|
||||||
# Debug info in log
|
return await ctx.send("No users have 10,000 or more credits!")
|
||||||
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
|
|
||||||
|
|
||||||
items_per_page = 10
|
items_per_page = 10
|
||||||
chunks = [leaderboard_data[i:i + items_per_page]
|
chunks = [leaderboard_data[i:i + items_per_page]
|
||||||
for i in range(0, len(leaderboard_data), items_per_page)]
|
for i in range(0, len(leaderboard_data), items_per_page)]
|
||||||
|
|
||||||
if not chunks:
|
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 = []
|
embeds = []
|
||||||
for page_num, entries in enumerate(chunks, 1):
|
for page_num, entries in enumerate(chunks, 1):
|
||||||
|
@ -453,7 +402,7 @@ class Leaderboard(commands.Cog):
|
||||||
credits = await self.get_user_credits(member)
|
credits = await self.get_user_credits(member)
|
||||||
|
|
||||||
# Debug logging
|
# 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
|
# Get user's rank
|
||||||
leaderboard_data = await self.get_all_balances()
|
leaderboard_data = await self.get_all_balances()
|
||||||
|
@ -476,10 +425,7 @@ class Leaderboard(commands.Cog):
|
||||||
f"**ID:** {member.id}"
|
f"**ID:** {member.id}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
embed.description = (
|
embed.description = f"{member.mention} has less than 10,000 credits ({humanize_number(credits)} total)"
|
||||||
f"{member.mention} has **{humanize_number(credits)}** credits\n"
|
|
||||||
f"*Need {humanize_number(10000 - credits)} more credits to appear on the leaderboard*"
|
|
||||||
)
|
|
||||||
|
|
||||||
embed.set_thumbnail(url=member.display_avatar.url)
|
embed.set_thumbnail(url=member.display_avatar.url)
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
@ -487,13 +433,13 @@ class Leaderboard(commands.Cog):
|
||||||
@globalboard.command(name="resync")
|
@globalboard.command(name="resync")
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
async def resync_leaderboard(self, ctx: commands.Context):
|
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"):
|
if not self.session or not self.admin_secret or not self.admin_secret.get("admin_secret"):
|
||||||
await ctx.send("API is not configured.")
|
await ctx.send("API is not configured.")
|
||||||
return
|
return
|
||||||
|
|
||||||
async with ctx.typing():
|
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:
|
try:
|
||||||
success_count, fail_count = await self._perform_full_sync()
|
success_count, fail_count = await self._perform_full_sync()
|
||||||
|
@ -501,9 +447,8 @@ class Leaderboard(commands.Cog):
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="🔄 Global Leaderboard Resync Complete",
|
title="🔄 Global Leaderboard Resync Complete",
|
||||||
description=(
|
description=(
|
||||||
f"Successfully updated: **{success_count}** users with 10,000+ credits\n"
|
f"Successfully updated: **{success_count}** users\n"
|
||||||
f"Failed to update: **{fail_count}** users\n\n"
|
f"Failed to update: **{fail_count}** users"
|
||||||
"Note: Updates may take a few moments to appear on the website."
|
|
||||||
),
|
),
|
||||||
color=await ctx.embed_color(),
|
color=await ctx.embed_color(),
|
||||||
timestamp=datetime.now(timezone.utc)
|
timestamp=datetime.now(timezone.utc)
|
||||||
|
|
Loading…
Add table
Reference in a new issue