#!/usr/bin/env python3 """ RedFlag Discord Management Bot Interactive Discord server management with secure configuration """ import discord import asyncio import logging import sys from typing import Optional, Dict, List from discord.ext import commands from discord import app_commands from env_manager import discord_env # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class DiscordManager: """Interactive Discord server manager""" def __init__(self): self.bot_token = discord_env.get_required('DISCORD_BOT_TOKEN') self.server_id = int(discord_env.get_required('DISCORD_SERVER_ID')) self.application_id = discord_env.get_required('DISCORD_APPLICATION_ID') self.public_key = discord_env.get_required('DISCORD_PUBLIC_KEY') # Bot setup with required intents intents = discord.Intents.default() intents.guilds = True intents.message_content = True # Initialize bot self.bot = commands.Bot( command_prefix='!', intents=intents, help_command=None # We'll create custom help ) self.setup_events() self.setup_commands() def setup_events(self): """Setup bot event handlers""" @self.bot.event async def on_ready(): logger.info(f'✅ Bot logged in as {self.bot.user}') logger.info(f'Serving server: {self.bot.user.name} (ID: {self.bot.user.id})') # Sync commands await self.bot.tree.sync() logger.info('✅ Commands synced') # Get server info guild = self.bot.get_guild(self.server_id) if guild: logger.info(f'✅ Connected to server: {guild.name}') await self.print_server_status(guild) else: logger.error('❌ Could not find server!') @self.bot.event async def on_command_error(ctx, error): """Handle command errors""" logger.error(f'Command error in {ctx.command}: {error}') if isinstance(error, commands.MissingRequiredArgument): await ctx.send(f'❌ Missing required argument: `{error.param}`') elif isinstance(error, commands.BadArgument): await ctx.send(f'❌ Invalid argument: {error}') else: await ctx.send(f'❌ An error occurred: {error}') def setup_commands(self): """Setup slash commands""" # Server management commands @self.bot.tree.command(name="status", description="Show current Discord server status") async def cmd_status(interaction: discord.Interaction): await self.cmd_status(interaction) @self.bot.tree.command(name="create-channels", description="Create standard RedFlag channels") async def cmd_create_channels(interaction: discord.Interaction): await self.cmd_create_channels(interaction) @self.bot.tree.command(name="send-message", description="Send a message to a channel") @app_commands.describe(channel="Channel to send message to", message="Message to send") async def cmd_send_message(interaction: discord.Interaction, channel: str, message: str): await self.cmd_send_message(interaction, channel, message) @self.bot.tree.command(name="list-channels", description="List all channels and categories") async def cmd_list_channels(interaction: discord.Interaction): await self.cmd_list_channels(interaction) @self.bot.tree.command(name="create-category", description="Create a channel category") @app_commands.describe(name="Category name") async def cmd_create_category(interaction: discord.Interaction, name: str): await self.cmd_create_category(interaction, name) @self.bot.tree.command(name="create-test-channel", description="Create one simple test channel") async def cmd_create_test_channel(interaction: discord.Interaction): await self.cmd_create_test_channel(interaction) @self.bot.tree.command(name="help", description="Show available commands") async def cmd_help(interaction: discord.Interaction): await self.cmd_help(interaction) async def cmd_status(self, interaction: discord.Interaction): """Show server status""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return embed = discord.Embed( title="📊 RedFlag Discord Server Status", color=discord.Color.blue(), description=f"Server: **{guild.name}**" ) embed.add_field(name="👥 Members", value=str(guild.member_count), inline=True) embed.add_field(name="💬 Channels", value=str(len(guild.channels)), inline=True) embed.add_field(name="🎭 Roles", value=str(len(guild.roles)), inline=True) embed.add_field(name="📅 Created", value=guild.created_at.strftime("%Y-%m-%d"), inline=True) embed.add_field(name="👑 Owner", value=f"<@{guild.owner_id}>", inline=True) embed.add_field(name="🚀 Boost Level", value=str(guild.premium_tier), inline=True) await interaction.response.send_message(embed=embed) async def cmd_create_channels(self, interaction: discord.Interaction): """Create standard RedFlag channels""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return await interaction.response.defer(ephemeral=True) results = [] # Create categories first try: # Community category community_cat = await guild.create_category_channel("🌍 Community") discord_env.update_category_ids("community", str(community_cat.id)) results.append("✅ Community category") # Development category dev_cat = await guild.create_category_channel("💻 Development") discord_env.update_category_ids("development", str(dev_cat.id)) results.append("✅ Development category") # Security category security_cat = await guild.create_category_channel("🔒 Security") discord_env.update_category_ids("security", str(security_cat.id)) results.append("✅ Security category") except Exception as e: logger.error(f"Error creating categories: {e}") results.append(f"❌ Categories: {e}") # Create channels (with small delays to avoid rate limits) await asyncio.sleep(1) # Community channels try: general = await guild.create_text_channel( "general", category=discord.Object(id=int(discord_env.get('COMMUNITY_CATEGORY_ID', 0))), reason="Community general discussion" ) discord_env.update_channel_ids("general", str(general.id)) results.append("✅ #general") announcements = await guild.create_text_channel( "announcements", category=discord.Object(id=int(discord_env.get('COMMUNITY_CATEGORY_ID', 0))), reason="Project announcements" ) discord_env.update_channel_ids("announcements", str(announcements.id)) results.append("✅ #announcements") except Exception as e: logger.error(f"Error creating community channels: {e}") results.append(f"❌ Community channels: {e}") await asyncio.sleep(1) # Security channels try: security_alerts = await guild.create_text_channel( "security-alerts", category=discord.Object(id=int(discord_env.get('SECURITY_CATEGORY_ID', 0))), reason="Security alerts and notifications" ) discord_env.update_channel_ids("security-alerts", str(security_alerts.id)) results.append("✅ #security-alerts") except Exception as e: logger.error(f"Error creating security channels: {e}") results.append(f"❌ Security channels: {e}") await asyncio.sleep(1) # Development channels try: dev_chat = await guild.create_text_channel( "dev-chat", category=discord.Object(id=int(discord_env.get('DEVELOPMENT_CATEGORY_ID', 0))), reason="Development discussions" ) discord_env.update_channel_ids("dev-chat", str(dev_chat.id)) results.append("✅ #dev-chat") bug_reports = await guild.create_text_channel( "bug-reports", category=discord.Object(id=int(discord_env.get('DEVELOPMENT_CATEGORY_ID', 0))), reason="Bug reports and issues" ) discord_env.update_channel_ids("bug-reports", str(bug_reports.id)) results.append("✅ #bug-reports") except Exception as e: logger.error(f"Error creating development channels: {e}") results.append(f"❌ Development channels: {e}") # Send results embed = discord.Embed( title="🔧 Channel Creation Results", color=discord.Color.green() if "❌" not in str(results) else discord.Color.red(), description="\n".join(results) ) await interaction.followup.send(embed=embed, ephemeral=True) async def cmd_send_message(self, interaction: discord.Interaction, channel: str, message: str): """Send a message to a specific channel""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return # Find channel by name target_channel = discord.utils.get(guild.text_channels, name=channel.lower()) if not target_channel: await interaction.response.send_message(f"❌ Channel '{channel}' not found!", ephemeral=True) return try: await target_channel.send(message) await interaction.response.send_message( f"✅ Message sent to #{channel}!", ephemeral=True ) except Exception as e: logger.error(f"Error sending message: {e}") await interaction.response.send_message( f"❌ Failed to send message: {e}", ephemeral=True ) async def cmd_list_channels(self, interaction: discord.Interaction): """List all channels and categories""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return embed = discord.Embed( title="📋 Server Channels", color=discord.Color.blue() ) # List categories categories = [c for c in guild.categories if c.name] if categories: category_text = "\n".join([f"**{c.name}** (ID: {c.id})" for c in categories]) embed.add_field(name="📂 Categories", value=category_text or "None", inline=False) # List text channels text_channels = [c for c in guild.text_channels if c.category] if text_channels: channel_text = "\n".join([f"#{c.name} (ID: {c.id})" for c in text_channels]) embed.add_field(name="💬 Text Channels", value=channel_text or "None", inline=False) # List voice channels voice_channels = [c for c in guild.voice_channels if c.category] if voice_channels: voice_text = "\n".join([f"🎤 {c.name} (ID: {c.id})" for c in voice_channels]) embed.add_field(name="🎤 Voice Channels", value=voice_text or "None", inline=False) await interaction.response.send_message(embed=embed, ephemeral=True) async def cmd_create_category(self, interaction: discord.Interaction, name: str): """Create a new category""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return try: category = await guild.create_category_channel(name) await interaction.response.send_message( f"✅ Created category: **{name}** (ID: {category.id})", ephemeral=True ) except Exception as e: logger.error(f"Error creating category: {e}") await interaction.response.send_message( f"❌ Failed to create category: {e}", ephemeral=True ) async def cmd_create_test_channel(self, interaction: discord.Interaction): """Create one simple test channel""" guild = self.bot.get_guild(self.server_id) if not guild: await interaction.response.send_message("❌ Could not find server!", ephemeral=True) return try: # Create a simple text channel test_channel = await guild.create_text_channel( "test-channel", reason="Testing bot channel creation" ) await interaction.response.send_message( f"✅ Created test channel: **#{test_channel.name}**", ephemeral=True ) except Exception as e: logger.error(f"Error creating test channel: {e}") await interaction.response.send_message( f"❌ Failed to create test channel: {e}", ephemeral=True ) async def cmd_help(self, interaction: discord.Interaction): """Show help information""" embed = discord.Embed( title="🤖 RedFlag Discord Bot Help", description="Interactive Discord server management commands", color=discord.Color.blue() ) commands_info = [ ("`/status`", "📊 Show server status"), ("`/create-channels`", "🔧 Create standard channels"), ("`/list-channels`", "📋 List all channels"), ("`/send-message`", "💬 Send message to channel"), ("`/create-category`", "📂 Create new category"), ("`/create-test-channel`", "🧪 Create one test channel"), ("`/help`", "❓ Show this help"), ] for cmd, desc in commands_info: embed.add_field(name=cmd, value=desc, inline=False) embed.add_field( name="🛡️ Security Features", value="✅ Secure configuration management\n✅ Token protection\n✅ Rate limiting", inline=False ) embed.add_field( name="🔄 Live Updates", value="Configuration changes are saved instantly to .env file", inline=False ) embed.set_footer(text="RedFlag Security Discord Management v1.0") await interaction.response.send_message(embed=embed, ephemeral=True) async def print_server_status(self, guild): """Print detailed server status to console""" print(f"\n{'='*60}") print(f"🚀 REDFLAG DISCORD SERVER STATUS") print(f"{'='*60}") print(f"Server Name: {guild.name}") print(f"Server ID: {guild.id}") print(f"Members: {guild.member_count}") print(f"Channels: {len(guild.channels)}") print(f"Roles: {len(guild.roles)}") print(f"Owner: <@{guild.owner_id}>") print(f"Created: {guild.created_at}") print(f"Boost Level: {guild.premium_tier}") print(f"{'='*60}\n") async def run(self): """Start the bot""" try: # Connect to Discord await self.bot.start(self.bot_token) except discord.errors.LoginFailure: logger.error("❌ Invalid Discord bot token!") logger.error("Please check your DISCORD_BOT_TOKEN in .env file") sys.exit(1) except Exception as e: logger.error(f"❌ Failed to start bot: {e}") sys.exit(1) def main(): """Main entry point""" print("🚀 Starting RedFlag Discord Management Bot...") # Check configuration if not discord_env.is_configured(): print("❌ Discord not configured!") print("Please:") print("1. Copy .env.example to .env") print("2. Fill in your Discord bot token and server ID") print("3. Run this script again") sys.exit(1) # Create and run bot bot = DiscordManager() asyncio.run(bot.run()) if __name__ == "__main__": main()