migration vers new components V2, et ajout de diverse fonctions !
This commit is contained in:
parent
3590cb748f
commit
ba9e297cb8
14 changed files with 851 additions and 371 deletions
|
|
@ -6,118 +6,63 @@ from datetime import datetime, timedelta
|
|||
import re
|
||||
|
||||
class ModReasonModal(disnake.ui.Modal):
|
||||
def __init__(self, parent_view, action_type):
|
||||
super().__init__(title=f"Raison pour {action_type}", components=[])
|
||||
self.parent_view = parent_view
|
||||
def __init__(self, cog, action_type, member):
|
||||
self.cog = cog
|
||||
self.action_type = action_type
|
||||
self.member = member
|
||||
|
||||
self.reason = disnake.ui.TextInput(
|
||||
self.reason_input = disnake.ui.TextInput(
|
||||
label="Raison",
|
||||
placeholder="Entrez la raison ici...",
|
||||
required=True,
|
||||
max_length=500
|
||||
max_length=500,
|
||||
custom_id="reason"
|
||||
)
|
||||
self.append_component(self.reason)
|
||||
|
||||
components = [self.reason_input]
|
||||
|
||||
if action_type == "TIMEOUT":
|
||||
self.duration = disnake.ui.TextInput(
|
||||
self.duration_input = disnake.ui.TextInput(
|
||||
label="Durée (ex: 1h, 30m, 1d)",
|
||||
placeholder="1h",
|
||||
required=True,
|
||||
max_length=10
|
||||
max_length=10,
|
||||
custom_id="duration"
|
||||
)
|
||||
self.append_component(self.duration)
|
||||
components.append(self.duration_input)
|
||||
|
||||
async def callback(self, interaction: disnake.Interaction):
|
||||
reason = self.reason.value
|
||||
member = self.parent_view.member
|
||||
super().__init__(title=f"Raison pour {action_type}", components=components)
|
||||
|
||||
async def callback(self, interaction: disnake.ModalInteraction):
|
||||
reason = interaction.text_values["reason"]
|
||||
member = self.member
|
||||
|
||||
cog = interaction.bot.get_cog("Moderation")
|
||||
if not cog:
|
||||
return await interaction.response.send_message("❌ Erreur interne.", ephemeral=True)
|
||||
|
||||
if self.action_type == "WARN":
|
||||
await cog.warn(interaction, member, reason)
|
||||
await self.cog.warn(interaction, member, reason)
|
||||
elif self.action_type == "TIMEOUT":
|
||||
await cog.timeout(interaction, member, self.duration.value, reason)
|
||||
duration = interaction.text_values["duration"]
|
||||
await self.cog.timeout(interaction, member, duration, reason)
|
||||
elif self.action_type == "KICK":
|
||||
await cog.kick(interaction, member, reason)
|
||||
await self.cog.kick(interaction, member, reason)
|
||||
elif self.action_type == "BAN":
|
||||
await cog.ban(interaction, member, reason)
|
||||
await self.cog.ban(interaction, member, reason)
|
||||
|
||||
await self.parent_view.update_panel(interaction)
|
||||
|
||||
class ModPanelView(disnake.ui.View):
|
||||
def __init__(self, member, moderator, db_path):
|
||||
super().__init__(timeout=120)
|
||||
self.member = member
|
||||
self.moderator = moderator
|
||||
self.db_path = db_path
|
||||
|
||||
async def update_panel(self, interaction: disnake.Interaction):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT COUNT(*) FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "WARN" AND status = "ACTIVE"', (interaction.guild_id, self.member.id))
|
||||
warn_count = cursor.fetchone()[0]
|
||||
conn.close()
|
||||
|
||||
embed = disnake.Embed(
|
||||
title=f"🛡️ Panel de Modération - {self.member.name}",
|
||||
description=f"Gestion de l'utilisateur {self.member.mention}",
|
||||
color=disnake.Color.blue(),
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
embed.set_thumbnail(url=self.member.display_avatar.url)
|
||||
embed.add_field(name="👤 Utilisateur", value=f"ID: `{self.member.id}`\nTag: `{self.member}`", inline=True)
|
||||
embed.add_field(name="⚠️ Avertissements", value=f"Actifs: **{warn_count}**", inline=True)
|
||||
embed.add_field(name="📅 Dates", value=f"Rejoint: <t:{int(self.member.joined_at.timestamp())}:R>\nCréé: <t:{int(self.member.created_at.timestamp())}:R>", inline=False)
|
||||
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.edit_message(embed=embed, view=self)
|
||||
else:
|
||||
await interaction.edit_original_response(embed=embed, view=self)
|
||||
|
||||
@disnake.ui.button(label="Avertir", style=disnake.ButtonStyle.secondary, emoji="⚠️")
|
||||
async def warn_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
await interaction.response.send_modal(ModReasonModal(self, "WARN"))
|
||||
|
||||
@disnake.ui.button(label="Timeout", style=disnake.ButtonStyle.secondary, emoji="⏳")
|
||||
async def timeout_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
await interaction.response.send_modal(ModReasonModal(self, "TIMEOUT"))
|
||||
|
||||
@disnake.ui.button(label="Expulser", style=disnake.ButtonStyle.secondary, emoji="👢")
|
||||
async def kick_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
await interaction.response.send_modal(ModReasonModal(self, "KICK"))
|
||||
|
||||
@disnake.ui.button(label="Bannir", style=disnake.ButtonStyle.danger, emoji="🔨")
|
||||
async def ban_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
await interaction.response.send_modal(ModReasonModal(self, "BAN"))
|
||||
|
||||
@disnake.ui.button(label="Historique", style=disnake.ButtonStyle.primary, emoji="📜")
|
||||
async def history_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
cog = interaction.bot.get_cog("Moderation")
|
||||
await cog.history(interaction, self.member)
|
||||
|
||||
@disnake.ui.button(label="Clear Warns", style=disnake.ButtonStyle.danger, emoji="🧹")
|
||||
async def clear_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
cog = interaction.bot.get_cog("Moderation")
|
||||
await cog.clearwarns(interaction, self.member, reason="Nettoyage via Panel")
|
||||
await self.update_panel(interaction)
|
||||
await self.cog.send_modpanel_v2(interaction, member, edit=True)
|
||||
|
||||
class ModConfigModal(disnake.ui.Modal):
|
||||
def __init__(self, field_name, current_value):
|
||||
super().__init__(components=[], title=f"Modifier {field_name}")
|
||||
self.field_name = field_name
|
||||
self.input = disnake.ui.TextInput(
|
||||
self.input_field = disnake.ui.TextInput(
|
||||
label=field_name,
|
||||
value=str(current_value),
|
||||
placeholder="Entrez une valeur numérique...",
|
||||
required=True
|
||||
required=True,
|
||||
custom_id="value"
|
||||
)
|
||||
self.append_component(self.input)
|
||||
super().__init__(title=f"Modifier {field_name}", components=[self.input_field])
|
||||
|
||||
async def callback(self, interaction: disnake.Interaction):
|
||||
value = self.input.value
|
||||
async def callback(self, interaction: disnake.ModalInteraction):
|
||||
value = interaction.text_values["value"]
|
||||
cog = interaction.bot.get_cog("Moderation")
|
||||
|
||||
mapping = {
|
||||
|
|
@ -137,62 +82,8 @@ class ModConfigModal(disnake.ui.Modal):
|
|||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
await interaction.response.send_message(f"✅ {self.field_name} mis à jour sur **{value}**.", ephemeral=True)
|
||||
|
||||
class ModConfigView(disnake.ui.View):
|
||||
def __init__(self, guild_id, db_path):
|
||||
super().__init__(timeout=60)
|
||||
self.guild_id = guild_id
|
||||
self.db_path = db_path
|
||||
|
||||
@disnake.ui.button(label="Log Channel", style=disnake.ButtonStyle.primary, emoji="📁")
|
||||
async def log_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
view = disnake.ui.View()
|
||||
select = disnake.ui.ChannelSelect(
|
||||
placeholder="Sélectionnez le salon de logs...",
|
||||
channel_types=[disnake.ChannelType.text]
|
||||
)
|
||||
|
||||
async def select_callback(interaction: disnake.Interaction):
|
||||
channel = select.values[0]
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("UPDATE mod_config SET log_channel_id = ? WHERE guild_id = ?", (channel.id, interaction.guild_id))
|
||||
conn.commit(); conn.close()
|
||||
await interaction.response.send_message(f"✅ Salon de logs défini sur {channel.mention}", ephemeral=True)
|
||||
|
||||
select.callback = select_callback
|
||||
view.add_item(select)
|
||||
await interaction.response.send_message("Choisissez le salon de logs :", view=view, ephemeral=True)
|
||||
|
||||
@disnake.ui.button(label="Paliers Warns", style=disnake.ButtonStyle.secondary, emoji="⚖️")
|
||||
async def limits_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT warn_limit_timeout, warn_limit_kick, warn_limit_ban FROM mod_config WHERE guild_id = ?", (self.guild_id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
|
||||
view = disnake.ui.View()
|
||||
|
||||
btn_t = disnake.ui.Button(label=f"Timeout ({res[0]})", style=disnake.ButtonStyle.secondary)
|
||||
btn_t.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Timeout", res[0]))
|
||||
|
||||
btn_k = disnake.ui.Button(label=f"Kick ({res[1]})", style=disnake.ButtonStyle.secondary)
|
||||
btn_k.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Kick", res[1]))
|
||||
|
||||
btn_b = disnake.ui.Button(label=f"Ban ({res[2]})", style=disnake.ButtonStyle.secondary)
|
||||
btn_b.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Ban", res[2]))
|
||||
|
||||
view.add_item(btn_t); view.add_item(btn_k); view.add_item(btn_b)
|
||||
await interaction.response.send_message("Quelle limite modifier ?", view=view, ephemeral=True)
|
||||
|
||||
@disnake.ui.button(label="Durée Timeout", style=disnake.ButtonStyle.secondary, emoji="⏱️")
|
||||
async def duration_btn(self, button: disnake.ui.Button, interaction: disnake.Interaction):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT timeout_duration FROM mod_config WHERE guild_id = ?", (self.guild_id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
await interaction.response.send_modal(ModConfigModal("Durée Timeout (s)", res[0]))
|
||||
await interaction.response.send_message(f"✅ {self.field_name} mis à jour.", ephemeral=True)
|
||||
await cog.send_modconfig_v2(interaction, edit=True)
|
||||
|
||||
class Moderation(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
|
|
@ -204,7 +95,17 @@ class Moderation(commands.Cog):
|
|||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''CREATE TABLE IF NOT EXISTS sanctions (id INTEGER PRIMARY KEY AUTOINCREMENT, guild_id INTEGER, user_id INTEGER, moderator_id INTEGER, type TEXT, reason TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, duration INTEGER, status TEXT DEFAULT 'ACTIVE')''')
|
||||
cursor.execute('''CREATE TABLE IF NOT EXISTS mod_config (guild_id INTEGER PRIMARY KEY, log_channel_id INTEGER, warn_limit_timeout INTEGER DEFAULT 3, warn_limit_kick INTEGER DEFAULT 5, warn_limit_ban INTEGER DEFAULT 10, timeout_duration INTEGER DEFAULT 3600)''')
|
||||
cursor.execute('''CREATE TABLE IF NOT EXISTS mod_config (
|
||||
guild_id INTEGER PRIMARY KEY,
|
||||
log_channel_id INTEGER,
|
||||
board_channel_id INTEGER,
|
||||
warn_limit_timeout INTEGER DEFAULT 3,
|
||||
warn_limit_kick INTEGER DEFAULT 5,
|
||||
warn_limit_ban INTEGER DEFAULT 10,
|
||||
timeout_duration INTEGER DEFAULT 3600
|
||||
)''')
|
||||
try: cursor.execute("ALTER TABLE mod_config ADD COLUMN board_channel_id INTEGER")
|
||||
except: pass
|
||||
conn.commit(); conn.close()
|
||||
|
||||
def parse_duration(self, duration_str: str) -> int:
|
||||
|
|
@ -215,110 +116,226 @@ class Moderation(commands.Cog):
|
|||
for value, unit in matches: total_seconds += int(value) * units[unit]
|
||||
return total_seconds
|
||||
|
||||
async def send_mod_log(self, guild, embed):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT log_channel_id FROM mod_config WHERE guild_id = ?', (guild.id,))
|
||||
result = cursor.fetchone(); conn.close()
|
||||
if result and result[0]:
|
||||
log_channel = guild.get_channel(result[0])
|
||||
if log_channel: await log_channel.send(embed=embed)
|
||||
def _type_to_emoji(self, s_type: str) -> str:
|
||||
return {"WARN": "⚠️", "TIMEOUT": "⏳", "KICK": "👢", "BAN": "🔨"}.get(s_type, "🛡️")
|
||||
|
||||
@commands.slash_command(name="modpanel", description="Ouvre le panel de modération pour un membre")
|
||||
def build_sanction_board_components(self, *, s_type: str, user, moderator, reason, timestamp, s_id, duration=None) -> list:
|
||||
ts_int = int(datetime.fromisoformat(str(timestamp)).timestamp()) if not isinstance(timestamp, datetime) else int(timestamp.timestamp())
|
||||
emoji = self._type_to_emoji(s_type)
|
||||
|
||||
children = [
|
||||
disnake.ui.Section(f"{emoji} **SANCTION : {s_type}**", accessory=disnake.ui.Thumbnail(user.display_avatar.url)),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.TextDisplay(f"- **Membre sanctionné** : {user.mention}"),
|
||||
disnake.ui.TextDisplay(f"- **Modérateur** : {moderator.mention}"),
|
||||
disnake.ui.TextDisplay(f"- **Raison** : “ *{reason if reason else 'Aucune raison spécifiée'}* ”"),
|
||||
disnake.ui.TextDisplay(f"- **Date** : <t:{ts_int}:F>")
|
||||
]
|
||||
|
||||
if s_type == "TIMEOUT" and duration:
|
||||
children.append(disnake.ui.TextDisplay(f"- **Durée** : `{duration}s`"))
|
||||
|
||||
children.extend([
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.TextDisplay(f"🆔 **ID** : `{s_id}`")
|
||||
])
|
||||
|
||||
return [disnake.ui.Container(*children)]
|
||||
|
||||
async def send_mod_log(self, guild, components):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('SELECT log_channel_id FROM mod_config WHERE guild_id = ?', (guild.id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
if res and res[0]:
|
||||
chan = guild.get_channel(res[0])
|
||||
if chan: await chan.send(components=components)
|
||||
|
||||
async def send_sanction_board(self, guild, components):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('SELECT board_channel_id FROM mod_config WHERE guild_id = ?', (guild.id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
if res and res[0]:
|
||||
chan = guild.get_channel(res[0])
|
||||
if chan: await chan.send(components=components)
|
||||
|
||||
async def send_modpanel_v2(self, interaction: disnake.Interaction, member: disnake.Member, edit=False):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('SELECT COUNT(*) FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "WARN" AND status = "ACTIVE"', (interaction.guild_id, member.id))
|
||||
warn_count = cursor.fetchone()[0]
|
||||
cursor.execute('SELECT type, reason, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? AND status = "ACTIVE" ORDER BY timestamp DESC LIMIT 3', (interaction.guild_id, member.id))
|
||||
rows = cursor.fetchall(); conn.close()
|
||||
|
||||
children = [
|
||||
disnake.ui.Section(f"🛡️ Panel : {member.name}", accessory=disnake.ui.Thumbnail(member.display_avatar.url)),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.TextDisplay(f"👤 **Membre :** {member.mention} (`{member.id}`)"),
|
||||
disnake.ui.TextDisplay(f"⚠️ **Avertissements actifs :** {warn_count}"),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.Section("⚠️ Infliger un avertissement", accessory=disnake.ui.Button(label="Warn", style=disnake.ButtonStyle.secondary, custom_id=f"modpan_warn:{member.id}")),
|
||||
disnake.ui.Section("⏳ Mettre en sourdine", accessory=disnake.ui.Button(label="Timeout", style=disnake.ButtonStyle.secondary, custom_id=f"modpan_timeout:{member.id}")),
|
||||
disnake.ui.Section("👢 Expulser du serveur", accessory=disnake.ui.Button(label="Kick", style=disnake.ButtonStyle.secondary, custom_id=f"modpan_kick:{member.id}")),
|
||||
disnake.ui.Section("🔨 Bannir définitivement", accessory=disnake.ui.Button(label="Ban", style=disnake.ButtonStyle.danger, custom_id=f"modpan_ban:{member.id}")),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.Section("🧹 Réinitialiser les warns", accessory=disnake.ui.Button(label="Clear", style=disnake.ButtonStyle.danger, custom_id=f"modpan_clear:{member.id}"))
|
||||
]
|
||||
|
||||
if rows:
|
||||
children.append(disnake.ui.Separator(divider=True))
|
||||
children.append(disnake.ui.TextDisplay("📌 **Dernières sanctions :**"))
|
||||
for t, r, ts in rows:
|
||||
ts_int = int(datetime.fromisoformat(str(ts)).timestamp()) if not isinstance(ts, datetime) else int(ts.timestamp())
|
||||
children.append(disnake.ui.TextDisplay(f"• {self._type_to_emoji(t)} **{t}** — {r} (<t:{ts_int}:R>)"))
|
||||
|
||||
components = [disnake.ui.Container(*children)]
|
||||
|
||||
if edit:
|
||||
if not interaction.response.is_done(): await interaction.response.edit_message(content=None, components=components)
|
||||
else: await interaction.edit_original_response(content=None, components=components)
|
||||
else:
|
||||
await interaction.response.send_message(components=components, ephemeral=True)
|
||||
|
||||
@commands.slash_command(name="modpanel", description="Ouvre le panel de modération")
|
||||
@commands.has_permissions(moderate_members=True)
|
||||
async def modpanel(self, interaction: disnake.ApplicationCommandInteraction, member: disnake.Member):
|
||||
if member.top_role >= interaction.user.top_role and interaction.user.id != interaction.guild.owner_id:
|
||||
return await interaction.response.send_message("❌ Impossible de gérer ce membre.", ephemeral=True)
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT COUNT(*) FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "WARN" AND status = "ACTIVE"', (interaction.guild_id, member.id))
|
||||
warn_count = cursor.fetchone()[0]; conn.close()
|
||||
embed = disnake.Embed(title=f"🛡️ Panel de Modération - {member.name}", description=f"Gestion de {member.mention}", color=disnake.Color.blue(), timestamp=datetime.now())
|
||||
embed.set_thumbnail(url=member.display_avatar.url)
|
||||
embed.add_field(name="👤 Utilisateur", value=f"ID: `{member.id}`", inline=True)
|
||||
embed.add_field(name="⚠️ Avertissements", value=f"Actifs: **{warn_count}**", inline=True)
|
||||
view = ModPanelView(member, interaction.user, self.db_path)
|
||||
await interaction.response.send_message(embed=embed, view=view, ephemeral=True)
|
||||
return await interaction.response.send_message("❌ Permissions insuffisantes.", ephemeral=True)
|
||||
await self.send_modpanel_v2(interaction, member)
|
||||
|
||||
async def send_modconfig_v2(self, interaction: disnake.Interaction, edit=False):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('SELECT log_channel_id, board_channel_id, warn_limit_timeout, warn_limit_kick, warn_limit_ban, timeout_duration FROM mod_config WHERE guild_id = ?', (interaction.guild_id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
if not res: return
|
||||
|
||||
children = [
|
||||
disnake.ui.Section("⚙️ Configuration Modération", accessory=disnake.ui.Thumbnail(interaction.guild.icon.url if interaction.guild.icon else None)),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.TextDisplay(f"📁 **Logs internes :** <#{res[0]}>" if res[0] else "📁 **Logs :** Non défini"),
|
||||
disnake.ui.TextDisplay(f"📢 **Affichage public :** <#{res[1]}>" if res[1] else "📢 **Affichage :** Non défini"),
|
||||
disnake.ui.TextDisplay(f"⚖️ **Paliers :** T:{res[2]} | K:{res[3]} | B:{res[4]}"),
|
||||
disnake.ui.TextDisplay(f"⏱️ **Timeout auto :** {res[5]}s"),
|
||||
disnake.ui.Separator(divider=True),
|
||||
disnake.ui.Section("📁 Salon des logs", accessory=disnake.ui.Button(label="Logs", style=disnake.ButtonStyle.primary, custom_id="modcfg_logs")),
|
||||
disnake.ui.Section("📢 Salon d'affichage", accessory=disnake.ui.Button(label="Public", style=disnake.ButtonStyle.primary, custom_id="modcfg_board")),
|
||||
disnake.ui.Section("⚖️ Modifier paliers", accessory=disnake.ui.Button(label="Paliers", style=disnake.ButtonStyle.secondary, custom_id="modcfg_limits")),
|
||||
disnake.ui.Section("⏱️ Durée timeout", accessory=disnake.ui.Button(label="Durée", style=disnake.ButtonStyle.secondary, custom_id="modcfg_duration"))
|
||||
]
|
||||
|
||||
components = [disnake.ui.Container(*children)]
|
||||
|
||||
if edit:
|
||||
if not interaction.response.is_done(): await interaction.response.edit_message(content=None, components=components)
|
||||
else: await interaction.edit_original_response(content=None, components=components)
|
||||
else:
|
||||
await interaction.response.send_message(components=components, ephemeral=True)
|
||||
|
||||
@commands.slash_command(name="modconfig", description="Configuration de la modération")
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def modconfig(self, interaction: disnake.ApplicationCommandInteraction):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('INSERT OR IGNORE INTO mod_config (guild_id) VALUES (?)', (interaction.guild_id,))
|
||||
cursor.execute('SELECT log_channel_id, warn_limit_timeout, warn_limit_kick, warn_limit_ban, timeout_duration FROM mod_config WHERE guild_id = ?', (interaction.guild_id,))
|
||||
res = cursor.fetchone(); conn.commit(); conn.close()
|
||||
embed = disnake.Embed(title="⚙️ Configuration Modération", description="Modifiez les paramètres ci-dessous.", color=disnake.Color.blue())
|
||||
embed.add_field(name="📁 Logs", value=f"<#{res[0]}>" if res[0] else "Non défini", inline=False)
|
||||
embed.add_field(name="⚖️ Paliers", value=f"Timeout: **{res[1]}**, Kick: **{res[2]}**, Ban: **{res[3]}**", inline=False)
|
||||
embed.add_field(name="⏱️ Durée Timeout Auto", value=f"**{res[4]}s**", inline=False)
|
||||
view = ModConfigView(interaction.guild_id, self.db_path)
|
||||
await interaction.response.send_message(embed=embed, view=view, ephemeral=True)
|
||||
conn.commit(); conn.close()
|
||||
await self.send_modconfig_v2(interaction)
|
||||
|
||||
async def warn(self, interaction: disnake.Interaction, member: disnake.Member, reason: str):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
@commands.Cog.listener("on_message_interaction")
|
||||
async def on_mod_interaction(self, interaction: disnake.MessageInteraction):
|
||||
cid = interaction.data.custom_id
|
||||
if not cid: return
|
||||
|
||||
if cid.startswith("modpan_"):
|
||||
action, mid = cid.replace("modpan_", "").split(":")
|
||||
member = interaction.guild.get_member(int(mid))
|
||||
if not member: return await interaction.response.send_message("❌ Membre introuvable.", ephemeral=True)
|
||||
if action == "clear":
|
||||
await self.clearwarns(interaction, member, "Nettoyage Panel")
|
||||
await self.send_modpanel_v2(interaction, member, edit=True)
|
||||
else:
|
||||
await interaction.response.send_modal(ModReasonModal(self, action.upper(), member))
|
||||
elif cid.startswith("modcfg_"):
|
||||
act = cid.replace("modcfg_", "")
|
||||
if act in ["logs", "board"]:
|
||||
col = "log_channel_id" if act == "logs" else "board_channel_id"
|
||||
view = disnake.ui.View()
|
||||
sel = disnake.ui.ChannelSelect(placeholder="Choisir salon...", channel_types=[disnake.ChannelType.text])
|
||||
async def sel_cb(i):
|
||||
c = sel.values[0]
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute(f"UPDATE mod_config SET {col} = ? WHERE guild_id = ?", (c.id, i.guild_id))
|
||||
conn.commit(); conn.close()
|
||||
await i.response.send_message(f"✅ Salon mis à jour.", ephemeral=True)
|
||||
await self.send_modconfig_v2(interaction, edit=True)
|
||||
sel.callback = sel_cb; view.add_item(sel)
|
||||
await interaction.response.send_message(f"Sélectionnez le salon pour {act} :", view=view, ephemeral=True)
|
||||
elif act == "limits":
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute("SELECT warn_limit_timeout, warn_limit_kick, warn_limit_ban FROM mod_config WHERE guild_id = ?", (interaction.guild_id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
view = disnake.ui.View()
|
||||
b1 = disnake.ui.Button(label=f"Timeout ({res[0]})"); b1.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Timeout", res[0]))
|
||||
b2 = disnake.ui.Button(label=f"Kick ({res[1]})"); b2.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Kick", res[1]))
|
||||
b3 = disnake.ui.Button(label=f"Ban ({res[2]})"); b3.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Ban", res[2]))
|
||||
view.add_item(b1); view.add_item(b2); view.add_item(b3)
|
||||
await interaction.response.send_message("Modifier quel palier ?", view=view, ephemeral=True)
|
||||
elif act == "duration":
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute("SELECT timeout_duration FROM mod_config WHERE guild_id = ?", (interaction.guild_id,))
|
||||
res = cursor.fetchone(); conn.close()
|
||||
await interaction.response.send_modal(ModConfigModal("Durée Timeout (s)", res[0]))
|
||||
|
||||
async def warn(self, interaction, member, reason):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('INSERT INTO sanctions (guild_id, user_id, moderator_id, type, reason) VALUES (?, ?, ?, ?, ?)', (interaction.guild_id, member.id, interaction.user.id, 'WARN', reason))
|
||||
cursor.execute('SELECT COUNT(*) FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "WARN" AND status = "ACTIVE"', (interaction.guild_id, member.id))
|
||||
warn_count = cursor.fetchone()[0]
|
||||
count = cursor.fetchone()[0]
|
||||
cursor.execute('SELECT warn_limit_timeout, warn_limit_kick, warn_limit_ban, timeout_duration FROM mod_config WHERE guild_id = ?', (interaction.guild_id,))
|
||||
config = cursor.fetchone(); conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {member.mention} averti ({warn_count}).", ephemeral=True)
|
||||
else: await interaction.followup.send(f"✅ {member.mention} averti ({warn_count}).", ephemeral=True)
|
||||
await self.send_mod_log(interaction.guild, disnake.Embed(title="⚠️ Avertissement", description=f"Membre: {member.mention}\nRaison: {reason}\nTotal: {warn_count}", color=disnake.Color.orange()))
|
||||
cfg = cursor.fetchone()
|
||||
cursor.execute('SELECT id, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "WARN" ORDER BY id DESC LIMIT 1', (interaction.guild_id, member.id))
|
||||
s_id, ts = cursor.fetchone(); conn.commit(); conn.close()
|
||||
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {member.mention} averti ({count}).", ephemeral=True)
|
||||
await self.send_mod_log(interaction.guild, [disnake.ui.Container(disnake.ui.Section(f"⚠️ Warn : {member}", accessory=disnake.ui.Thumbnail(member.display_avatar.url)), disnake.ui.Separator(divider=True), disnake.ui.TextDisplay(f"Raison: {reason}"), disnake.ui.TextDisplay(f"Total: {count}"))])
|
||||
await self.send_sanction_board(interaction.guild, self.build_sanction_board_components(s_type="WARN", user=member, moderator=interaction.user, reason=reason, timestamp=ts, s_id=s_id))
|
||||
try: await member.send(f"⚠️ Avertissement sur **{interaction.guild.name}**\nRaison: {reason}")
|
||||
except: pass
|
||||
if config:
|
||||
l_t, l_k, l_b, dur = config
|
||||
if warn_count >= l_b: await member.ban(reason=f"Auto: {warn_count} warns")
|
||||
elif warn_count >= l_k: await member.kick(reason=f"Auto: {warn_count} warns")
|
||||
elif warn_count >= l_t: await member.timeout(duration=timedelta(seconds=dur), reason=f"Auto: {warn_count} warns")
|
||||
if cfg:
|
||||
lt, lk, lb, dur = cfg
|
||||
if count >= lb: await member.ban(reason=f"Auto: {count} warns")
|
||||
elif count >= lk: await member.kick(reason=f"Auto: {count} warns")
|
||||
elif count >= lt: await member.timeout(duration=timedelta(seconds=dur), reason=f"Auto: {count} warns")
|
||||
|
||||
async def timeout(self, interaction: disnake.Interaction, member: disnake.Member, duration_str: str, reason: str):
|
||||
async def timeout(self, interaction, member, duration_str, reason):
|
||||
secs = self.parse_duration(duration_str)
|
||||
if secs <= 0: return await interaction.response.send_message("❌ Durée invalide.", ephemeral=True)
|
||||
await member.timeout(duration=timedelta(seconds=secs), reason=reason)
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('INSERT INTO sanctions (guild_id, user_id, moderator_id, type, reason, duration) VALUES (?, ?, ?, ?, ?, ?)', (interaction.guild_id, member.id, interaction.user.id, 'TIMEOUT', reason, secs))
|
||||
conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {member.mention} mis en sourdine.", ephemeral=True)
|
||||
else: await interaction.followup.send(f"✅ {member.mention} mis en sourdine.", ephemeral=True)
|
||||
cursor.execute('SELECT id, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "TIMEOUT" ORDER BY id DESC LIMIT 1', (interaction.guild_id, member.id))
|
||||
s_id, ts = cursor.fetchone(); conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {member.mention} timeout.", ephemeral=True)
|
||||
await self.send_sanction_board(interaction.guild, self.build_sanction_board_components(s_type="TIMEOUT", user=member, moderator=interaction.user, reason=reason, timestamp=ts, s_id=s_id, duration=secs))
|
||||
|
||||
async def kick(self, interaction: disnake.Interaction, member: disnake.Member, reason: str):
|
||||
async def kick(self, interaction, member, reason):
|
||||
await member.kick(reason=reason)
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('INSERT INTO sanctions (guild_id, user_id, moderator_id, type, reason) VALUES (?, ?, ?, ?, ?)', (interaction.guild_id, member.id, interaction.user.id, 'KICK', reason))
|
||||
conn.commit(); conn.close()
|
||||
cursor.execute('SELECT id, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "KICK" ORDER BY id DESC LIMIT 1', (interaction.guild_id, member.id))
|
||||
s_id, ts = cursor.fetchone(); conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {member.name} expulsé.", ephemeral=True)
|
||||
else: await interaction.followup.send(f"✅ {member.name} expulsé.", ephemeral=True)
|
||||
await self.send_sanction_board(interaction.guild, self.build_sanction_board_components(s_type="KICK", user=member, moderator=interaction.user, reason=reason, timestamp=ts, s_id=s_id))
|
||||
|
||||
async def ban(self, interaction: disnake.Interaction, user: disnake.User, reason: str):
|
||||
async def ban(self, interaction, user, reason):
|
||||
await interaction.guild.ban(user, reason=reason)
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('INSERT INTO sanctions (guild_id, user_id, moderator_id, type, reason) VALUES (?, ?, ?, ?, ?)', (interaction.guild_id, user.id, interaction.user.id, 'BAN', reason))
|
||||
conn.commit(); conn.close()
|
||||
cursor.execute('SELECT id, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? AND type = "BAN" ORDER BY id DESC LIMIT 1', (interaction.guild_id, user.id))
|
||||
s_id, ts = cursor.fetchone(); conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ {user.name} banni.", ephemeral=True)
|
||||
else: await interaction.followup.send(f"✅ {user.name} banni.", ephemeral=True)
|
||||
await self.send_sanction_board(interaction.guild, self.build_sanction_board_components(s_type="BAN", user=user, moderator=interaction.user, reason=reason, timestamp=ts, s_id=s_id))
|
||||
|
||||
async def history(self, interaction: disnake.Interaction, user: disnake.User):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('SELECT id, type, reason, timestamp FROM sanctions WHERE guild_id = ? AND user_id = ? ORDER BY timestamp DESC', (interaction.guild_id, user.id))
|
||||
results = cursor.fetchall(); conn.close()
|
||||
if not results:
|
||||
if not interaction.response.is_done(): return await interaction.response.send_message("ℹ️ Aucun historique.", ephemeral=True)
|
||||
else: return await interaction.followup.send("ℹ️ Aucun historique.", ephemeral=True)
|
||||
embed = disnake.Embed(title=f"📜 Historique de {user.name}", color=disnake.Color.blue())
|
||||
for s_id, s_type, reason, ts in results[:10]:
|
||||
embed.add_field(name=f"#{s_id} - {s_type}", value=f"**Date:** {ts}\n**Raison:** {reason}", inline=False)
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
else: await interaction.followup.send(embed=embed, ephemeral=True)
|
||||
|
||||
async def clearwarns(self, interaction: disnake.Interaction, member: disnake.Member, reason: str):
|
||||
async def clearwarns(self, interaction, member, reason):
|
||||
conn = sqlite3.connect(self.db_path); cursor = conn.cursor()
|
||||
cursor.execute('UPDATE sanctions SET status = "REVOKED" WHERE guild_id = ? AND user_id = ? AND type = "WARN"', (interaction.guild_id, member.id))
|
||||
conn.commit(); conn.close()
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ Warns de {member.name} effacés.", ephemeral=True)
|
||||
else: await interaction.followup.send(f"✅ Warns de {member.name} effacés.", ephemeral=True)
|
||||
if not interaction.response.is_done(): await interaction.response.send_message(f"✅ Warns effacés.", ephemeral=True)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Moderation(bot))
|
||||
def setup(bot): bot.add_cog(Moderation(bot))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue