This commit is contained in:
Slipstream 2025-05-13 11:56:20 -06:00
parent 3078f237f1
commit a0d13b643b
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
2 changed files with 114 additions and 26 deletions

View File

@ -192,29 +192,3 @@ async def owoify_context_menu_ai_reply(interaction: discord.Interaction, message
_owo_conversations[convo_key] = convo[-10:] # Keep last 10 messages
except Exception as e:
await interaction.followup.send(f"AI owo reply failed: {e} >w<", ephemeral=True)
async def setup(bot: commands.Bot):
cog = OwoifyCog(bot)
await bot.add_cog(cog)
# Add context menu commands globally (do not use guild=... for global commands)
bot.tree.add_command(owoify_context_menu)
bot.tree.add_command(owoify_context_menu_ai)
bot.tree.add_command(owoify_context_menu_ai_reply)
print("OwoifyCog woaded! uwu")
# Conversation handler: reply to mentions or replies to the bot in owo style
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author.bot:
return
if (self.bot.user in message.mentions) or (message.reference and getattr(message.reference.resolved, "author", None) == self.bot.user):
convo_key = message.channel.id
convo = _owo_conversations.get(convo_key, [])
convo.append({"role": "user", "content": message.content})
try:
ai_reply = await _owoify_text_ai_with_messages(convo, system_mode="reply")
await message.reply(ai_reply)
convo.append({"role": "assistant", "content": ai_reply})
_owo_conversations[convo_key] = convo[-10:]
except Exception as e:
await message.channel.send(f"AI owo conversation failed: {e} >w<")

114
cogs/teto_cog.py Normal file
View File

@ -0,0 +1,114 @@
import discord
from discord.ext import commands
from discord import app_commands
# In-memory conversation history for Kasane Teto AI (keyed by channel id)
_teto_conversations = {}
import os
import aiohttp
async def _teto_reply_ai_with_messages(messages, system_mode="reply"):
"""
Use OpenRouter AI to generate a Kasane Teto-style response.
system_mode: "reply" for replying as Kasane Teto.
"""
api_key = os.getenv("AI_API_KEY")
if not api_key:
raise RuntimeError("AI_API_KEY environment variable not set.")
url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
system_prompt = (
"You are Kasane Teto, a cheeky and energetic vocaloid/utau character. "
"Reply to the user in a short, conversational manner, staying in character. "
"Use Teto's signature phrases like 'desu~' and 'arigatou'. Be playful and a little mischievous."
)
payload = {
"model": "google/gemini-2.0-flash-exp:free",
"messages": [{"role": "system", "content": system_prompt}] + messages
}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=payload) as resp:
if resp.content_type == "application/json":
data = await resp.json()
return data["choices"][0]["message"]["content"]
else:
text = await resp.text()
raise RuntimeError(f"OpenRouter API returned non-JSON response (status {resp.status}): {text[:500]}")
async def _teto_reply_ai(text: str) -> str:
"""Replies to the text as Kasane Teto using AI via OpenRouter."""
return await _teto_reply_ai_with_messages([{"role": "user", "content": text}])
class TetoCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author.bot:
return
# Check if the bot was mentioned or replied to
if (self.bot.user in message.mentions) or (message.reference and getattr(message.reference.resolved, "author", None) == self.bot.user):
channel = message.channel
convo_key = channel.id
convo = _teto_conversations.get(convo_key, [])
# Get message history
history = []
async for msg in channel.history(limit=10):
if msg.content:
role = "assistant" if msg.author == self.bot.user else "user"
history.insert(0, {"role": role, "content": msg.content})
convo = history
convo.append({"role": "user", "content": message.content})
try:
ai_reply = await _teto_reply_ai_with_messages(convo)
await message.reply(ai_reply)
convo.append({"role": "assistant", "content": ai_reply})
_teto_conversations[convo_key] = convo[-10:]
except Exception as e:
await channel.send(f"Teto AI conversation failed: {e} desu~")
@app_commands.context_menu(name="Teto AI Reply")
async def teto_context_menu_ai_reply(interaction: discord.Interaction, message: discord.Message):
"""Replies to the selected message as a Teto AI."""
if not message.content:
await interaction.response.send_message("The selected message has no text content to reply to! >.<", ephemeral=True)
return
await interaction.response.defer(ephemeral=True)
channel = interaction.channel
convo_key = channel.id
convo = _teto_conversations.get(convo_key, [])
# Get message history
history = []
async for msg in channel.history(limit=10):
if msg.content:
role = "assistant" if msg.author == interaction.client.user else "user"
history.insert(0, {"role": role, "content": msg.content})
convo = history
convo.append({"role": "user", "content": message.content})
try:
ai_reply = await _teto_reply_ai_with_messages(convo)
await message.reply(ai_reply)
await interaction.followup.send("Teto AI replied desu~", ephemeral=True)
convo.append({"role": "assistant", "content": ai_reply})
_teto_conversations[convo_key] = convo[-10:]
except Exception as e:
await interaction.followup.send(f"Teto AI reply failed: {e} desu~", ephemeral=True)
async def setup(bot: commands.Bot):
cog = TetoCog(bot)
await bot.add_cog(cog)
bot.tree.add_command(TetoCog.teto_context_menu_ai_reply)
print("TetoCog loaded! desu~")