2026-09-19 12:11:06 +02:00
|
|
|
import logging
|
2026-09-19 12:31:30 +02:00
|
|
|
import os
|
2026-09-19 15:30:07 +02:00
|
|
|
|
2026-09-19 12:11:06 +02:00
|
|
|
import discord
|
|
|
|
|
from discord.ext import commands
|
|
|
|
|
|
|
|
|
|
import config
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("vision")
|
|
|
|
|
|
2026-09-19 12:31:30 +02:00
|
|
|
DEV_GUILD_IDS = [int(x) for x in os.getenv("DEV_GUILD_IDS", "").split(",") if x.strip()]
|
|
|
|
|
|
2026-09-19 12:11:06 +02:00
|
|
|
|
|
|
|
|
class VisionBot(commands.Bot):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
intents = discord.Intents.default()
|
|
|
|
|
intents.message_content = True
|
|
|
|
|
intents.members = True
|
|
|
|
|
|
|
|
|
|
super().__init__(
|
|
|
|
|
command_prefix="!",
|
|
|
|
|
intents=intents,
|
|
|
|
|
application_id=config.APPLICATION_ID,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def setup_hook(self):
|
|
|
|
|
logger.info("Chargement des cogs...")
|
|
|
|
|
await self.load_extension("commandes.hello")
|
feat(ticket): rewrite ticket system from scratch inspired by Kuby
New minimal ticket system (dev only):
- Panel with create ticket button (Components V2, no accent colors)
- Category selection via dropdown or direct modal (1 cat)
- Ticket channel creation with permissions, staff role, welcome message
- Close flow: staff check, transcript HTML (dark theme), log, auto-delete
- /ticket setup, /ticket config (interactive panel)
- Storage: JSON per-ticket + config.json (singleton pattern)
- Modular architecture: data.py, storage.py, ui.py, transcript.py, cog.py
- Removed old modmail system
2026-09-20 20:04:10 +02:00
|
|
|
await self.load_extension("tickets.cog")
|
2026-09-19 12:31:30 +02:00
|
|
|
|
2026-09-20 18:56:06 +02:00
|
|
|
self.tree.on_error = self.on_app_command_error
|
|
|
|
|
|
2026-09-19 12:31:30 +02:00
|
|
|
if config.DEV and DEV_GUILD_IDS:
|
|
|
|
|
logger.info(f"Mode DEV — sync des commands sur les guilds: {DEV_GUILD_IDS}")
|
|
|
|
|
for guild_id in DEV_GUILD_IDS:
|
2026-09-19 16:47:04 +02:00
|
|
|
guild = discord.Object(id=guild_id)
|
|
|
|
|
self.tree.copy_global_to(guild=guild)
|
|
|
|
|
try:
|
|
|
|
|
await self.tree.sync(guild=guild)
|
|
|
|
|
logger.info(f" ✓ Sync guild {guild_id}")
|
|
|
|
|
except discord.HTTPException as e:
|
2026-09-20 18:56:06 +02:00
|
|
|
logger.warning(f" ✗ Sync guild {guild_id} impossible ({e}). Le bot est-il invité sur ce serveur ?")
|
2026-09-19 12:31:30 +02:00
|
|
|
else:
|
|
|
|
|
logger.info("Mode PROD — sync global des slash commands...")
|
|
|
|
|
await self.tree.sync()
|
|
|
|
|
logger.info("Slash commands synchronisées (global)")
|
2026-09-19 12:11:06 +02:00
|
|
|
|
2026-09-20 18:56:06 +02:00
|
|
|
async def on_app_command_error(self, interaction: discord.Interaction, error: discord.app_commands.AppCommandError):
|
|
|
|
|
logger.error(
|
|
|
|
|
f"Erreur commande /{interaction.command.name if interaction.command else '?'} : {error}", exc_info=error
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
if interaction.response.is_done():
|
|
|
|
|
await interaction.followup.send(f"Erreur : `{error}`", ephemeral=True)
|
|
|
|
|
else:
|
|
|
|
|
await interaction.response.send_message(f"Erreur : `{error}`", ephemeral=True)
|
|
|
|
|
except discord.HTTPException:
|
|
|
|
|
pass
|
|
|
|
|
|
2026-09-19 12:11:06 +02:00
|
|
|
async def on_ready(self):
|
2026-09-19 12:31:30 +02:00
|
|
|
instance = "DEV" if config.DEV else "PROD"
|
|
|
|
|
logger.info(f"[{instance}] Connecté en tant que {self.user} (ID: {self.user.id})")
|
|
|
|
|
logger.info(f"[{instance}] Serveurs : {len(self.guilds)}")
|
2026-09-19 12:11:06 +02:00
|
|
|
await self.change_presence(
|
|
|
|
|
activity=discord.Activity(
|
|
|
|
|
type=discord.ActivityType.watching,
|
|
|
|
|
name="Vision Studio",
|
|
|
|
|
)
|
|
|
|
|
)
|