adding temp
Browse files
components/gateways/headlines_to_wa.py
CHANGED
@@ -5,8 +5,7 @@ import requests
|
|
5 |
from fastapi import FastAPI
|
6 |
from fastapi.responses import JSONResponse
|
7 |
import logging
|
8 |
-
import
|
9 |
-
from typing import List, Dict, Any, Optional # Import Optional and List for type hints
|
10 |
|
11 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
12 |
|
@@ -27,7 +26,18 @@ except Exception as e:
|
|
27 |
logging.error(f"❌ Failed to connect to Redis: {e}")
|
28 |
raise
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
# 🧾 Fetch and format headlines
|
|
|
31 |
def fetch_cached_headlines() -> str:
|
32 |
try:
|
33 |
raw = redis_client.get("detailed_news_feed_cache")
|
@@ -39,32 +49,46 @@ def fetch_cached_headlines() -> str:
|
|
39 |
logging.error(f"❌ Error reading from Redis: {e}")
|
40 |
return f"❌ Error reading from Redis: {e}"
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
45 |
|
46 |
-
|
47 |
-
stories = data[topic_key]
|
48 |
-
title = topic_key.replace("_", " ").title()
|
49 |
-
message_parts.append(f"\n🏷️ *{title}*")
|
50 |
-
|
51 |
-
sorted_story_ids = sorted(stories.keys(), key=int)
|
52 |
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
return "\n".join(message_parts)
|
61 |
|
62 |
# 📤 Send via Gupshup WhatsApp API
|
63 |
-
def send_to_whatsapp(
|
64 |
-
message_text: str,
|
65 |
-
destination_number: str,
|
66 |
-
quick_reply_options: Optional[List[str]] = None # <<< NEW PARAMETER
|
67 |
-
) -> dict:
|
68 |
# Validate critical environment variables
|
69 |
if not WHATSAPP_TOKEN or \
|
70 |
not GUPSHUP_SOURCE_NUMBER or \
|
@@ -85,26 +109,10 @@ def send_to_whatsapp(
|
|
85 |
"Cache-Control": "no-cache"
|
86 |
}
|
87 |
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
return {"status": "failed", "error": "Quick reply options count out of range", "code": 400}
|
93 |
-
|
94 |
-
whatsapp_message_content = {
|
95 |
-
"type": "quick_reply",
|
96 |
-
"msgid": f"outgoing_msg_{int(time.time())}", # Dynamic msgid for quick reply
|
97 |
-
"content": {
|
98 |
-
"type": "text",
|
99 |
-
"text": message_text # Main text of the quick reply message
|
100 |
-
},
|
101 |
-
"options": quick_reply_options # The quick reply buttons
|
102 |
-
}
|
103 |
-
else:
|
104 |
-
whatsapp_message_content = {
|
105 |
-
"type": "text",
|
106 |
-
"text": message_text # Standard text message
|
107 |
-
}
|
108 |
|
109 |
payload = {
|
110 |
"channel": "whatsapp",
|
@@ -115,7 +123,7 @@ def send_to_whatsapp(
|
|
115 |
}
|
116 |
|
117 |
try:
|
118 |
-
logging.info(f"Attempting to send
|
119 |
response = requests.post(
|
120 |
WHATSAPP_API_URL,
|
121 |
headers=headers,
|
@@ -131,8 +139,8 @@ def send_to_whatsapp(
|
|
131 |
logging.error(f"❌ An unexpected error occurred during WhatsApp send: {e}")
|
132 |
return {"status": "failed", "error": str(e), "code": 500}
|
133 |
|
134 |
-
# 🚀 FastAPI App (
|
135 |
-
app = FastAPI()
|
136 |
|
137 |
@app.get("/send-daily-whatsapp")
|
138 |
def send_daily_whatsapp_digest():
|
@@ -144,20 +152,8 @@ def send_daily_whatsapp_digest():
|
|
144 |
logging.warning(f"Returning error due to issue fetching headlines: {full_message_text}")
|
145 |
return JSONResponse(status_code=404, content={"error": full_message_text})
|
146 |
|
147 |
-
#
|
148 |
-
|
149 |
-
quick_reply_options_for_digest = [
|
150 |
-
"Read more",
|
151 |
-
"Unsubscribe",
|
152 |
-
"Contact Support"
|
153 |
-
]
|
154 |
-
|
155 |
-
# Call send_to_whatsapp with quick reply options
|
156 |
-
result = send_to_whatsapp(
|
157 |
-
message_text=full_message_text,
|
158 |
-
destination_number=os.environ.get("WHATSAPP_TO_NUMBER"),
|
159 |
-
quick_reply_options=quick_reply_options_for_digest # Pass the options here
|
160 |
-
)
|
161 |
|
162 |
if result.get("status") == "success":
|
163 |
logging.info("✅ WhatsApp message sent successfully.")
|
@@ -169,39 +165,49 @@ def send_daily_whatsapp_digest():
|
|
169 |
# 🧪 For local testing
|
170 |
if __name__ == "__main__":
|
171 |
# For local testing, ensure these environment variables are set in your shell or .env file.
|
|
|
172 |
os.environ.setdefault("WHATSAPP_API_URL", "https://api.gupshup.io/wa/api/v1/msg")
|
173 |
os.environ.setdefault("WHATSAPP_TOKEN", "YOUR_GUPSHUP_API_KEY")
|
174 |
-
os.environ.setdefault("WHATSAPP_TO_NUMBER", "
|
175 |
os.environ.setdefault("GUPSHUP_SOURCE_NUMBER", "15557926439")
|
176 |
os.environ.setdefault("GUPSHUP_APP_NAME", "NuseAI")
|
177 |
|
178 |
# Simulate a cached detailed feed for testing
|
179 |
dummy_cached_detailed_feed = {
|
180 |
"india": {
|
181 |
-
"1": {"title": "
|
182 |
-
"2": {"title": "
|
|
|
183 |
},
|
184 |
"world": {
|
185 |
-
"
|
186 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
}
|
188 |
}
|
189 |
# Store dummy data in Redis for testing fetch_cached_headlines
|
190 |
redis_client.set("detailed_news_feed_cache", json.dumps(dummy_cached_detailed_feed))
|
191 |
|
192 |
|
193 |
-
logging.info("\n--- WhatsApp Message Preview
|
194 |
-
|
195 |
-
print(
|
196 |
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
# This will attempt to send a real Quick Reply message if your env vars are valid
|
201 |
-
test_result = send_to_whatsapp(msg_body_preview, destination_number=os.environ.get("WHATSAPP_TO_NUMBER"), quick_reply_options=test_options)
|
202 |
-
print(test_result)
|
203 |
-
|
204 |
-
logging.info("\n--- Sending WhatsApp Standard Text Message (Test) ---\n")
|
205 |
-
# This will attempt to send a real standard text message
|
206 |
-
test_result_standard = send_to_whatsapp("This is a test of a standard text message.", destination_number=os.environ.get("WHATSAPP_TO_NUMBER"), quick_reply_options=None)
|
207 |
-
print(test_result_standard)
|
|
|
5 |
from fastapi import FastAPI
|
6 |
from fastapi.responses import JSONResponse
|
7 |
import logging
|
8 |
+
import datetime # Import datetime for dynamic date
|
|
|
9 |
|
10 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
11 |
|
|
|
26 |
logging.error(f"❌ Failed to connect to Redis: {e}")
|
27 |
raise
|
28 |
|
29 |
+
# --- New: Topic mapping for display names and emojis ---
|
30 |
+
TOPIC_DISPLAY_MAP = {
|
31 |
+
"india": {"name": "India", "emoji": "🇮🇳"},
|
32 |
+
"world": {"name": "World", "emoji": "🌍"},
|
33 |
+
"tech": {"name": "Technology", "emoji": "🧠"}, # Used brain emoji from your curl
|
34 |
+
"finance": {"name": "Business & Markets", "emoji": "💰"}, # Used money bag emoji from your curl
|
35 |
+
"sports": {"name": "Sports", "emoji": "🏆"}, # Used trophy emoji from your curl
|
36 |
+
}
|
37 |
+
|
38 |
+
|
39 |
# 🧾 Fetch and format headlines
|
40 |
+
# UPDATED: To match the exact "NUSE DAILY" format
|
41 |
def fetch_cached_headlines() -> str:
|
42 |
try:
|
43 |
raw = redis_client.get("detailed_news_feed_cache")
|
|
|
49 |
logging.error(f"❌ Error reading from Redis: {e}")
|
50 |
return f"❌ Error reading from Redis: {e}"
|
51 |
|
52 |
+
# Get current date for the header
|
53 |
+
today = datetime.date.today()
|
54 |
+
# Format: July 22, 2025
|
55 |
+
formatted_date = today.strftime("%B %d, %Y")
|
56 |
|
57 |
+
message_parts = []
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
# Header section
|
60 |
+
message_parts.append("🗞️ *NUSE DAILY* 🟡")
|
61 |
+
message_parts.append(f"{formatted_date} — Morning Edition")
|
62 |
+
message_parts.append("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") # Unicode Box Drawings Light Horizontal
|
63 |
+
message_parts.append("") # Empty line for spacing
|
64 |
+
|
65 |
+
# Iterate through topics in a defined order for consistent output
|
66 |
+
# Using the keys from TOPIC_DISPLAY_MAP to ensure order and access details
|
67 |
+
for topic_key, display_info in TOPIC_DISPLAY_MAP.items():
|
68 |
+
stories_for_topic = data.get(topic_key) # Get stories for this topic key
|
69 |
+
|
70 |
+
if stories_for_topic: # Only add topic section if there are stories
|
71 |
+
message_parts.append(f"{display_info['emoji']} *{display_info['name']}*")
|
72 |
|
73 |
+
sorted_story_ids = sorted(stories_for_topic.keys(), key=int)
|
74 |
+
|
75 |
+
for ref_id in sorted_story_ids:
|
76 |
+
item = stories_for_topic[ref_id]
|
77 |
+
summary = item.get("title", "")
|
78 |
+
description = item.get("description", "")
|
79 |
+
|
80 |
+
# Using U+2007 (Figure Space) for alignment as seen in your example
|
81 |
+
message_parts.append(f"\u2007{ref_id}. \u2007{summary}\n_Why this matters_: {description}")
|
82 |
+
message_parts.append("") # Empty line for spacing between topics
|
83 |
+
|
84 |
+
# Footer section
|
85 |
+
message_parts.append("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
86 |
+
message_parts.append("🟠 Curated by Nuse Editorial Desk")
|
87 |
|
88 |
return "\n".join(message_parts)
|
89 |
|
90 |
# 📤 Send via Gupshup WhatsApp API
|
91 |
+
def send_to_whatsapp(message_text: str, destination_number: str) -> dict:
|
|
|
|
|
|
|
|
|
92 |
# Validate critical environment variables
|
93 |
if not WHATSAPP_TOKEN or \
|
94 |
not GUPSHUP_SOURCE_NUMBER or \
|
|
|
109 |
"Cache-Control": "no-cache"
|
110 |
}
|
111 |
|
112 |
+
whatsapp_message_content = {
|
113 |
+
"type": "text",
|
114 |
+
"text": message_text
|
115 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
payload = {
|
118 |
"channel": "whatsapp",
|
|
|
123 |
}
|
124 |
|
125 |
try:
|
126 |
+
logging.info(f"Attempting to send standard text WhatsApp message to {destination_number} via Gupshup. API URL: {WHATSAPP_API_URL}")
|
127 |
response = requests.post(
|
128 |
WHATSAPP_API_URL,
|
129 |
headers=headers,
|
|
|
139 |
logging.error(f"❌ An unexpected error occurred during WhatsApp send: {e}")
|
140 |
return {"status": "failed", "error": str(e), "code": 500}
|
141 |
|
142 |
+
# 🚀 FastAPI App (This part should ideally be in routes/api/wa_headlines.py, not here)
|
143 |
+
app = FastAPI()
|
144 |
|
145 |
@app.get("/send-daily-whatsapp")
|
146 |
def send_daily_whatsapp_digest():
|
|
|
152 |
logging.warning(f"Returning error due to issue fetching headlines: {full_message_text}")
|
153 |
return JSONResponse(status_code=404, content={"error": full_message_text})
|
154 |
|
155 |
+
# Call send_to_whatsapp with the default destination (from its os.environ.get)
|
156 |
+
result = send_to_whatsapp(full_message_text, destination_number=os.environ.get("WHATSAPP_TO_NUMBER"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
|
158 |
if result.get("status") == "success":
|
159 |
logging.info("✅ WhatsApp message sent successfully.")
|
|
|
165 |
# 🧪 For local testing
|
166 |
if __name__ == "__main__":
|
167 |
# For local testing, ensure these environment variables are set in your shell or .env file.
|
168 |
+
os.environ.setdefault("UPSTASH_REDIS_URL", "redis://localhost:6379")
|
169 |
os.environ.setdefault("WHATSAPP_API_URL", "https://api.gupshup.io/wa/api/v1/msg")
|
170 |
os.environ.setdefault("WHATSAPP_TOKEN", "YOUR_GUPSHUP_API_KEY")
|
171 |
+
os.environ.setdefault("WHATSAPP_TO_NUMBER", "353899495777") # Use a valid test number
|
172 |
os.environ.setdefault("GUPSHUP_SOURCE_NUMBER", "15557926439")
|
173 |
os.environ.setdefault("GUPSHUP_APP_NAME", "NuseAI")
|
174 |
|
175 |
# Simulate a cached detailed feed for testing
|
176 |
dummy_cached_detailed_feed = {
|
177 |
"india": {
|
178 |
+
"1": {"title": "RBI Holds Rates at 6.5% as Inflation Cools to 5.1%, Boosting Confidence Ahead of Festive Demand", "description": "The central bank's decision signals stability and aims to encourage consumer spending during the upcoming festive season, crucial for economic momentum. Lower inflation provides relief to households and supports growth prospects. The cautious approach balances economic recovery with price control.", "sources": ["Times of India"]},
|
179 |
+
"2": {"title": "Modi Inaugurates Global Summit Venue in Gandhinagar to Cement India’s Role as East–West Diplomatic Bridge", "description": "This new venue positions India as a key player in international diplomacy, fostering dialogue and collaboration between Asian and Western nations. It highlights the country's growing influence on global affairs and capacity for large-scale international events, boosting its soft power.", "sources": ["Indian Express"]},
|
180 |
+
"3": {"title": "Adani Pursues $50B Gulf Green Hydrogen Deal to Accelerate India’s Clean Energy Ambitions", "description": "This massive investment underscores India's commitment to renewable energy and its transition away from fossil fuels. Partnering with Gulf nations secures critical resources and technology, advancing India's position as a leader in green hydrogen production and reducing its carbon footprint.", "sources": ["The Economic Times"]}
|
181 |
},
|
182 |
"world": {
|
183 |
+
"4": {"title": "Biden Signs $95B Indo-Pacific Aid Bill, Reinforcing U.S. Strategy Against Chinese Expansion", "description": "The bill provides significant financial assistance to allies in the Indo-Pacific, strengthening regional security and economic ties. This move aims to counter China's growing influence and maintain stability in a strategically vital area, signaling continued U.S. commitment to its partners.", "sources": ["Reuters"]},
|
184 |
+
"5": {"title": "Hurricane Felix Strengthens Into Category 4, Florida Declares Statewide Emergency", "description": "The rapid intensification of Hurricane Felix poses a severe threat to coastal communities in Florida, prompting urgent evacuation orders and extensive preparedness measures. Residents are urged to secure their homes and follow safety guidelines as the storm approaches, emphasizing climate change impacts.", "sources": ["CNN World"]},
|
185 |
+
"6": {"title": "Iran’s Hardliner President Takes Office Amid Nuclear Tensions and Diplomatic Uncertainty", "description": "The new leadership in Iran signals a potential shift in foreign policy, raising concerns among international observers about the future of nuclear negotiations. Diplomatic tensions could escalate, impacting regional stability and global energy markets as nations reassess their strategies.", "sources": ["BBC News"]}
|
186 |
+
},
|
187 |
+
"finance": {
|
188 |
+
"7": {"title": "Nasdaq Surges as Nvidia and AI Stocks Power New Record Highs", "description": "The technology-heavy Nasdaq's rise reflects robust investor confidence in the artificial intelligence sector, with Nvidia leading the charge. This surge indicates strong market enthusiasm for innovation and potential future growth in tech, driving overall market performance.", "sources": ["CNBC"]},
|
189 |
+
"8": {"title": "Eurozone Inflation Falls Below 2.5%, Raising Hopes for ECB Rate Cuts in Q4", "description": "Declining inflation figures in the Eurozone signal easing price pressures, increasing the likelihood of interest rate reductions by the European Central Bank. Such cuts could stimulate economic activity and provide relief to consumers and businesses, supporting recovery efforts.", "sources": ["Financial Times"]},
|
190 |
+
"9": {"title": "Infosys Beats Earnings Expectations, Announces $1.5B Buyback", "description": "Infosys' strong financial performance demonstrates resilience in the IT services sector despite global economic uncertainties. The share buyback initiative signals confidence in the company's future prospects and aims to return value to shareholders, potentially boosting stock performance.", "sources": ["Economic Times"]}
|
191 |
+
},
|
192 |
+
"tech": {
|
193 |
+
"10": {"title": "Meta’s Llama 3.5 Rivals GPT-4 in Benchmarks, Marking Breakthrough for Open-Source AI", "description": "Meta's latest large language model, Llama 3.5, demonstrates impressive performance, challenging proprietary models like GPT-4. This development significantly advances open-source AI capabilities, promoting wider accessibility and innovation in artificial intelligence research and application development globally.", "sources": ["Wired"]},
|
194 |
+
"11": {"title": "Apple Unveils Foldable iPhone 17 with AI-Native OS, Signaling Shift Toward Software Innovation", "description": "Apple's new foldable smartphone, featuring a deeply integrated AI operating system, marks a significant technological leap. This move emphasizes a strategic shift towards software-driven experiences and design innovation, potentially reshaping the smartphone market and user interaction paradigms.", "sources": ["The Verge"]},
|
195 |
+
"12": {"title": "Google Partners With Indian Startups to Expand Regional AI Tools", "description": "Google's collaboration with Indian startups aims to foster localized AI solutions, addressing unique market needs and promoting technological advancement in the region. This initiative supports the growth of India's tech ecosystem and expands Google's reach into emerging markets, boosting digital transformation.", "sources": ["TechCrunch"]}
|
196 |
+
},
|
197 |
+
"sports": {
|
198 |
+
"13": {"title": "India Clinches T20 Series vs West Indies With Last-Over Thriller in Port of Spain", "description": "India's dramatic series victory showcases their resilience and strategic prowess in high-pressure cricket matches. This win boosts team morale and demonstrates strong form ahead of upcoming international tournaments, solidifying their position in global T20 rankings.", "sources": ["ESPN Cricinfo"]},
|
199 |
+
"14": {"title": "Messi Breaks MLS Scoring Record With Hat-Trick for Inter Miami", "description": "Lionel Messi continues to redefine Major League Soccer, setting a new scoring record with his exceptional performance for Inter Miami. His consistent brilliance elevates the league's global profile and inspires a new generation of football fans across North America.", "sources": ["MLS Soccer"]},
|
200 |
+
"15": {"title": "Olympic Torch Relay Kicks Off in Paris With 100 Days Until Opening", "description": "The ceremonial start of the Olympic Torch Relay in Paris ignites excitement and anticipation for the upcoming Summer Games. This milestone event symbolizes unity and athletic spirit, bringing the global community together in the countdown to the prestigious sporting spectacle.", "sources": ["Olympic.org"]}
|
201 |
}
|
202 |
}
|
203 |
# Store dummy data in Redis for testing fetch_cached_headlines
|
204 |
redis_client.set("detailed_news_feed_cache", json.dumps(dummy_cached_detailed_feed))
|
205 |
|
206 |
|
207 |
+
logging.info("\n--- WhatsApp Message Preview ---\n")
|
208 |
+
msg_preview = fetch_cached_headlines()
|
209 |
+
print(msg_preview)
|
210 |
|
211 |
+
logging.info("\n--- Sending WhatsApp Message (Test) ---\n")
|
212 |
+
test_result = send_to_whatsapp(msg_preview, destination_number=os.environ.get("WHATSAPP_TO_NUMBER"))
|
213 |
+
print(test_result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|