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

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))