File size: 6,204 Bytes
3566f32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
112
113
114
# routes/api/whatsapp_webhook.py
from fastapi import APIRouter, Request, HTTPException, status
from fastapi.responses import JSONResponse
import logging
import json

# Import your function to send messages back
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()

# WhatsApp/Gupshup webhook endpoint
@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:
        # Gupshup sends data as application/x-www-form-urlencoded
        # or sometimes as raw JSON depending on setup.
        # We need to try parsing both.
        try:
            form_data = await request.form()
            payload_str = form_data.get('payload') # Gupshup often wraps JSON in a 'payload' field
            if payload_str:
                incoming_message = json.loads(payload_str)
            else: # If not 'payload' field, try direct form parsing
                incoming_message = dict(form_data)
        except json.JSONDecodeError:
            # Fallback for raw JSON body (less common for Gupshup, but good to have)
            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)}")

        # Extract relevant info (Gupshup webhook structure can vary, common fields used below)
        # This part might need fine-tuning based on actual Gupshup webhook JSON
        message_data = incoming_message.get('payload', {}).get('payload', {}) # Gupshup often double-nests 'payload'
        
        # Try a different path if the above didn't work (common for raw JSON webhooks)
        if not message_data:
            message_data = incoming_message.get('message', {})
            if not message_data: # Sometimes the direct message object is at the top level
                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') # Common text fields

        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}")

        # Check for specific commands to send the digest
        if message_text.lower().strip() == "digest":
            logging.info(f"User {from_number} requested daily digest.")
            
            # Fetch the digest headlines
            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 an error message back to the user
                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"})
            
            # Send the digest back to the user who requested it
            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 an error message back to the user
                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}'")
            # Optional: Send a generic response for unhandled commands
            # send_to_whatsapp("Sorry, I only understand 'Digest' for now.", destination_number=from_number)
            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)})

# Gupshup webhook verification endpoint (GET request with 'hub.mode' and 'hub.challenge')
@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") # You might set an env var for this

    # Gupshup typically doesn't require a verify_token, unlike Facebook directly.
    # However, if you configure it, you should check it.
    # For now, we'll just return the challenge if mode is 'subscribe'.
    
    if mode == "subscribe" and challenge:
        logging.info(f"Webhook verification successful. Challenge: {challenge}")
        return JSONResponse(status_code=200, content=int(challenge)) # Challenge needs to be an integer
    else:
        logging.warning(f"Webhook verification failed. Mode: {mode}, Challenge: {challenge}")
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Verification failed")