242 lines
6.9 KiB
Python
242 lines
6.9 KiB
Python
"""Custom-bot management commands: create, list, and delete."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
from discord.ext import commands
|
|
|
|
from vibe_bot.app import invalidate_bot_cache
|
|
from vibe_bot.commands._state import require_app, set_app
|
|
|
|
if TYPE_CHECKING:
|
|
from discord.ext.commands import Bot
|
|
from discord.ext.commands import Context as CommandsContext
|
|
|
|
from vibe_bot.app import App
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MIN_BOT_NAME_LENGTH = 2
|
|
MAX_BOT_NAME_LENGTH = 50
|
|
MIN_PERSONALITY_LENGTH = 10
|
|
MAX_PERSONALITY_LENGTH = 1000
|
|
|
|
|
|
def register(bot: commands.Bot, app: App) -> None:
|
|
"""Register the custom-bot management commands."""
|
|
set_app(app)
|
|
bot.command(name="custom-bot")(custom_bot)
|
|
bot.command(name="list-custom-bots")(list_custom_bots)
|
|
bot.command(name="delete-custom-bot")(delete_custom_bot)
|
|
|
|
|
|
async def custom_bot(
|
|
ctx: CommandsContext[Bot],
|
|
bot_name: str,
|
|
*,
|
|
personality: str,
|
|
) -> None:
|
|
"""Create a custom bot with a name and personality.
|
|
|
|
Usage: !custom-bot <bot_name> <personality_description>
|
|
Example: !custom-bot alfred you are a proper british butler
|
|
"""
|
|
app = require_app()
|
|
logger.info(
|
|
"Custom bot command initiated by user %s: name=%r, personality_chars=%d",
|
|
ctx.author.id,
|
|
bot_name,
|
|
len(personality),
|
|
)
|
|
|
|
# Validate bot name
|
|
name_length = 0 if not bot_name else len(bot_name)
|
|
if (
|
|
not bot_name
|
|
or name_length < MIN_BOT_NAME_LENGTH
|
|
or name_length > MAX_BOT_NAME_LENGTH
|
|
):
|
|
logger.warning(
|
|
"Invalid bot name from user %s: %r (length: %d)",
|
|
ctx.author.id,
|
|
bot_name,
|
|
name_length,
|
|
)
|
|
await ctx.send("Invalid bot name. Name must be between 2 and 50 characters.")
|
|
return
|
|
|
|
logger.debug("Bot name validation passed for %r", bot_name)
|
|
|
|
# Validate personality
|
|
personality_length = 0 if not personality else len(personality)
|
|
if not personality or personality_length < MIN_PERSONALITY_LENGTH:
|
|
logger.warning(
|
|
"Invalid personality from user %s: length=%d",
|
|
ctx.author.id,
|
|
personality_length,
|
|
)
|
|
await ctx.send(
|
|
"Invalid personality. Description must be at least 10 characters.",
|
|
)
|
|
return
|
|
if personality_length > MAX_PERSONALITY_LENGTH:
|
|
logger.warning(
|
|
"Personality too long from user %s: length=%d",
|
|
ctx.author.id,
|
|
personality_length,
|
|
)
|
|
await ctx.send(
|
|
f"Personality too long. " f"Max {MAX_PERSONALITY_LENGTH} characters.",
|
|
)
|
|
return
|
|
|
|
logger.debug("Personality validation passed for bot %r", bot_name)
|
|
|
|
# Create the custom bot
|
|
logger.debug(
|
|
"Attempting to create custom bot %r for user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
result = await asyncio.to_thread(
|
|
app.manager.create_custom_bot,
|
|
bot_name=bot_name,
|
|
system_prompt=personality,
|
|
created_by=str(ctx.author.id),
|
|
)
|
|
|
|
if result is False:
|
|
logger.warning(
|
|
"Failed to create custom bot %r for user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await ctx.send("Failed to create custom bot.")
|
|
return
|
|
|
|
await asyncio.to_thread(invalidate_bot_cache, app)
|
|
|
|
if result == "replaced":
|
|
logger.info(
|
|
"Replaced existing custom bot %r for user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await ctx.send(
|
|
f"Custom bot **'{bot_name}'** already existed and has been "
|
|
f"**replaced** with personality: *{personality}*",
|
|
)
|
|
else:
|
|
logger.info(
|
|
"Successfully created custom bot %r for user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await ctx.send(
|
|
f"Custom bot **'{bot_name}'** has been created "
|
|
f"with personality: *{personality}*",
|
|
)
|
|
await ctx.send(
|
|
f"\nYou can now use this bot with: " f"`!{bot_name} <your message>`",
|
|
)
|
|
|
|
|
|
async def list_custom_bots(ctx: CommandsContext[Bot]) -> None:
|
|
"""List all custom bots available in the server."""
|
|
app = require_app()
|
|
logger.info("Listing custom bots requested by user %s", ctx.author.id)
|
|
|
|
logger.debug("Fetching list of custom bots from database")
|
|
bots = await asyncio.to_thread(app.manager.list_custom_bots)
|
|
|
|
if not bots:
|
|
logger.debug("No custom bots found for user %s", ctx.author.id)
|
|
await ctx.send(
|
|
"No custom bots have been created yet. "
|
|
"Use `!custom-bot <name> <personality>` to create one.",
|
|
)
|
|
return
|
|
|
|
logger.debug(
|
|
"Found %d custom bots, displaying top 10 for user %s",
|
|
len(bots),
|
|
ctx.author.id,
|
|
)
|
|
bot_list = "Available Custom Bots:\n\n"
|
|
for name, _prompt, _creator in bots:
|
|
bot_list += f"* {name}\n"
|
|
|
|
logger.debug("Sending bot list response to user %s", ctx.author.id)
|
|
await ctx.send(bot_list)
|
|
|
|
|
|
async def delete_custom_bot(ctx: CommandsContext[Bot], bot_name: str) -> None:
|
|
"""Delete a custom bot (only the creator can delete).
|
|
|
|
Usage: !delete-custom-bot <bot_name>
|
|
"""
|
|
app = require_app()
|
|
logger.info(
|
|
"Delete custom bot command initiated by user %s: bot_name=%r",
|
|
ctx.author.id,
|
|
bot_name,
|
|
)
|
|
|
|
# Get bot info
|
|
logger.debug("Looking up custom bot %r in database", bot_name)
|
|
bot_info = await asyncio.to_thread(app.manager.get_custom_bot, bot_name)
|
|
|
|
if not bot_info:
|
|
logger.warning(
|
|
"Custom bot %r not found by user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await ctx.send(f"Custom bot '{bot_name}' not found.")
|
|
return
|
|
|
|
logger.debug(
|
|
"Custom bot %r found, owned by user %s",
|
|
bot_name,
|
|
bot_info[2],
|
|
)
|
|
|
|
# Check ownership
|
|
if bot_info[2] != str(ctx.author.id):
|
|
logger.warning(
|
|
"User %s attempted to delete bot %r they don't own",
|
|
ctx.author.id,
|
|
bot_name,
|
|
)
|
|
await ctx.send("You can only delete your own custom bots.")
|
|
return
|
|
|
|
logger.debug(
|
|
"User %s is authorized to delete bot %r",
|
|
ctx.author.id,
|
|
bot_name,
|
|
)
|
|
|
|
# Delete the bot
|
|
logger.debug("Deleting custom bot %r from database", bot_name)
|
|
success = await asyncio.to_thread(app.manager.delete_custom_bot, bot_name)
|
|
|
|
if success:
|
|
logger.info(
|
|
"Successfully deleted custom bot %r by user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await asyncio.to_thread(invalidate_bot_cache, app)
|
|
await ctx.send(f"Custom bot '{bot_name}' has been deleted.")
|
|
else:
|
|
logger.warning(
|
|
"Failed to delete custom bot %r by user %s",
|
|
bot_name,
|
|
ctx.author.id,
|
|
)
|
|
await ctx.send("Failed to delete custom bot.")
|