import disnake from disnake.ext import commands, tasks import json import os import asyncio from datetime import datetime, timedelta BLACKLIST_FILE = "data/blacklist.json" AGENTS_FILE = "data/agents.json" TICKETS_FILE = "data/tickets_blacklist.json" MAIN_GUILD_ID = 1369669999345537145 # ID serveur principal TICKET_CATEGORY_ID = 1369669999677145104 # Catégorie tickets # --------- Utils JSON --------- def load_json(path, default): if not os.path.exists(path): with open(path, "w") as f: json.dump(default, f, indent=4) with open(path, "r") as f: return json.load(f) def save_json(path, data): with open(path, "w") as f: json.dump(data, f, indent=4) # --------- Cog --------- class Blacklist(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.check_ban_expiry.start() # boucle auto pour bannir après 7j def cog_unload(self): self.check_ban_expiry.cancel() async def cog_load(self): """Enregistrer les vues persistantes au démarrage""" await self._register_persistent_views() async def _register_persistent_views(self): """Enregistre les vues pour les tickets ouverts""" try: tickets = load_json(TICKETS_FILE, {}) for user_id_str, data in tickets.items(): user_id = int(user_id_str) # Enregistrer le bouton d'ouverture de ticket view = TicketButton(user_id) self.bot.add_view(view) # Enregistrer le bouton de fermeture close_view = CloseTicketView(user_id) self.bot.add_view(close_view) print(f"[Blacklist] {len(tickets)} vues de tickets enregistrées") except Exception as e: print(f"[Blacklist] Erreur lors de l'enregistrement des vues: {e}") def is_agent(self, user_id: int): agents = load_json(AGENTS_FILE, {"agents": []}) return str(user_id) in agents["agents"] @commands.slash_command(name="blacklist", description="Blacklist un utilisateur") async def blacklist(self, interaction: disnake.ApplicationCommandInteraction, user: disnake.User, reason: str = "Aucune raison fournie"): if not self.is_agent(interaction.user.id): return await interaction.response.send_message("⛔ Tu n’as pas la permission.", ephemeral=True) blacklist = load_json(BLACKLIST_FILE, {}) blacklist[str(user.id)] = { "reason": reason, "timestamp": datetime.utcnow().isoformat() } save_json(BLACKLIST_FILE, blacklist) # Préparer l'embed pro embed = disnake.Embed( title="🚫 Mise en blacklist - Réseau Omega Kube", description=( f"Bonjour {user.mention},\n\n" f"Vous avez été **blacklisté** du **Réseau Omega Kube**.\n\n" f"**Raison :** {reason}\n\n" "⚠️ Vous êtes banni de tous nos serveurs partenaires. " "Cependant, vous restez présent sur le **serveur principal**, " "mais tu ne peux pas interagir avec la communauté.\n\n" "📩 Vous disposez de **7 jours** pour contester cette décision en ouvrant un ticket via le bouton ci-dessous.\n\n" "⏳ Passé ce délai, vous serez **définitivement banni de tout le réseau**, sans autre recours possible." ), color=disnake.Color.red() ) embed.set_footer(text="Omega Kube - Nixfix06 & Mgstudios") view = TicketButton(user.id) # Envoyer MP avant action try: await user.send(embed=embed, view=view) await asyncio.sleep(2) except: pass # Bannir de tous les serveurs sauf principal for guild in self.bot.guilds: if guild.id == MAIN_GUILD_ID: member = guild.get_member(user.id) if member: try: # Supprime la permission d'écrire partout for channel in guild.channels: overwrite = channel.overwrites_for(member) overwrite.send_messages = False overwrite.add_reactions = False await channel.set_permissions(member, overwrite=overwrite) except: pass else: member = guild.get_member(user.id) if member: try: await member.ban(reason=f"Blacklisté : {reason}") except: pass await interaction.response.send_message( f"✅ {user.mention} blacklisté, banni des partenaires et restreint sur le serveur principal.", ephemeral=True ) @commands.slash_command(name="unblacklist", description="Retirer un utilisateur de la blacklist") async def unblacklist(self, interaction: disnake.ApplicationCommandInteraction, user: disnake.User): if not self.is_agent(interaction.user.id): return await interaction.response.send_message("⛔ Tu n’as pas la permission.", ephemeral=True) blacklist = load_json(BLACKLIST_FILE, {}) if str(user.id) in blacklist: del blacklist[str(user.id)] save_json(BLACKLIST_FILE, blacklist) await interaction.response.send_message(f"✅ {user.mention} retiré de la blacklist.", ephemeral=True) else: await interaction.response.send_message("❌ Cet utilisateur n’est pas blacklisté.", ephemeral=True) @tasks.loop(hours=1) async def check_ban_expiry(self): """Boucle qui vérifie si 7 jours sont écoulés → ban total.""" blacklist = load_json(BLACKLIST_FILE, {}) to_remove = [] for user_id, data in blacklist.items(): try: ts = datetime.fromisoformat(data["timestamp"]) if datetime.utcnow() > ts + timedelta(days=7): # Ban définitif partout user = await self.bot.fetch_user(int(user_id)) for guild in self.bot.guilds: member = guild.get_member(int(user_id)) if member: try: await member.ban(reason="Blacklist définitive (7j écoulés)") except: pass to_remove.append(user_id) except: continue for uid in to_remove: del blacklist[uid] save_json(BLACKLIST_FILE, blacklist) @commands.Cog.listener() async def on_message(self, message: disnake.Message): if message.author.bot: return tickets = load_json(TICKETS_FILE, {}) if isinstance(message.channel, disnake.DMChannel): user_id = str(message.author.id) if user_id in tickets: guild = self.bot.get_guild(MAIN_GUILD_ID) ticket_channel = guild.get_channel(tickets[user_id]["channel_id"]) if ticket_channel: embed = disnake.Embed( description=message.content, color=disnake.Color.blue() ).set_author(name=message.author, icon_url=message.author.display_avatar.url) await ticket_channel.send(embed=embed) else: for user_id, data in tickets.items(): if message.channel.id == data["channel_id"] and self.is_agent(message.author.id): user = await self.bot.fetch_user(int(user_id)) try: embed = disnake.Embed( description=message.content, color=disnake.Color.green() ).set_author(name=f"Agent {message.author}", icon_url=message.author.display_avatar.url) await user.send(embed=embed) except: pass # --------- Boutons --------- class TicketButton(disnake.ui.View): def __init__(self, user_id: int): super().__init__(timeout=None) self.user_id = user_id @disnake.ui.button(label="📩 Ouvrir une demande de révision", style=disnake.ButtonStyle.primary, custom_id="blacklist_open_ticket") async def open_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction): if interaction.user.id != self.user_id: return await interaction.response.send_message("❌ Ce bouton ne t’appartient pas.", ephemeral=True) tickets = load_json(TICKETS_FILE, {}) if str(self.user_id) in tickets: return await interaction.response.send_message("❌ Tu as déjà un ticket ouvert.", ephemeral=True) guild = interaction.bot.get_guild(MAIN_GUILD_ID) category = guild.get_channel(TICKET_CATEGORY_ID) ticket_channel = await guild.create_text_channel( name=f"ticket-{self.user_id}", category=category, topic=f"Ticket de l'utilisateur {self.user_id}" ) tickets[str(self.user_id)] = {"channel_id": ticket_channel.id} save_json(TICKETS_FILE, tickets) view_close = CloseTicketView(self.user_id) embed = disnake.Embed( title="📩 Ticket ouvert", description=f"Un utilisateur blacklisté (<@{self.user_id}>) a ouvert une demande de révision.\n\nUn agent Omega Kube prendra en charge ce dossier.", color=disnake.Color.blurple() ) await ticket_channel.send(embed=embed, view=view_close) await interaction.response.send_message("✅ Ton ticket a bien été créé.", ephemeral=True) button.disabled = True self.stop() class CloseTicketView(disnake.ui.View): def __init__(self, user_id: int): super().__init__(timeout=None) self.user_id = user_id @disnake.ui.button(label="❌ Fermer le ticket", style=disnake.ButtonStyle.danger, custom_id="blacklist_close_ticket") async def close_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction): if not interaction.bot.get_cog("Blacklist").is_agent(interaction.user.id): return await interaction.response.send_message("⛔ Seuls les agents peuvent fermer ce ticket.", ephemeral=True) view = ConfirmCloseView(self.user_id) await interaction.response.send_message("⚠️ Veux-tu vraiment fermer ce ticket ?", view=view, ephemeral=True) class ConfirmCloseView(disnake.ui.View): def __init__(self, user_id: int): super().__init__(timeout=30) self.user_id = user_id @disnake.ui.button(label="✅ Oui", style=disnake.ButtonStyle.success, custom_id="blacklist_confirm_close") async def confirm(self, button: disnake.ui.Button, interaction: disnake.Interaction): if not interaction.bot.get_cog("Blacklist").is_agent(interaction.user.id): return await interaction.response.send_message("⛔ Tu n’as pas la permission.", ephemeral=True) tickets = load_json(TICKETS_FILE, {}) if str(self.user_id) in tickets: channel_id = tickets[str(self.user_id)]["channel_id"] guild = interaction.guild channel = guild.get_channel(channel_id) if channel: await channel.delete() del tickets[str(self.user_id)] save_json(TICKETS_FILE, tickets) user = await interaction.bot.fetch_user(int(self.user_id)) try: embed = disnake.Embed( title="🔒 Ticket fermé", description="Ton ticket a été examiné et fermé.\n\n⚠️ Après analyse, la sanction est confirmée et tu restes blacklisté de l’ensemble du réseau.", color=disnake.Color.orange() ) await user.send(embed=embed) await asyncio.sleep(2) except: pass for guild in interaction.bot.guilds: member = guild.get_member(self.user_id) if member: try: await member.ban(reason="Ticket blacklist fermé") except: pass await interaction.response.send_message("✅ Ticket fermé avec succès.", ephemeral=True) @disnake.ui.button(label="❌ Non", style=disnake.ButtonStyle.secondary, custom_id="blacklist_cancel_close") async def cancel(self, button: disnake.ui.Button, interaction: disnake.Interaction): await interaction.response.send_message("❌ Fermeture annulée.", ephemeral=True) # --------- Setup --------- def setup(bot): bot.add_cog(Blacklist(bot))