wpms commited on
Commit
a7b8d3e
·
verified ·
1 Parent(s): ea547d9

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +120 -127
main.py CHANGED
@@ -2,42 +2,70 @@ import discord
2
  from discord.ext import commands, tasks
3
  import os
4
  import asyncio
5
- from datetime import datetime
6
  import logging
 
 
 
7
 
8
- # Setup logging
9
  logging.basicConfig(
10
  level=logging.INFO,
11
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
12
  )
13
  logger = logging.getLogger('discord_bot')
14
 
15
- # Bot configuration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  TOKEN = os.getenv('DISCORD_TOKEN')
17
  if not TOKEN:
 
18
  raise ValueError("DISCORD_TOKEN environment variable is required!")
19
 
20
- # Bot intents
21
  intents = discord.Intents.default()
22
- intents.message_content = True # Required for reading message content
23
  intents.members = True
24
  intents.presences = True
 
 
 
25
 
26
- # Create bot instance
27
  bot = commands.Bot(
28
  command_prefix='!',
29
  intents=intents,
30
- help_command=None, # Custom help command
31
  case_insensitive=True
32
  )
33
 
34
- # Event: Bot ready
 
 
 
35
  @bot.event
36
  async def on_ready():
37
  logger.info(f'✅ Bot logged in as {bot.user.name} ({bot.user.id})')
38
  logger.info(f'🌐 Connected to {len(bot.guilds)} guilds')
39
 
40
- # Set bot status
41
  await bot.change_presence(
42
  activity=discord.Activity(
43
  type=discord.ActivityType.watching,
@@ -46,48 +74,37 @@ async def on_ready():
46
  status=discord.Status.online
47
  )
48
 
49
- # Start background tasks
50
  status_update.start()
51
 
52
- # Background task: Update status every 5 minutes
53
- @tasks.loop(minutes=5)
54
- async def status_update():
55
- await bot.change_presence(
56
- activity=discord.Activity(
57
- type=discord.ActivityType.watching,
58
- name=f"{len(bot.guilds)} servers | !help"
59
- ),
60
- status=discord.Status.online
61
- )
 
62
 
63
- # Event: When bot joins a new guild
64
  @bot.event
65
  async def on_guild_join(guild):
66
  logger.info(f'➕ Joined guild: {guild.name} ({guild.id})')
67
 
68
- # Event: When bot leaves a guild
69
  @bot.event
70
  async def on_guild_remove(guild):
71
  logger.info(f'➖ Left guild: {guild.name} ({guild.id})')
72
 
73
- # Event: Handle messages (for non-command messages)
74
  @bot.event
75
- async def on_message(message):
76
- # Ignore bot messages
77
- if message.author.bot:
78
- return
79
-
80
- # Process commands
81
- await bot.process_commands(message)
82
 
83
  # ==================== COMMANDS ====================
84
-
85
- # Help Command
86
  @bot.command(name='help', aliases=['h', 'commands'])
87
  async def help_command(ctx, command_name: str = None):
88
  """Show help information"""
89
  if command_name:
90
- # Show specific command help
91
  command = bot.get_command(command_name)
92
  if command:
93
  embed = discord.Embed(
@@ -110,7 +127,6 @@ async def help_command(ctx, command_name: str = None):
110
  else:
111
  await ctx.send(f"❌ Command `!{command_name}` not found!")
112
  else:
113
- # Show all commands
114
  embed = discord.Embed(
115
  title="🤖 Bot Commands",
116
  description="Here are all available commands:",
@@ -118,7 +134,6 @@ async def help_command(ctx, command_name: str = None):
118
  timestamp=datetime.now()
119
  )
120
 
121
- # Categorize commands
122
  categories = {
123
  "General": ['help', 'ping', 'info', 'serverinfo', 'userinfo'],
124
  "Fun": ['say', 'embed', 'poll', '8ball', 'coinflip'],
@@ -141,7 +156,6 @@ async def help_command(ctx, command_name: str = None):
141
  embed.set_footer(text=f"Use !help <command> for more details | Prefix: !")
142
  await ctx.send(embed=embed)
143
 
144
- # Ping Command
145
  @bot.command(name='ping', aliases=['latency'])
146
  async def ping_command(ctx):
147
  """Check bot latency"""
@@ -149,19 +163,18 @@ async def ping_command(ctx):
149
 
150
  embed = discord.Embed(
151
  title="🏓 Pong!",
152
- description=f"**Latency:** `{latency}ms`\n**API Latency:** `{round(bot.latency * 1000)}ms`",
153
  color=discord.Color.green() if latency < 200 else discord.Color.orange() if latency < 500 else discord.Color.red()
154
  )
155
  embed.set_footer(text=f"Requested by {ctx.author.name}")
156
  await ctx.send(embed=embed)
157
 
158
- # Info Command
159
  @bot.command(name='info', aliases=['botinfo', 'about'])
160
  async def info_command(ctx):
161
  """Show bot information"""
162
  embed = discord.Embed(
163
  title="🤖 Bot Information",
164
- description="A Discord bot powered by discord.py",
165
  color=discord.Color.blue(),
166
  timestamp=datetime.now()
167
  )
@@ -170,17 +183,14 @@ async def info_command(ctx):
170
  embed.add_field(name="Bot ID", value=bot.user.id, inline=True)
171
  embed.add_field(name="Servers", value=len(bot.guilds), inline=True)
172
  embed.add_field(name="Users", value=sum(g.member_count for g in bot.guilds), inline=True)
173
- embed.add_field(name="Library", value="discord.py", inline=True)
174
- embed.add_field(name="Python Version", value="3.10+", inline=True)
175
- embed.add_field(name="Created", value=bot.user.created_at.strftime("%Y-%m-%d"), inline=True)
176
  embed.add_field(name="Uptime", value=get_uptime(), inline=True)
177
 
178
  embed.set_thumbnail(url=bot.user.avatar.url if bot.user.avatar else bot.user.default_avatar.url)
179
- embed.set_footer(text="Made with ❤️ using discord.py")
180
 
181
  await ctx.send(embed=embed)
182
 
183
- # Server Info Command
184
  @bot.command(name='serverinfo', aliases=['server', 'guildinfo'])
185
  async def serverinfo_command(ctx):
186
  """Show server information"""
@@ -198,14 +208,12 @@ async def serverinfo_command(ctx):
198
  embed.add_field(name="Channels", value=f"{len(guild.text_channels)} Text | {len(guild.voice_channels)} Voice", inline=True)
199
  embed.add_field(name="Roles", value=len(guild.roles), inline=True)
200
  embed.add_field(name="Created", value=guild.created_at.strftime("%Y-%m-%d"), inline=True)
201
- embed.add_field(name="Boost Level", value=f"Level {guild.premium_tier} ({guild.premium_subscription_count} boosts)", inline=True)
202
 
203
  if guild.icon:
204
  embed.set_thumbnail(url=guild.icon.url)
205
 
206
  await ctx.send(embed=embed)
207
 
208
- # User Info Command
209
  @bot.command(name='userinfo', aliases=['whois', 'profile'])
210
  async def userinfo_command(ctx, member: discord.Member = None):
211
  """Show user information"""
@@ -220,46 +228,40 @@ async def userinfo_command(ctx, member: discord.Member = None):
220
  embed.add_field(name="Username", value=member.name, inline=True)
221
  embed.add_field(name="Display Name", value=member.display_name, inline=True)
222
  embed.add_field(name="ID", value=member.id, inline=True)
223
- embed.add_field(name="Joined Server", value=member.joined_at.strftime("%Y-%m-%d %H:%M") if member.joined_at else "Unknown", inline=True)
224
- embed.add_field(name="Account Created", value=member.created_at.strftime("%Y-%m-%d %H:%M"), inline=True)
225
  embed.add_field(name="Status", value=str(member.status).title(), inline=True)
226
  embed.add_field(name="Top Role", value=member.top_role.mention, inline=True)
227
- embed.add_field(name="Bot?", value="Yes" if member.bot else "No", inline=True)
228
 
229
  roles = [role.mention for role in member.roles if role != ctx.guild.default_role]
230
  if roles:
231
  embed.add_field(name=f"Roles ({len(roles)})", value=" ".join(roles[:10]) + ("..." if len(roles) > 10 else ""), inline=False)
232
 
233
  embed.set_thumbnail(url=member.avatar.url if member.avatar else member.default_avatar.url)
234
- embed.set_footer(text=f"Requested by {ctx.author.name}")
235
-
236
  await ctx.send(embed=embed)
237
 
238
- # Avatar Command
239
  @bot.command(name='avatar', aliases=['av', 'pfp'])
240
  async def avatar_command(ctx, member: discord.Member = None):
241
  """Show user's avatar"""
242
  member = member or ctx.author
 
243
 
244
  embed = discord.Embed(
245
  title=f"🖼️ {member.name}'s Avatar",
246
  color=discord.Color.blue()
247
  )
248
-
249
- avatar_url = member.avatar.url if member.avatar else member.default_avatar.url
250
  embed.set_image(url=avatar_url)
251
- 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)
252
-
253
  await ctx.send(embed=embed)
254
 
255
- # Say Command
256
  @bot.command(name='say', aliases=['echo'])
257
  async def say_command(ctx, *, message: str):
258
  """Make the bot say something"""
259
- await ctx.message.delete()
 
 
 
260
  await ctx.send(message)
261
 
262
- # Embed Command
263
  @bot.command(name='embed', aliases=['em'])
264
  async def embed_command(ctx, *, message: str):
265
  """Create a custom embed"""
@@ -271,7 +273,6 @@ async def embed_command(ctx, *, message: str):
271
  embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
272
  await ctx.send(embed=embed)
273
 
274
- # Poll Command
275
  @bot.command(name='poll', aliases=['vote'])
276
  async def poll_command(ctx, *, question: str):
277
  """Create a poll"""
@@ -288,76 +289,43 @@ async def poll_command(ctx, *, question: str):
288
  await poll_message.add_reaction("👎")
289
  await poll_message.add_reaction("🤷")
290
 
291
- # 8Ball Command
292
  @bot.command(name='8ball', aliases=['magic8ball', 'ask'])
293
  async def eightball_command(ctx, *, question: str):
294
  """Ask the magic 8-ball"""
295
  import random
296
-
297
  responses = [
298
- "🎱 It is certain.",
299
- "🎱 It is decidedly so.",
300
- "🎱 Without a doubt.",
301
- "🎱 Yes definitely.",
302
- "🎱 You may rely on it.",
303
- "🎱 As I see it, yes.",
304
- "🎱 Most likely.",
305
- "🎱 Outlook good.",
306
- "🎱 Yes.",
307
- "🎱 Signs point to yes.",
308
- "🎱 Reply hazy, try again.",
309
- "🎱 Ask again later.",
310
- "🎱 Better not tell you now.",
311
- "🎱 Cannot predict now.",
312
- "🎱 Concentrate and ask again.",
313
- "🎱 Don't count on it.",
314
- "🎱 My reply is no.",
315
- "🎱 My sources say no.",
316
- "🎱 Outlook not so good.",
317
- "🎱 Very doubtful."
318
  ]
319
 
320
- embed = discord.Embed(
321
- title="🎱 Magic 8-Ball",
322
- color=discord.Color.purple()
323
- )
324
  embed.add_field(name="Question", value=question, inline=False)
325
  embed.add_field(name="Answer", value=random.choice(responses), inline=False)
326
-
327
  await ctx.send(embed=embed)
328
 
329
- # Coin Flip Command
330
  @bot.command(name='coinflip', aliases=['flip', 'coin'])
331
  async def coinflip_command(ctx):
332
  """Flip a coin"""
333
  import random
334
-
335
- result = random.choice(["Heads", "Tails"])
336
- emoji = "🪙" if result == "Heads" else "🪙"
337
 
338
  embed = discord.Embed(
339
- title=f"{emoji} Coin Flip",
340
  description=f"**Result:** {result}",
341
  color=discord.Color.gold()
342
  )
343
-
344
  await ctx.send(embed=embed)
345
 
346
- # Invite Command
347
  @bot.command(name='invite', aliases=['addbot'])
348
  async def invite_command(ctx):
349
  """Get bot invite link"""
350
  permissions = discord.Permissions(
351
- send_messages=True,
352
- embed_links=True,
353
- read_messages=True,
354
- read_message_history=True,
355
- add_reactions=True,
356
- use_external_emojis=True,
357
- attach_files=True,
358
- manage_messages=True,
359
- connect=True,
360
- speak=True
361
  )
362
 
363
  invite_url = discord.utils.oauth_url(bot.user.id, permissions=permissions)
@@ -367,15 +335,12 @@ async def invite_command(ctx):
367
  description=f"[Click here to invite me!]({invite_url})",
368
  color=discord.Color.green()
369
  )
370
- embed.add_field(name="Bot ID", value=bot.user.id, inline=False)
371
-
372
  await ctx.send(embed=embed)
373
 
374
- # Clear Command
375
  @bot.command(name='clear', aliases=['purge', 'clean'])
376
  @commands.has_permissions(manage_messages=True)
377
  async def clear_command(ctx, amount: int = 5):
378
- """Clear messages (requires Manage Messages permission)"""
379
  if amount < 1 or amount > 100:
380
  await ctx.send("❌ Please specify a number between 1 and 100!")
381
  return
@@ -389,7 +354,6 @@ async def clear_command(ctx, amount: int = 5):
389
  await asyncio.sleep(3)
390
  await msg.delete()
391
 
392
- # Uptime Command
393
  @bot.command(name='uptime', aliases=['up'])
394
  async def uptime_command(ctx):
395
  """Show bot uptime"""
@@ -400,16 +364,16 @@ async def uptime_command(ctx):
400
  )
401
  await ctx.send(embed=embed)
402
 
403
- # Error Handler
404
  @bot.event
405
  async def on_command_error(ctx, error):
406
  if isinstance(error, commands.CommandNotFound):
407
- return # Ignore unknown commands
408
 
409
  elif isinstance(error, commands.MissingRequiredArgument):
410
  embed = discord.Embed(
411
  title="❌ Missing Argument",
412
- description=f"Missing required argument: `{error.param.name}`\nUse `!help {ctx.command.name}` for usage info.",
413
  color=discord.Color.red()
414
  )
415
  await ctx.send(embed=embed)
@@ -417,7 +381,7 @@ async def on_command_error(ctx, error):
417
  elif isinstance(error, commands.MissingPermissions):
418
  embed = discord.Embed(
419
  title="❌ Missing Permissions",
420
- description=f"You need `{', '.join(error.missing_permissions)}` permission to use this command.",
421
  color=discord.Color.red()
422
  )
423
  await ctx.send(embed=embed)
@@ -425,7 +389,7 @@ async def on_command_error(ctx, error):
425
  elif isinstance(error, commands.MemberNotFound):
426
  embed = discord.Embed(
427
  title="❌ User Not Found",
428
- description="The specified user was not found.",
429
  color=discord.Color.red()
430
  )
431
  await ctx.send(embed=embed)
@@ -434,14 +398,24 @@ async def on_command_error(ctx, error):
434
  logger.error(f"Command error: {error}")
435
  embed = discord.Embed(
436
  title="❌ Error",
437
- description="An unexpected error occurred. Please try again later.",
438
  color=discord.Color.red()
439
  )
440
  await ctx.send(embed=embed)
441
 
442
- # Helper function: Get uptime
443
- start_time = datetime.now()
 
 
 
 
 
 
 
 
 
444
 
 
445
  def get_uptime():
446
  delta = datetime.now() - start_time
447
  days = delta.days
@@ -449,20 +423,39 @@ def get_uptime():
449
  minutes, seconds = divmod(remainder, 60)
450
 
451
  parts = []
452
- if days > 0:
453
- parts.append(f"{days}d")
454
- if hours > 0:
455
- parts.append(f"{hours}h")
456
- if minutes > 0:
457
- parts.append(f"{minutes}m")
458
  parts.append(f"{seconds}s")
459
 
460
  return " ".join(parts)
461
 
462
- # Run the bot
463
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  try:
465
- bot.run(TOKEN)
466
  except Exception as e:
467
- logger.error(f"Failed to start bot: {e}")
468
  raise
 
 
 
 
2
  from discord.ext import commands, tasks
