Merge pull request #4 from Mow910/discord-transfert-msg

Discord transfert msg
This commit is contained in:
Mow910
2026-02-14 18:34:03 +01:00
committed by GitHub
3 changed files with 320 additions and 3 deletions
+6 -1
View File
@@ -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
+306 -2
View File
@@ -4,12 +4,13 @@ import time
import os
import re
import discord
import io
from datetime import datetime, timezone, timedelta
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'
@@ -1055,7 +1056,15 @@ 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 #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)\n"
" Pour les forums, la raison devient le titre du post"
),
inline=False
)
@@ -1405,3 +1414,298 @@ 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):
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
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
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
message_id = None
source_channel = message.channel
if 'discord.com/channels/' in parts[2]:
try:
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:
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
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
reason = parts[3] if len(parts) > 3 else "Message posté dans le mauvais canal"
content = original_message.content
embeds = original_message.embeds
files_to_send = []
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}")
transferred_message = None
if isinstance(target_channel, ForumChannel):
try:
post_title = f"{original_message.author.display_name} - "
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:
post_title += "Message transféré"
if len(post_title) > 100:
post_title = post_title[:97] + "..."
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"
full_content = transfer_notice + (content or "")
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
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:
webhooks = await target_channel.webhooks()
webhook = None
for wh in webhooks:
if wh.user == bot.user:
webhook = wh
break
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
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
)
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
try:
await original_message.delete()
except discord.Forbidden:
logging.warning(f"Impossible de supprimer le message original (ID: {message_id})")
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()
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))
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)
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)
await safe_delete_message(message)
+8
View File
@@ -95,6 +95,10 @@
<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>
</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>
</details>
@@ -129,6 +133,10 @@
<span class="text-xs font-medium text-yellow-600 dark:text-yellow-400">Warn</span>
{% elif mod_event.type == 'unban' %}
<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 %}
<span class="text-xs font-medium text-slate-600 dark:text-slate-400">{{ mod_event.type }}</span>
{% endif %}