feat: Agent Office — AI 에이전트 가상 오피스 #2
82
agent-office/app/telegram_bot.py
Normal file
82
agent-office/app/telegram_bot.py
Normal file
@@ -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})
|
||||||
Reference in New Issue
Block a user