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
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
9b8d4be301
commit
7eddd891bc
1 changed files with 69 additions and 40 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:
|
||||||
await self._perform_full_sync()
|
success_count, fail_count = await self._perform_full_sync()
|
||||||
log.info("Completed automatic leaderboard sync")
|
log.info(f"Completed automatic leaderboard sync: {success_count} successes, {fail_count} failures")
|
||||||
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,27 +137,69 @@ 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 = []
|
||||||
|
|
||||||
|
# First, collect all 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)
|
||||||
|
opted_out = await self.config.user(member).opted_out()
|
||||||
|
|
||||||
# Update API
|
# Only include users with sufficient credits or who are opted out
|
||||||
if await self._try_api_update(str(member.id), str(member), credits):
|
# (we need to sync opted-out status even for users below threshold)
|
||||||
success_count += 1
|
if credits >= 10000 or opted_out:
|
||||||
else:
|
users_to_sync.append({
|
||||||
fail_count += 1
|
"member": member,
|
||||||
|
"credits": credits,
|
||||||
|
"opted_out": opted_out
|
||||||
|
})
|
||||||
processed_users.add(member.id)
|
processed_users.add(member.id)
|
||||||
except Exception as e:
|
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
|
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
|
return success_count, fail_count
|
||||||
|
|
||||||
async def cog_load(self):
|
async def cog_load(self):
|
||||||
|
@ -166,38 +208,18 @@ 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 credits from the user's current server
|
# Use Red's bank API to get the user's balance
|
||||||
try:
|
# This automatically handles the global bank if enabled
|
||||||
total_credits = await bank.get_balance(user)
|
return 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
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Error getting total credits for {user}: {e}")
|
log.error(f"Error getting 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
|
||||||
|
|
||||||
# First get all unique members across all guilds
|
|
||||||
processed_users = set()
|
processed_users = set()
|
||||||
|
|
||||||
# Process each guild
|
# Process each guild
|
||||||
|
@ -215,13 +237,16 @@ class Leaderboard(commands.Cog):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
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] = {
|
all_users[member.id] = {
|
||||||
"userId": str(member.id),
|
"userId": str(member.id),
|
||||||
"username": str(member),
|
"username": str(member),
|
||||||
"points": total_credits
|
"points": credits
|
||||||
}
|
}
|
||||||
|
|
||||||
processed_users.add(member.id)
|
processed_users.add(member.id)
|
||||||
|
@ -255,6 +280,9 @@ 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={
|
||||||
|
@ -409,7 +437,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 check for {member}: {credits} credits")
|
log.info(f"Credits check for {member} ({member.id}): {credits} credits")
|
||||||
|
|
||||||
# Get user's rank
|
# Get user's rank
|
||||||
leaderboard_data = await self.get_all_balances()
|
leaderboard_data = await self.get_all_balances()
|
||||||
|
@ -455,7 +483,8 @@ class Leaderboard(commands.Cog):
|
||||||
title="🔄 Global Leaderboard Resync Complete",
|
title="🔄 Global Leaderboard Resync Complete",
|
||||||
description=(
|
description=(
|
||||||
f"Successfully updated: **{success_count}** users\n"
|
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(),
|
color=await ctx.embed_color(),
|
||||||
timestamp=datetime.now(timezone.utc)
|
timestamp=datetime.now(timezone.utc)
|
||||||
|
|
Loading…
Add table
Reference in a new issue