from __future__ import annotations from datetime import datetime, timezone from typing import Any import requests from fastapi import HTTPException from app.services.harborforge_config import get_discord_wakeup_config DISCORD_API_BASE = "https://discord.com/api/v10" WAKEUP_CATEGORY_NAME = "HarborForge Wakeup" def _headers(bot_token: str) -> dict[str, str]: return { "Authorization": f"Bot {bot_token}", "Content-Type": "application/json", } def _ensure_category(guild_id: str, bot_token: str) -> str | None: resp = requests.get(f"{DISCORD_API_BASE}/guilds/{guild_id}/channels", headers=_headers(bot_token), timeout=15) if not resp.ok: raise HTTPException(status_code=502, detail=f"Discord list channels failed: {resp.text}") for ch in resp.json(): if ch.get("type") == 4 and ch.get("name") == WAKEUP_CATEGORY_NAME: return ch.get("id") payload = {"name": WAKEUP_CATEGORY_NAME, "type": 4} created = requests.post(f"{DISCORD_API_BASE}/guilds/{guild_id}/channels", headers=_headers(bot_token), json=payload, timeout=15) if not created.ok: raise HTTPException(status_code=502, detail=f"Discord create category failed: {created.text}") return created.json().get("id") def create_private_wakeup_channel(discord_user_id: str, title: str, message: str) -> dict[str, Any]: cfg = get_discord_wakeup_config() guild_id = cfg.get("guild_id") bot_token = cfg.get("bot_token") if not guild_id or not bot_token: raise HTTPException(status_code=400, detail="Discord wakeup config is incomplete") category_id = _ensure_category(guild_id, bot_token) channel_name = f"wake-{discord_user_id[-6:]}-{int(datetime.now(timezone.utc).timestamp())}" payload = { "name": channel_name, "type": 0, "parent_id": category_id, "permission_overwrites": [ {"id": guild_id, "type": 0, "deny": "1024"}, {"id": discord_user_id, "type": 1, "allow": "1024"}, ], "topic": title, } created = requests.post(f"{DISCORD_API_BASE}/guilds/{guild_id}/channels", headers=_headers(bot_token), json=payload, timeout=15) if not created.ok: raise HTTPException(status_code=502, detail=f"Discord create channel failed: {created.text}") channel = created.json() sent = requests.post( f"{DISCORD_API_BASE}/channels/{channel['id']}/messages", headers=_headers(bot_token), json={"content": message}, timeout=15, ) if not sent.ok: raise HTTPException(status_code=502, detail=f"Discord send message failed: {sent.text}") return { "guild_id": guild_id, "channel_id": channel.get("id"), "channel_name": channel.get("name"), "message_id": sent.json().get("id"), }