From 11348bda39962c3e22baf0b497293268dba3e2ec Mon Sep 17 00:00:00 2001 From: Mow910 Date: Sat, 14 Feb 2026 16:55:52 +0100 Subject: [PATCH 1/4] =?UTF-8?q?Ajout=20de=20la=20commande=20!transfert=20p?= =?UTF-8?q?our=20transf=C3=A9rer=20des=20messages=20entre=20canaux=20tout?= =?UTF-8?q?=20en=20pr=C3=A9servant=20l'identit=C3=A9=20de=20l'auteur.=20Mi?= =?UTF-8?q?se=20=C3=A0=20jour=20de=20la=20documentation=20des=20commandes?= =?UTF-8?q?=20pour=20inclure=20cette=20nouvelle=20fonctionnalit=C3=A9=20et?= =?UTF-8?q?=20ses=20alias=20(!transfer,=20!move).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discordbot/__init__.py | 7 +- discordbot/moderation.py | 250 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 255 insertions(+), 2 deletions(-) diff --git a/discordbot/__init__.py b/discordbot/__init__.py index ce06bef..45f8da3 100644 --- a/discordbot/__init__.py +++ b/discordbot/__init__.py @@ -21,7 +21,8 @@ from discordbot.moderation import ( handle_ban_list_command, handle_staff_help_command, handle_timeout_command, - handle_say_command + handle_say_command, + handle_transfer_command ) from discordbot.welcome import sendWelcomeMessage, sendLeaveMessage, updateInviteCache from discordbot.youtube import checkYouTubeVideos @@ -167,6 +168,10 @@ async def on_message(message: Message): await handle_say_command(message, bot) return + if command_name in ['!transfert', '!transfer', '!move']: + await handle_transfer_command(message, bot) + return + if command_name in ['!aide', '!help']: await handle_staff_help_command(message, bot) return diff --git a/discordbot/moderation.py b/discordbot/moderation.py index 366aeb9..35bc4fc 100644 --- a/discordbot/moderation.py +++ b/discordbot/moderation.py @@ -4,6 +4,7 @@ import time import os import re import discord +import io from datetime import datetime, timezone, timedelta from zoneinfo import ZoneInfo from database import db @@ -1055,7 +1056,13 @@ async def handle_staff_help_command(message: Message, bot): value=( "• `!say #channel message`\n" " Envoie un message en tant que bot\n" - " Ex: `!say #annonces Nouvelle fonctionnalité !`" + " Ex: `!say #annonces Nouvelle fonctionnalité !`\n\n" + "• `!transfert #canal message_id [raison]`\n" + " Transfère un message vers un autre canal\n" + " *Alias: !transfer, !move*\n" + " Ex: `!transfert #entraide 123456789012345678`\n" + " Ex: `!transfert #general https://discord.com/channels/.../...`\n" + " Le message sera envoyé comme si c'était l'auteur original" ), inline=False ) @@ -1405,3 +1412,244 @@ async def handle_say_command(message: Message, bot): except Exception as e: logging.error(f"Erreur lors de l'envoi du message: {e}") +async def handle_transfer_command(message: Message, bot): + """ + Commande pour transférer un message d'un canal à un autre en préservant l'identité de l'auteur original. + Syntaxe: !transfert #canal_destination [raison] + """ + if not has_staff_role(message.author.roles): + await send_access_denied(message.channel) + return + + parts = message.content.split(maxsplit=3) + + if len(parts) < 3: + embed = discord.Embed( + title="📋 Utilisation de la commande", + description="**Syntaxe :** `!transfert #canal message_id [raison]` ou `!transfert #canal lien_message [raison]`", + color=discord.Color.blue() + ) + embed.add_field( + name="Exemples", + value=( + "• `!transfert #entraide 123456789012345678`\n" + "• `!transfert #general https://discord.com/channels/.../...`\n" + "• `!transfert #entraide 123456789012345678 Message posté dans le mauvais canal`" + ), + inline=False + ) + embed.add_field( + name="Aliases", + value="`!transfert`, `!transfer`, `!move`", + inline=False + ) + embed.add_field( + name="💡 Astuce", + value="Faites un clic droit sur un message → Copier l'identifiant du message, ou copiez le lien du message", + inline=False + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Récupérer le canal de destination + target_channel = None + if message.channel_mentions: + target_channel = message.channel_mentions[0] + else: + try: + channel_id = int(parts[1].strip('<#>')) + target_channel = bot.get_channel(channel_id) + except ValueError: + pass + + if not target_channel: + embed = discord.Embed( + title="❌ Erreur", + description="Canal de destination invalide. Mentionnez un canal avec #canal.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Récupérer le message à transférer + message_id = None + source_channel = message.channel + + # Vérifier si c'est un lien Discord + if 'discord.com/channels/' in parts[2]: + try: + # Format: https://discord.com/channels/guild_id/channel_id/message_id + link_parts = parts[2].split('/') + source_channel_id = int(link_parts[-2]) + message_id = int(link_parts[-1]) + source_channel = bot.get_channel(source_channel_id) + + if not source_channel: + embed = discord.Embed( + title="❌ Erreur", + description="Canal source introuvable.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + except (ValueError, IndexError): + embed = discord.Embed( + title="❌ Erreur", + description="Lien de message invalide.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + else: + # C'est un ID de message + try: + message_id = int(parts[2]) + except ValueError: + embed = discord.Embed( + title="❌ Erreur", + description="ID de message invalide. Utilisez un ID numérique ou un lien Discord.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Récupérer le message original + try: + original_message = await source_channel.fetch_message(message_id) + except discord.NotFound: + embed = discord.Embed( + title="❌ Erreur", + description="Message introuvable. Vérifiez l'ID ou le lien.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + except discord.Forbidden: + embed = discord.Embed( + title="❌ Erreur", + description="Je n'ai pas la permission d'accéder à ce message.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Raison du transfert (optionnel) + reason = parts[3] if len(parts) > 3 else "Message posté dans le mauvais canal" + + # Créer un webhook pour envoyer le message en tant que l'auteur original + webhooks = await target_channel.webhooks() + webhook = None + + # Chercher un webhook existant créé par le bot + for wh in webhooks: + if wh.user == bot.user: + webhook = wh + break + + # Créer un webhook si nécessaire + if not webhook: + try: + webhook = await target_channel.create_webhook(name="Mamie Henriette - Transfert") + except discord.Forbidden: + embed = discord.Embed( + title="❌ Erreur", + description="Je n'ai pas la permission de créer un webhook dans le canal de destination.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Préparer le contenu du message + content = original_message.content + embeds = original_message.embeds + files_to_send = [] + + # Télécharger les pièces jointes + for attachment in original_message.attachments: + try: + file_data = await attachment.read() + files_to_send.append(discord.File( + fp=io.BytesIO(file_data), + filename=attachment.filename + )) + except Exception as e: + logging.error(f"Erreur lors du téléchargement de la pièce jointe: {e}") + + # Envoyer le message via le webhook + try: + await webhook.send( + content=content, + username=original_message.author.display_name, + avatar_url=original_message.author.display_avatar.url, + embeds=embeds[:10] if embeds else [], # Discord limite à 10 embeds + files=files_to_send, + allowed_mentions=discord.AllowedMentions.none() + ) + + # Supprimer le message original + try: + await original_message.delete() + except discord.Forbidden: + logging.warning(f"Impossible de supprimer le message original (ID: {message_id})") + + # Envoyer un message de confirmation dans le canal source + local_now = _to_local(datetime.now(timezone.utc)) + embed = discord.Embed( + title="✅ Message transféré", + description=f"Le message de **{original_message.author.name}** a été transféré vers {target_channel.mention}", + color=discord.Color.green(), + timestamp=datetime.now(timezone.utc) + ) + embed.add_field(name="👤 Auteur original", value=f"{original_message.author.mention}", inline=True) + embed.add_field(name="📤 Canal source", value=source_channel.mention, inline=True) + embed.add_field(name="📥 Canal destination", value=target_channel.mention, inline=True) + embed.add_field(name="🛡️ Modérateur", value=message.author.mention, inline=True) + embed.add_field(name="📝 Raison", value=reason, inline=False) + + confirmation_msg = await source_channel.send(embed=embed) + asyncio.create_task(delete_after_delay(confirmation_msg)) + + # Logger dans le canal de modération + log_embed = discord.Embed( + title="📨 Transfert de message", + description=f"Un message de **{original_message.author.name}** a été transféré", + color=discord.Color.blue(), + timestamp=datetime.now(timezone.utc) + ) + log_embed.add_field(name="👤 Auteur original", value=f"{original_message.author.name}\n`{original_message.author.id}`", inline=True) + log_embed.add_field(name="🛡️ Modérateur", value=f"**{message.author.name}**", inline=True) + log_embed.add_field(name="📅 Date et heure", value=local_now.strftime('%d/%m/%Y à %H:%M'), inline=True) + log_embed.add_field(name="📤 De", value=source_channel.mention, inline=True) + log_embed.add_field(name="📥 Vers", value=target_channel.mention, inline=True) + log_embed.add_field(name="📝 Raison", value=reason, inline=False) + + # Ajouter un aperçu du contenu + preview = content[:100] + "..." if len(content) > 100 else content + if preview: + log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False) + + log_embed.set_footer(text=f"ID Auteur: {original_message.author.id} • Serveur: {message.guild.name}") + + await send_to_moderation_log_channel(bot, log_embed) + + # Supprimer la commande de transfert + await safe_delete_message(message) + + except discord.HTTPException as e: + logging.error(f"Erreur lors du transfert du message: {e}") + embed = discord.Embed( + title="❌ Erreur", + description=f"Une erreur est survenue lors du transfert du message: {str(e)}", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + From 5709b1c0a3425fa1c5a1be48ddd91bcf095d8621 Mon Sep 17 00:00:00 2001 From: Mow910 Date: Sat, 14 Feb 2026 18:01:31 +0100 Subject: [PATCH 2/4] =?UTF-8?q?Am=C3=A9lioration=20de=20la=20commande=20!t?= =?UTF-8?q?ransfert=20pour=20supporter=20les=20canaux=20textuels,=20thread?= =?UTF-8?q?s=20et=20forums.=20Ajout=20de=20v=C3=A9rifications=20de=20compa?= =?UTF-8?q?tibilit=C3=A9=20pour=20le=20canal=20de=20destination=20et=20mis?= =?UTF-8?q?e=20=C3=A0=20jour=20de=20la=20documentation=20des=20commandes.?= =?UTF-8?q?=20Gestion=20des=20transferts=20de=20messages=20avec=20cr=C3=A9?= =?UTF-8?q?ation=20de=20posts=20dans=20les=20forums=20et=20envoi=20via=20w?= =?UTF-8?q?ebhooks=20pour=20les=20autres=20types=20de=20canaux.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discordbot/moderation.py | 268 ++++++++++++++++++++----------- webapp/templates/moderation.html | 8 + 2 files changed, 184 insertions(+), 92 deletions(-) diff --git a/discordbot/moderation.py b/discordbot/moderation.py index 35bc4fc..b8baf13 100644 --- a/discordbot/moderation.py +++ b/discordbot/moderation.py @@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo from database import db from database.helpers import ConfigurationHelper from database.models import ModerationEvent -from discord import Message +from discord import Message, TextChannel, ForumChannel, Thread def _get_local_tz(): tz_name = os.environ.get('APP_TZ') or os.environ.get('TZ') or 'Europe/Paris' @@ -1061,8 +1061,9 @@ async def handle_staff_help_command(message: Message, bot): " Transfère un message vers un autre canal\n" " *Alias: !transfer, !move*\n" " Ex: `!transfert #entraide 123456789012345678`\n" - " Ex: `!transfert #general https://discord.com/channels/.../...`\n" - " Le message sera envoyé comme si c'était l'auteur original" + " Ex: `!transfert #général https://discord.com/channels/.../...`\n" + " Le message sera envoyé comme si c'était l'auteur original\n" + " Supporte les canaux textuels, threads et forums (crée un post)" ), inline=False ) @@ -1473,6 +1474,17 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return + # Vérifier que le canal de destination est compatible + if not isinstance(target_channel, (TextChannel, ForumChannel, Thread)): + embed = discord.Embed( + title="❌ Erreur", + description=f"Le transfert n'est pas supporté vers ce type de canal. Utilisez un canal textuel, un forum ou un thread.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + # Récupérer le message à transférer message_id = None source_channel = message.channel @@ -1543,30 +1555,6 @@ async def handle_transfer_command(message: Message, bot): # Raison du transfert (optionnel) reason = parts[3] if len(parts) > 3 else "Message posté dans le mauvais canal" - # Créer un webhook pour envoyer le message en tant que l'auteur original - webhooks = await target_channel.webhooks() - webhook = None - - # Chercher un webhook existant créé par le bot - for wh in webhooks: - if wh.user == bot.user: - webhook = wh - break - - # Créer un webhook si nécessaire - if not webhook: - try: - webhook = await target_channel.create_webhook(name="Mamie Henriette - Transfert") - except discord.Forbidden: - embed = discord.Embed( - title="❌ Erreur", - description="Je n'ai pas la permission de créer un webhook dans le canal de destination.", - color=discord.Color.red() - ) - msg = await message.channel.send(embed=embed) - asyncio.create_task(delete_after_delay(msg)) - return - # Préparer le contenu du message content = original_message.content embeds = original_message.embeds @@ -1583,73 +1571,169 @@ async def handle_transfer_command(message: Message, bot): except Exception as e: logging.error(f"Erreur lors du téléchargement de la pièce jointe: {e}") - # Envoyer le message via le webhook - try: - await webhook.send( - content=content, - username=original_message.author.display_name, - avatar_url=original_message.author.display_avatar.url, - embeds=embeds[:10] if embeds else [], # Discord limite à 10 embeds - files=files_to_send, - allowed_mentions=discord.AllowedMentions.none() - ) - - # Supprimer le message original + # Gestion différente selon le type de canal + transferred_message = None + + if isinstance(target_channel, ForumChannel): + # Pour un forum : créer un nouveau post (thread) try: - await original_message.delete() - except discord.Forbidden: - logging.warning(f"Impossible de supprimer le message original (ID: {message_id})") + # Générer un titre pour le post avec le nom de l'auteur original + post_title = f"{original_message.author.display_name} - " + if content and len(content) > 0: + # Utiliser le contenu pour le reste du titre + remaining_length = 100 - len(post_title) + post_title += content[:remaining_length] + else: + post_title += "Message transféré" + + if len(post_title) > 100: + post_title = post_title[:97] + "..." + + # Ajouter un message informatif au début du contenu + transfer_notice = f"**Message original de {original_message.author.mention}**\n" + transfer_notice += f"*Ce message a été transféré par un membre du staff depuis {source_channel.mention}*\n" + transfer_notice += "─" * 50 + "\n\n" + + # Combiner le message informatif avec le contenu original + full_content = transfer_notice + (content or "") + + # Créer le post dans le forum + thread = await target_channel.create_thread( + name=post_title, + content=full_content, + embeds=embeds[:10] if embeds else [], + files=files_to_send, + reason=f"Transfert depuis {source_channel.name} par {message.author.name}" + ) + transferred_message = thread.message # Le message initial du thread + + except discord.HTTPException as e: + logging.error(f"Erreur lors de la création du post dans le forum: {e}") + embed = discord.Embed( + title="❌ Erreur", + description=f"Une erreur est survenue lors de la création du post dans le forum: {str(e)}", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + else: + # Pour un canal textuel ou thread : utiliser un webhook + # Créer un webhook pour envoyer le message en tant que l'auteur original + webhooks = await target_channel.webhooks() + webhook = None - # Envoyer un message de confirmation dans le canal source - local_now = _to_local(datetime.now(timezone.utc)) - embed = discord.Embed( - title="✅ Message transféré", - description=f"Le message de **{original_message.author.name}** a été transféré vers {target_channel.mention}", - color=discord.Color.green(), - timestamp=datetime.now(timezone.utc) - ) - embed.add_field(name="👤 Auteur original", value=f"{original_message.author.mention}", inline=True) - embed.add_field(name="📤 Canal source", value=source_channel.mention, inline=True) - embed.add_field(name="📥 Canal destination", value=target_channel.mention, inline=True) - embed.add_field(name="🛡️ Modérateur", value=message.author.mention, inline=True) - embed.add_field(name="📝 Raison", value=reason, inline=False) + # Chercher un webhook existant créé par le bot + for wh in webhooks: + if wh.user == bot.user: + webhook = wh + break - confirmation_msg = await source_channel.send(embed=embed) - asyncio.create_task(delete_after_delay(confirmation_msg)) + # Créer un webhook si nécessaire + if not webhook: + try: + webhook = await target_channel.create_webhook(name="Mamie Henriette - Transfert") + except discord.Forbidden: + embed = discord.Embed( + title="❌ Erreur", + description="Je n'ai pas la permission de créer un webhook dans le canal de destination.", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return - # Logger dans le canal de modération - log_embed = discord.Embed( - title="📨 Transfert de message", - description=f"Un message de **{original_message.author.name}** a été transféré", - color=discord.Color.blue(), - timestamp=datetime.now(timezone.utc) - ) - log_embed.add_field(name="👤 Auteur original", value=f"{original_message.author.name}\n`{original_message.author.id}`", inline=True) - log_embed.add_field(name="🛡️ Modérateur", value=f"**{message.author.name}**", inline=True) - log_embed.add_field(name="📅 Date et heure", value=local_now.strftime('%d/%m/%Y à %H:%M'), inline=True) - log_embed.add_field(name="📤 De", value=source_channel.mention, inline=True) - log_embed.add_field(name="📥 Vers", value=target_channel.mention, inline=True) - log_embed.add_field(name="📝 Raison", value=reason, inline=False) - - # Ajouter un aperçu du contenu - preview = content[:100] + "..." if len(content) > 100 else content - if preview: - log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False) - - log_embed.set_footer(text=f"ID Auteur: {original_message.author.id} • Serveur: {message.guild.name}") - - await send_to_moderation_log_channel(bot, log_embed) - - # Supprimer la commande de transfert - await safe_delete_message(message) - - except discord.HTTPException as e: - logging.error(f"Erreur lors du transfert du message: {e}") - embed = discord.Embed( - title="❌ Erreur", - description=f"Une erreur est survenue lors du transfert du message: {str(e)}", - color=discord.Color.red() - ) - msg = await message.channel.send(embed=embed) - asyncio.create_task(delete_after_delay(msg)) + # Envoyer le message via le webhook + try: + transferred_message = await webhook.send( + content=content, + username=original_message.author.display_name, + avatar_url=original_message.author.display_avatar.url, + embeds=embeds[:10] if embeds else [], + files=files_to_send, + allowed_mentions=discord.AllowedMentions.none(), + wait=True # Attendre pour récupérer le message envoyé + ) + except discord.HTTPException as e: + logging.error(f"Erreur lors du transfert du message: {e}") + embed = discord.Embed( + title="❌ Erreur", + description=f"Une erreur est survenue lors du transfert du message: {str(e)}", + color=discord.Color.red() + ) + msg = await message.channel.send(embed=embed) + asyncio.create_task(delete_after_delay(msg)) + return + + # Supprimer le message original + try: + await original_message.delete() + except discord.Forbidden: + logging.warning(f"Impossible de supprimer le message original (ID: {message_id})") + + # Enregistrer l'événement de transfert dans la base de données + transfer_details = f"De {source_channel.name} vers {target_channel.name}" + if isinstance(target_channel, ForumChannel): + transfer_details += " (forum)" + + transfer_event = ModerationEvent( + type='transfer', + username=original_message.author.name, + discord_id=str(original_message.author.id), + created_at=datetime.now(timezone.utc), + reason=f"{reason} | {transfer_details}", + staff_id=str(message.author.id), + staff_name=message.author.name + ) + db.session.add(transfer_event) + _commit_with_retry() + + # Envoyer un message de confirmation dans le canal source + local_now = _to_local(datetime.now(timezone.utc)) + destination_info = target_channel.mention if isinstance(target_channel, (TextChannel, Thread)) else f"le forum {target_channel.name}" + + embed = discord.Embed( + title="✅ Message transféré", + description=f"Le message de **{original_message.author.name}** a été transféré vers {destination_info}", + color=discord.Color.green(), + timestamp=datetime.now(timezone.utc) + ) + embed.add_field(name="👤 Auteur original", value=f"{original_message.author.mention}", inline=True) + embed.add_field(name="📤 Canal source", value=source_channel.mention, inline=True) + embed.add_field(name="📥 Canal destination", value=destination_info, inline=True) + embed.add_field(name="🛡️ Modérateur", value=message.author.mention, inline=True) + embed.add_field(name="📝 Raison", value=reason, inline=False) + + if isinstance(target_channel, ForumChannel): + embed.add_field(name="ℹ️ Type", value="Nouveau post créé dans le forum", inline=False) + + confirmation_msg = await source_channel.send(embed=embed) + asyncio.create_task(delete_after_delay(confirmation_msg)) + + # Logger dans le canal de modération + log_embed = discord.Embed( + title="📨 Transfert de message", + description=f"Un message de **{original_message.author.name}** a été transféré", + color=discord.Color.blue(), + timestamp=datetime.now(timezone.utc) + ) + log_embed.add_field(name="👤 Auteur original", value=f"{original_message.author.name}\n`{original_message.author.id}`", inline=True) + log_embed.add_field(name="🛡️ Modérateur", value=f"**{message.author.name}**", inline=True) + log_embed.add_field(name="📅 Date et heure", value=local_now.strftime('%d/%m/%Y à %H:%M'), inline=True) + log_embed.add_field(name="📤 De", value=source_channel.mention, inline=True) + log_embed.add_field(name="📥 Vers", value=f"{target_channel.name} ({type(target_channel).__name__})", inline=True) + log_embed.add_field(name="📝 Raison", value=reason, inline=False) + + # Ajouter un aperçu du contenu + preview = content[:100] + "..." if content and len(content) > 100 else content + if preview: + log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False) + + log_embed.set_footer(text=f"ID Auteur: {original_message.author.id} • Serveur: {message.guild.name}") + + await send_to_moderation_log_channel(bot, log_embed) + + # Supprimer la commande de transfert + await safe_delete_message(message) diff --git a/webapp/templates/moderation.html b/webapp/templates/moderation.html index b35f643..13bbad8 100644 --- a/webapp/templates/moderation.html +++ b/webapp/templates/moderation.html @@ -95,6 +95,10 @@ !banlist Liste des utilisateurs bannis +
+ !transfert #canal message_id + Transfère un message vers un autre canal (textuel, thread ou forum) +
@@ -129,6 +133,10 @@ Warn {% elif mod_event.type == 'unban' %} Unban + {% elif mod_event.type == 'transfer' %} + Transfert + {% elif mod_event.type == 'timeout' %} + Timeout {% else %} {{ mod_event.type }} {% endif %} From 830ce61796b3a221f3f5dba9d292193a6c01ec3b Mon Sep 17 00:00:00 2001 From: Mow910 Date: Sat, 14 Feb 2026 18:28:03 +0100 Subject: [PATCH 3/4] sans com --- discordbot/moderation.py | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/discordbot/moderation.py b/discordbot/moderation.py index b8baf13..6bf6b0a 100644 --- a/discordbot/moderation.py +++ b/discordbot/moderation.py @@ -1414,10 +1414,6 @@ async def handle_say_command(message: Message, bot): logging.error(f"Erreur lors de l'envoi du message: {e}") async def handle_transfer_command(message: Message, bot): - """ - Commande pour transférer un message d'un canal à un autre en préservant l'identité de l'auteur original. - Syntaxe: !transfert #canal_destination [raison] - """ if not has_staff_role(message.author.roles): await send_access_denied(message.channel) return @@ -1453,7 +1449,6 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Récupérer le canal de destination target_channel = None if message.channel_mentions: target_channel = message.channel_mentions[0] @@ -1474,7 +1469,6 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Vérifier que le canal de destination est compatible if not isinstance(target_channel, (TextChannel, ForumChannel, Thread)): embed = discord.Embed( title="❌ Erreur", @@ -1485,14 +1479,11 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Récupérer le message à transférer message_id = None source_channel = message.channel - # Vérifier si c'est un lien Discord if 'discord.com/channels/' in parts[2]: try: - # Format: https://discord.com/channels/guild_id/channel_id/message_id link_parts = parts[2].split('/') source_channel_id = int(link_parts[-2]) message_id = int(link_parts[-1]) @@ -1517,7 +1508,6 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return else: - # C'est un ID de message try: message_id = int(parts[2]) except ValueError: @@ -1530,7 +1520,6 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Récupérer le message original try: original_message = await source_channel.fetch_message(message_id) except discord.NotFound: @@ -1552,15 +1541,12 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Raison du transfert (optionnel) reason = parts[3] if len(parts) > 3 else "Message posté dans le mauvais canal" - # Préparer le contenu du message content = original_message.content embeds = original_message.embeds files_to_send = [] - # Télécharger les pièces jointes for attachment in original_message.attachments: try: file_data = await attachment.read() @@ -1571,16 +1557,12 @@ async def handle_transfer_command(message: Message, bot): except Exception as e: logging.error(f"Erreur lors du téléchargement de la pièce jointe: {e}") - # Gestion différente selon le type de canal transferred_message = None if isinstance(target_channel, ForumChannel): - # Pour un forum : créer un nouveau post (thread) try: - # Générer un titre pour le post avec le nom de l'auteur original post_title = f"{original_message.author.display_name} - " if content and len(content) > 0: - # Utiliser le contenu pour le reste du titre remaining_length = 100 - len(post_title) post_title += content[:remaining_length] else: @@ -1589,15 +1571,12 @@ async def handle_transfer_command(message: Message, bot): if len(post_title) > 100: post_title = post_title[:97] + "..." - # Ajouter un message informatif au début du contenu transfer_notice = f"**Message original de {original_message.author.mention}**\n" transfer_notice += f"*Ce message a été transféré par un membre du staff depuis {source_channel.mention}*\n" transfer_notice += "─" * 50 + "\n\n" - # Combiner le message informatif avec le contenu original full_content = transfer_notice + (content or "") - # Créer le post dans le forum thread = await target_channel.create_thread( name=post_title, content=full_content, @@ -1605,7 +1584,7 @@ async def handle_transfer_command(message: Message, bot): files=files_to_send, reason=f"Transfert depuis {source_channel.name} par {message.author.name}" ) - transferred_message = thread.message # Le message initial du thread + transferred_message = thread.message except discord.HTTPException as e: logging.error(f"Erreur lors de la création du post dans le forum: {e}") @@ -1619,18 +1598,14 @@ async def handle_transfer_command(message: Message, bot): return else: - # Pour un canal textuel ou thread : utiliser un webhook - # Créer un webhook pour envoyer le message en tant que l'auteur original webhooks = await target_channel.webhooks() webhook = None - # Chercher un webhook existant créé par le bot for wh in webhooks: if wh.user == bot.user: webhook = wh break - # Créer un webhook si nécessaire if not webhook: try: webhook = await target_channel.create_webhook(name="Mamie Henriette - Transfert") @@ -1644,7 +1619,6 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Envoyer le message via le webhook try: transferred_message = await webhook.send( content=content, @@ -1653,7 +1627,7 @@ async def handle_transfer_command(message: Message, bot): embeds=embeds[:10] if embeds else [], files=files_to_send, allowed_mentions=discord.AllowedMentions.none(), - wait=True # Attendre pour récupérer le message envoyé + wait=True ) except discord.HTTPException as e: logging.error(f"Erreur lors du transfert du message: {e}") @@ -1666,13 +1640,11 @@ async def handle_transfer_command(message: Message, bot): asyncio.create_task(delete_after_delay(msg)) return - # Supprimer le message original try: await original_message.delete() except discord.Forbidden: logging.warning(f"Impossible de supprimer le message original (ID: {message_id})") - # Enregistrer l'événement de transfert dans la base de données transfer_details = f"De {source_channel.name} vers {target_channel.name}" if isinstance(target_channel, ForumChannel): transfer_details += " (forum)" @@ -1689,7 +1661,6 @@ async def handle_transfer_command(message: Message, bot): db.session.add(transfer_event) _commit_with_retry() - # Envoyer un message de confirmation dans le canal source local_now = _to_local(datetime.now(timezone.utc)) destination_info = target_channel.mention if isinstance(target_channel, (TextChannel, Thread)) else f"le forum {target_channel.name}" @@ -1711,7 +1682,6 @@ async def handle_transfer_command(message: Message, bot): confirmation_msg = await source_channel.send(embed=embed) asyncio.create_task(delete_after_delay(confirmation_msg)) - # Logger dans le canal de modération log_embed = discord.Embed( title="📨 Transfert de message", description=f"Un message de **{original_message.author.name}** a été transféré", @@ -1725,7 +1695,6 @@ async def handle_transfer_command(message: Message, bot): log_embed.add_field(name="📥 Vers", value=f"{target_channel.name} ({type(target_channel).__name__})", inline=True) log_embed.add_field(name="📝 Raison", value=reason, inline=False) - # Ajouter un aperçu du contenu preview = content[:100] + "..." if content and len(content) > 100 else content if preview: log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False) @@ -1734,6 +1703,5 @@ async def handle_transfer_command(message: Message, bot): await send_to_moderation_log_channel(bot, log_embed) - # Supprimer la commande de transfert await safe_delete_message(message) From 179876d2ce2b51ec2cbb7fc1b382ac0c5a7de13d Mon Sep 17 00:00:00 2001 From: Mow910 Date: Sat, 14 Feb 2026 18:32:32 +0100 Subject: [PATCH 4/4] Raison pour le titre du post dans le forum --- discordbot/moderation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/discordbot/moderation.py b/discordbot/moderation.py index 6bf6b0a..4935080 100644 --- a/discordbot/moderation.py +++ b/discordbot/moderation.py @@ -1063,7 +1063,8 @@ async def handle_staff_help_command(message: Message, bot): " Ex: `!transfert #entraide 123456789012345678`\n" " Ex: `!transfert #général https://discord.com/channels/.../...`\n" " Le message sera envoyé comme si c'était l'auteur original\n" - " Supporte les canaux textuels, threads et forums (crée un post)" + " Supporte les canaux textuels, threads et forums (crée un post)\n" + " Pour les forums, la raison devient le titre du post" ), inline=False ) @@ -1562,7 +1563,10 @@ async def handle_transfer_command(message: Message, bot): if isinstance(target_channel, ForumChannel): try: post_title = f"{original_message.author.display_name} - " - if content and len(content) > 0: + if reason and reason != "Message posté dans le mauvais canal": + remaining_length = 100 - len(post_title) + post_title += reason[:remaining_length] + elif content and len(content) > 0: remaining_length = 100 - len(post_title) post_title += content[:remaining_length] else: