Feat | Cogs, slight readme changes - chersbobers :3

This commit is contained in:
Charlie 2026-01-25 19:57:18 +13:00
parent a3bffbf895
commit 5596e854cf
6 changed files with 859 additions and 663 deletions

View file

@ -1,2 +1,8 @@
# booly
booly is a fork of tooly beacuse og tooly got bad
Revamped tooly designed for ease of use.
two use modes:
1. premade *servers may lag
2. host your self it comes with the whole bot all you need is a token and a host
you can fork expand pull request host just but this in the readme or site ![Powered by Booly](https://img.shields.io/github/v/release/chersbobers/booly?label=powered%20by%20booly&color=5865F2&logo=discord&logoColor=white)

682
bot.py
View file

@ -1,21 +1,14 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
from discord.ext import commands
import os
import json
import random
import asyncio
from datetime import datetime
import aiohttp
import logging
from aiohttp import web
import feedparser
import asyncio
# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('bot')
# Configuration
CONFIG = {
'xp_min': 15,
'xp_max': 25,
@ -23,10 +16,9 @@ CONFIG = {
'xp_per_level': 100,
'level_up_multiplier': 10,
'data_file': 'data.json',
'video_check_interval': 300 # 5 minutes
'video_check_interval': 300
}
# Simple JSON Database
class SimpleDB:
def __init__(self, filename):
self.filename = filename
@ -73,7 +65,6 @@ class SimpleDB:
users.sort(key=lambda x: (x['data']['level'], x['data']['xp']), reverse=True)
return users
# Bot setup
class MyBot(commands.Bot):
def __init__(self):
intents = discord.Intents.default()
@ -82,100 +73,23 @@ class MyBot(commands.Bot):
super().__init__(command_prefix='/', intents=intents)
self.db = SimpleDB(CONFIG['data_file'])
self.config = CONFIG
async def setup_hook(self):
# Start YouTube checker
self.check_youtube.start()
# Sync slash commands
await self.load_extension('cogs.leveling')
await self.load_extension('cogs.system')
await self.load_extension('cogs.economy')
await self.load_extension('cogs.fun')
await self.tree.sync()
logger.info('✅ Slash commands synced!')
@tasks.loop(seconds=CONFIG['video_check_interval'])
async def check_youtube(self):
"""Check for new YouTube videos"""
youtube_channel_id = os.getenv('YOUTUBE_CHANNEL_ID')
if not youtube_channel_id:
return
try:
feed_url = f'https://www.youtube.com/feeds/videos.xml?channel_id={youtube_channel_id}'
# Parse feed
feed = await asyncio.to_thread(feedparser.parse, feed_url)
if not feed.entries:
return
latest = feed.entries[0]
video_id = latest.yt_videoid if hasattr(latest, 'yt_videoid') else latest.id.split(':')[-1]
# Check each guild for notifications
for guild in self.guilds:
guild_id = str(guild.id)
# Get guild settings
if 'youtube' not in self.db.data:
self.db.data['youtube'] = {}
if guild_id not in self.db.data['youtube']:
self.db.data['youtube'][guild_id] = {
'enabled': True,
'channel_id': None,
'last_video_id': None
}
guild_settings = self.db.data['youtube'][guild_id]
# Skip if disabled or no channel set
if not guild_settings['enabled'] or not guild_settings['channel_id']:
continue
# Check if this is a new video
if video_id != guild_settings['last_video_id'] and guild_settings['last_video_id']:
channel = self.get_channel(int(guild_settings['channel_id']))
if channel:
embed = discord.Embed(
title='🎬 New YouTube Video!',
description=f'**{latest.title}**',
url=latest.link,
color=0xFF0000,
timestamp=datetime.utcnow()
)
# Add thumbnail if available
if hasattr(latest, 'media_thumbnail') and latest.media_thumbnail:
embed.set_thumbnail(url=latest.media_thumbnail[0]['url'])
embed.add_field(name='Channel', value=latest.author, inline=True)
# Parse published date
if hasattr(latest, 'published'):
embed.add_field(name='Published', value=latest.published, inline=True)
await channel.send('📺 New video alert! @everyone', embed=embed)
logger.info(f'📺 Sent notification for: {latest.title}')
# Update last video ID
guild_settings['last_video_id'] = video_id
self.db.save()
except Exception as e:
logger.error(f'❌ Error checking YouTube: {e}')
@check_youtube.before_loop
async def before_check_youtube(self):
await self.wait_until_ready()
logger.info('✅ All cogs loaded and commands synced!')
bot = MyBot()
# Simple HTTP server for Render health checks
async def health_check(request):
return web.Response(text="Bot is running! ✅")
async def start_web_server():
"""Start a simple web server for Render health checks"""
app = web.Application()
app.router.add_get('/', health_check)
app.router.add_get('/health', health_check)
@ -188,10 +102,8 @@ async def start_web_server():
await site.start()
logger.info(f'🌐 Health check server running on port {port}')
# Events
@bot.event
async def on_ready():
# Start web server for Render
asyncio.create_task(start_web_server())
logger.info(f'✅ Logged in as {bot.user}')
@ -199,43 +111,6 @@ async def on_ready():
await bot.change_presence(activity=discord.Game(name="/help"))
logger.info('🚀 All systems operational!')
@bot.event
async def on_message(message):
if message.author.bot or not message.guild:
return
# XP System
user_data = bot.db.get_user(str(message.guild.id), str(message.author.id))
now = datetime.now().timestamp()
if now - user_data['last_message'] >= CONFIG['xp_cooldown']:
user_data['last_message'] = now
xp_gain = random.randint(CONFIG['xp_min'], CONFIG['xp_max'])
user_data['xp'] += xp_gain
xp_needed = user_data['level'] * CONFIG['xp_per_level']
if user_data['xp'] >= xp_needed:
user_data['level'] += 1
user_data['xp'] = 0
coin_reward = user_data['level'] * CONFIG['level_up_multiplier']
user_data['coins'] += coin_reward
messages = [
f'🎉 GG {message.author.mention}! You leveled up to **Level {user_data["level"]}**!',
f'⭐ Congrats {message.author.mention}! You\'re now **Level {user_data["level"]}**!',
f'🚀 Level up! {message.author.mention} reached **Level {user_data["level"]}**!'
]
await message.channel.send(
f'{random.choice(messages)} You earned **{coin_reward:,} coins**! 💰'
)
bot.db.set_user(str(message.guild.id), str(message.author.id), user_data)
await bot.process_commands(message)
# Slash Commands
@bot.tree.command(name='help', description='Show all available commands')
async def help_command(interaction: discord.Interaction):
embed = discord.Embed(
@ -244,13 +119,8 @@ async def help_command(interaction: discord.Interaction):
color=0x5865F2
)
embed.add_field(
name='📊 Leveling',
value='`/rank` - View your rank\n`/leaderboard` - Top 10 users',
inline=False
)
embed.add_field(
name='💰 Economy',
value='`/balance` - Check balance\n`/daily` - Daily reward\n`/work` - Work for coins',
name='📊 Leveling & Economy',
value='`/rank` - View your rank\n`/leaderboard` - Top 10 users\n`/balance` - Check balance\n`/daily` - Daily reward\n`/work` - Work for coins',
inline=False
)
embed.add_field(
@ -259,534 +129,28 @@ async def help_command(interaction: discord.Interaction):
inline=False
)
embed.add_field(
name=' Info',
value='`/ping` - Check bot latency\n`/serverinfo` - Server information',
name='🛡️ System & Moderation',
value='`/kick` - Kick member\n`/ban` - Ban member\n`/unban` - Unban user\n`/timeout` - Timeout member\n`/warn` - Warn member\n`/warnings` - View warnings\n`/clearwarnings` - Clear warnings\n`/purge` - Delete messages\n`/lock` - Lock channel\n`/unlock` - Unlock channel',
inline=False
)
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='ping', description='Check bot latency')
async def ping(interaction: discord.Interaction):
latency = round(bot.latency * 1000)
await interaction.response.send_message(f'🏓 Pong! Latency: `{latency}ms`')
@bot.tree.command(name='rank', description='View your rank and level')
async def rank(interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
user_data = bot.db.get_user(str(interaction.guild.id), str(target.id))
xp_needed = user_data['level'] * CONFIG['xp_per_level']
all_users = bot.db.get_all_guild_users(str(interaction.guild.id))
rank = next((i + 1 for i, u in enumerate(all_users) if u['user_id'] == str(target.id)), 'Unranked')
progress = int((user_data['xp'] / xp_needed) * 20) if xp_needed > 0 else 0
bar = '' * progress + '' * (20 - progress)
color = 0xFF6B6B if user_data['level'] >= 50 else 0xFFD93D if user_data['level'] >= 30 else 0x6BCB77 if user_data['level'] >= 15 else 0x4D96FF
embed = discord.Embed(color=color)
embed.set_author(name=f"{target.display_name}'s Profile", icon_url=target.display_avatar.url)
embed.description = f"""
**RANK** #{rank} / {len(all_users)}
**LEVEL** {user_data['level']}
**XP** {user_data['xp']:,} / {xp_needed:,}
`{bar}`
**💰 BALANCE** {user_data['coins']:,} coins
"""
embed.set_thumbnail(url=target.display_avatar.url)
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='leaderboard', description='View the server leaderboard')
async def leaderboard(interaction: discord.Interaction):
all_users = bot.db.get_all_guild_users(str(interaction.guild.id))[:10]
description = []
for i, u in enumerate(all_users):
medal = '🥇' if i == 0 else '🥈' if i == 1 else '🥉' if i == 2 else f'**{i+1}.**'
description.append(
f'{medal} <@{u["user_id"]}>\n'
f'└ Level {u["data"]["level"]} ({u["data"]["xp"]:,} XP) • {u["data"]["coins"]:,} coins'
)
embed = discord.Embed(
title='🏆 Server Leaderboard',
description='\n'.join(description) if description else 'No users yet!',
color=0x9B59B6,
timestamp=datetime.utcnow()
)
embed.set_footer(text='Top 10 users by level and XP')
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='balance', description='Check your coin balance')
async def balance(interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
user_data = bot.db.get_user(str(interaction.guild.id), str(target.id))
embed = discord.Embed(
title='💰 Balance',
description=f'{target.mention} has **{user_data["coins"]:,}** coins in wallet and **{user_data["bank"]:,}** in bank!\n**Total:** {user_data["coins"] + user_data["bank"]:,} coins',
color=0xFFD700
)
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='daily', description='Claim your daily reward')
async def daily(interaction: discord.Interaction):
user_data = bot.db.get_user(str(interaction.guild.id), str(interaction.user.id))
now = datetime.now().timestamp()
if now - user_data['last_daily'] < 86400:
time_left = 86400 - (now - user_data['last_daily'])
hours = int(time_left / 3600)
await interaction.response.send_message(f'⏳ You already claimed your daily! Come back in {hours} hours.', ephemeral=True)
return
reward = 100
user_data['coins'] += reward
user_data['last_daily'] = now
bot.db.set_user(str(interaction.guild.id), str(interaction.user.id), user_data)
await interaction.response.send_message(f'✅ You claimed your daily reward of **{reward:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
@bot.tree.command(name='work', description='Work to earn coins')
async def work(interaction: discord.Interaction):
user_data = bot.db.get_user(str(interaction.guild.id), str(interaction.user.id))
now = datetime.now().timestamp()
if now - user_data['last_work'] < 3600:
time_left = 3600 - (now - user_data['last_work'])
minutes = int(time_left / 60)
await interaction.response.send_message(f'⏳ You need to rest! Come back in {minutes} minutes.', ephemeral=True)
return
earnings = random.randint(10, 50)
user_data['coins'] += earnings
user_data['last_work'] = now
bot.db.set_user(str(interaction.guild.id), str(interaction.user.id), user_data)
jobs = [
'You worked as a programmer and earned',
'You delivered pizza and earned',
'You streamed on Twitch and earned',
'You mowed lawns and earned',
'You washed cars and earned',
'You tutored students and earned'
]
await interaction.response.send_message(f'💼 {random.choice(jobs)} **{earnings:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
@bot.tree.command(name='8ball', description='Ask the magic 8ball a question')
async def eightball(interaction: discord.Interaction, question: str):
responses = [
'Yes, definitely!', 'It is certain.', 'Without a doubt.', 'You may rely on it.',
'As I see it, yes.', 'Most likely.', 'Outlook good.', 'Signs point to yes.',
'Reply hazy, try again.', 'Ask again later.', 'Better not tell you now.',
'Cannot predict now.', "Don't count on it.", 'My reply is no.', 'Very doubtful.'
]
await interaction.response.send_message(f'🔮 **{question}**\n{random.choice(responses)}')
@bot.tree.command(name='roll', description='Roll a dice')
async def roll(interaction: discord.Interaction, sides: int = 6):
if sides < 2 or sides > 100:
await interaction.response.send_message('❌ Dice must have between 2 and 100 sides!', ephemeral=True)
return
result = random.randint(1, sides)
await interaction.response.send_message(f'🎲 You rolled a **{result}** (1-{sides})')
@bot.tree.command(name='flip', description='Flip a coin')
async def flip(interaction: discord.Interaction):
result = random.choice(['Heads', 'Tails'])
await interaction.response.send_message(f'🪙 The coin landed on **{result}**!')
@bot.tree.command(name='cat', description='Get a random cat picture')
async def cat(interaction: discord.Interaction):
await interaction.response.defer()
async with aiohttp.ClientSession() as session:
try:
async with session.get('https://api.thecatapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐱 Random Kitty!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a cat picture 😿')
@bot.tree.command(name='dog', description='Get a random dog picture')
async def dog(interaction: discord.Interaction):
await interaction.response.defer()
async with aiohttp.ClientSession() as session:
try:
async with session.get('https://api.thedogapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐶 Random Doggy!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a dog picture 😥')
@bot.tree.command(name='serverinfo', description='Display server information')
async def serverinfo(interaction: discord.Interaction):
guild = interaction.guild
embed = discord.Embed(
title=f'📊 {guild.name}',
color=0x5865F2,
timestamp=datetime.utcnow()
)
if guild.icon:
embed.set_thumbnail(url=guild.icon.url)
embed.add_field(name='👑 Owner', value=guild.owner.mention, inline=True)
embed.add_field(name='👥 Members', value=guild.member_count, inline=True)
embed.add_field(name='📅 Created', value=guild.created_at.strftime('%Y-%m-%d'), inline=True)
embed.add_field(name='💬 Channels', value=len(guild.channels), inline=True)
embed.add_field(name='😀 Emojis', value=len(guild.emojis), inline=True)
embed.add_field(name='🎭 Roles', value=len(guild.roles), inline=True)
await interaction.response.send_message(embed=embed)
# Reaction Roles
@bot.event
async def on_raw_reaction_add(payload):
if payload.user_id == bot.user.id:
return
# Get reaction role data
guild_id = str(payload.guild_id)
message_id = str(payload.message_id)
if 'reaction_roles' not in bot.db.data:
bot.db.data['reaction_roles'] = {}
if guild_id not in bot.db.data['reaction_roles']:
return
if message_id not in bot.db.data['reaction_roles'][guild_id]:
return
emoji_str = str(payload.emoji)
role_id = bot.db.data['reaction_roles'][guild_id][message_id].get(emoji_str)
if not role_id:
return
guild = bot.get_guild(payload.guild_id)
if not guild:
return
role = guild.get_role(int(role_id))
if not role:
return
member = guild.get_member(payload.user_id)
if not member:
return
try:
await member.add_roles(role)
logger.info(f'✅ Added role {role.name} to {member.name}')
except Exception as e:
logger.error(f'❌ Error adding role: {e}')
@bot.event
async def on_raw_reaction_remove(payload):
if payload.user_id == bot.user.id:
return
guild_id = str(payload.guild_id)
message_id = str(payload.message_id)
if 'reaction_roles' not in bot.db.data:
return
if guild_id not in bot.db.data.get('reaction_roles', {}):
return
if message_id not in bot.db.data['reaction_roles'][guild_id]:
return
emoji_str = str(payload.emoji)
role_id = bot.db.data['reaction_roles'][guild_id][message_id].get(emoji_str)
if not role_id:
return
guild = bot.get_guild(payload.guild_id)
if not guild:
return
role = guild.get_role(int(role_id))
if not role:
return
member = guild.get_member(payload.user_id)
if not member:
return
try:
await member.remove_roles(role)
logger.info(f' Removed role {role.name} from {member.name}')
except Exception as e:
logger.error(f'❌ Error removing role: {e}')
@bot.tree.command(name='reactionrole', description='[ADMIN] Create a reaction role')
@app_commands.describe(
message_id='Message ID to add reactions to',
emoji='Emoji to use (e.g., 🎮)',
role='Role to assign'
)
@app_commands.default_permissions(administrator=True)
async def reactionrole(interaction: discord.Interaction, message_id: str, emoji: str, role: discord.Role):
try:
message = await interaction.channel.fetch_message(int(message_id))
except discord.NotFound:
await interaction.response.send_message('❌ Message not found in this channel!', ephemeral=True)
return
except ValueError:
await interaction.response.send_message('❌ Invalid message ID!', ephemeral=True)
return
try:
await message.add_reaction(emoji)
except discord.HTTPException:
await interaction.response.send_message('❌ Invalid emoji or unable to add reaction!', ephemeral=True)
return
# Save reaction role
guild_id = str(interaction.guild.id)
if 'reaction_roles' not in bot.db.data:
bot.db.data['reaction_roles'] = {}
if guild_id not in bot.db.data['reaction_roles']:
bot.db.data['reaction_roles'][guild_id] = {}
if message_id not in bot.db.data['reaction_roles'][guild_id]:
bot.db.data['reaction_roles'][guild_id][message_id] = {}
bot.db.data['reaction_roles'][guild_id][message_id][emoji] = str(role.id)
bot.db.save()
embed = discord.Embed(
title='✅ Reaction Role Created',
description=f'React with {emoji} on the message to get {role.mention}',
color=0x00FF00
)
embed.add_field(name='Message ID', value=message_id, inline=True)
embed.add_field(name='Emoji', value=emoji, inline=True)
embed.add_field(name='Role', value=role.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'✅ Created reaction role: {emoji} -> {role.name}')
@bot.tree.command(name='removereactionrole', description='[ADMIN] Remove a reaction role')
@app_commands.describe(
message_id='Message ID',
emoji='Emoji to remove (leave empty to remove all)'
)
@app_commands.default_permissions(administrator=True)
async def removereactionrole(interaction: discord.Interaction, message_id: str, emoji: str = None):
guild_id = str(interaction.guild.id)
if 'reaction_roles' not in bot.db.data:
bot.db.data['reaction_roles'] = {}
if guild_id not in bot.db.data['reaction_roles'] or message_id not in bot.db.data['reaction_roles'].get(guild_id, {}):
await interaction.response.send_message('❌ No reaction roles found for that message!', ephemeral=True)
return
if emoji:
if emoji not in bot.db.data['reaction_roles'][guild_id][message_id]:
await interaction.response.send_message('❌ That emoji is not set up for reaction roles!', ephemeral=True)
return
del bot.db.data['reaction_roles'][guild_id][message_id][emoji]
bot.db.save()
await interaction.response.send_message(f'✅ Removed reaction role for {emoji}')
else:
del bot.db.data['reaction_roles'][guild_id][message_id]
bot.db.save()
await interaction.response.send_message(f'✅ Removed all reaction roles from message {message_id}')
@bot.tree.command(name='listreactionroles', description='List all reaction roles')
async def listreactionroles(interaction: discord.Interaction):
guild_id = str(interaction.guild.id)
if 'reaction_roles' not in bot.db.data:
bot.db.data['reaction_roles'] = {}
guild_reactions = bot.db.data['reaction_roles'].get(guild_id, {})
if not guild_reactions:
await interaction.response.send_message('No reaction roles configured yet!')
return
embed = discord.Embed(
title='🎭 Reaction Roles',
color=0x9B59B6
)
for msg_id, reactions in guild_reactions.items():
roles_text = []
for emoji, role_id in reactions.items():
role = interaction.guild.get_role(int(role_id))
role_name = role.mention if role else f'Role ID: {role_id}'
roles_text.append(f'{emoji}{role_name}')
embed.add_field(
name=f'Message ID: {msg_id}',
value='\n'.join(roles_text) if roles_text else 'No reactions',
name='🎭 Reaction Roles & YouTube',
value='`/reactionrole` - Create reaction role\n`/removereactionrole` - Remove reaction role\n`/listreactionroles` - List reaction roles\n`/createreactionpanel` - Create panel\n`/setupyoutube` - Setup YouTube\n`/toggleyoutube` - Toggle YouTube\n`/youtubestatus` - YouTube status\n`/testlastvideo` - Test video',
inline=False
)
embed.add_field(
name=' Info',
value='`/ping` - Bot latency\n`/serverinfo` - Server info\n`/userinfo` - User info',
inline=False
)
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='createreactionpanel', description='[ADMIN] Create a reaction role panel')
@app_commands.describe(
title='Panel title',
description='Panel description'
)
@app_commands.default_permissions(administrator=True)
async def createreactionpanel(interaction: discord.Interaction, title: str, description: str):
embed = discord.Embed(
title=f'🎭 {title}',
description=description,
color=0x9B59B6
)
embed.set_footer(text='React below to get your roles!')
message = await interaction.channel.send(embed=embed)
await interaction.response.send_message(
f'✅ Panel created! Message ID: `{message.id}`\n'
f'Use `/reactionrole {message.id} <emoji> <role>` to add roles to it.',
ephemeral=True
)
# YouTube Notifications Commands
@bot.tree.command(name='setupyoutube', description='[ADMIN] Set up YouTube notifications in this channel')
@app_commands.default_permissions(administrator=True)
async def setupyoutube(interaction: discord.Interaction):
guild_id = str(interaction.guild.id)
if 'youtube' not in bot.db.data:
bot.db.data['youtube'] = {}
if guild_id not in bot.db.data['youtube']:
bot.db.data['youtube'][guild_id] = {
'enabled': True,
'channel_id': None,
'last_video_id': None
}
bot.db.data['youtube'][guild_id]['channel_id'] = str(interaction.channel.id)
bot.db.data['youtube'][guild_id]['enabled'] = True
bot.db.save()
youtube_channel_id = os.getenv('YOUTUBE_CHANNEL_ID')
if not youtube_channel_id:
await interaction.response.send_message(
'⚠️ YouTube notifications set up in this channel, but `YOUTUBE_CHANNEL_ID` environment variable is not set!\n'
'Ask the bot owner to set it on Render.',
ephemeral=True
)
return
embed = discord.Embed(
title='✅ YouTube Notifications Enabled',
description=f'New video notifications will be posted in {interaction.channel.mention}',
color=0xFF0000
)
embed.add_field(name='Check Interval', value='Every 5 minutes', inline=True)
embed.add_field(name='Status', value='🟢 Active', inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'✅ YouTube notifications enabled in {interaction.guild.name} → #{interaction.channel.name}')
@bot.tree.command(name='toggleyoutube', description='[ADMIN] Toggle YouTube notifications on/off')
@app_commands.default_permissions(administrator=True)
async def toggleyoutube(interaction: discord.Interaction):
guild_id = str(interaction.guild.id)
if 'youtube' not in bot.db.data:
bot.db.data['youtube'] = {}
if guild_id not in bot.db.data['youtube']:
bot.db.data['youtube'][guild_id] = {
'enabled': False,
'channel_id': None,
'last_video_id': None
}
# Toggle
bot.db.data['youtube'][guild_id]['enabled'] = not bot.db.data['youtube'][guild_id].get('enabled', False)
bot.db.save()
status = 'enabled ✅' if bot.db.data['youtube'][guild_id]['enabled'] else 'disabled ❌'
color = 0x00FF00 if bot.db.data['youtube'][guild_id]['enabled'] else 0x808080
embed = discord.Embed(
title='🔔 YouTube Notifications',
description=f'YouTube notifications are now **{status}**',
color=color,
timestamp=datetime.utcnow()
)
await interaction.response.send_message(embed=embed)
@bot.tree.command(name='youtubestatus', description='Check YouTube notification status')
async def youtubestatus(interaction: discord.Interaction):
guild_id = str(interaction.guild.id)
if 'youtube' not in bot.db.data:
bot.db.data['youtube'] = {}
settings = bot.db.data['youtube'].get(guild_id, {
'enabled': False,
'channel_id': None,
'last_video_id': None
})
youtube_channel_id = os.getenv('YOUTUBE_CHANNEL_ID')
embed = discord.Embed(
title='📺 YouTube Notification Status',
color=0xFF0000
)
status = '🟢 Enabled' if settings.get('enabled') else '🔴 Disabled'
embed.add_field(name='Status', value=status, inline=True)
if settings.get('channel_id'):
channel = interaction.guild.get_channel(int(settings['channel_id']))
channel_name = channel.mention if channel else 'Channel not found'
embed.add_field(name='Notification Channel', value=channel_name, inline=True)
else:
embed.add_field(name='Notification Channel', value='Not set', inline=True)
if youtube_channel_id:
embed.add_field(name='YouTube Channel ID', value=f'`{youtube_channel_id}`', inline=False)
else:
embed.add_field(name='⚠️ Warning', value='`YOUTUBE_CHANNEL_ID` not set in environment variables', inline=False)
if settings.get('last_video_id'):
embed.add_field(name='Last Video ID', value=f'`{settings["last_video_id"]}`', inline=False)
embed.set_footer(text='Use /setupyoutube to configure')
await interaction.response.send_message(embed=embed)
# Run bot
if __name__ == '__main__':
token = os.getenv('DISCORD_TOKEN')
if not token:
logger.error('❌ DISCORD_TOKEN not set!')
exit(1)
logger.info('🚀 Starting bot...')
logger.info("everythings working")
logger.info('hello from chersbobers and booly co :3')
bot.run(token)

69
cogs/economy.py Normal file
View file

@ -0,0 +1,69 @@
import discord
from discord import app_commands
from discord.ext import commands
import random
from datetime import datetime
class Economy(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name='balance', description='Check your coin balance')
async def balance(self, interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
user_data = self.bot.db.get_user(str(interaction.guild.id), str(target.id))
embed = discord.Embed(
title='💰 Balance',
description=f'{target.mention} has **{user_data["coins"]:,}** coins in wallet and **{user_data["bank"]:,}** in bank!\n**Total:** {user_data["coins"] + user_data["bank"]:,} coins',
color=0xFFD700
)
await interaction.response.send_message(embed=embed)
@app_commands.command(name='daily', description='Claim your daily reward')
async def daily(self, interaction: discord.Interaction):
user_data = self.bot.db.get_user(str(interaction.guild.id), str(interaction.user.id))
now = datetime.now().timestamp()
if now - user_data['last_daily'] < 86400:
time_left = 86400 - (now - user_data['last_daily'])
hours = int(time_left / 3600)
await interaction.response.send_message(f'⏳ You already claimed your daily! Come back in {hours} hours.', ephemeral=True)
return
reward = 100
user_data['coins'] += reward
user_data['last_daily'] = now
self.bot.db.set_user(str(interaction.guild.id), str(interaction.user.id), user_data)
await interaction.response.send_message(f'✅ You claimed your daily reward of **{reward:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
@app_commands.command(name='work', description='Work to earn coins')
async def work(self, interaction: discord.Interaction):
user_data = self.bot.db.get_user(str(interaction.guild.id), str(interaction.user.id))
now = datetime.now().timestamp()
if now - user_data['last_work'] < 3600:
time_left = 3600 - (now - user_data['last_work'])
minutes = int(time_left / 60)
await interaction.response.send_message(f'⏳ You need to rest! Come back in {minutes} minutes.', ephemeral=True)
return
earnings = random.randint(10, 50)
user_data['coins'] += earnings
user_data['last_work'] = now
self.bot.db.set_user(str(interaction.guild.id), str(interaction.user.id), user_data)
jobs = [
'You worked as a programmer and earned',
'You delivered pizza and earned',
'You streamed on Twitch and earned',
'You mowed lawns and earned',
'You washed cars and earned',
'You tutored students and earned'
]
await interaction.response.send_message(f'💼 {random.choice(jobs)} **{earnings:,}** coins!\n💰 New balance: **{user_data["coins"]:,}** coins')
async def setup(bot):
await bot.add_cog(Economy(bot))

63
cogs/fun.py Normal file
View file

@ -0,0 +1,63 @@
import discord
from discord import app_commands
from discord.ext import commands
import random
import aiohttp
class Fun(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name='8ball', description='Ask the magic 8ball a question')
async def eightball(self, interaction: discord.Interaction, question: str):
responses = [
'Yes, definitely!', 'It is certain.', 'Without a doubt.', 'You may rely on it.',
'As I see it, yes.', 'Most likely.', 'Outlook good.', 'Signs point to yes.',
'Reply hazy, try again.', 'Ask again later.', 'Better not tell you now.',
'Cannot predict now.', "Don't count on it.", 'My reply is no.', 'Very doubtful.'
]
await interaction.response.send_message(f'🔮 **{question}**\n{random.choice(responses)}')
@app_commands.command(name='roll', description='Roll a dice')
async def roll(self, interaction: discord.Interaction, sides: int = 6):
if sides < 2 or sides > 100:
await interaction.response.send_message('❌ Dice must have between 2 and 100 sides!', ephemeral=True)
return
result = random.randint(1, sides)
await interaction.response.send_message(f'🎲 You rolled a **{result}** (1-{sides})')
@app_commands.command(name='flip', description='Flip a coin')
async def flip(self, interaction: discord.Interaction):
result = random.choice(['Heads', 'Tails'])
await interaction.response.send_message(f'🪙 The coin landed on **{result}**!')
@app_commands.command(name='cat', description='Get a random cat picture')
async def cat(self, interaction: discord.Interaction):
await interaction.response.defer()
async with aiohttp.ClientSession() as session:
try:
async with session.get('https://api.thecatapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐱 Random Kitty!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a cat picture 😿')
@app_commands.command(name='dog', description='Get a random dog picture')
async def dog(self, interaction: discord.Interaction):
await interaction.response.defer()
async with aiohttp.ClientSession() as session:
try:
async with session.get('https://api.thedogapi.com/v1/images/search') as resp:
data = await resp.json()
embed = discord.Embed(title='🐶 Random Doggy!', color=0xFF69B4)
embed.set_image(url=data[0]['url'])
embed.set_footer(text=f'Requested by {interaction.user.name}')
await interaction.followup.send(embed=embed)
except:
await interaction.followup.send('Failed to fetch a dog picture 😥')
async def setup(bot):
await bot.add_cog(Fun(bot))

94
cogs/leveling.py Normal file
View file

@ -0,0 +1,94 @@
import discord
from discord import app_commands
from discord.ext import commands
import random
from datetime import datetime
class Leveling(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot or not message.guild:
return
user_data = self.bot.db.get_user(str(message.guild.id), str(message.author.id))
now = datetime.now().timestamp()
if now - user_data['last_message'] >= self.bot.config['xp_cooldown']:
user_data['last_message'] = now
xp_gain = random.randint(self.bot.config['xp_min'], self.bot.config['xp_max'])
user_data['xp'] += xp_gain
xp_needed = user_data['level'] * self.bot.config['xp_per_level']
if user_data['xp'] >= xp_needed:
user_data['level'] += 1
user_data['xp'] = 0
coin_reward = user_data['level'] * self.bot.config['level_up_multiplier']
user_data['coins'] += coin_reward
messages = [
f'🎉 GG {message.author.mention}! You leveled up to **Level {user_data["level"]}**!',
f'⭐ Congrats {message.author.mention}! You\'re now **Level {user_data["level"]}**!',
f'🚀 Level up! {message.author.mention} reached **Level {user_data["level"]}**!'
]
await message.channel.send(
f'{random.choice(messages)} You earned **{coin_reward:,} coins**! 💰'
)
self.bot.db.set_user(str(message.guild.id), str(message.author.id), user_data)
@app_commands.command(name='rank', description='View your rank and level')
async def rank(self, interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
user_data = self.bot.db.get_user(str(interaction.guild.id), str(target.id))
xp_needed = user_data['level'] * self.bot.config['xp_per_level']
all_users = self.bot.db.get_all_guild_users(str(interaction.guild.id))
rank = next((i + 1 for i, u in enumerate(all_users) if u['user_id'] == str(target.id)), 'Unranked')
progress = int((user_data['xp'] / xp_needed) * 20) if xp_needed > 0 else 0
bar = '' * progress + '' * (20 - progress)
color = 0xFF6B6B if user_data['level'] >= 50 else 0xFFD93D if user_data['level'] >= 30 else 0x6BCB77 if user_data['level'] >= 15 else 0x4D96FF
embed = discord.Embed(color=color)
embed.set_author(name=f"{target.display_name}'s Profile", icon_url=target.display_avatar.url)
embed.description = f"""
**RANK** #{rank} / {len(all_users)}
**LEVEL** {user_data['level']}
**XP** {user_data['xp']:,} / {xp_needed:,}
`{bar}`
**💰 BALANCE** {user_data['coins']:,} coins
"""
embed.set_thumbnail(url=target.display_avatar.url)
await interaction.response.send_message(embed=embed)
@app_commands.command(name='leaderboard', description='View the server leaderboard')
async def leaderboard(self, interaction: discord.Interaction):
all_users = self.bot.db.get_all_guild_users(str(interaction.guild.id))[:10]
description = []
for i, u in enumerate(all_users):
medal = '🥇' if i == 0 else '🥈' if i == 1 else '🥉' if i == 2 else f'**{i+1}.**'
description.append(
f'{medal} <@{u["user_id"]}>\n'
f'└ Level {u["data"]["level"]} ({u["data"]["xp"]:,} XP) • {u["data"]["coins"]:,} coins'
)
embed = discord.Embed(
title='🏆 Server Leaderboard',
description='\n'.join(description) if description else 'No users yet!',
color=0x9B59B6,
timestamp=datetime.utcnow()
)
embed.set_footer(text='Top 10 users by level and XP')
await interaction.response.send_message(embed=embed)
async def setup(bot):
await bot.add_cog(Leveling(bot))

600
cogs/system.py Normal file
View file

@ -0,0 +1,600 @@
import discord
from discord import app_commands
from discord.ext import commands, tasks
import asyncio
from datetime import datetime, timedelta
import logging
import feedparser
logger = logging.getLogger('bot')
class System(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.check_youtube.start()
def cog_unload(self):
self.check_youtube.cancel()
@tasks.loop(seconds=300)
async def check_youtube(self):
if 'youtube' not in self.bot.db.data:
return
for guild_id, settings in self.bot.db.data['youtube'].items():
if not settings.get('enabled') or not settings.get('channel_id') or not settings.get('youtube_channel_id'):
continue
try:
feed_url = f'https://www.youtube.com/feeds/videos.xml?channel_id={settings["youtube_channel_id"]}'
feed = await asyncio.to_thread(feedparser.parse, feed_url)
if not feed.entries:
continue
latest = feed.entries[0]
video_id = latest.yt_videoid if hasattr(latest, 'yt_videoid') else latest.id.split(':')[-1]
if video_id != settings.get('last_video_id') and settings.get('last_video_id'):
guild = self.bot.get_guild(int(guild_id))
if not guild:
continue
channel = guild.get_channel(int(settings['channel_id']))
if not channel:
continue
embed = discord.Embed(
title='🎬 New YouTube Video!',
description=f'**{latest.title}**',
url=latest.link,
color=0xFF0000,
timestamp=datetime.utcnow()
)
if hasattr(latest, 'media_thumbnail') and latest.media_thumbnail:
embed.set_thumbnail(url=latest.media_thumbnail[0]['url'])
embed.add_field(name='Channel', value=latest.author, inline=True)
if hasattr(latest, 'published'):
embed.add_field(name='Published', value=latest.published, inline=True)
await channel.send('📺 New video alert! @everyone', embed=embed)
logger.info(f'📺 Sent notification for: {latest.title}')
settings['last_video_id'] = video_id
self.bot.db.save()
except Exception as e:
logger.error(f'❌ Error checking YouTube: {e}')
@check_youtube.before_loop
async def before_check_youtube(self):
await self.bot.wait_until_ready()
@app_commands.command(name='ping', description='Check bot latency')
async def ping(self, interaction: discord.Interaction):
latency = round(self.bot.latency * 1000)
await interaction.response.send_message(f'🏓 Pong! Latency: `{latency}ms`')
@app_commands.command(name='serverinfo', description='Display server information')
async def serverinfo(self, interaction: discord.Interaction):
guild = interaction.guild
embed = discord.Embed(
title=f'📊 {guild.name}',
color=0x5865F2,
timestamp=datetime.utcnow()
)
if guild.icon:
embed.set_thumbnail(url=guild.icon.url)
embed.add_field(name='👑 Owner', value=guild.owner.mention, inline=True)
embed.add_field(name='👥 Members', value=guild.member_count, inline=True)
embed.add_field(name='📅 Created', value=guild.created_at.strftime('%Y-%m-%d'), inline=True)
embed.add_field(name='💬 Channels', value=len(guild.channels), inline=True)
embed.add_field(name='😀 Emojis', value=len(guild.emojis), inline=True)
embed.add_field(name='🎭 Roles', value=len(guild.roles), inline=True)
await interaction.response.send_message(embed=embed)
@app_commands.command(name='userinfo', description='Display user information')
async def userinfo(self, interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
embed = discord.Embed(
title=f'👤 {target.name}',
color=target.color if target.color != discord.Color.default() else 0x5865F2,
timestamp=datetime.utcnow()
)
embed.set_thumbnail(url=target.display_avatar.url)
embed.add_field(name='🆔 ID', value=target.id, inline=True)
embed.add_field(name='📛 Nickname', value=target.nick or 'None', inline=True)
embed.add_field(name='🤖 Bot', value='Yes' if target.bot else 'No', inline=True)
embed.add_field(name='📅 Joined Server', value=target.joined_at.strftime('%Y-%m-%d %H:%M') if target.joined_at else 'Unknown', inline=True)
embed.add_field(name='📅 Account Created', value=target.created_at.strftime('%Y-%m-%d %H:%M'), inline=True)
roles = [role.mention for role in target.roles if role.name != '@everyone']
embed.add_field(name=f'🎭 Roles ({len(roles)})', value=' '.join(roles) if roles else 'None', inline=False)
await interaction.response.send_message(embed=embed)
@app_commands.command(name='kick', description='[MOD] Kick a member from the server')
@app_commands.describe(member='Member to kick', reason='Reason for kick')
@app_commands.default_permissions(kick_members=True)
async def kick(self, interaction: discord.Interaction, member: discord.Member, reason: str = 'No reason provided'):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('❌ You cannot kick this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('❌ You cannot kick the server owner!', ephemeral=True)
return
try:
await member.kick(reason=f'{reason} | Kicked by {interaction.user}')
embed = discord.Embed(
title='👢 Member Kicked',
description=f'{member.mention} has been kicked from the server',
color=0xFF9500,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'👢 {member} kicked by {interaction.user} - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to kick this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='ban', description='[MOD] Ban a member from the server')
@app_commands.describe(member='Member to ban', reason='Reason for ban', delete_days='Days of messages to delete (0-7)')
@app_commands.default_permissions(ban_members=True)
async def ban(self, interaction: discord.Interaction, member: discord.Member, reason: str = 'No reason provided', delete_days: int = 0):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('❌ You cannot ban this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('❌ You cannot ban the server owner!', ephemeral=True)
return
if delete_days < 0 or delete_days > 7:
await interaction.response.send_message('❌ Delete days must be between 0 and 7!', ephemeral=True)
return
try:
await member.ban(reason=f'{reason} | Banned by {interaction.user}', delete_message_days=delete_days)
embed = discord.Embed(
title='🔨 Member Banned',
description=f'{member.mention} has been banned from the server',
color=0xFF0000,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'🔨 {member} banned by {interaction.user} - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to ban this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='unban', description='[MOD] Unban a user from the server')
@app_commands.describe(user_id='User ID to unban', reason='Reason for unban')
@app_commands.default_permissions(ban_members=True)
async def unban(self, interaction: discord.Interaction, user_id: str, reason: str = 'No reason provided'):
try:
user = await self.bot.fetch_user(int(user_id))
except:
await interaction.response.send_message('❌ Invalid user ID!', ephemeral=True)
return
try:
await interaction.guild.unban(user, reason=f'{reason} | Unbanned by {interaction.user}')
embed = discord.Embed(
title='✅ User Unbanned',
description=f'{user.mention} has been unbanned from the server',
color=0x00FF00,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'{user} unbanned by {interaction.user} - Reason: {reason}')
except discord.NotFound:
await interaction.response.send_message('❌ This user is not banned!', ephemeral=True)
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to unban users!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='timeout', description='[MOD] Timeout a member')
@app_commands.describe(member='Member to timeout', duration='Duration in minutes', reason='Reason for timeout')
@app_commands.default_permissions(moderate_members=True)
async def timeout(self, interaction: discord.Interaction, member: discord.Member, duration: int, reason: str = 'No reason provided'):
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message('❌ You cannot timeout this member!', ephemeral=True)
return
if member == interaction.guild.owner:
await interaction.response.send_message('❌ You cannot timeout the server owner!', ephemeral=True)
return
if duration < 1 or duration > 40320:
await interaction.response.send_message('❌ Duration must be between 1 minute and 28 days!', ephemeral=True)
return
try:
timeout_until = datetime.utcnow() + timedelta(minutes=duration)
await member.timeout(timeout_until, reason=f'{reason} | Timed out by {interaction.user}')
embed = discord.Embed(
title='⏰ Member Timed Out',
description=f'{member.mention} has been timed out',
color=0xFFA500,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
embed.add_field(name='Duration', value=f'{duration} minutes', inline=True)
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
logger.info(f'{member} timed out by {interaction.user} for {duration}m - Reason: {reason}')
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to timeout this member!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='warn', description='[MOD] Warn a member')
@app_commands.describe(member='Member to warn', reason='Reason for warning')
@app_commands.default_permissions(moderate_members=True)
async def warn(self, interaction: discord.Interaction, member: discord.Member, reason: str):
guild_id = str(interaction.guild.id)
user_id = str(member.id)
if 'warnings' not in self.bot.db.data:
self.bot.db.data['warnings'] = {}
if guild_id not in self.bot.db.data['warnings']:
self.bot.db.data['warnings'][guild_id] = {}
if user_id not in self.bot.db.data['warnings'][guild_id]:
self.bot.db.data['warnings'][guild_id][user_id] = []
warning = {
'reason': reason,
'moderator': str(interaction.user.id),
'timestamp': datetime.utcnow().isoformat()
}
self.bot.db.data['warnings'][guild_id][user_id].append(warning)
self.bot.db.save()
warning_count = len(self.bot.db.data['warnings'][guild_id][user_id])
embed = discord.Embed(
title='⚠️ Member Warned',
description=f'{member.mention} has been warned',
color=0xFFFF00,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
embed.add_field(name='Total Warnings', value=warning_count, inline=True)
embed.add_field(name='Reason', value=reason, inline=False)
await interaction.response.send_message(embed=embed)
try:
await member.send(f'⚠️ You have been warned in **{interaction.guild.name}**\n**Reason:** {reason}\n**Total Warnings:** {warning_count}')
except:
pass
logger.info(f'⚠️ {member} warned by {interaction.user} - Reason: {reason}')
@app_commands.command(name='warnings', description='View warnings for a member')
@app_commands.describe(member='Member to check warnings for')
async def warnings(self, interaction: discord.Interaction, member: discord.Member = None):
target = member or interaction.user
guild_id = str(interaction.guild.id)
user_id = str(target.id)
if 'warnings' not in self.bot.db.data:
self.bot.db.data['warnings'] = {}
user_warnings = self.bot.db.data['warnings'].get(guild_id, {}).get(user_id, [])
if not user_warnings:
await interaction.response.send_message(f'{target.mention} has no warnings!', ephemeral=True)
return
embed = discord.Embed(
title=f'⚠️ Warnings for {target.display_name}',
color=0xFFFF00,
timestamp=datetime.utcnow()
)
for i, warning in enumerate(user_warnings[-10:], 1):
moderator = interaction.guild.get_member(int(warning['moderator']))
mod_name = moderator.mention if moderator else f"<@{warning['moderator']}>"
timestamp = datetime.fromisoformat(warning['timestamp']).strftime('%Y-%m-%d %H:%M')
embed.add_field(
name=f'Warning #{i}',
value=f'**Moderator:** {mod_name}\n**Reason:** {warning["reason"]}\n**Date:** {timestamp}',
inline=False
)
embed.set_footer(text=f'Total warnings: {len(user_warnings)} | Showing last 10')
await interaction.response.send_message(embed=embed)
@app_commands.command(name='clearwarnings', description='[MOD] Clear warnings for a member')
@app_commands.describe(member='Member to clear warnings for')
@app_commands.default_permissions(moderate_members=True)
async def clearwarnings(self, interaction: discord.Interaction, member: discord.Member):
guild_id = str(interaction.guild.id)
user_id = str(member.id)
if 'warnings' not in self.bot.db.data:
self.bot.db.data['warnings'] = {}
if guild_id not in self.bot.db.data['warnings'] or user_id not in self.bot.db.data['warnings'][guild_id]:
await interaction.response.send_message(f'{member.mention} has no warnings to clear!', ephemeral=True)
return
warning_count = len(self.bot.db.data['warnings'][guild_id][user_id])
self.bot.db.data['warnings'][guild_id][user_id] = []
self.bot.db.save()
embed = discord.Embed(
title='✅ Warnings Cleared',
description=f'Cleared {warning_count} warning(s) for {member.mention}',
color=0x00FF00,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'✅ Cleared {warning_count} warnings for {member} by {interaction.user}')
@app_commands.command(name='purge', description='[MOD] Delete multiple messages')
@app_commands.describe(amount='Number of messages to delete (1-100)', member='Only delete messages from this member (optional)')
@app_commands.default_permissions(manage_messages=True)
async def purge(self, interaction: discord.Interaction, amount: int, member: discord.Member = None):
if amount < 1 or amount > 100:
await interaction.response.send_message('❌ Amount must be between 1 and 100!', ephemeral=True)
return
await interaction.response.defer(ephemeral=True)
try:
if member:
deleted = await interaction.channel.purge(limit=amount, check=lambda m: m.author == member)
else:
deleted = await interaction.channel.purge(limit=amount)
await interaction.followup.send(f'✅ Deleted {len(deleted)} message(s)!', ephemeral=True)
logger.info(f'🗑️ {interaction.user} purged {len(deleted)} messages in #{interaction.channel.name}')
except discord.Forbidden:
await interaction.followup.send('❌ I do not have permission to delete messages!', ephemeral=True)
except Exception as e:
await interaction.followup.send(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='lock', description='[MOD] Lock a channel')
@app_commands.describe(channel='Channel to lock (defaults to current channel)')
@app_commands.default_permissions(manage_channels=True)
async def lock(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
target_channel = channel or interaction.channel
try:
await target_channel.set_permissions(interaction.guild.default_role, send_messages=False)
embed = discord.Embed(
title='🔒 Channel Locked',
description=f'{target_channel.mention} has been locked',
color=0xFF0000,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'🔒 {target_channel.name} locked by {interaction.user}')
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to lock this channel!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@app_commands.command(name='unlock', description='[MOD] Unlock a channel')
@app_commands.describe(channel='Channel to unlock (defaults to current channel)')
@app_commands.default_permissions(manage_channels=True)
async def unlock(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
target_channel = channel or interaction.channel
try:
await target_channel.set_permissions(interaction.guild.default_role, send_messages=None)
embed = discord.Embed(
title='🔓 Channel Unlocked',
description=f'{target_channel.mention} has been unlocked',
color=0x00FF00,
timestamp=datetime.utcnow()
)
embed.add_field(name='Moderator', value=interaction.user.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'🔓 {target_channel.name} unlocked by {interaction.user}')
except discord.Forbidden:
await interaction.response.send_message('❌ I do not have permission to unlock this channel!', ephemeral=True)
except Exception as e:
await interaction.response.send_message(f'❌ An error occurred: {e}', ephemeral=True)
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
if payload.user_id == self.bot.user.id:
return
guild_id = str(payload.guild_id)
message_id = str(payload.message_id)
if 'reaction_roles' not in self.bot.db.data:
self.bot.db.data['reaction_roles'] = {}
if guild_id not in self.bot.db.data['reaction_roles']:
return
if message_id not in self.bot.db.data['reaction_roles'][guild_id]:
return
emoji_str = str(payload.emoji)
role_id = self.bot.db.data['reaction_roles'][guild_id][message_id].get(emoji_str)
if not role_id:
return
guild = self.bot.get_guild(payload.guild_id)
if not guild:
return
role = guild.get_role(int(role_id))
if not role:
return
member = guild.get_member(payload.user_id)
if not member:
return
try:
await member.add_roles(role)
logger.info(f'✅ Added role {role.name} to {member.name}')
except Exception as e:
logger.error(f'❌ Error adding role: {e}')
@commands.Cog.listener()
async def on_raw_reaction_remove(self, payload):
if payload.user_id == self.bot.user.id:
return
guild_id = str(payload.guild_id)
message_id = str(payload.message_id)
if 'reaction_roles' not in self.bot.db.data:
return
if guild_id not in self.bot.db.data.get('reaction_roles', {}):
return
if message_id not in self.bot.db.data['reaction_roles'][guild_id]:
return
emoji_str = str(payload.emoji)
role_id = self.bot.db.data['reaction_roles'][guild_id][message_id].get(emoji_str)
if not role_id:
return
guild = self.bot.get_guild(payload.guild_id)
if not guild:
return
role = guild.get_role(int(role_id))
if not role:
return
member = guild.get_member(payload.user_id)
if not member:
return
try:
await member.remove_roles(role)
logger.info(f' Removed role {role.name} from {member.name}')
except Exception as e:
logger.error(f'❌ Error removing role: {e}')
@app_commands.command(name='reactionrole', description='[ADMIN] Create a reaction role')
@app_commands.describe(message_id='Message ID to add reactions to', emoji='Emoji to use (e.g., 🎮)', role='Role to assign')
@app_commands.default_permissions(administrator=True)
async def reactionrole(self, interaction: discord.Interaction, message_id: str, emoji: str, role: discord.Role):
try:
message = await interaction.channel.fetch_message(int(message_id))
except discord.NotFound:
await interaction.response.send_message('❌ Message not found in this channel!', ephemeral=True)
return
except ValueError:
await interaction.response.send_message('❌ Invalid message ID!', ephemeral=True)
return
try:
await message.add_reaction(emoji)
except discord.HTTPException:
await interaction.response.send_message('❌ Invalid emoji or unable to add reaction!', ephemeral=True)
return
guild_id = str(interaction.guild.id)
if 'reaction_roles' not in self.bot.db.data:
self.bot.db.data['reaction_roles'] = {}
if guild_id not in self.bot.db.data['reaction_roles']:
self.bot.db.data['reaction_roles'][guild_id] = {}
if message_id not in self.bot.db.data['reaction_roles'][guild_id]:
self.bot.db.data['reaction_roles'][guild_id][message_id] = {}
self.bot.db.data['reaction_roles'][guild_id][message_id][emoji] = str(role.id)
self.bot.db.save()
embed = discord.Embed(
title='✅ Reaction Role Created',
description=f'React with {emoji} on the message to get {role.mention}',
color=0x00FF00
)
embed.add_field(name='Message ID', value=message_id, inline=True)
embed.add_field(name='Emoji', value=emoji, inline=True)
embed.add_field(name='Role', value=role.mention, inline=True)
await interaction.response.send_message(embed=embed)
logger.info(f'✅ Created reaction role: {emoji} -> {role.name}')
@app_commands.command(name='removereactionrole', description='[ADMIN] Remove a reaction role')
@app_commands.describe(message_id='Message ID', emoji='Emoji to remove (leave empty to remove all)')
@app_commands.default_permissions(administrator=True)
async def removereactionrole(self, interaction: discord.Interaction, message_id: str, emoji: str = None):
guild_id = str(interaction.guild.id)
if 'reaction_roles' not in self.bot.db.data:
self.bot.db.data['reaction_roles'] = {}
if guild_id not in self.bot.db.data['reaction_roles'] or message_id not in self.bot.db.data['reaction_roles'].get(guild_id, {}):
await interaction.response.send_message('❌ No reaction roles found for that message!', ephemeral=True)
return
if emoji:
if emoji not in self.bot.db.data['reaction_roles'][guild_id][message_id]:
await interaction.response.send_message('❌ That emoji is not set up for reaction roles!', ephemeral=True)
return
del self.bot.db.data['reaction_roles'][guild_id][message_id][emoji]
self.bot.db.save()
await interaction.response.send_message(f'✅ Removed reaction role for {emoji}')
else:
del self.bot.db.data['reaction_roles'][guild_id][message_id]
self.bot.db.save()
await interaction.response.send_message(f'✅ Removed all reaction roles from message {message_id}')
@app_commands.command(name='listreactionroles', description='List all reaction roles')
async def listreactionroles(self, interaction: discord.Interaction):
guild_id = str(interaction.guild.id)
if 'reaction