diff --git a/leaderboard/leaderboard.py b/leaderboard/leaderboard.py index 23694df..c211ff3 100644 --- a/leaderboard/leaderboard.py +++ b/leaderboard/leaderboard.py @@ -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 - ) \ No newline at end of file + # Update points in API + await self._update_points(str(message.author.id), str(message.author)) \ No newline at end of file