Refactor leaderboard data fetching in Leaderboard cog to retrieve global leaderboard from API, enhancing error handling and caching. Update point management logic to calculate points dynamically and improve user interaction by updating points on member data changes.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-26 04:20:46 -04:00
parent da273be3c3
commit b688f7a605

View file

@ -133,41 +133,93 @@ class Leaderboard(commands.Cog):
return total_points return total_points
async def _get_leaderboard(self) -> Optional[list]: async def _get_leaderboard(self) -> Optional[list]:
"""Fetch and combine leaderboard data from bank and LevelUp.""" """Fetch the global leaderboard from the API."""
try: if not self.admin_secret or not self.admin_secret.get("admin_secret"):
all_users = {} log.error("Admin secret not configured")
return None
# Get all guild members if not self.session:
log.error("Session not initialized")
return None
try:
now = datetime.now(timezone.utc).timestamp()
# Return cached data if available and fresh
if self._cache and now - self._last_update.get("leaderboard", 0) < self.cache_time:
return self._cache.get("leaderboard")
async with self.session.get(
f"{self.api_base_url}/leaderboard",
headers={"Authorization": self.admin_secret.get("admin_secret")}
) as resp:
if resp.status == 200:
data = await resp.json()
if not isinstance(data, dict) or "leaderboard" not in data:
log.error("Invalid API response format")
return None
self._cache["leaderboard"] = data["leaderboard"]
self._last_update["leaderboard"] = now
return self._cache["leaderboard"]
elif resp.status == 401:
log.error("Unauthorized: Invalid admin secret")
return None
else:
log.error(f"Failed to fetch leaderboard: Status {resp.status}")
return None
except Exception as e:
log.error(f"Error fetching leaderboard: {e}")
return None
async def _update_points(self, user_id: str, username: str, points: int = None) -> bool:
"""Update a user's points in the global leaderboard."""
if not self.admin_secret or not self.admin_secret.get("admin_secret"):
log.error("Admin secret not configured")
return False
if not self.session:
log.error("Session not initialized")
return False
try:
# If points not provided, calculate from Red's systems
if points is None:
member = None
for guild in self.bot.guilds: for guild in self.bot.guilds:
for member in guild.members: member = guild.get_member(int(user_id))
if member.bot: if member:
continue break
if not member:
return False
points = await self.get_user_points(member) points = await self.get_user_points(member)
if points > 0: async with self.session.post(
all_users[str(member.id)] = { f"{self.api_base_url}/leaderboard",
"userId": str(member.id), headers={
"username": str(member), "Authorization": self.admin_secret.get("admin_secret"),
"Content-Type": "application/json"
},
json={
"userId": user_id,
"username": username,
"points": points "points": points
} }
) as resp:
# Sort by points and convert to list if resp.status == 200:
sorted_users = sorted( # Clear cache to ensure fresh data on next fetch
all_users.values(), self._cache.pop("leaderboard", None)
key=lambda x: x["points"],
reverse=True
)
return sorted_users
except Exception as e:
log.error(f"Error generating leaderboard: {e}")
return None
async def _update_points(self, user_id: str, username: str, points: int) -> bool:
"""Update points is no longer needed as points are pulled from bank and LevelUp."""
return True return True
elif resp.status == 401:
log.error("Unauthorized: Invalid admin secret")
return False
else:
log.error(f"Failed to update points: Status {resp.status}")
return False
except Exception as e:
log.error(f"Error updating points: {e}")
return False
@commands.group(name="globalboard", aliases=["glb"]) @commands.group(name="globalboard", aliases=["glb"])
async def globalboard(self, ctx: commands.Context): async def globalboard(self, ctx: commands.Context):
@ -271,14 +323,22 @@ class Leaderboard(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.Cog.listener() @commands.Cog.listener()
async def on_message(self, message): async def on_member_update(self, before: discord.Member, after: discord.Member):
"""Award points for activity.""" """Update points when member's data changes."""
if before.bot:
return
# Update points in API when bank balance or XP changes
await self._update_points(str(after.id), str(after))
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
"""Update points periodically during user activity."""
if message.author.bot or not message.guild: if message.author.bot or not message.guild:
return return
# Get guild settings # Get guild settings
guild_settings = await self.config.guild(message.guild).all() guild_settings = await self.config.guild(message.guild).all()
points_per_message = guild_settings["points_per_message"]
min_length = guild_settings["min_message_length"] min_length = guild_settings["min_message_length"]
cooldown = guild_settings["cooldown"] cooldown = guild_settings["cooldown"]
@ -295,22 +355,5 @@ class Leaderboard(commands.Cog):
self._last_update[f"msg_{message.author.id}"] = now self._last_update[f"msg_{message.author.id}"] = now
# Get current points # Update points in API
leaderboard_data = await self._get_leaderboard() await self._update_points(str(message.author.id), str(message.author))
current_points = 0
if leaderboard_data:
user_data = next(
(entry for entry in leaderboard_data if entry.get("userId") == str(message.author.id)),
None
)
if user_data:
current_points = user_data.get("points", 0)
# Update points
new_points = current_points + points_per_message
await self._update_points(
str(message.author.id),
str(message.author),
new_points
)