From 10923f58c397c1344e4070e59472b546ce81f76a Mon Sep 17 00:00:00 2001 From: Mow910 Date: Wed, 25 Mar 2026 22:58:06 +0100 Subject: [PATCH] patch --- discordbot/member_stats.py | 39 ++++++++++++++++++++++++++++++-------- webapp/discord_members.py | 9 ++++++++- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/discordbot/member_stats.py b/discordbot/member_stats.py index 360b431..129f79a 100644 --- a/discordbot/member_stats.py +++ b/discordbot/member_stats.py @@ -1,5 +1,6 @@ """Compteurs messages / vocal par membre pour la webapp (table guild_member_stats).""" import asyncio +import concurrent.futures import logging from datetime import datetime, timezone @@ -114,9 +115,14 @@ async def fetch_guild_members_snapshot(bot: discord.Client, guild_id: int | None {"guilds": [{"id": g.id, "name": g.name} for g in guilds]}, ) try: - await chosen.chunk(cache=True) + await asyncio.wait_for(chosen.chunk(cache=True), timeout=120.0) + except asyncio.TimeoutError: + logger.warning( + "guild.chunk timeout (%s), suite avec le cache membres partiel", + chosen.name, + ) except Exception as e: - logger.warning("guild.chunk: %s", e) + logger.warning("guild.chunk: %s", e, exc_info=True) members_out = [] for m in chosen.members: if m.bot: @@ -144,17 +150,34 @@ async def fetch_guild_members_snapshot(bot: discord.Client, guild_id: int | None } -def get_discord_members_snapshot_sync(bot: discord.Client, guild_id: int | None = None, timeout: float = 90.0) -> tuple[bool, str | None, dict]: +def get_discord_members_snapshot_sync(bot: discord.Client, guild_id: int | None = None, timeout: float = 180.0) -> tuple[bool, str | None, dict]: """Appel thread-safe depuis Flask (run_coroutine_threadsafe sur la boucle du bot).""" - if bot.loop is None or not bot.is_ready(): - return False, "Bot Discord non connecté.", {} + loop = getattr(bot, "loop", None) + if loop is None: + logger.error("get_discord_members_snapshot_sync: bot.loop est None (bot non démarré ?)") + return False, "Bot Discord non démarré (aucune boucle événements).", {} + if not bot.is_ready(): + return False, "Bot Discord pas encore prêt (connexion en cours). Réessayez dans quelques secondes.", {} try: future = asyncio.run_coroutine_threadsafe( fetch_guild_members_snapshot(bot, guild_id), - bot.loop, + loop, ) ok, err, payload = future.result(timeout=timeout) return ok, err, payload + except (concurrent.futures.TimeoutError, TimeoutError) as e: + logger.error( + "get_discord_members_snapshot_sync: délai dépassé après %.0fs (%s)", + timeout, + type(e).__name__, + exc_info=True, + ) + return ( + False, + "Délai dépassé pendant le chargement des membres (serveur volumineux ou bot occupé). Réessayez dans un instant.", + {}, + ) except Exception as e: - logger.error("get_discord_members_snapshot_sync: %s", e) - return False, str(e), {} + logger.exception("get_discord_members_snapshot_sync: %s", type(e).__name__) + msg = str(e) if str(e) else repr(e) + return False, msg or "Erreur lors de l’appel au bot Discord.", {} diff --git a/webapp/discord_members.py b/webapp/discord_members.py index 8ab650b..5e1eb76 100644 --- a/webapp/discord_members.py +++ b/webapp/discord_members.py @@ -1,7 +1,7 @@ from collections import defaultdict from datetime import datetime -from flask import render_template, request +from flask import redirect, render_template, request, url_for from sqlalchemy import text from webapp import webapp @@ -71,6 +71,13 @@ def _load_sanctions_by_user(user_ids: list[str]) -> dict[str, list]: return by_user +@webapp.route("/discord-members") +@webapp.route("/discord-members.html") +def discord_members_redirect(): + """Redirection : l’URL réelle de la page est /discord-membres (route Flask, pas un fichier .html).""" + return redirect(url_for("discord_members", **request.args), code=302) + + @webapp.route("/discord-membres") @require_page("discord_members") def discord_members():