Merge branch 'dev' into 'main'
Ajout du système de sanctions, mise en déploiement pour test à grande échelle See merge request Omega_Kube/kuby!12
This commit is contained in:
commit
8429efe396
4 changed files with 369 additions and 27 deletions
1
bot.py
1
bot.py
|
|
@ -90,6 +90,7 @@ class MyBot(commands.Bot):
|
||||||
"commandes.convocation",
|
"commandes.convocation",
|
||||||
"commandes.absence_staff",
|
"commandes.absence_staff",
|
||||||
"commandes.website_sync",
|
"commandes.website_sync",
|
||||||
|
"commandes.moderation",
|
||||||
]
|
]
|
||||||
|
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
|
|
|
||||||
325
commandes/moderation.py
Normal file
325
commandes/moderation.py
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord import app_commands
|
||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import re
|
||||||
|
|
||||||
|
class ModReasonModal(discord.ui.Modal):
|
||||||
|
def __init__(self, parent_view, action_type):
|
||||||
|
super().__init__(title=f"Raison pour {action_type}")
|
||||||
|
self.parent_view = parent_view
|
||||||
|
self.action_type = action_type
|
||||||
|
|
||||||
|
self.reason = discord.ui.TextInput(
|
||||||
|
label="Raison",
|
||||||
|
placeholder="Entrez la raison ici...",
|
||||||
|
required=True,
|
||||||
|
max_length=500
|
||||||
|
)
|
||||||
|
self.add_item(self.reason)
|
||||||
|
|
||||||
|
if action_type == "TIMEOUT":
|
||||||
|
self.duration = discord.ui.TextInput(
|
||||||
|
label="Durée (ex: 1h, 30m, 1d)",
|
||||||
|
placeholder="1h",
|
||||||
|
required=True,
|
||||||
|
max_length=10
|
||||||
|
)
|
||||||
|
self.add_item(self.duration)
|
||||||
|
|
||||||
|
async def on_submit(self, interaction: discord.Interaction):
|
||||||
|
reason = self.reason.value
|
||||||
|
member = self.parent_view.member
|
||||||
|
|
||||||
|
cog = interaction.client.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)
|
||||||
|
elif self.action_type == "TIMEOUT":
|
||||||
|
await cog.timeout(interaction, member, self.duration.value, reason)
|
||||||
|
elif self.action_type == "KICK":
|
||||||
|
await cog.kick(interaction, member, reason)
|
||||||
|
elif self.action_type == "BAN":
|
||||||
|
await cog.ban(interaction, member, reason)
|
||||||
|
|
||||||
|
await self.parent_view.update_panel(interaction)
|
||||||
|
|
||||||
|
class ModPanelView(discord.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: discord.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 = discord.Embed(
|
||||||
|
title=f"🛡️ Panel de Modération - {self.member.name}",
|
||||||
|
description=f"Gestion de l'utilisateur {self.member.mention}",
|
||||||
|
color=discord.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)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Avertir", style=discord.ButtonStyle.secondary, emoji="⚠️")
|
||||||
|
async def warn_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await interaction.response.send_modal(ModReasonModal(self, "WARN"))
|
||||||
|
|
||||||
|
@discord.ui.button(label="Timeout", style=discord.ButtonStyle.secondary, emoji="⏳")
|
||||||
|
async def timeout_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await interaction.response.send_modal(ModReasonModal(self, "TIMEOUT"))
|
||||||
|
|
||||||
|
@discord.ui.button(label="Expulser", style=discord.ButtonStyle.secondary, emoji="👢")
|
||||||
|
async def kick_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await interaction.response.send_modal(ModReasonModal(self, "KICK"))
|
||||||
|
|
||||||
|
@discord.ui.button(label="Bannir", style=discord.ButtonStyle.danger, emoji="🔨")
|
||||||
|
async def ban_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
await interaction.response.send_modal(ModReasonModal(self, "BAN"))
|
||||||
|
|
||||||
|
@discord.ui.button(label="Historique", style=discord.ButtonStyle.primary, emoji="📜")
|
||||||
|
async def history_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
cog = interaction.client.get_cog("Moderation")
|
||||||
|
await cog.history(interaction, self.member)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Clear Warns", style=discord.ButtonStyle.danger, emoji="🧹")
|
||||||
|
async def clear_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
cog = interaction.client.get_cog("Moderation")
|
||||||
|
await cog.clearwarns(interaction, self.member, reason="Nettoyage via Panel")
|
||||||
|
await self.update_panel(interaction)
|
||||||
|
|
||||||
|
class ModConfigModal(discord.ui.Modal):
|
||||||
|
def __init__(self, field_name, current_value):
|
||||||
|
super().__init__(title=f"Modifier {field_name}")
|
||||||
|
self.field_name = field_name
|
||||||
|
self.input = discord.ui.TextInput(
|
||||||
|
label=field_name,
|
||||||
|
default=str(current_value),
|
||||||
|
placeholder="Entrez une valeur numérique...",
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
self.add_item(self.input)
|
||||||
|
|
||||||
|
async def on_submit(self, interaction: discord.Interaction):
|
||||||
|
value = self.input.value
|
||||||
|
cog = interaction.client.get_cog("Moderation")
|
||||||
|
|
||||||
|
mapping = {
|
||||||
|
"Limite Timeout": "warn_limit_timeout",
|
||||||
|
"Limite Kick": "warn_limit_kick",
|
||||||
|
"Limite Ban": "warn_limit_ban",
|
||||||
|
"Durée Timeout (s)": "timeout_duration"
|
||||||
|
}
|
||||||
|
|
||||||
|
if not value.isdigit():
|
||||||
|
return await interaction.response.send_message("❌ Veuillez entrer un nombre valide.", ephemeral=True)
|
||||||
|
|
||||||
|
db_field = mapping.get(self.field_name)
|
||||||
|
conn = sqlite3.connect(cog.db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(f"UPDATE mod_config SET {db_field} = ? WHERE guild_id = ?", (int(value), interaction.guild_id))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
await interaction.response.send_message(f"✅ {self.field_name} mis à jour sur **{value}**.", ephemeral=True)
|
||||||
|
|
||||||
|
class ModConfigView(discord.ui.View):
|
||||||
|
def __init__(self, guild_id, db_path):
|
||||||
|
super().__init__(timeout=60)
|
||||||
|
self.guild_id = guild_id
|
||||||
|
self.db_path = db_path
|
||||||
|
|
||||||
|
@discord.ui.button(label="Log Channel", style=discord.ButtonStyle.primary, emoji="📁")
|
||||||
|
async def log_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
view = discord.ui.View()
|
||||||
|
select = discord.ui.ChannelSelect(
|
||||||
|
placeholder="Sélectionnez le salon de logs...",
|
||||||
|
channel_types=[discord.ChannelType.text]
|
||||||
|
)
|
||||||
|
|
||||||
|
async def select_callback(interaction: discord.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)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Paliers Warns", style=discord.ButtonStyle.secondary, emoji="⚖️")
|
||||||
|
async def limits_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
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 = discord.ui.View()
|
||||||
|
|
||||||
|
btn_t = discord.ui.Button(label=f"Timeout ({res[0]})", style=discord.ButtonStyle.secondary)
|
||||||
|
btn_t.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Timeout", res[0]))
|
||||||
|
|
||||||
|
btn_k = discord.ui.Button(label=f"Kick ({res[1]})", style=discord.ButtonStyle.secondary)
|
||||||
|
btn_k.callback = lambda i: i.response.send_modal(ModConfigModal("Limite Kick", res[1]))
|
||||||
|
|
||||||
|
btn_b = discord.ui.Button(label=f"Ban ({res[2]})", style=discord.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)
|
||||||
|
|
||||||
|
@discord.ui.button(label="Durée Timeout", style=discord.ButtonStyle.secondary, emoji="⏱️")
|
||||||
|
async def duration_btn(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
|
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]))
|
||||||
|
|
||||||
|
class Moderation(commands.Cog):
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.db_path = os.path.join(os.path.dirname(__file__), "..", "config.db")
|
||||||
|
self._init_db()
|
||||||
|
|
||||||
|
def _init_db(self):
|
||||||
|
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)''')
|
||||||
|
conn.commit(); conn.close()
|
||||||
|
|
||||||
|
def parse_duration(self, duration_str: str) -> int:
|
||||||
|
if not duration_str: return 0
|
||||||
|
units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400, 'w': 604800}
|
||||||
|
total_seconds = 0
|
||||||
|
matches = re.findall(r'(\d+)([smhdw])', duration_str.lower())
|
||||||
|
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)
|
||||||
|
|
||||||
|
@app_commands.command(name="modpanel", description="Ouvre le panel de modération pour un membre")
|
||||||
|
@app_commands.checks.has_permissions(moderate_members=True)
|
||||||
|
async def modpanel(self, interaction: discord.Interaction, member: discord.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 = discord.Embed(title=f"🛡️ Panel de Modération - {member.name}", description=f"Gestion de {member.mention}", color=discord.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)
|
||||||
|
|
||||||
|
@app_commands.command(name="modconfig", description="Configuration de la modération")
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
async def modconfig(self, interaction: discord.Interaction):
|
||||||
|
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 = discord.Embed(title="⚙️ Configuration Modération", description="Modifiez les paramètres ci-dessous.", color=discord.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)
|
||||||
|
|
||||||
|
async def warn(self, interaction: discord.Interaction, member: discord.Member, reason: str):
|
||||||
|
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]
|
||||||
|
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, discord.Embed(title="⚠️ Avertissement", description=f"Membre: {member.mention}\nRaison: {reason}\nTotal: {warn_count}", color=discord.Color.orange()))
|
||||||
|
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(timedelta(seconds=dur), reason=f"Auto: {warn_count} warns")
|
||||||
|
|
||||||
|
async def timeout(self, interaction: discord.Interaction, member: discord.Member, duration_str: str, reason: str):
|
||||||
|
secs = self.parse_duration(duration_str)
|
||||||
|
if secs <= 0: return await interaction.response.send_message("❌ Durée invalide.", ephemeral=True)
|
||||||
|
await member.timeout(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)
|
||||||
|
|
||||||
|
async def kick(self, interaction: discord.Interaction, member: discord.Member, reason: str):
|
||||||
|
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()
|
||||||
|
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)
|
||||||
|
|
||||||
|
async def ban(self, interaction: discord.Interaction, user: discord.User, reason: str):
|
||||||
|
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()
|
||||||
|
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)
|
||||||
|
|
||||||
|
async def history(self, interaction: discord.Interaction, user: discord.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 = discord.Embed(title=f"📜 Historique de {user.name}", color=discord.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: discord.Interaction, member: discord.Member, reason: str):
|
||||||
|
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)
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
await bot.add_cog(Moderation(bot))
|
||||||
|
|
@ -1,4 +1,31 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"date": "2026-05-01T14:42:53.534230+00:00",
|
||||||
|
"trigger": "auto",
|
||||||
|
"serversCount": 1,
|
||||||
|
"membersCount": 12,
|
||||||
|
"guildName": "Omega Kube Bêta Test",
|
||||||
|
"errors": [],
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-05-01T14:34:33.777534+00:00",
|
||||||
|
"trigger": "auto",
|
||||||
|
"serversCount": 1,
|
||||||
|
"membersCount": 12,
|
||||||
|
"guildName": "Omega Kube Bêta Test",
|
||||||
|
"errors": [],
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2026-05-01T14:31:01.918316+00:00",
|
||||||
|
"trigger": "auto",
|
||||||
|
"serversCount": 1,
|
||||||
|
"membersCount": 12,
|
||||||
|
"guildName": "Omega Kube Bêta Test",
|
||||||
|
"errors": [],
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"date": "2026-05-01T14:11:08.947818+00:00",
|
"date": "2026-05-01T14:11:08.947818+00:00",
|
||||||
"trigger": "auto",
|
"trigger": "auto",
|
||||||
|
|
@ -151,32 +178,5 @@
|
||||||
"guildName": "Omega Kube Bêta Test",
|
"guildName": "Omega Kube Bêta Test",
|
||||||
"errors": [],
|
"errors": [],
|
||||||
"success": true
|
"success": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-04-01T12:07:50.185260+00:00",
|
|
||||||
"trigger": "auto",
|
|
||||||
"serversCount": 1,
|
|
||||||
"membersCount": 12,
|
|
||||||
"guildName": "Omega Kube Bêta Test",
|
|
||||||
"errors": [],
|
|
||||||
"success": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-04-01T11:07:49.769763+00:00",
|
|
||||||
"trigger": "auto",
|
|
||||||
"serversCount": 1,
|
|
||||||
"membersCount": 12,
|
|
||||||
"guildName": "Omega Kube Bêta Test",
|
|
||||||
"errors": [],
|
|
||||||
"success": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-04-01T10:07:49.758778+00:00",
|
|
||||||
"trigger": "auto",
|
|
||||||
"serversCount": 1,
|
|
||||||
"membersCount": 12,
|
|
||||||
"guildName": "Omega Kube Bêta Test",
|
|
||||||
"errors": [],
|
|
||||||
"success": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
16
scratch/check_db.py
Normal file
16
scratch/check_db.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
|
||||||
|
db_path = "config.db"
|
||||||
|
if os.path.exists(db_path):
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
||||||
|
tables = cursor.fetchall()
|
||||||
|
print(f"Tables: {tables}")
|
||||||
|
for table in tables:
|
||||||
|
cursor.execute(f"PRAGMA table_info({table[0]});")
|
||||||
|
print(f"Schema for {table[0]}: {cursor.fetchall()}")
|
||||||
|
conn.close()
|
||||||
|
else:
|
||||||
|
print("Database not found.")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue