diff --git a/bot/api.py b/bot/api.py index d94f246..dbafb63 100644 --- a/bot/api.py +++ b/bot/api.py @@ -844,6 +844,79 @@ async def manual_send( except Exception as e: return {"status": "error", "message": f"Error: {e}"} + +@app.post("/manual/send-webhook") +async def manual_send_webhook( + message: str = Form(...), + channel_id: str = Form(...), + persona: str = Form("miku"), # "miku" or "evil" + files: List[UploadFile] = File(default=[]), + reply_to_message_id: str = Form(None), + mention_author: bool = Form(True) +): + """Send a manual message via webhook as either Hatsune Miku or Evil Miku""" + try: + from utils.bipolar_mode import get_or_create_webhooks_for_channel, get_miku_display_name, get_evil_miku_display_name + + channel = globals.client.get_channel(int(channel_id)) + if not channel: + return {"status": "error", "message": "Channel not found"} + + # Validate persona + if persona not in ["miku", "evil"]: + return {"status": "error", "message": "Invalid persona. Must be 'miku' or 'evil'"} + + # Get or create webhooks for this channel + webhooks = await get_or_create_webhooks_for_channel(channel) + if not webhooks: + return {"status": "error", "message": "Failed to create webhooks for this channel"} + + # Select the appropriate webhook + webhook = webhooks["evil_miku"] if persona == "evil" else webhooks["miku"] + display_name = get_evil_miku_display_name() if persona == "evil" else get_miku_display_name() + + # Read file content immediately before the request closes + file_data = [] + for file in files: + try: + file_content = await file.read() + file_data.append({ + 'filename': file.filename, + 'content': file_content + }) + except Exception as e: + print(f"❌ Failed to read file {file.filename}: {e}") + return {"status": "error", "message": f"Failed to read file {file.filename}: {e}"} + + # Use create_task to avoid timeout context manager error + async def send_webhook_message(): + try: + # Prepare files for webhook + discord_files = [] + for file_info in file_data: + discord_files.append(discord.File(io.BytesIO(file_info['content']), filename=file_info['filename'])) + + # Send via webhook with display name + await webhook.send( + content=message, + username=display_name, + files=discord_files if discord_files else None, + wait=True + ) + + persona_name = "Evil Miku" if persona == "evil" else "Hatsune Miku" + print(f"✅ Manual webhook message sent as {persona_name} to #{channel.name}") + + except Exception as e: + print(f"❌ Failed to send webhook message: {e}") + + globals.client.loop.create_task(send_webhook_message()) + return {"status": "ok", "message": f"Webhook message queued for sending as {persona}"} + + except Exception as e: + return {"status": "error", "message": f"Error: {e}"} + + @app.get("/status") def status(): # Get per-server mood summary diff --git a/bot/static/index.html b/bot/static/index.html index 373649a..71acab2 100644 --- a/bot/static/index.html +++ b/bot/static/index.html @@ -997,6 +997,28 @@