import os import asyncio import discord from discord.ext import commands from dotenv import load_dotenv from configparser import ConfigParser from data_manager import DataManager import pathlib # Load env load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') # Where to read settings from CONFIG_PATH = os.getenv('SHAI_CONFIG', '/config/settings.conf') # Load settings config = ConfigParser() read_ok = config.read(CONFIG_PATH) if not read_ok: print(f"[Config] WARNING: could not read config at {CONFIG_PATH}; using in-image defaults where possible.") # Intents (enable Members + Message Content in Dev Portal) intents = discord.Intents.default() intents.guilds = True intents.members = True intents.message_content = True intents.reactions = True intents.emojis_and_stickers = True intents.voice_states = True # Data file path (prefer INI, else env, else sensible default) data_file = config['DEFAULT'].get('data_file', os.getenv('SHAI_DATA', '/data/data.json')) bot = commands.Bot(command_prefix='!', intents=intents) bot.config = config bot.data_manager = DataManager(data_file) async def _guild_selfcheck(g: discord.Guild, cfg): problems = [] def _need_channel(id_key, *perms): raw = cfg.get(id_key) if not raw: problems.append(f"Missing config key: {id_key}") return try: cid = int(raw) except Exception: problems.append(f"Bad channel id for {id_key}: {raw}") return ch = g.get_channel(cid) if not ch: problems.append(f"Channel not found: {id_key}={cid}") return me = g.me p = ch.permissions_for(me) for perm in perms: if not getattr(p, perm, False): problems.append(f"Missing permission on #{ch.name}: {perm}") # Mod channel (for report moderation) _need_channel('mod_channel_id', 'read_messages', 'send_messages', 'add_reactions', 'read_message_history') # Modlog channel _need_channel('modlog_channel_id', 'read_messages', 'send_messages') # Pirates list channel _need_channel('pirates_list_channel_id', 'read_messages', 'send_messages') # Auto-VC category/trigger are handled inside the cog if problems: print(f"[SelfCheck:{g.name}]") for p in problems: print(" -", p) @bot.event async def on_ready(): print(f"Logged in as {bot.user} (ID: {bot.user.id})") print("[Intents] members:", bot.intents.members, "/ message_content:", bot.intents.message_content, "/ voice_states:", bot.intents.voice_states) # Per-guild permission sanity checks (console log) await asyncio.gather(*[_guild_selfcheck(g, bot.config['DEFAULT']) for g in bot.guilds]) # Slash command sync try: dev_gid = bot.config['DEFAULT'].get('dev_guild_id') if dev_gid: guild = bot.get_guild(int(dev_gid)) if guild: synced = await bot.tree.sync(guild=guild) print(f"[Slash] Synced {len(synced)} commands to {guild.name}") else: synced = await bot.tree.sync() print(f"[Slash] Synced {len(synced)} commands globally (dev_guild_id not in cache)") else: synced = await bot.tree.sync() print(f"[Slash] Synced {len(synced)} commands globally") except Exception as e: print("[Slash] Sync failed:", repr(e)) # Auto-discover extensions modules_path = pathlib.Path(__file__).parent / 'modules' extensions = [] for folder in modules_path.iterdir(): if folder.is_dir(): for file in folder.glob('*.py'): if file.name == '__init__.py': continue extensions.append(f"modules.{folder.name}.{file.stem}") async def main(): async with bot: for ext in extensions: try: await bot.load_extension(ext) print(f"[Modules] Loaded: {ext}") except Exception as e: print(f"[Modules] Failed to load {ext}:", repr(e)) await bot.start(TOKEN) if __name__ == '__main__': asyncio.run(main())