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
async def _get_leaderboard(self) -> Optional[list]:
"""Fetch and combine leaderboard data from bank and LevelUp."""
"""Fetch the global leaderboard from the API."""
if not self.admin_secret or not self.admin_secret.get("admin_secret"):
log.error("Admin secret not configured")
return None
if not self.session:
log.error("Session not initialized")
return None
try:
all_users = {}
# Get all guild members
for guild in self.bot.guilds:
for member in guild.members:
if member.bot:
continue
points = await self.get_user_points(member)
if points > 0:
all_users[str(member.id)] = {
"userId": str(member.id),
"username": str(member),
"points": points
}
# Sort by points and convert to list
sorted_users = sorted(
all_users.values(),
key=lambda x: x["points"],
reverse=True
)
return sorted_users
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 generating leaderboard: {e}")
log.error(f"Error fetching 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
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:
member = guild.get_member(int(user_id))
if member:
break
if not member:
return False
points = await self.get_user_points(member)
async with self.session.post(
f"{self.api_base_url}/leaderboard",
headers={
"Authorization": self.admin_secret.get("admin_secret"),
"Content-Type": "application/json"
},
json={
"userId": user_id,
"username": username,
"points": points
}
) as resp:
if resp.status == 200:
# Clear cache to ensure fresh data on next fetch
self._cache.pop("leaderboard", None)
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"])
async def globalboard(self, ctx: commands.Context):
@ -271,14 +323,22 @@ class Leaderboard(commands.Cog):
await ctx.send(embed=embed)
@commands.Cog.listener()
async def on_message(self, message):
"""Award points for activity."""
async def on_member_update(self, before: discord.Member, after: discord.Member):
"""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:
return
# Get guild settings
guild_settings = await self.config.guild(message.guild).all()
points_per_message = guild_settings["points_per_message"]
min_length = guild_settings["min_message_length"]
cooldown = guild_settings["cooldown"]
@ -295,22 +355,5 @@ class Leaderboard(commands.Cog):
self._last_update[f"msg_{message.author.id}"] = now
# Get current points
leaderboard_data = await self._get_leaderboard()
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
)
# Update points in API
await self._update_points(str(message.author.id), str(message.author))