3
  import os
4
  import asyncio
5
+ import threading
6
  import logging
7
+ from datetime import datetime
8
+ from flask import Flask
9
+ import requests
10
 
11
+ # ==================== LOGGING ====================
12
  logging.basicConfig(
13
  level=logging.INFO,
14
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
15
  )
16
  logger = logging.getLogger('discord_bot')
17
 
18
+ # ==================== FLASK WEB SERVER ====================
19
+ app = Flask(__name__)
20
+
21
+ @app.route('/')
22
+ def home():
23
+ return {
24
+ "status": "online",
25
+ "bot": bot.user.name if bot.is_ready() else "starting",
26
+ "servers": len(bot.guilds) if bot.is_ready() else 0,
27
+ "timestamp": datetime.now().isoformat()
28
+ }
29
+
30
+ @app.route('/health')
31
+ def health():
32
+ return {"status": "healthy", "bot_ready": bot.is_ready()}
33
+
34
+ def run_flask():
35
+ port = int(os.environ.get("PORT", 7860))
36
+ app.run(host='0.0.0.0', port=port, debug=False, use_reloader=False)
37
+
38
+ # ==================== BOT CONFIGURATION ====================
39
  TOKEN = os.getenv('DISCORD_TOKEN')
40
  if not TOKEN:
41
+ logger.error("❌ DISCORD_TOKEN not found in environment variables!")
42
  raise ValueError("DISCORD_TOKEN environment variable is required!")
43
 
44
+ # Bot intents - ALL REQUIRED INTENTS MUST BE ENABLED IN DEVELOPER PORTAL
45
  intents = discord.Intents.default()
46
+ intents.message_content = True
47
  intents.members = True
48
  intents.presences = True
49
+ intents.guilds = True
50
+ intents.messages = True
51
+ intents.reactions = True
52
 
 
53
  bot = commands.Bot(
54
  command_prefix='!',
55
  intents=intents,
56
+ help_command=None,
57
  case_insensitive=True
58
  )
59
 
60
+ # Uptime tracking
61
+ start_time = datetime.now()
62
+
63
+ # ==================== EVENTS ====================
64
  @bot.event
65
  async def on_ready():
66
  logger.info(f'✅ Bot logged in as {bot.user.name} ({bot.user.id})')
67
  logger.info(f'🌐 Connected to {len(bot.guilds)} guilds')
