FastAPI / routes /wa_gateway.py
raghavNCI
another try
658ffaf
# 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"})