import discord from discord.ext import commands, tasks import os import asyncio from datetime import datetime import logging from flask import Flask from threading import Thread # Setup logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('discord_bot') # Flask web server app = Flask(__name__) @app.route('/') def home(): return "hey, bot startedβœ…" def run_web(): app.run(host='0.0.0.0', port=7860) # Start web server in a thread web_thread = Thread(target=run_web) web_thread.start() # Bot configuration TOKEN = os.getenv('DISCORD_TOKEN') if not TOKEN: raise ValueError("DISCORD_TOKEN environment variable is required!") # Bot intents intents = discord.Intents.default() intents.message_content = True intents.members = True intents.presences = True # Create bot instance bot = commands.Bot( command_prefix='!', intents=intents, help_command=None, case_insensitive=True ) # Event: Bot ready @bot.event async def on_ready(): logger.info(f'βœ… Bot logged in as {bot.user.name} ({bot.user.id})') logger.info(f'🌐 Connected to {len(bot.guilds)} guilds') await bot.change_presence( activity=discord.Activity( type=discord.ActivityType.watching, name=f"{len(bot.guilds)} servers | !help" ), status=discord.Status.online ) status_update.start() # Background task: Update status every 5 minutes @tasks.loop(minutes=5) async def status_update(): await bot.change_presence( activity=discord.Activity( type=discord.ActivityType.watching, name=f"{len(bot.guilds)} servers | !help" ), status=discord.Status.online ) @bot.event async def on_guild_join(guild): logger.info(f'βž• Joined guild: {guild.name} ({guild.id})') @bot.event async def on_guild_remove(guild): logger.info(f'βž– Left guild: {guild.name} ({guild.id})') @bot.event async def on_message(message): if message.author.bot: return await bot.process_commands(message) # ==================== COMMANDS ==================== @bot.command(name='help', aliases=['h', 'commands']) async def help_command(ctx, command_name: str = None): """Show help information""" if command_name: command = bot.get_command(command_name) if command: embed = discord.Embed( title=f"πŸ“– Help: !{command.name}", description=command.help or "No description available", color=discord.Color.blue() ) embed.add_field( name="Usage", value=f"`!{command.name} {command.signature}`" if command.signature else f"`!{command.name}`", inline=False ) if command.aliases: embed.add_field( name="Aliases", value=", ".join([f"`!{alias}`" for alias in command.aliases]), inline=False ) await ctx.send(embed=embed) else: await ctx.send(f"❌ Command `!{command_name}` not found!") else: embed = discord.Embed( title="πŸ€– Bot Commands", description="Here are all available commands:", color=discord.Color.blue(), timestamp=datetime.now() ) categories = { "General": ['help', 'ping', 'info', 'serverinfo', 'userinfo'], "Fun": ['say', 'embed', 'poll', '8ball', 'coinflip'], "Utility": ['avatar', 'invite', 'uptime', 'clear'] } for category, cmds in categories.items(): valid_cmds = [] for cmd_name in cmds: cmd = bot.get_command(cmd_name) if cmd: valid_cmds.append(f"`!{cmd.name}`") if valid_cmds: embed.add_field( name=f"πŸ“ {category}", value=" | ".join(valid_cmds), inline=False ) embed.set_footer(text=f"Use !help for more details | Prefix: !") await ctx.send(embed=embed) @bot.command(name='ping', aliases=['latency']) async def ping_command(ctx): """Check bot latency""" latency = round(bot.latency * 1000) embed = discord.Embed( title="πŸ“ Pong!", description=f"**Latency:** `{latency}ms`\n**API Latency:** `{round(bot.latency * 1000)}ms`", color=discord.Color.green() if latency < 200 else discord.Color.orange() if latency < 500 else discord.Color.red() ) embed.set_footer(text=f"Requested by {ctx.author.name}") await ctx.send(embed=embed) @bot.command(name='info', aliases=['botinfo', 'about']) async def info_command(ctx): """Show bot information""" embed = discord.Embed( title="πŸ€– Bot Information", description="A Discord bot powered by discord.py", color=discord.Color.blue(), timestamp=datetime.now() ) embed.add_field(name="Bot Name", value=bot.user.name, inline=True) embed.add_field(name="Bot ID", value=bot.user.id, inline=True) embed.add_field(name="Servers", value=len(bot.guilds), inline=True) embed.add_field(name="Users", value=sum(g.member_count for g in bot.guilds), inline=True) embed.add_field(name="Library", value="discord.py", inline=True) embed.add_field(name="Python Version", value="3.10+", inline=True) embed.add_field(name="Created", value=bot.user.created_at.strftime("%Y-%m-%d"), inline=True) embed.add_field(name="Uptime", value=get_uptime(), inline=True) embed.set_thumbnail(url=bot.user.avatar.url if bot.user.avatar else bot.user.default_avatar.url) embed.set_footer(text="Made with ❀️ using discord.py") await ctx.send(embed=embed) @bot.command(name='serverinfo', aliases=['server', 'guildinfo']) async def serverinfo_command(ctx): """Show server information""" guild = ctx.guild embed = discord.Embed( title=f"πŸ“Š {guild.name} Server Info", color=discord.Color.blue(), timestamp=datetime.now() ) embed.add_field(name="Server ID", value=guild.id, inline=True) embed.add_field(name="Owner", value=guild.owner.mention if guild.owner else "Unknown", inline=True) embed.add_field(name="Members", value=guild.member_count, inline=True) embed.add_field(name="Channels", value=f"{len(guild.text_channels)} Text | {len(guild.voice_channels)} Voice", inline=True) embed.add_field(name="Roles", value=len(guild.roles), inline=True) embed.add_field(name="Created", value=guild.created_at.strftime("%Y-%m-%d"), inline=True) embed.add_field(name="Boost Level", value=f"Level {guild.premium_tier} ({guild.premium_subscription_count} boosts)", inline=True) if guild.icon: embed.set_thumbnail(url=guild.icon.url) await ctx.send(embed=embed) @bot.command(name='userinfo', aliases=['whois', 'profile']) async def userinfo_command(ctx, member: discord.Member = None): """Show user information""" member = member or ctx.author embed = discord.Embed( title=f"πŸ‘€ {member.name}'s Profile", color=member.color if member.color != discord.Color.default() else discord.Color.blue(), timestamp=datetime.now() ) embed.add_field(name="Username", value=member.name, inline=True) embed.add_field(name="Display Name", value=member.display_name, inline=True) embed.add_field(name="ID", value=member.id, inline=True) embed.add_field(name="Joined Server", value=member.joined_at.strftime("%Y-%m-%d %H:%M") if member.joined_at else "Unknown", inline=True) embed.add_field(name="Account Created", value=member.created_at.strftime("%Y-%m-%d %H:%M"), inline=True) embed.add_field(name="Status", value=str(member.status).title(), inline=True) embed.add_field(name="Top Role", value=member.top_role.mention, inline=True) embed.add_field(name="Bot?", value="Yes" if member.bot else "No", inline=True) roles = [role.mention for role in member.roles if role != ctx.guild.default_role] if roles: embed.add_field(name=f"Roles ({len(roles)})", value=" ".join(roles[:10]) + ("..." if len(roles) > 10 else ""), inline=False) embed.set_thumbnail(url=member.avatar.url if member.avatar else member.default_avatar.url) embed.set_footer(text=f"Requested by {ctx.author.name}") await ctx.send(embed=embed) @bot.command(name='avatar', aliases=['av', 'pfp']) async def avatar_command(ctx, member: discord.Member = None): """Show user's avatar""" member = member or ctx.author embed = discord.Embed( title=f"πŸ–ΌοΈ {member.name}'s Avatar", color=discord.Color.blue() ) avatar_url = member.avatar.url if member.avatar else member.default_avatar.url embed.set_image(url=avatar_url) embed.add_field(name="Links", value=f"[PNG]({member.avatar.url if member.avatar else member.default_avatar.url}) | [WEBP]({member.avatar.url if member.avatar else member.default_avatar.url})", inline=False) await ctx.send(embed=embed) @bot.command(name='say', aliases=['echo']) async def say_command(ctx, *, message: str): """Make the bot say something""" await ctx.message.delete() await ctx.send(message) @bot.command(name='embed', aliases=['em']) async def embed_command(ctx, *, message: str): """Create a custom embed""" embed = discord.Embed( description=message, color=discord.Color.blue(), timestamp=datetime.now() ) embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url) await ctx.send(embed=embed) @bot.command(name='poll', aliases=['vote']) async def poll_command(ctx, *, question: str): """Create a poll""" embed = discord.Embed( title="πŸ“Š Poll", description=question, color=discord.Color.blue(), timestamp=datetime.now() ) embed.set_footer(text=f"Poll by {ctx.author.name}") poll_message = await ctx.send(embed=embed) await poll_message.add_reaction("πŸ‘") await poll_message.add_reaction("πŸ‘Ž") await poll_message.add_reaction("🀷") @bot.command(name='8ball', aliases=['magic8ball', 'ask']) async def eightball_command(ctx, *, question: str): """Ask the magic 8-ball""" import random responses = [ "🎱 It is certain.", "🎱 It is decidedly so.", "🎱 Without a doubt.", "🎱 Yes definitely.", "🎱 You may rely on it.", "🎱 As I see it, yes.", "🎱 Most likely.", "🎱 Outlook good.", "🎱 Yes.", "🎱 Signs point to yes.", "🎱 Reply hazy, try again.", "🎱 Ask again later.", "🎱 Better not tell you now.", "🎱 Cannot predict now.", "🎱 Concentrate and ask again.", "🎱 Don't count on it.", "🎱 My reply is no.", "🎱 My sources say no.", "🎱 Outlook not so good.", "🎱 Very doubtful." ] embed = discord.Embed( title="🎱 Magic 8-Ball", color=discord.Color.purple() ) embed.add_field(name="Question", value=question, inline=False) embed.add_field(name="Answer", value=random.choice(responses), inline=False) await ctx.send(embed=embed) @bot.command(name='coinflip', aliases=['flip', 'coin']) async def coinflip_command(ctx): """Flip a coin""" import random result = random.choice(["Heads", "Tails"]) emoji = "πŸͺ™" if result == "Heads" else "πŸͺ™" embed = discord.Embed( title=f"{emoji} Coin Flip", description=f"**Result:** {result}", color=discord.Color.gold() ) await ctx.send(embed=embed) @bot.command(name='invite', aliases=['addbot']) async def invite_command(ctx): """Get bot invite link""" permissions = discord.Permissions( send_messages=True, embed_links=True, read_messages=True, read_message_history=True, add_reactions=True, use_external_emojis=True, attach_files=True, manage_messages=True, connect=True, speak=True ) invite_url = discord.utils.oauth_url(bot.user.id, permissions=permissions) embed = discord.Embed( title="πŸ”— Invite Bot", description=f"[Click here to invite me!]({invite_url})", color=discord.Color.green() ) embed.add_field(name="Bot ID", value=bot.user.id, inline=False) await ctx.send(embed=embed) @bot.command(name='clear', aliases=['purge', 'clean']) @commands.has_permissions(manage_messages=True) async def clear_command(ctx, amount: int = 5): """Clear messages (requires Manage Messages permission)""" if amount < 1 or amount > 100: await ctx.send("❌ Please specify a number between 1 and 100!") return deleted = await ctx.channel.purge(limit=amount + 1) embed = discord.Embed( description=f"πŸ—‘οΈ Deleted {len(deleted) - 1} messages!", color=discord.Color.green() ) msg = await ctx.send(embed=embed) await asyncio.sleep(3) await msg.delete() @bot.command(name='uptime', aliases=['up']) async def uptime_command(ctx): """Show bot uptime""" embed = discord.Embed( title="⏱️ Bot Uptime", description=get_uptime(), color=discord.Color.blue() ) await ctx.send(embed=embed) # ==================== ERROR HANDLER (FIXED) ==================== @bot.event async def on_command_error(ctx, error): # Ignore if we can't send messages due to connection issues try: if isinstance(error, commands.CommandNotFound): return elif isinstance(error, commands.MissingRequiredArgument): embed = discord.Embed( title="❌ Missing Argument", description=f"Missing required argument: `{error.param.name}`\nUse `!help {ctx.command.name}` for usage info.", color=discord.Color.red() ) await ctx.send(embed=embed) elif isinstance(error, commands.MissingPermissions): embed = discord.Embed( title="❌ Missing Permissions", description=f"You need `{', '.join(error.missing_permissions)}` permission to use this command.", color=discord.Color.red() ) await ctx.send(embed=embed) elif isinstance(error, commands.MemberNotFound): embed = discord.Embed( title="❌ User Not Found", description="The specified user was not found.", color=discord.Color.red() ) await ctx.send(embed=embed) elif isinstance(error, commands.CommandInvokeError): # Handle connection errors gracefully original = error.original if isinstance(original, aiohttp.ClientConnectorError): logger.warning(f"Connection error in command {ctx.command.name}: {original}") # Don't try to send message - connection is down return else: logger.error(f"Command error: {error}") embed = discord.Embed( title="❌ Error", description="An unexpected error occurred. Please try again later.", color=discord.Color.red() ) await ctx.send(embed=embed) else: logger.error(f"Command error: {error}") embed = discord.Embed( title="❌ Error", description="An unexpected error occurred. Please try again later.", color=discord.Color.red() ) await ctx.send(embed=embed) except Exception as e: # If we can't even send the error message, just log it logger.error(f"Failed to send error message: {e}") # Helper function: Get uptime start_time = datetime.now() def get_uptime(): delta = datetime.now() - start_time days = delta.days hours, remainder = divmod(delta.seconds, 3600) minutes, seconds = divmod(remainder, 60) parts = [] if days > 0: parts.append(f"{days}d") if hours > 0: parts.append(f"{hours}h") if minutes > 0: parts.append(f"{minutes}m") parts.append(f"{seconds}s") return " ".join(parts) # Run the bot with auto-reconnect if __name__ == "__main__": while True: try: bot.run(TOKEN, reconnect=True) except Exception as e: logger.error(f"Bot crashed: {e}") logger.info("Restarting bot in 10 seconds...") import time time.sleep(10)