68
 
 
69
  await bot.change_presence(
70
  activity=discord.Activity(
71
  type=discord.ActivityType.watching,
 
74
  status=discord.Status.online
75
  )
76
 
 
77
  status_update.start()
78
 
79
+ @bot.event
80
+ async on_connect():
81
+ logger.info("🔗 Connected to Discord gateway")
82
+
83
+ @bot.event
84
+ async def on_disconnect():
85
+ logger.warning("⚠️ Disconnected from Discord gateway")
86
+
87
+ @bot.event
88
+ async def on_resumed():
89
+ logger.info("🔄 Session resumed")
90
 
 
91
  @bot.event
92
  async def on_guild_join(guild):
93
  logger.info(f'➕ Joined guild: {guild.name} ({guild.id})')
94
 
 
95
  @bot.event
96
  async def on_guild_remove(guild):
97
  logger.info(f'➖ Left guild: {guild.name} ({guild.id})')
98
 
 
99
  @bot.event
100
+ async def on_error(event, *args, **kwargs):
101
+ logger.error(f"❌ Error in event {event}: {args} {kwargs}")
 
 
 
 
 
102
 
103
  # ==================== COMMANDS ====================
 
 
104
  @bot.command(name='help', aliases=['h', 'commands'])
105
  async def help_command(ctx, command_name: str = None):
106
  """Show help information"""
107
  if command_name:
 
108
  command = bot.get_command(command_name)
109
  if command:
110
  embed = discord.Embed(
 
127
  else:
128
  await ctx.send(f"❌ Command `!{command_name}` not found!")
129
  else:
 
130
  embed = discord.Embed(
131
  title="🤖 Bot Commands",
132
  description="Here are all available commands:",
 
134
  timestamp=datetime.now()
135
  )
136
 
 
137
  categories = {
138
  "General": ['help', 'ping', 'info', 'serverinfo', 'userinfo'],
139
  "Fun": ['say', 'embed', 'poll', '8ball', 'coinflip'],
 
156
  embed.set_footer(text=f"Use !help <command> for more details | Prefix: !")
157
  await ctx.send(embed=embed)
158
 
 
159
  @bot.command(name='ping', aliases=['latency'])
160
  async def ping_command(ctx):
161
  """Check bot latency"""
 
163
 
164
  embed = discord.Embed(
165
  title="🏓 Pong!",
166
+ description=f"**Latency:** `{latency}ms`",
167
  color=discord.Color.green() if latency < 200 else discord.Color.orange() if latency < 500 else discord.Color.red()
168
  )
169
  embed.set_footer(text=f"Requested by {ctx.author.name}")
170
  await ctx.send(embed=embed)
171
 
 
172
  @bot.command(name='info', aliases=['botinfo', 'about'])
173
  async def info_command(ctx):
174
  """Show bot information"""
175
  embed = discord.Embed(
176
  title="🤖 Bot Information",
177
+ description="A Discord bot powered by discord.py on Hugging Face",
178
  color=discord.Color.blue(),
179
  timestamp=datetime.now()
180
  )
 
183
  embed.add_field(name="Bot ID", value=bot.user.id, inline=True)
184
  embed.add_field(name="Servers", value=len(bot.guilds), inline=True)
185
  embed.add_field(name="Users", value=sum(g.member_count for g in bot.guilds), inline=True)
186
+ embed.add_field(name="Library", value="discord.py v2.3+", inline=True)
 
 
187
  embed.add_field(name="Uptime", value=get_uptime(), inline=True)
188
 
189
  embed.set_thumbnail(url=bot.user.avatar.url if bot.user.avatar else bot.user.default_avatar.url)
190
+ embed.set_footer(text="Running on Hugging Face Spaces")
191
 
192
  await ctx.send(embed=embed)
193
 
 
194
  @bot.command(name='serverinfo', aliases=['server', 'guildinfo'])
195
  async def serverinfo_command(ctx):
196
  """Show server information"""
 
208
  embed.add_field(name="Channels", value=f"{len(guild.text_channels)} Text | {len(guild.voice_channels)} Voice", inline=True)
209
  embed.add_field(name="Roles", value=len(guild.roles), inline=True)
210
  embed.add_field(name="Created", value=guild.created_at.strftime("%Y-%m-%d"), inline=True)
 
211
 
212
  if guild.icon:
213
  embed.set_thumbnail(url=guild.icon.url)
214
 
215
  await ctx.send(embed=embed)
216
 
 
217
  @bot.command(name='userinfo', aliases=['whois', 'profile'])
218
  async def userinfo_command(ctx, member: discord.Member = None):
219
  """Show user information"""
 
228
  embed.add_field(name="Username", value=member.name, inline=True)
229
  embed.add_field(name="Display Name", value=member.display_name, inline=True)
230
  embed.add_field(name="ID", value=member.id, inline=True)
231
+ embed.add_field(name="Joined Server", value=member.joined_at.strftime("%Y-%m-%d") if member.joined_at else "Unknown", inline=True)
232
+ embed.add_field(name="Account Created", value=member.created_at.strftime("%Y-%m-%d"), inline=True)
233
  embed.add_field(name="Status", value=str(member.status).title(), inline=True)
234
  embed.add_field(name="Top Role", value=member.top_role.mention, inline=True)
 
235
 
236
  roles = [role.mention for role in member.roles if role != ctx.guild.default_role]
237
  if roles:
238
  embed.add_field(name=f"Roles ({len(roles)})", value=" ".join(roles[:10]) + ("..." if len(roles) > 10 else ""), inline=False)
239
 
240
  embed.set_thumbnail(url=member.avatar.url if member.avatar else member.default_avatar.url)
 
 
241
  await ctx.send(embed=embed)
242
 
 
243
  @bot.command(name='avatar', aliases=['av', 'pfp'])
244
  async def avatar_command(ctx, member: discord.Member = None):
245
  """Show user's avatar"""
246
  member = member or ctx.author
247
+ avatar_url = member.avatar.url if member.avatar else member.default_avatar.url
248
 
249
  embed = discord.Embed(
250
  title=f"🖼️ {member.name}'s Avatar",
251
  color=discord.Color.blue()
252
  )
 
 
253
  embed.set_image(url=avatar_url)
 
 
254
  await ctx.send(embed=embed)
255
 
 
256
  @bot.command(name='say', aliases=['echo'])
257
  async def say_command(ctx, *, message: str):
258
  """Make the bot say something"""
259
+ try:
260
+ await ctx.message.delete()
261
+ except:
262
+ pass
263
  await ctx.send(message)
264
 
 
265
  @bot.command(name='embed', aliases=['em'])
266
  async def embed_command(ctx, *, message: str):
267
  """Create a custom embed"""
 
273
  embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url if ctx.author.avatar else ctx.author.default_avatar.url)
274
  await ctx.send(embed=embed)
275
 
 
276
  @bot.command(name='poll', aliases=['vote'])
277
  async def poll_command(ctx, *, question: str):
278
  """Create a poll"""
 
289
  await poll_message.add_reaction("👎")
290
  await poll_message.add_reaction("🤷")
291
 
 
292
  @bot.command(name='8ball', aliases=['magic8ball', 'ask'])
293
  async def eightball_command(ctx, *, question: str):
294
  """Ask the magic 8-ball"""
295
  import random
 
296
  responses = [
297
+ "🎱 It is certain.", "🎱 It is decidedly so.", "🎱 Without a doubt.",
298
+ "🎱 Yes definitely.", "🎱 You may rely on it.", "🎱 As I see it, yes.",
299
+ "🎱 Most likely.", "🎱 Outlook good.", "🎱 Yes.",
300
+ "🎱 Reply hazy, try again.", "🎱 Ask again later.", "🎱 Cannot predict now.",
301
+ "🎱 Don't count on it.", "🎱 My reply is no.", "🎱 Very doubtful."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  ]
303
 
304
+ embed = discord.Embed(title="🎱 Magic 8-Ball", color=discord.Color.purple())
 
 
 
305
  embed.add_field(name="Question", value=question, inline=False)
306
  embed.add_field(name="Answer", value=random.choice(responses), inline=False)
 
307
  await ctx.send(embed=embed)
308
 
 
309
  @bot.command(name='coinflip', aliases=['flip', 'coin'])
310
  async def coinflip_command(ctx):
311
  """Flip a coin"""
312
  import random
313
+ result = random.choice(["Heads 🪙", "Tails 🪙"])
 
 
314
 
315
  embed = discord.Embed(
316
+ title="🪙 Coin Flip",
317
  description=f"**Result:** {result}",
318
  color=discord.Color.gold()
319
  )
 
320
  await ctx.send(embed=embed)
321
 
 
322
  @bot.command(name='invite', aliases=['addbot'])
323
  async def invite_command(ctx):
324
  """Get bot invite link"""
325
  permissions = discord.Permissions(
326
+ send_messages=True, embed_links=True, read_messages=True,
327
+ read_message_history=True, add_reactions=True, use_external_emojis=True,
328
+ attach_files=True, manage_messages=True
 
 
 
 
 
 
 
329
  )
330
 
331
  invite_url = discord.utils.oauth_url(bot.user.id, permissions=permissions)
 
335
  description=f"[Click here to invite me!]({invite_url})",
336
  color=discord.Color.green()
337
  )
 
 
338
  await ctx.send(embed=embed)
339
 
 
340
  @bot.command(name='clear', aliases=['purge', 'clean'])
341
  @commands.has_permissions(manage_messages=True)
342
  async def clear_command(ctx, amount: int = 5):
343
+ """Clear messages"""
344
  if amount < 1 or amount > 100:
345
  await ctx.send("❌ Please specify a number between 1 and 100!")
346
  return
 
354
  await asyncio.sleep(3)
355
  await msg.delete()
356
 
 
357
  @bot.command(name='uptime', aliases=['up'])
358
  async def uptime_command(ctx):
359
  """Show bot uptime"""
 
364
  )
365
  await ctx.send(embed=embed)
366
 
367
+ # ==================== ERROR HANDLER ====================
368
  @bot.event
369
  async def on_command_error(ctx, error):
370
  if isinstance(error, commands.CommandNotFound):
371
+ return
372
 
373
  elif isinstance(error, commands.MissingRequiredArgument):
374
  embed = discord.Embed(
375
  title="❌ Missing Argument",
376
+ description=f"Missing: `{error.param.name}`\nUse `!help {ctx.command.name}` for usage.",
377
  color=discord.Color.red()
378
  )
379
  await ctx.send(embed=embed)
 
381
  elif isinstance(error, commands.MissingPermissions):
382
  embed = discord.Embed(
383
  title="❌ Missing Permissions",
384
+ description=f"You need: `{', '.join(error.missing_permissions)}`",
385
  color=discord.Color.red()
386
  )
387
  await ctx.send(embed=embed)
 
389
  elif isinstance(error, commands.MemberNotFound):
390
  embed = discord.Embed(
391
  title="❌ User Not Found",
392
+ description="User not found.",
393
  color=discord.Color.red()
394
  )
395
  await ctx.send(embed=embed)
 
398
  logger.error(f"Command error: {error}")
399
  embed = discord.Embed(
400
  title="❌ Error",
401
+ description="An error occurred. Please try again.",
402
  color=discord.Color.red()
403
  )
404
  await ctx.send(embed=embed)
405
 
406
+ # ==================== BACKGROUND TASKS ====================
407
+ @tasks.loop(minutes=5)
408
+ async def status_update():
409
+ if bot.is_ready():
410
+ await bot.change_presence(
411
+ activity=discord.Activity(
412
+ type=discord.ActivityType.watching,
413
+ name=f"{len(bot.guilds)} servers | !help"
414
+ ),
415
+ status=discord.Status.online
416
+ )
417
 
418
+ # ==================== HELPER FUNCTIONS ====================
419
  def get_uptime():
420
  delta = datetime.now() - start_time
421
  days = delta.days
 
423
  minutes, seconds = divmod(remainder, 60)
424
 
425
  parts = []
426
+ if days > 0: parts.append(f"{days}d")
427
+ if hours > 0: parts.append(f"{hours}h")
428
+ if minutes > 0: parts.append(f"{minutes}m")
 
 
 
429
  parts.append(f"{seconds}s")
430
 
431
  return " ".join(parts)
432
 
433
+ # ==================== MAIN ====================
434
+ def main():
435
+ # Start Flask web server in background thread
436
+ flask_thread = threading.Thread(target=run_flask, daemon=True)
437
+ flask_thread.start()
438
+ logger.info("🌐 Flask web server started on port 7860")
439
+
440
+ # Keep-alive ping (optional - prevents free tier sleep)
441
+ def keep_alive():
442
+ while True:
443
+ try:
444
+ requests.get("http://localhost:7860/health", timeout=5)
445
+ except:
446
+ pass
447
+ asyncio.sleep(300) # Every 5 minutes
448
+
449
+ keep_alive_thread = threading.Thread(target=keep_alive, daemon=True)
450
+ keep_alive_thread.start()
451
+
452
+ # Run Discord bot (blocking)
453
+ logger.info("🤖 Starting Discord bot...")
454
  try:
455
+ bot.run(TOKEN, reconnect=True)
456
  except Exception as e:
457
+ logger.error(f"Bot crashed: {e}")
458
  raise
459
+
460
+ if __name__ == "__main__":
461
+ main()