"""Telegram notification module for Content Brief v2 approval flow. Sends DMs to Matt when a brief is submitted for approval. Uses the same Telegram bot token as the family assistant (Hermes). """ import os import json from typing import Optional import requests TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "") MATT_CHAT_ID = os.getenv("TELEGRAM_MATT_CHAT_ID", "8386527252") API_BASE = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}" def _send_message(chat_id: str, text: str, reply_markup: Optional[dict] = None) -> bool: """Send a Telegram message. Returns True on success.""" if not TELEGRAM_BOT_TOKEN: print("[telegram] No bot token configured, skipping notification") return False payload = { "chat_id": chat_id, "text": text, "parse_mode": "HTML", } if reply_markup: payload["reply_markup"] = json.dumps(reply_markup) try: resp = requests.post(f"{API_BASE}/sendMessage", json=payload, timeout=10) resp.raise_for_status() result = resp.json() if result.get("ok"): print(f"[telegram] Sent notification to {chat_id}") return True else: print(f"[telegram] Failed: {result}") return False except Exception as e: print(f"[telegram] Error sending message: {e}") return False def notify_brief_submitted(brief_id: str, title: str, created_by: str, base_url: str = "https://notes.hoffdesk.com") -> bool: """Notify Matt that a new brief is pending approval. Sends an inline keyboard with Approve / Reject / Edit buttons. """ text = ( f"📝 New Content Brief Pending Approval\n\n" f"Title: {title}\n" f"Author: {created_by}\n" f"ID: {brief_id}\n\n" f"Review and approve to trigger content generation." ) reply_markup = { "inline_keyboard": [ [ {"text": "✅ Approve", "callback_data": f"brief_approve:{brief_id}"}, {"text": "❌ Reject", "callback_data": f"brief_reject:{brief_id}"}, ], [ {"text": "✏️ Edit in Admin", "url": f"{base_url}/admin/content/briefs/{brief_id}"}, ], ] } return _send_message(MATT_CHAT_ID, text, reply_markup) def notify_brief_approved(brief_id: str, title: str, generation_job_id: str) -> bool: """Notify that a brief has been approved and generation started.""" text = ( f"✅ Brief Approved — Generation Started\n\n" f"Title: {title}\n" f"Job ID: {generation_job_id}\n\n" f"Content is being generated on the local GPU. You'll be notified when it's ready." ) return _send_message(MATT_CHAT_ID, text) def notify_generation_complete(brief_id: str, title: str, struggle_score: float, output_url: str) -> bool: """Notify that content generation is complete.""" score_emoji = "🟢" if struggle_score >= 75 else "🟡" if struggle_score >= 50 else "🔴" text = ( f"✨ Content Ready for Review\n\n" f"Title: {title}\n" f"Struggle Score: {score_emoji} {struggle_score:.1f}/100\n\n" f"Preview Content" ) reply_markup = { "inline_keyboard": [ [ {"text": "📄 Preview", "url": output_url}, {"text": "🚀 Publish", "callback_data": f"brief_publish:{brief_id}"}, ], [ {"text": "🔄 Regenerate", "callback_data": f"brief_regenerate:{brief_id}"}, ], ] } return _send_message(MATT_CHAT_ID, text, reply_markup) def notify_brief_rejected(brief_id: str, title: str, feedback: str) -> bool: """Notify that a brief was rejected.""" text = ( f"❌ Brief Rejected\n\n" f"Title: {title}\n" f"Feedback: {feedback or 'No feedback provided'}\n\n" f"The brief has been returned to draft status. Edit and resubmit when ready." ) return _send_message(MATT_CHAT_ID, text) # ─── Hook into brief lifecycle ──────────────────────────────────────────── def on_brief_submitted(brief_id: str, title: str, created_by: str) -> bool: """Called when a brief is submitted for approval.""" return notify_brief_submitted(brief_id, title, created_by) def on_brief_approved(brief_id: str, title: str, generation_job_id: str) -> bool: """Called when a brief is approved.""" return notify_brief_approved(brief_id, title, generation_job_id) def on_generation_complete(brief_id: str, title: str, struggle_score: float, output_url: str) -> bool: """Called when generation completes.""" return notify_generation_complete(brief_id, title, struggle_score, output_url)