# app/routes/wa_gateway.py from fastapi import APIRouter, Request import os, requests, asyncio from dotenv import load_dotenv from fastapi.responses import PlainTextResponse, JSONResponse # ----- import the Q&A logic ----- from routes.question import ask_question, QuestionInput # ← new load_dotenv() wa_router = APIRouter() VERIFY_TOKEN = os.getenv("VERIFY_TOKEN") ACCESS_TOKEN = os.getenv("WHATSAPP_ACCESS_TOKEN") PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID") def send_whatsapp_text(to: str, text: str) -> dict: """ Send a plain text message (max 4096 chars per WhatsApp limits). Returns {'status': 'sent' | 'failed', ... } for easy logging. """ url = f"https://graph.facebook.com/v17.0/{PHONE_NUMBER_ID}/messages" headers = { "Authorization": f"Bearer {ACCESS_TOKEN}", "Content-Type": "application/json", } payload = { "messaging_product": "whatsapp", "to": to, "type": "text", "text": {"body": text[:4096]}, } try: r = requests.post(url, headers=headers, json=payload) r.raise_for_status() return {"status": "sent", "response": r.json()} except Exception as e: return {"status": "failed", "error": str(e)} async def nuse_interact(to: str, name: str, prompt: str) -> dict: """ 1. Call ask_question() directly (no HTTP round-trip); 2. Extract the answer text (+sources or headlines); 3. Push it back to the user via WhatsApp API using link previews. """ qa_result = await ask_question(QuestionInput(question=prompt)) answer_text = qa_result["answer"] # If headlines were returned (qid == 13) if "headlines" in qa_result: headlines = qa_result["headlines"] if not headlines: return send_whatsapp_text(to, "Sorry, no headlines found today.") # Build preview message preview_msgs = [] for h in headlines: preview_msgs.append( f"šŸ—žļø *{h['title']}* ({h.get('category', '').title()})\n{h['summary']}\nšŸ”— {h['url']}" ) message = f"{answer_text}\n\n" + "\n\n".join(preview_msgs[:10]) # show up to 10 return send_whatsapp_text(to, message) # Fallback: regular Q&A with optional sources if qa_result.get("sources"): bullet_list = "\n".join(f"• {s['title']} – {s['url']}" for s in qa_result["sources"]) answer_text = f"{answer_text}\n\nSources:\n{bullet_list}" return send_whatsapp_text(to, answer_text) @wa_router.get("/webhook") async def verify_webhook(request: Request): params = request.query_params if params.get("hub.mode") == "subscribe" and params.get("hub.verify_token") == VERIFY_TOKEN: return PlainTextResponse(content=params.get("hub.challenge")) return JSONResponse(status_code=403, content={"error": "Verification failed"}) @wa_router.post("/webhook") async def receive_whatsapp_event(request: Request): body = await request.json() print("[WEBHOOK] Incoming:", body) try: entry = body["entry"][0] changes = entry["changes"][0] value = changes["value"] messages = value.get("messages") contacts = value.get("contacts") if messages and contacts: msg = messages[0] contact = contacts[0] from_number = msg["from"] sender_name = contact["profile"]["name"] incoming_txt = msg["text"]["body"] print(f"[INFO] {sender_name} ({from_number}): {incoming_txt}") # Hand off to Q&A -> WhatsApp reply result = await nuse_interact(from_number, sender_name, incoming_txt) print("[INFO] Reply status:", result) except Exception as e: print("[ERROR] Webhook processing failed:", str(e)) return JSONResponse(content={"status": "received"})