fix(bot): améliorer la robustesse des handlers d'erreurs Discord

- advanced_logger.py: parsing NDJSON plus robuste pour les fichiers JSON
  corrompus ou malformés lors de la vérification des rôles précédents.
  Évite l'erreur "Expecting value: line 2 column 1".

- bot.py: simplification de la détection d'erreur 403 Missing Permissions
  (codes 50013 et 20023) lors de la restauration des rôles au join d'un membre.
  Évite le log d'erreur "Failed to restore safe roles" quand c'est juste un
  problème de permissions/bot role hierarchy.

- bug_report.py: simplification de la détection du code 50007 (Cannot send
  messages to this user) lors de l'envoi de notifications de labels GitLab.
  Évite le log "Erreur inattendue dans la notification de label".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lowei 2026-05-22 18:46:32 +02:00
parent 6621e87b56
commit 3534363f98
3 changed files with 27 additions and 18 deletions

7
bot.py
View file

@ -189,6 +189,13 @@ class MyBot(commands.Bot):
is_transient = isinstance(e, disnake.HTTPException) and e.status in [500, 502, 503, 504] is_transient = isinstance(e, disnake.HTTPException) and e.status in [500, 502, 503, 504]
if not isinstance(e, disnake.HTTPException): if not isinstance(e, disnake.HTTPException):
is_transient = True is_transient = True
# 403 with code 50013 = Missing Permissions - don't retry
if isinstance(e, disnake.HTTPException) and e.status == 403:
error_code = getattr(e, 'code', None)
# 50013 = Missing Permissions, 20023 = Cannot modify a role higher than bot's highest role
if error_code in (50013, 20023):
kuby_logger.warning(f"Missing Manage Roles permission to restore safe roles for {member} in {member.guild.name} - please ensure the bot has the Manage Roles permission")
break
if is_transient and i < retries - 1: if is_transient and i < retries - 1:
await asyncio.sleep((i + 1) * 2) await asyncio.sleep((i + 1) * 2)
continue continue

View file

@ -323,11 +323,10 @@ class BugReport(commands.Cog):
f"**{current_labels_str}** pour l'issue #{issue_iid} ({issue_title})." f"**{current_labels_str}** pour l'issue #{issue_iid} ({issue_title})."
) )
except (disnake.Forbidden, disnake.HTTPException) as e: except (disnake.Forbidden, disnake.HTTPException) as e:
if isinstance(e, disnake.Forbidden) or ( error_code = getattr(e, 'code', None)
isinstance(e, disnake.HTTPException) and e.code == 50007 if isinstance(e, disnake.Forbidden) or error_code == 50007:
):
kuby_logger.warning( kuby_logger.warning(
f"Impossible d'envoyer un MP à {user} ({user.id}) - MPs désactivés (Code: {getattr(e, 'code', 'Unknown')})" f"Impossible d'envoyer un MP à {user} ({user.id}) - MPs désactivés (Code: {error_code})"
) )
if dev and dev.id != user.id: if dev and dev.id != user.id:
await dev.send( await dev.send(

View file

@ -123,22 +123,25 @@ class AdvancedLogger:
try: try:
logs = json.loads(content) logs = json.loads(content)
except json.JSONDecodeError: except json.JSONDecodeError:
# Fallback: try to strip brackets and commas # File is corrupt or semi-corrupt, try line-by-line
content_clean = content.strip('[]').strip()
# This is getting complex, let's just try line-by-line fallback
pass pass
# If not a list or failed list parse, try NDJSON line-by-line # Try NDJSON line-by-line (handles both legacy and current format)
if not logs: lines = content.splitlines()
for line in content.splitlines(): for line in lines:
line = line.strip() line = line.strip()
if not line: continue if not line:
# Strip trailing/leading commas in case of semi-corrupt legacy conversion continue
line = line.strip(',') # Strip trailing/leading commas in case of semi-corrupt lines
try: line = line.strip(',')
logs.append(json.loads(line)) if not line:
except json.JSONDecodeError: continue
continue # Skip corrupt lines try:
parsed = json.loads(line)
if isinstance(parsed, dict):
logs.append(parsed)
except json.JSONDecodeError:
continue # Skip corrupt lines
# Guard against empty logs list after parsing # Guard against empty logs list after parsing
if not logs: if not logs: