diff --git a/agent-office/app/telegram_bot.py b/agent-office/app/telegram_bot.py new file mode 100644 index 0000000..fb34670 --- /dev/null +++ b/agent-office/app/telegram_bot.py @@ -0,0 +1,82 @@ +import json +import uuid +import httpx +from typing import Optional + +from .config import TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_WEBHOOK_URL +from .db import save_telegram_callback, get_telegram_callback, mark_telegram_responded + +_BASE = "https://api.telegram.org/bot" + +def _enabled() -> bool: + return bool(TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID) + +async def _api(method: str, payload: dict) -> dict: + if not _enabled(): + return {"ok": False, "description": "Telegram not configured"} + async with httpx.AsyncClient(timeout=10.0) as client: + resp = await client.post(f"{_BASE}{TELEGRAM_BOT_TOKEN}/{method}", json=payload) + return resp.json() + +async def send_message(text: str, reply_markup: dict = None) -> dict: + payload = { + "chat_id": TELEGRAM_CHAT_ID, + "text": text, + "parse_mode": "HTML", + } + if reply_markup: + payload["reply_markup"] = reply_markup + return await _api("sendMessage", payload) + +async def send_stock_summary(summary: str) -> dict: + return await send_message(summary) + +async def send_approval_request(agent_id: str, task_id: str, title: str, detail: str) -> dict: + approve_id = f"approve_{uuid.uuid4().hex[:8]}" + reject_id = f"reject_{uuid.uuid4().hex[:8]}" + + save_telegram_callback(approve_id, task_id, agent_id) + save_telegram_callback(reject_id, task_id, agent_id) + + text = f"{title}\n{'━' * 20}\n{detail}" + reply_markup = { + "inline_keyboard": [[ + {"text": "✅ 승인", "callback_data": approve_id}, + {"text": "❌ 거절", "callback_data": reject_id}, + ]] + } + return await send_message(text, reply_markup) + +async def send_task_result(agent_id: str, title: str, result: str) -> dict: + text = f"{title}\n{'━' * 20}\n{result}" + return await send_message(text) + +async def handle_webhook(data: dict) -> Optional[dict]: + callback_query = data.get("callback_query") + if not callback_query: + return None + + callback_id = callback_query.get("data", "") + cb = get_telegram_callback(callback_id) + if not cb: + return None + + action = "approve" if callback_id.startswith("approve_") else "reject" + mark_telegram_responded(callback_id, action) + + await _api("answerCallbackQuery", { + "callback_query_id": callback_query["id"], + "text": "승인됨 ✅" if action == "approve" else "거절됨 ❌", + }) + + return { + "task_id": cb["task_id"], + "agent_id": cb["agent_id"], + "action": action, + "approved": action == "approve", + } + +async def setup_webhook() -> dict: + if not _enabled() or not TELEGRAM_WEBHOOK_URL: + return {"ok": False, "description": "Webhook URL not configured"} + return await _api("setWebhook", {"url": TELEGRAM_WEBHOOK_URL})