First layout

This commit is contained in:
2026-06-17 12:33:19 -05:00
parent 2e91115a1e
commit cfff3e7366
10 changed files with 1531 additions and 1 deletions

1
cogs/__init__.py Normal file
View File

@@ -0,0 +1 @@
# OC Bot Cogs Package

106
cogs/config.py Normal file
View File

@@ -0,0 +1,106 @@
import discord
from discord.ext import commands
import os
class Config(commands.Cog):
def __init__(self, bot):
self.bot = bot
async def is_bot_owner(self, ctx):
"""Check if user is bot owner"""
return ctx.author.id == self.bot.owner_id or await self.bot.is_owner(ctx.author)
@commands.command(name='reload')
async def reload(self, ctx):
"""Reload all cogs (owner only)"""
if not await self.is_bot_owner(ctx):
await ctx.send('❌ You don\'t have permission to use this command.')
return
cogs_dir = 'cogs'
reloaded = []
failed = []
for filename in os.listdir(cogs_dir):
if filename.endswith('.py') and not filename.startswith('_'):
cog_name = filename[:-3]
try:
await self.bot.reload_extension(f'cogs.{cog_name}')
reloaded.append(cog_name)
except Exception as e:
failed.append(f'{cog_name}: {str(e)}')
embed = discord.Embed(
title='🔄 Cog Reload',
color=discord.Color.green() if not failed else discord.Color.orange()
)
if reloaded:
embed.add_field(
name='✅ Reloaded',
value=', '.join(reloaded),
inline=False
)
if failed:
embed.add_field(
name='❌ Failed',
value='\n'.join(failed),
inline=False
)
await ctx.send(embed=embed)
@commands.command(name='debug')
async def debug(self, ctx):
"""Show debug information (owner only)"""
if not await self.is_bot_owner(ctx):
await ctx.send('❌ You don\'t have permission to use this command.')
return
embed = discord.Embed(
title='🔧 Debug Info',
color=discord.Color.blue()
)
embed.add_field(
name='Bot ID',
value=str(self.bot.user.id),
inline=True
)
embed.add_field(
name='Latency',
value=f'{self.bot.latency * 1000:.2f}ms',
inline=True
)
embed.add_field(
name='Guilds',
value=str(len(self.bot.guilds)),
inline=True
)
embed.add_field(
name='Cogs Loaded',
value=str(len(self.bot.cogs)),
inline=True
)
if hasattr(self.bot, 'db') and self.bot.db:
embed.add_field(
name='Database',
value='✅ Connected',
inline=True
)
else:
embed.add_field(
name='Database',
value='❌ Not connected',
inline=True
)
await ctx.send(embed=embed)
async def setup(bot):
await bot.add_cog(Config(bot))

425
cogs/help.py Normal file
View File

@@ -0,0 +1,425 @@
import discord
from discord.ext import commands
class Help(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(name='help')
async def help_command(self, ctx, topic=None):
"""Show help information"""
if not topic:
return await self.show_main_help(ctx)
topic = topic.lower()
if topic == 'create':
return await self.help_create(ctx)
elif topic == 'list':
return await self.help_list(ctx)
elif topic == 'edit':
return await self.help_edit(ctx)
elif topic == 'delete':
return await self.help_delete(ctx)
elif topic == 'info':
return await self.help_info(ctx)
elif topic == 'preview':
return await self.help_preview(ctx)
elif topic == 'triggers':
return await self.help_triggers(ctx)
elif topic == 'oc':
return await self.help_oc(ctx)
else:
await ctx.send('Unknown help topic. Use `,help` for available topics.')
async def show_main_help(self, ctx):
"""Show main help menu"""
embed = discord.Embed(
title='🎭 Original Character Bot - Help Menu',
description='Welcome to the OC Bot! Create and manage original characters with ease.',
color=discord.Color.blue()
)
embed.add_field(
name='📚 What are OCs?',
value='OCs (Original Characters) are fictional characters you create. This bot lets you manage them and send messages as them!',
inline=False
)
embed.add_field(
name='🚀 Getting Started',
value='`,oc create` - Start creating your first character!\n`,oc list` - View all your characters',
inline=False
)
embed.add_field(
name='📖 Commands',
value='**Character Management:**\n'
'`,oc create` - Create a new character\n'
'`,oc list` - List your characters\n'
'`,oc info <name>` - View character details\n'
'`,oc edit <name>` - Edit a character\n'
'`,oc delete <name>` - Delete a character\n'
'`,oc preview <name>` - Preview a character message\n\n'
'**Help:**\n'
'`,help` - Show this menu\n'
'`,help <topic>` - Show help for a specific topic',
inline=False
)
embed.add_field(
name='💡 Quick Example',
value='1. `,oc create` and follow the wizard\n'
'2. Use `,ba Hello everyone` to send as your character\n'
'3. `,oc list` to see all your characters',
inline=False
)
embed.add_field(
name='📝 Help Topics',
value='`,help create` - Character creation guide\n'
'`,help triggers` - How triggers work\n'
'`,help oc` - OC command overview\n'
'`,help edit` - Editing guide\n'
'`,help delete` - Deletion guide',
inline=False
)
await ctx.send(embed=embed)
async def help_create(self, ctx):
"""Help for character creation"""
embed = discord.Embed(
title='Character Creation Guide',
color=discord.Color.green()
)
embed.add_field(
name='Command',
value='`,oc create`',
inline=False
)
embed.add_field(
name='What is it?',
value='Starts an interactive wizard to create your first character.',
inline=False
)
embed.add_field(
name='Step-by-step',
value='1. Run `,oc create`\n'
'2. Enter your character\'s name\n'
'3. Provide an avatar URL (image link)\n'
'4. Choose a unique trigger (e.g., `ba`, `sa`)\n'
'5. Add an optional description\n'
'6. Confirm!',
inline=False
)
embed.add_field(
name='Avatar URL',
value='This should be a direct link to an image:\n'
'✅ `https://example.com/avatar.png`\n'
'❌ Discord user profile link\n'
'❌ Local file path',
inline=False
)
embed.add_field(
name='Trigger',
value='A short code (2-5 chars) that activates your character.\n'
'You\'ll use it like: `,ba Hello!`\n'
'Must be unique to you (others can have `,ba` too)',
inline=False
)
embed.add_field(
name='Example',
value='* Name: **Barny**\n'
'* Avatar: `https://example.com/barny.png`\n'
'* Trigger: `ba`\n'
'* Final command: `,ba Hello everyone!`',
inline=False
)
await ctx.send(embed=embed)
async def help_triggers(self, ctx):
"""Help for trigger system"""
embed = discord.Embed(
title='Trigger System Guide',
color=discord.Color.orange()
)
embed.add_field(
name='What is a trigger?',
value='A trigger is a short code that activates your character when you use it with the comma prefix.',
inline=False
)
embed.add_field(
name='How to use triggers',
value='If your character has trigger `ba`:\n'
'`,ba Hello!` → Message sent as your character\n'
'`,ba I\'m excited!` → Another message as them',
inline=False
)
embed.add_field(
name='Trigger Rules',
value='✅ 1-10 characters\n'
'✅ Lowercase letters (a-z)\n'
'✅ Numbers (0-9)\n'
'❌ Cannot be reserved words (help, oc, ping, etc.)\n'
'❌ Must be unique to YOU (no duplicates per user)',
inline=False
)
embed.add_field(
name='Multiple Users, Same Trigger',
value='User A can have `,ba = Barny`\n'
'User B can have `,ba = Bob`\n'
'User C can have `,ba = Dragon`\n\n'
'Bot checks: Your ID + Trigger = Your character',
inline=False
)
embed.add_field(
name='Reserved Triggers',
value='These cannot be used as character triggers:\n'
'help, oc, ping, stats, invite, support, about,\n'
'reload, debug, admin, config',
inline=False
)
await ctx.send(embed=embed)
async def help_oc(self, ctx):
"""Help for oc command"""
embed = discord.Embed(
title='OC Command Overview',
color=discord.Color.purple()
)
embed.add_field(
name='`,oc create`',
value='Start an interactive wizard to create a character',
inline=False
)
embed.add_field(
name='`,oc list`',
value='See all your characters and their triggers',
inline=False
)
embed.add_field(
name='`,oc info <name>`',
value='View detailed information about a character',
inline=False
)
embed.add_field(
name='`,oc edit <name>`',
value='Modify character details (name, avatar, trigger, description)',
inline=False
)
embed.add_field(
name='`,oc delete <name>`',
value='Delete a character (requires confirmation)',
inline=False
)
embed.add_field(
name='`,oc preview <name>`',
value='See how a character\'s message will look',
inline=False
)
await ctx.send(embed=embed)
async def help_edit(self, ctx):
"""Help for editing"""
embed = discord.Embed(
title='Editing Characters',
color=discord.Color.gold()
)
embed.add_field(
name='Command',
value='`,oc edit <character_name>`',
inline=False
)
embed.add_field(
name='What can you edit?',
value='• **Name** - Your character\'s name\n'
'• **Avatar** - Their profile picture URL\n'
'• **Trigger** - Their command code\n'
'• **Description** - Background info',
inline=False
)
embed.add_field(
name='How to edit',
value='1. `,oc edit Barny`\n'
'2. Choose what to edit (number 1-4)\n'
'3. Provide new value\n'
'4. Done!',
inline=False
)
embed.add_field(
name='Trigger Change Tips',
value='When changing a trigger:\n'
'• Old trigger becomes unusable\n'
'• New trigger must be unique\n'
'• Use new trigger immediately\n'
'• E.g., if `,ba` → `,bn`, use `,bn` now',
inline=False
)
await ctx.send(embed=embed)
async def help_delete(self, ctx):
"""Help for deletion"""
embed = discord.Embed(
title='Deleting Characters',
color=discord.Color.red()
)
embed.add_field(
name='Command',
value='`,oc delete <character_name>`',
inline=False
)
embed.add_field(
name='What happens?',
value='1. Bot asks for confirmation\n'
'2. Type `YES` within 30 seconds\n'
'3. Character is permanently deleted\n'
'4. Trigger becomes available again',
inline=False
)
embed.add_field(
name='⚠️ Warning',
value='Deletion is **permanent**! You cannot undo this.\n'
'Create a backup/description if needed.',
inline=False
)
embed.add_field(
name='Example',
value='User: `,oc delete Barny`\n'
'Bot: Are you sure? Type YES to confirm.\n'
'User: `YES`\n'
'Bot: Character Barny deleted.',
inline=False
)
await ctx.send(embed=embed)
async def help_list(self, ctx):
"""Help for listing characters"""
embed = discord.Embed(
title='Listing Your Characters',
color=discord.Color.blue()
)
embed.add_field(
name='Command',
value='`,oc list`',
inline=False
)
embed.add_field(
name='What does it show?',
value='A list of all your characters with their triggers',
inline=False
)
embed.add_field(
name='Example Output',
value='**Your Characters**\n'
'Barny\n'
'Trigger: `,ba`\n\n'
'Sarah\n'
'Trigger: `,sa`\n\n'
'Knight\n'
'Trigger: `,kn`',
inline=False
)
await ctx.send(embed=embed)
async def help_info(self, ctx):
"""Help for info command"""
embed = discord.Embed(
title='Character Information',
color=discord.Color.blurple()
)
embed.add_field(
name='Command',
value='`,oc info <character_name>`',
inline=False
)
embed.add_field(
name='What does it show?',
value='Detailed information about a character:\n'
'• Name\n'
'• Trigger code\n'
'• Avatar image\n'
'• Description\n'
'• Creation date\n'
'• Last time used',
inline=False
)
embed.add_field(
name='Example',
value='`,oc info Barny`',
inline=False
)
await ctx.send(embed=embed)
async def help_preview(self, ctx):
"""Help for preview command"""
embed = discord.Embed(
title='Character Preview',
color=discord.Color.green()
)
embed.add_field(
name='Command',
value='`,oc preview <character_name>`',
inline=False
)
embed.add_field(
name='What does it do?',
value='Shows you exactly how your character will appear when sending messages.\n'
'Useful to check:\n'
'• Avatar displays correctly\n'
'• Name looks good\n'
'• Everything works as expected',
inline=False
)
embed.add_field(
name='Example',
value='`,oc preview Barny`\n\n'
'Bot sends a sample message with Barny\'s avatar and name',
inline=False
)
await ctx.send(embed=embed)
async def setup(bot):
await bot.add_cog(Help(bot))

377
cogs/oc.py Normal file
View File

@@ -0,0 +1,377 @@
import discord
from discord.ext import commands
import logging
import asyncio
logger = logging.getLogger(__name__)
RESERVED_TRIGGERS = ['help', 'oc', 'ping', 'stats', 'invite', 'support', 'about', 'reload', 'debug', 'admin', 'config']
class OC(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(name='oc')
async def oc_command(self, ctx):
"""Base OC command"""
await ctx.send('Use `,oc create`, `,oc list`, `,oc edit`, `,oc delete`, `,oc info`, or `,oc preview`\n\nType `,help oc` for more information.')
@commands.command(name='oc')
async def oc_create(self, ctx):
"""Start interactive character creation wizard"""
if ctx.invoked_subcommand is not None:
return
await ctx.send('Use `,oc create` to start the wizard.')
async def prompt_user(self, ctx, prompt_text, timeout=120):
"""Helper to get user input with timeout"""
await ctx.send(prompt_text)
try:
msg = await self.bot.wait_for(
'message',
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=timeout
)
return msg.content
except asyncio.TimeoutError:
await ctx.send('❌ Character creation cancelled (timeout).')
return None
@oc_create.command(name='create')
async def create(self, ctx):
"""Interactive character creation"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
# Get character name
name = await self.prompt_user(ctx, 'What is your character name?')
if not name:
return
# Get avatar URL
avatar_url = await self.prompt_user(ctx, 'What avatar URL would you like to use?')
if not avatar_url:
return
# Get trigger
while True:
trigger = await self.prompt_user(ctx, 'What trigger would you like? (Example: `ba`)')
if not trigger:
return
# Check if reserved
if trigger.lower() in RESERVED_TRIGGERS:
await ctx.send(f'❌ `{trigger}` is reserved. Please choose another.')
continue
# Check if available
if await db.is_trigger_available(ctx.author.id, trigger):
break
else:
await ctx.send(f'❌ Trigger `,{trigger}` is already in use. Please choose another.')
# Get description (optional)
await ctx.send('Description? (optional - type `skip` to skip)')
try:
msg = await self.bot.wait_for(
'message',
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=120
)
description = '' if msg.content.lower() == 'skip' else msg.content
except asyncio.TimeoutError:
await ctx.send('❌ Character creation cancelled.')
return
# Create character
char_id = await db.create_character(
ctx.author.id,
name,
trigger,
avatar_url,
description
)
if char_id:
embed = discord.Embed(
title='✅ Character Created!',
color=discord.Color.green()
)
embed.add_field(name='Name', value=name, inline=False)
embed.add_field(name='Trigger', value=f'`,{trigger}`', inline=False)
embed.add_field(name='Avatar', value=f'[Link]({avatar_url})', inline=False)
if description:
embed.add_field(name='Description', value=description, inline=False)
await ctx.send(embed=embed)
else:
await ctx.send('❌ Failed to create character.')
@oc_create.command(name='list')
async def list_characters(self, ctx):
"""List all user's characters"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
characters = await db.get_user_characters(ctx.author.id)
if not characters:
await ctx.send('You have no characters yet. Use `,oc create` to create one.')
return
embed = discord.Embed(
title='Your Characters',
color=discord.Color.blue()
)
for char in characters:
embed.add_field(
name=char['name'],
value=f"Trigger: `,{char['trigger']}`",
inline=False
)
await ctx.send(embed=embed)
@oc_create.command(name='info')
async def info(self, ctx, *, character_name):
"""Show character information"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
# Find character by name
characters = await db.get_user_characters(ctx.author.id)
character = None
for char in characters:
if char['name'].lower() == character_name.lower():
character = char
break
if not character:
await ctx.send(f'❌ Character `{character_name}` not found.')
return
embed = discord.Embed(
title=character['name'],
color=discord.Color.purple()
)
embed.add_field(name='Trigger', value=f"`,{character['trigger']}`", inline=False)
embed.add_field(name='Avatar', value=f"[Link]({character['avatar_url']})", inline=False)
if character['description']:
embed.add_field(name='Description', value=character['description'], inline=False)
embed.add_field(name='Created', value=character['created_at'], inline=True)
embed.add_field(name='Last Used', value=character['last_used'] or 'Never', inline=True)
embed.set_thumbnail(url=character['avatar_url'])
await ctx.send(embed=embed)
@oc_create.command(name='edit')
async def edit(self, ctx, character_name=None):
"""Edit a character"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
characters = await db.get_user_characters(ctx.author.id)
if not characters:
await ctx.send('You have no characters to edit.')
return
# If no character specified, show selection
if not character_name:
embed = discord.Embed(
title='Select a character to edit:',
color=discord.Color.blue()
)
for i, char in enumerate(characters, 1):
embed.add_field(
name=f'{i}. {char["name"]}',
value=f"Trigger: `,{char['trigger']}`",
inline=False
)
await ctx.send(embed=embed)
await ctx.send('Reply with the number of the character you want to edit.')
try:
msg = await self.bot.wait_for(
'message',
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=120
)
try:
idx = int(msg.content) - 1
if 0 <= idx < len(characters):
character = characters[idx]
else:
await ctx.send('❌ Invalid selection.')
return
except ValueError:
await ctx.send('❌ Invalid input.')
return
except asyncio.TimeoutError:
await ctx.send('❌ Edit cancelled.')
return
else:
# Find by name
character = None
for char in characters:
if char['name'].lower() == character_name.lower():
character = char
break
if not character:
await ctx.send(f'❌ Character `{character_name}` not found.')
return
# Show edit menu
embed = discord.Embed(
title=f'Editing: {character["name"]}',
color=discord.Color.gold()
)
embed.add_field(name='1', value='Edit Name', inline=False)
embed.add_field(name='2', value='Edit Avatar', inline=False)
embed.add_field(name='3', value='Edit Trigger', inline=False)
embed.add_field(name='4', value='Edit Description', inline=False)
await ctx.send(embed=embed)
try:
msg = await self.bot.wait_for(
'message',
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=120
)
choice = msg.content
except asyncio.TimeoutError:
await ctx.send('❌ Edit cancelled.')
return
updates = {}
if choice == '1':
new_value = await self.prompt_user(ctx, 'New name?')
if new_value:
updates['name'] = new_value
elif choice == '2':
new_value = await self.prompt_user(ctx, 'New avatar URL?')
if new_value:
updates['avatar_url'] = new_value
elif choice == '3':
while True:
new_value = await self.prompt_user(ctx, 'New trigger?')
if not new_value:
return
if new_value.lower() in RESERVED_TRIGGERS:
await ctx.send(f'❌ `{new_value}` is reserved.')
continue
if await db.is_trigger_available(ctx.author.id, new_value):
updates['trigger'] = new_value
break
else:
await ctx.send(f'❌ Trigger `,{new_value}` is already in use.')
elif choice == '4':
new_value = await self.prompt_user(ctx, 'New description?')
if new_value:
updates['description'] = new_value
if updates and await db.update_character(character['id'], **updates):
await ctx.send(f'✅ Character updated!')
else:
await ctx.send('❌ Failed to update character.')
@oc_create.command(name='delete')
async def delete(self, ctx, *, character_name):
"""Delete a character"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
characters = await db.get_user_characters(ctx.author.id)
character = None
for char in characters:
if char['name'].lower() == character_name.lower():
character = char
break
if not character:
await ctx.send(f'❌ Character `{character_name}` not found.')
return
await ctx.send(f'Are you sure you want to delete **{character["name"]}**? Type `YES` within 30 seconds to confirm.')
try:
msg = await self.bot.wait_for(
'message',
check=lambda m: m.author == ctx.author and m.channel == ctx.channel,
timeout=30
)
if msg.content.upper() != 'YES':
await ctx.send('❌ Deletion cancelled.')
return
except asyncio.TimeoutError:
await ctx.send('❌ Deletion cancelled (timeout).')
return
if await db.delete_character(character['id']):
await ctx.send(f'✅ Character **{character["name"]}** deleted.')
else:
await ctx.send('❌ Failed to delete character.')
@oc_create.command(name='preview')
async def preview(self, ctx, *, character_name):
"""Preview how a character message will look"""
db = self.bot.db if hasattr(self.bot, 'db') else None
if not db:
await ctx.send('❌ Database connection error.')
return
characters = await db.get_user_characters(ctx.author.id)
character = None
for char in characters:
if char['name'].lower() == character_name.lower():
character = char
break
if not character:
await ctx.send(f'❌ Character `{character_name}` not found.')
return
await ctx.send('**Preview of how your character message will appear:**')
try:
webhooks = await ctx.channel.webhooks()
webhook = None
for wh in webhooks:
if wh.user.id == self.bot.user.id:
webhook = wh
break
if not webhook:
webhook = await ctx.channel.create_webhook(name='OC-Bot')
await webhook.send(
content='*This is a preview message*',
username=character['name'],
avatar_url=character['avatar_url']
)
except Exception as e:
logger.error(f'Preview error: {e}')
await ctx.send('❌ Failed to create preview.')
async def setup(bot):
await bot.add_cog(OC(bot))

119
cogs/utils.py Normal file
View File

@@ -0,0 +1,119 @@
import discord
from discord.ext import commands
import platform
class Utils(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(name='ping')
async def ping(self, ctx):
"""Check bot latency"""
latency = self.bot.latency * 1000
embed = discord.Embed(
title='🏓 Pong!',
description=f'Latency: {latency:.2f}ms',
color=discord.Color.green()
)
await ctx.send(embed=embed)
@commands.command(name='stats')
async def stats(self, ctx):
"""Show bot statistics"""
embed = discord.Embed(
title='📊 Bot Statistics',
color=discord.Color.blue()
)
embed.add_field(
name='Latency',
value=f'{self.bot.latency * 1000:.2f}ms',
inline=True
)
embed.add_field(
name='Servers',
value=str(len(self.bot.guilds)),
inline=True
)
embed.add_field(
name='Python Version',
value=platform.python_version(),
inline=True
)
embed.add_field(
name='discord.py Version',
value=discord.__version__,
inline=True
)
await ctx.send(embed=embed)
@commands.command(name='invite')
async def invite(self, ctx):
"""Get bot invite link"""
embed = discord.Embed(
title='🔗 Invite OC Bot',
description='Use this link to invite the bot to your server:',
color=discord.Color.blurple()
)
# Generate invite URL with proper permissions
permissions = discord.Permissions(
send_messages=True,
manage_messages=True,
manage_webhooks=True,
read_message_history=True,
embed_links=True
)
invite_url = discord.utils.oauth_url(
self.bot.user.id,
permissions=permissions
)
embed.add_field(
name='Invite Link',
value=f'[Click here]({invite_url})',
inline=False
)
await ctx.send(embed=embed)
@commands.command(name='about')
async def about(self, ctx):
"""About the bot"""
embed = discord.Embed(
title='🎭 About OC Bot',
description='A bot for managing and roleplaying as your original characters',
color=discord.Color.purple()
)
embed.add_field(
name='Features',
value='✅ Create and manage original characters\n'
'✅ Send messages as your characters\n'
'✅ Customize avatars and descriptions\n'
'✅ User-specific triggers\n'
'✅ Full character management',
inline=False
)
embed.add_field(
name='Prefix',
value='`,` (comma)',
inline=False
)
embed.add_field(
name='Help',
value='Use `,help` to get started',
inline=False
)
await ctx.send(embed=embed)
async def setup(bot):
await bot.add_cog(Utils(bot))