fix 106
Browse files
components/gateways/headlines_to_wa.py
CHANGED
@@ -4,44 +4,83 @@ import redis
|
|
4 |
import requests
|
5 |
from fastapi import FastAPI
|
6 |
from fastapi.responses import JSONResponse
|
|
|
|
|
|
|
7 |
|
8 |
# π Redis config
|
9 |
REDIS_URL = os.environ.get("UPSTASH_REDIS_URL", "redis://localhost:6379")
|
10 |
WHATSAPP_API_URL = os.environ.get("WHATSAPP_API_URL", "https://api.gupshup.io/sm/api/v1/msg")
|
11 |
-
WHATSAPP_TOKEN = os.environ.get("WHATSAPP_TOKEN"
|
12 |
-
WHATSAPP_TO_NUMBER = os.environ.get("WHATSAPP_TO_NUMBER"
|
13 |
-
GUPSHUP_SOURCE_NUMBER = os.environ.get("GUPSHUP_SOURCE_NUMBER"
|
14 |
-
GUPSHUP_APP_NAME = os.environ.get("GUPSHUP_APP_NAME"
|
15 |
|
16 |
# β
Redis connection
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
# π§Ύ Fetch and format headlines
|
20 |
def fetch_cached_headlines() -> str:
|
21 |
try:
|
22 |
-
|
|
|
|
|
23 |
if not raw:
|
|
|
24 |
return "β οΈ No daily headlines found in cache."
|
25 |
data = json.loads(raw)
|
26 |
except Exception as e:
|
|
|
27 |
return f"β Error reading from Redis: {e}"
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
-
return "\n".join(
|
40 |
|
41 |
# π€ Send via Gupshup WhatsApp API
|
42 |
def send_to_whatsapp(message_text: str) -> dict:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
headers = {
|
44 |
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
|
45 |
}
|
46 |
|
47 |
payload = {
|
@@ -55,35 +94,74 @@ def send_to_whatsapp(message_text: str) -> dict:
|
|
55 |
"src.name": GUPSHUP_APP_NAME,
|
56 |
}
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
64 |
|
65 |
-
if response.status_code == 200:
|
66 |
return {"status": "success", "details": response.json()}
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
69 |
|
70 |
# π FastAPI App
|
71 |
app = FastAPI()
|
72 |
|
73 |
@app.get("/send-daily-whatsapp")
|
74 |
def send_daily_whatsapp_digest():
|
|
|
75 |
message = fetch_cached_headlines()
|
76 |
|
77 |
if message.startswith("β") or message.startswith("β οΈ"):
|
|
|
78 |
return JSONResponse(status_code=404, content={"error": message})
|
79 |
|
80 |
result = send_to_whatsapp(message)
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
# π§ͺ For local testing
|
84 |
if __name__ == "__main__":
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import requests
|
5 |
from fastapi import FastAPI
|
6 |
from fastapi.responses import JSONResponse
|
7 |
+
import logging # Import logging for better output
|
8 |
+
|
9 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
10 |
|
11 |
# π Redis config
|
12 |
REDIS_URL = os.environ.get("UPSTASH_REDIS_URL", "redis://localhost:6379")
|
13 |
WHATSAPP_API_URL = os.environ.get("WHATSAPP_API_URL", "https://api.gupshup.io/sm/api/v1/msg")
|
14 |
+
WHATSAPP_TOKEN = os.environ.get("WHATSAPP_TOKEN")
|
15 |
+
WHATSAPP_TO_NUMBER = os.environ.get("WHATSAPP_TO_NUMBER") # e.g., "91xxxxxxxxxx"
|
16 |
+
GUPSHUP_SOURCE_NUMBER = os.environ.get("GUPSHUP_SOURCE_NUMBER") # e.g., your WABA number
|
17 |
+
GUPSHUP_APP_NAME = os.environ.get("GUPSHUP_APP_NAME") # e.g., your Gupshup app name
|
18 |
|
19 |
# β
Redis connection
|
20 |
+
try:
|
21 |
+
redis_client = redis.Redis.from_url(REDIS_URL, decode_responses=True)
|
22 |
+
redis_client.ping() # Test connection
|
23 |
+
logging.info("Redis client connected successfully.")
|
24 |
+
except Exception as e:
|
25 |
+
logging.error(f"β Failed to connect to Redis: {e}")
|
26 |
+
# Depending on your application's needs, you might want to exit or fail gracefully here.
|
27 |
+
raise
|
28 |
|
29 |
# π§Ύ Fetch and format headlines
|
30 |
def fetch_cached_headlines() -> str:
|
31 |
try:
|
32 |
+
# Assuming 'detailed_news_feed_cache' is the key for the final detailed headlines
|
33 |
+
# This key should match what's used in components/generators/detailed_explainer.py
|
34 |
+
raw = redis_client.get("detailed_news_feed_cache")
|
35 |
if not raw:
|
36 |
+
logging.warning("β οΈ No detailed news headlines found in cache.")
|
37 |
return "β οΈ No daily headlines found in cache."
|
38 |
data = json.loads(raw)
|
39 |
except Exception as e:
|
40 |
+
logging.error(f"β Error reading from Redis: {e}")
|
41 |
return f"β Error reading from Redis: {e}"
|
42 |
|
43 |
+
message_parts = ["ποΈ *Your Daily Digest* π‘\n"]
|
44 |
+
|
45 |
+
# Iterate through topics in the order defined by TOPIC_KEYS (if available, otherwise iterate data.items())
|
46 |
+
# Assuming TOPIC_KEYS is available globally or passed here if needed for consistent order
|
47 |
+
# For now, just iterate through the data items as they are in the cached JSON
|
48 |
+
|
49 |
+
# Sort topics alphabetically for consistent output if no external order is provided
|
50 |
+
sorted_topics = sorted(data.keys())
|
51 |
+
|
52 |
+
for topic_key in sorted_topics:
|
53 |
+
stories = data[topic_key]
|
54 |
+
# Format topic title nicely (e.g., "india" -> "India", "world" -> "World")
|
55 |
+
title = topic_key.replace("_", " ").title()
|
56 |
+
message_parts.append(f"π·οΈ *{title}*")
|
57 |
+
|
58 |
+
# Sort stories by their sequential ID for consistent order
|
59 |
+
sorted_story_ids = sorted(stories.keys(), key=int) # Ensure keys are treated as integers for sorting
|
60 |
+
|
61 |
+
for ref_id in sorted_story_ids:
|
62 |
+
item = stories[ref_id]
|
63 |
+
summary = item.get("title", "") # Use 'title' key for the summary/headline
|
64 |
+
description = item.get("description", "") # Use 'description' key for the explanation
|
65 |
+
|
66 |
+
# Ensure ref_id is a string for the message
|
67 |
+
message_parts.append(f"{ref_id}. {summary}\n_Why this matters_: {description}")
|
68 |
+
message_parts.append("") # spacing between sections
|
69 |
|
70 |
+
return "\n".join(message_parts)
|
71 |
|
72 |
# π€ Send via Gupshup WhatsApp API
|
73 |
def send_to_whatsapp(message_text: str) -> dict:
|
74 |
+
# Validate environment variables
|
75 |
+
if not WHATSAPP_TOKEN or not WHATSAPP_TO_NUMBER or not GUPSHUP_SOURCE_NUMBER or not GUPSHUP_APP_NAME:
|
76 |
+
error_msg = "β Missing one or more WhatsApp API environment variables (WHATSAPP_TOKEN, WHATSAPP_TO_NUMBER, GUPSHUP_SOURCE_NUMBER, GUPSHUP_APP_NAME)."
|
77 |
+
logging.error(error_msg)
|
78 |
+
return {"status": "failed", "error": error_msg, "code": 500}
|
79 |
+
|
80 |
+
# Gupshup requires the API key in the 'apikey' header
|
81 |
headers = {
|
82 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
83 |
+
"apikey": WHATSAPP_TOKEN # <<< FIX: API key in 'apikey' header
|
84 |
}
|
85 |
|
86 |
payload = {
|
|
|
94 |
"src.name": GUPSHUP_APP_NAME,
|
95 |
}
|
96 |
|
97 |
+
try:
|
98 |
+
logging.info(f"Attempting to send WhatsApp message to {WHATSAPP_TO_NUMBER} via Gupshup.")
|
99 |
+
response = requests.post(
|
100 |
+
WHATSAPP_API_URL,
|
101 |
+
headers=headers,
|
102 |
+
data=payload,
|
103 |
+
# auth=(WHATSAPP_TOKEN, '') # <<< REMOVED: This sends Basic Auth, which Gupshup doesn't support for API key
|
104 |
+
)
|
105 |
+
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
106 |
|
|
|
107 |
return {"status": "success", "details": response.json()}
|
108 |
+
except requests.exceptions.RequestException as e:
|
109 |
+
logging.error(f"β Failed to send WhatsApp message: {e}")
|
110 |
+
return {"status": "failed", "error": str(e), "code": e.response.status_code if e.response else 500}
|
111 |
+
except Exception as e:
|
112 |
+
logging.error(f"β An unexpected error occurred during WhatsApp send: {e}")
|
113 |
+
return {"status": "failed", "error": str(e), "code": 500}
|
114 |
|
115 |
# π FastAPI App
|
116 |
app = FastAPI()
|
117 |
|
118 |
@app.get("/send-daily-whatsapp")
|
119 |
def send_daily_whatsapp_digest():
|
120 |
+
logging.info("API Call: /send-daily-whatsapp initiated.")
|
121 |
message = fetch_cached_headlines()
|
122 |
|
123 |
if message.startswith("β") or message.startswith("β οΈ"):
|
124 |
+
logging.warning(f"Returning error due to issue fetching headlines: {message}")
|
125 |
return JSONResponse(status_code=404, content={"error": message})
|
126 |
|
127 |
result = send_to_whatsapp(message)
|
128 |
+
|
129 |
+
if result.get("status") == "success":
|
130 |
+
logging.info("β
WhatsApp message sent successfully.")
|
131 |
+
return JSONResponse(status_code=200, content=result)
|
132 |
+
else:
|
133 |
+
logging.error(f"β Failed to send WhatsApp message: {result.get('error')}")
|
134 |
+
return JSONResponse(status_code=result.get("code", 500), content=result)
|
135 |
|
136 |
# π§ͺ For local testing
|
137 |
if __name__ == "__main__":
|
138 |
+
# Set dummy environment variables for local testing if not already set
|
139 |
+
os.environ.setdefault("WHATSAPP_API_URL", "https://api.gupshup.io/sm/api/v1/msg")
|
140 |
+
os.environ.setdefault("WHATSAPP_TOKEN", "YOUR_GUPSHUP_API_KEY") # Replace with a real dummy or your actual key
|
141 |
+
os.environ.setdefault("WHATSAPP_TO_NUMBER", "919999999999") # Replace with a test number
|
142 |
+
os.environ.setdefault("GUPSHUP_SOURCE_NUMBER", "918888888888") # Replace with your WABA number
|
143 |
+
os.environ.setdefault("GUPSHUP_APP_NAME", "YourTestApp") # Replace with your app name
|
144 |
+
|
145 |
+
# Simulate a cached detailed feed for testing
|
146 |
+
dummy_cached_detailed_feed = {
|
147 |
+
"india": {
|
148 |
+
"1": {"title": "India's Economy Surges", "description": "Rapid growth in manufacturing and services sectors signals a strong economic recovery, boosting investor confidence and job creation.", "sources": ["Times of India"]},
|
149 |
+
"2": {"title": "New Tech Policy Unveiled", "description": "Government introduces new regulations to foster innovation while addressing data privacy concerns, aiming to balance growth with user protection.", "sources": ["Indian Express"]}
|
150 |
+
},
|
151 |
+
"world": {
|
152 |
+
"3": {"title": "Global Climate Talks Advance", "description": "Nations agree on ambitious new targets for emissions reduction, marking a significant step towards combating climate change despite earlier disagreements.", "sources": ["BBC News"]},
|
153 |
+
"4": {"title": "Space Mission Explores Mars", "description": "A new rover successfully lands on Mars, sending back groundbreaking data that could revolutionize our understanding of planetary geology and potential for life.", "sources": ["CNN World"]}
|
154 |
+
}
|
155 |
+
}
|
156 |
+
# Store dummy data in Redis for testing fetch_cached_headlines
|
157 |
+
redis_client.set("detailed_news_feed_cache", json.dumps(dummy_cached_detailed_feed))
|
158 |
+
|
159 |
+
|
160 |
+
logging.info("\n--- WhatsApp Message Preview ---\n")
|
161 |
+
msg_preview = fetch_cached_headlines()
|
162 |
+
print(msg_preview)
|
163 |
+
|
164 |
+
logging.info("\n--- Sending WhatsApp Message (Test) ---\n")
|
165 |
+
# This will attempt to send a real message if your env vars are valid
|
166 |
+
test_result = send_to_whatsapp(msg_preview)
|
167 |
+
print(test_result)
|