File size: 3,899 Bytes
ecbc095 bdd8925 68f07e2 ecbc095 047bda7 ecbc095 68f07e2 ecbc095 68f07e2 4a6e3e3 68f07e2 4a6e3e3 68f07e2 975c3a7 4a6e3e3 68f07e2 4a6e3e3 68f07e2 658ffaf 68f07e2 658ffaf 68f07e2 658ffaf 68f07e2 ecbc095 4691e67 047bda7 ecbc095 68f07e2 ecbc095 68f07e2 ecbc095 89615e3 ecbc095 89615e3 68f07e2 89615e3 68f07e2 de0e189 68f07e2 4a6e3e3 68f07e2 ecbc095 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# 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"})
|