Files
miku-discord/backups/2025-12-07/old-bot-bak-80825/api.py
koko210Serve 330cedd9d1 chore: organize backup files into dated directory structure
- Consolidated all .bak.* files from bot/ directory into backups/2025-12-07/
- Moved unused autonomous_wip.py to backups (verified not imported anywhere)
- Relocated old .bot.bak.80825/ backup directory into backups/2025-12-07/old-bot-bak-80825/
- Preserved autonomous_v1_legacy.py as it is still actively used by autonomous.py
- Created new backups/ directory with date-stamped subdirectory for better organization
2025-12-07 23:54:38 +02:00

208 lines
6.2 KiB
Python

# api.py
from fastapi import (
FastAPI,
Query,
BackgroundTasks,
Request, UploadFile,
File,
Form
)
from typing import List
from pydantic import BaseModel
import globals
from commands.actions import (
force_sleep,
wake_up,
set_mood,
reset_mood,
check_mood,
calm_miku,
reset_conversation,
send_bedtime_now
)
from utils.moods import nickname_mood_emoji
from utils.autonomous import (
miku_autonomous_tick,
miku_say_something_general,
miku_engage_random_user,
share_miku_tweet,
handle_custom_prompt
)
import asyncio
import nest_asyncio
import subprocess
import io
import discord
import aiofiles
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, PlainTextResponse
nest_asyncio.apply()
app = FastAPI()
# Serve static folder
app.mount("/static", StaticFiles(directory="static"), name="static")
# ========== Models ==========
class MoodSetRequest(BaseModel):
mood: str
class ConversationResetRequest(BaseModel):
user_id: str
class CustomPromptRequest(BaseModel):
prompt: str
# ========== Routes ==========
@app.get("/")
def read_index():
return FileResponse("static/index.html")
@app.get("/logs")
def get_logs():
try:
# Read last 100 lines of the log file
with open("/app/bot.log", "r", encoding="utf-8") as f:
lines = f.readlines()
last_100 = lines[-100:]
return "".join(lines[-100] if len(lines) >= 100 else lines)
except Exception as e:
return f"Error reading log file: {e}"
@app.get("/prompt")
def get_last_prompt():
return {"prompt": globals.LAST_FULL_PROMPT or "No prompt has been issued yet."}
@app.get("/mood")
def get_current_mood():
return {"mood": check_mood()}
@app.post("/mood")
async def set_mood_endpoint(data: MoodSetRequest):
success = set_mood(data.mood)
if success:
globals.client.loop.create_task(nickname_mood_emoji())
return {"status": "ok", "new_mood": data.mood}
return {"status": "error", "message": "Mood not recognized"}
@app.post("/mood/reset")
async def reset_mood_endpoint(background_tasks: BackgroundTasks):
reset_mood()
globals.client.loop.create_task(nickname_mood_emoji())
return {"status": "ok", "new_mood": "neutral"}
@app.post("/mood/calm")
def calm_miku_endpoint():
calm_miku()
return {"status": "ok", "message": "Miku has calmed down."}
@app.post("/conversation/reset")
def reset_convo(data: ConversationResetRequest):
reset_conversation(data.user_id)
return {"status": "ok", "message": f"Memory reset for {data.user_id}"}
@app.post("/sleep")
async def force_sleep_endpoint():
await force_sleep()
globals.client.loop.create_task(nickname_mood_emoji())
return {"status": "ok", "message": "Miku is now sleeping"}
@app.post("/wake")
async def wake_up_endpoint():
await wake_up()
globals.client.loop.create_task(nickname_mood_emoji())
return {"status": "ok", "message": "Miku is now awake"}
@app.post("/bedtime")
async def bedtime_endpoint(background_tasks: BackgroundTasks):
globals.client.loop.create_task(send_bedtime_now())
return {"status": "ok", "message": "Bedtime message sent"}
@app.post("/autonomous/general")
async def trigger_autonomous_general():
globals.client.loop.create_task(miku_autonomous_tick(force=True, force_action="general"))
return {"status": "ok", "message": "Miku say something general triggered manually"}
@app.post("/autonomous/engage")
async def trigger_autonomous_engage_user():
globals.client.loop.create_task(miku_autonomous_tick(force=True, force_action="engage_user"))
return {"status": "ok", "message": "Miku engage random user triggered manually"}
@app.post("/autonomous/tweet")
async def trigger_autonomous_tweet():
globals.client.loop.create_task(miku_autonomous_tick(force=True, force_action="share_tweet"))
return {"status": "ok", "message": "Miku share tweet triggered manually"}
@app.post("/autonomous/custom")
async def custom_autonomous_message(req: CustomPromptRequest):
try:
asyncio.run_coroutine_threadsafe(
handle_custom_prompt(req.prompt), globals.client.loop
)
return {"success": True, "message": "Miku is working on it!"}
except Exception as e:
print(f"❌ Error running custom prompt in bot loop: {repr(e)}")
return {"success": False, "error": str(e)}
@app.post("/manual/send")
async def manual_send(
message: str = Form(...),
channel_id: str = Form(...),
files: List[UploadFile] = File(default=[])
):
try:
# Get the Discord channel Miku should post in
channel = globals.client.get_channel(int(channel_id))
if not channel:
return {"success": False, "error": "Target channel not found"}
# Prepare file data (read in the async FastAPI thread)
prepared_files = []
for f in files:
contents = await f.read()
prepared_files.append((f.filename, contents))
# Define a coroutine that will run inside the bot loop
async def send_message():
channel = globals.client.get_channel(int(channel_id))
if not channel:
raise ValueError(f"Channel ID {channel_id} not found or bot cannot access it.")
discord_files = [
discord.File(io.BytesIO(content), filename=filename)
for filename, content in prepared_files
]
await channel.send(content=message or None, files=discord_files or None)
# Schedule coroutine in bot's event loop
future = asyncio.run_coroutine_threadsafe(send_message(), globals.client.loop)
future.result(timeout=10) # Wait max 10 seconds for it to finish
return {"success": True}
except Exception as e:
print(f"❌ Error in /manual/send: {repr(e)}")
return {"success": False, "error": str(e)}
@app.get("/status")
def status():
return {
"mood": globals.CURRENT_MOOD_NAME,
"is_sleeping": globals.IS_SLEEPING,
"previous_mood": globals.PREVIOUS_MOOD_NAME
}
@app.get("/conversation/{user_id}")
def get_conversation(user_id: str):
return globals.conversation_history.get(user_id, [])