Files
Redflag/discord/discord_manager.py
Fimeg ec3ba88459 feat: machine binding and version enforcement
migration 017 adds machine_id to agents table
middleware validates X-Machine-ID header on authed routes
agent client sends machine ID with requests
MIN_AGENT_VERSION config defaults 0.1.22
version utils added for comparison

blocks config copying attacks via hardware fingerprint
old agents get 426 upgrade required
breaking: <0.1.22 agents rejected
2025-11-02 09:30:04 -05:00

426 lines
17 KiB
Python
Executable File

#!/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()