|
|
|
from fastapi import APIRouter, Request, HTTPException, status |
|
from fastapi.responses import JSONResponse |
|
import logging |
|
import json |
|
|
|
|
|
from components.gateways.headlines_to_wa import fetch_cached_headlines, send_to_whatsapp |
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
|
|
router = APIRouter() |
|
|
|
|
|
@router.post("/message-received") |
|
async def whatsapp_webhook_receiver(request: Request): |
|
""" |
|
Receives incoming messages from Gupshup WhatsApp webhook. |
|
Sends a daily news digest if the user sends a specific command. |
|
""" |
|
try: |
|
|
|
|
|
|
|
try: |
|
form_data = await request.form() |
|
payload_str = form_data.get('payload') |
|
if payload_str: |
|
incoming_message = json.loads(payload_str) |
|
else: |
|
incoming_message = dict(form_data) |
|
except json.JSONDecodeError: |
|
|
|
incoming_message = await request.json() |
|
except Exception as e: |
|
logging.error(f"Error parsing webhook request body: {e}") |
|
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid request format"}) |
|
|
|
|
|
logging.info(f"Received WhatsApp webhook: {json.dumps(incoming_message, indent=2)}") |
|
|
|
|
|
|
|
message_data = incoming_message.get('payload', {}).get('payload', {}) |
|
|
|
|
|
if not message_data: |
|
message_data = incoming_message.get('message', {}) |
|
if not message_data: |
|
message_data = incoming_message |
|
|
|
from_number = message_data.get('sender', {}).get('phone') or message_data.get('from') |
|
message_text = message_data.get('message', {}).get('text') or message_data.get('body') |
|
|
|
if not from_number or not message_text: |
|
logging.warning("Received webhook without valid sender or message text.") |
|
return JSONResponse(status_code=200, content={"status": "ignored", "message": "Missing sender or text"}) |
|
|
|
logging.info(f"Message from {from_number}: {message_text}") |
|
|
|
|
|
if message_text.lower().strip() == "digest": |
|
logging.info(f"User {from_number} requested daily digest.") |
|
|
|
|
|
full_message_text = fetch_cached_headlines() |
|
|
|
if full_message_text.startswith("❌") or full_message_text.startswith("⚠️"): |
|
logging.error(f"Failed to fetch digest for {from_number}: {full_message_text}") |
|
|
|
send_to_whatsapp(f"Sorry, I couldn't fetch the news digest today. {full_message_text}", destination_number=from_number) |
|
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to fetch digest"}) |
|
|
|
|
|
result = send_to_whatsapp(full_message_text, destination_number=from_number) |
|
|
|
if result.get("status") == "success": |
|
logging.info(f"✅ Successfully sent digest to {from_number}.") |
|
return JSONResponse(status_code=200, content={"status": "success", "message": "Digest sent"}) |
|
else: |
|
logging.error(f"❌ Failed to send digest to {from_number}: {result.get('error')}") |
|
|
|
send_to_whatsapp(f"Sorry, I couldn't send the news digest to you. Error: {result.get('error', 'unknown')}", destination_number=from_number) |
|
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to send digest"}) |
|
else: |
|
logging.info(f"Received unhandled message from {from_number}: '{message_text}'") |
|
|
|
|
|
return JSONResponse(status_code=200, content={"status": "ignored", "message": "No action taken for this command"}) |
|
|
|
except Exception as e: |
|
logging.error(f"Error processing webhook: {e}", exc_info=True) |
|
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)}) |
|
|
|
|
|
@router.get("/message-received") |
|
async def whatsapp_webhook_verify(request: Request): |
|
""" |
|
Endpoint for Gupshup webhook verification. |
|
""" |
|
mode = request.query_params.get("hub.mode") |
|
challenge = request.query_params.get("hub.challenge") |
|
verify_token = request.query_params.get("hub.verify_token") |
|
|
|
|
|
|
|
|
|
|
|
if mode == "subscribe" and challenge: |
|
logging.info(f"Webhook verification successful. Challenge: {challenge}") |
|
return JSONResponse(status_code=200, content=int(challenge)) |
|
else: |
|
logging.warning(f"Webhook verification failed. Mode: {mode}, Challenge: {challenge}") |
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Verification failed") |