Amélioration de la commande !transfert pour supporter les canaux textuels, threads et forums. Ajout de vérifications de compatibilité pour le canal de destination et mise à jour de la documentation des commandes. Gestion des transferts de messages avec création de posts dans les forums et envoi via webhooks pour les autres types de canaux.

This commit is contained in:
2026-02-14 18:01:31 +01:00
parent 11348bda39
commit 5709b1c0a3
2 changed files with 184 additions and 92 deletions
+120 -36
View File
@@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo
from database import db from database import db
from database.helpers import ConfigurationHelper from database.helpers import ConfigurationHelper
from database.models import ModerationEvent from database.models import ModerationEvent
from discord import Message from discord import Message, TextChannel, ForumChannel, Thread
def _get_local_tz(): def _get_local_tz():
tz_name = os.environ.get('APP_TZ') or os.environ.get('TZ') or 'Europe/Paris' 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" " Transfère un message vers un autre canal\n"
" *Alias: !transfer, !move*\n" " *Alias: !transfer, !move*\n"
" Ex: `!transfert #entraide 123456789012345678`\n" " Ex: `!transfert #entraide 123456789012345678`\n"
" Ex: `!transfert #general https://discord.com/channels/.../...`\n" " Ex: `!transfert #général https://discord.com/channels/.../...`\n"
" Le message sera envoyé comme si c'était l'auteur original" " 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 inline=False
) )
@@ -1473,6 +1474,17 @@ async def handle_transfer_command(message: Message, bot):
asyncio.create_task(delete_after_delay(msg)) asyncio.create_task(delete_after_delay(msg))
return 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 # Récupérer le message à transférer
message_id = None message_id = None
source_channel = message.channel source_channel = message.channel
@@ -1543,6 +1555,71 @@ async def handle_transfer_command(message: Message, bot):
# Raison du transfert (optionnel) # Raison du transfert (optionnel)
reason = parts[3] if len(parts) > 3 else "Message posté dans le mauvais canal" 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()
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}")
# 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:
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 # Créer un webhook pour envoyer le message en tant que l'auteur original
webhooks = await target_channel.webhooks() webhooks = await target_channel.webhooks()
webhook = None webhook = None
@@ -1567,32 +1644,27 @@ async def handle_transfer_command(message: Message, bot):
asyncio.create_task(delete_after_delay(msg)) asyncio.create_task(delete_after_delay(msg))
return 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 # Envoyer le message via le webhook
try: try:
await webhook.send( transferred_message = await webhook.send(
content=content, content=content,
username=original_message.author.display_name, username=original_message.author.display_name,
avatar_url=original_message.author.display_avatar.url, avatar_url=original_message.author.display_avatar.url,
embeds=embeds[:10] if embeds else [], # Discord limite à 10 embeds embeds=embeds[:10] if embeds else [],
files=files_to_send, files=files_to_send,
allowed_mentions=discord.AllowedMentions.none() 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 # Supprimer le message original
try: try:
@@ -1600,20 +1672,42 @@ async def handle_transfer_command(message: Message, bot):
except discord.Forbidden: except discord.Forbidden:
logging.warning(f"Impossible de supprimer le message original (ID: {message_id})") 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 # Envoyer un message de confirmation dans le canal source
local_now = _to_local(datetime.now(timezone.utc)) 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( embed = discord.Embed(
title="✅ Message transféré", title="✅ Message transféré",
description=f"Le message de **{original_message.author.name}** a été transféré vers {target_channel.mention}", description=f"Le message de **{original_message.author.name}** a été transféré vers {destination_info}",
color=discord.Color.green(), color=discord.Color.green(),
timestamp=datetime.now(timezone.utc) timestamp=datetime.now(timezone.utc)
) )
embed.add_field(name="👤 Auteur original", value=f"{original_message.author.mention}", inline=True) 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 source", value=source_channel.mention, inline=True)
embed.add_field(name="📥 Canal destination", value=target_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="🛡️ Modérateur", value=message.author.mention, inline=True)
embed.add_field(name="📝 Raison", value=reason, inline=False) 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) confirmation_msg = await source_channel.send(embed=embed)
asyncio.create_task(delete_after_delay(confirmation_msg)) asyncio.create_task(delete_after_delay(confirmation_msg))
@@ -1628,11 +1722,11 @@ async def handle_transfer_command(message: Message, bot):
log_embed.add_field(name="🛡️ Modérateur", value=f"**{message.author.name}**", 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="📅 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="📤 De", value=source_channel.mention, inline=True)
log_embed.add_field(name="📥 Vers", value=target_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) log_embed.add_field(name="📝 Raison", value=reason, inline=False)
# Ajouter un aperçu du contenu # Ajouter un aperçu du contenu
preview = content[:100] + "..." if len(content) > 100 else content preview = content[:100] + "..." if content and len(content) > 100 else content
if preview: if preview:
log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False) log_embed.add_field(name="💬 Aperçu du message", value=preview, inline=False)
@@ -1643,13 +1737,3 @@ async def handle_transfer_command(message: Message, bot):
# Supprimer la commande de transfert # Supprimer la commande de transfert
await safe_delete_message(message) 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))
+8
View File
@@ -95,6 +95,10 @@
<code class="text-slate-700 dark:text-slate-300 font-mono">!banlist</code> <code class="text-slate-700 dark:text-slate-300 font-mono">!banlist</code>
<span class="text-slate-500 dark:text-slate-400">Liste des utilisateurs bannis</span> <span class="text-slate-500 dark:text-slate-400">Liste des utilisateurs bannis</span>
</div> </div>
<div class="px-5 py-3 flex flex-col sm:flex-row sm:items-start gap-1 sm:gap-4">
<code class="text-slate-700 dark:text-slate-300 font-mono">!transfert #canal message_id</code>
<span class="text-slate-500 dark:text-slate-400">Transfère un message vers un autre canal (textuel, thread ou forum)</span>
</div>
</div> </div>
</div> </div>
</details> </details>
@@ -129,6 +133,10 @@
<span class="text-xs font-medium text-yellow-600 dark:text-yellow-400">Warn</span> <span class="text-xs font-medium text-yellow-600 dark:text-yellow-400">Warn</span>
{% elif mod_event.type == 'unban' %} {% elif mod_event.type == 'unban' %}
<span class="text-xs font-medium text-green-600 dark:text-green-400">Unban</span> <span class="text-xs font-medium text-green-600 dark:text-green-400">Unban</span>
{% elif mod_event.type == 'transfer' %}
<span class="text-xs font-medium text-blue-600 dark:text-blue-400">Transfert</span>
{% elif mod_event.type == 'timeout' %}
<span class="text-xs font-medium text-purple-600 dark:text-purple-400">Timeout</span>
{% else %} {% else %}
<span class="text-xs font-medium text-slate-600 dark:text-slate-400">{{ mod_event.type }}</span> <span class="text-xs font-medium text-slate-600 dark:text-slate-400">{{ mod_event.type }}</span>
{% endif %} {% endif %}