import os import json import redis import requests from fastapi import FastAPI # This import is not strictly needed in this file if it's just a module from fastapi.responses import JSONResponse # This import is not strictly needed in this file if it's just a module import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # ๐ŸŒ Configuration from Environment Variables # These variables MUST be set in your environment (e.g., .env file, shell exports, deployment configs) REDIS_URL = os.environ.get("UPSTASH_REDIS_URL", "redis://localhost:6379") WHATSAPP_API_URL = os.environ.get("WHATSAPP_API_URL", "https://api.gupshup.io/wa/api/v1/msg") WHATSAPP_TOKEN = os.environ.get("WHATSAPP_TOKEN") WHATSAPP_TO_NUMBER = os.environ.get("WHATSAPP_TO_NUMBER", "353899495777") # e.g., "91xxxxxxxxxx" GUPSHUP_SOURCE_NUMBER = os.environ.get("GUPSHUP_SOURCE_NUMBER") # e.g., your WABA number GUPSHUP_APP_NAME = os.environ.get("GUPSHUP_APP_NAME") # e.g., your Gupshup app name # โœ… Redis connection try: redis_client = redis.Redis.from_url(REDIS_URL, decode_responses=True) redis_client.ping() # Test connection logging.info("Redis client connected successfully.") except Exception as e: logging.error(f"โŒ Failed to connect to Redis: {e}") raise # ๐Ÿงพ Fetch and format headlines def fetch_cached_headlines() -> str: try: raw = redis_client.get("detailed_news_feed_cache") if not raw: logging.warning("โš ๏ธ No detailed news headlines found in cache.") return "โš ๏ธ No daily headlines found in cache." data = json.loads(raw) except Exception as e: logging.error(f"โŒ Error reading from Redis: {e}") return f"โŒ Error reading from Redis: {e}" message_parts = ["๐Ÿ—ž๏ธ *Your Daily Digest* ๐ŸŸก\n"] sorted_topics = sorted(data.keys()) for topic_key in sorted_topics: stories = data[topic_key] title = topic_key.replace("_", " ").title() message_parts.append(f"\n๐Ÿท๏ธ *{title}*") # Added newline before topic title sorted_story_ids = sorted(stories.keys(), key=int) for ref_id in sorted_story_ids: item = stories[ref_id] summary = item.get("title", "") description = item.get("description", "") message_parts.append(f"{ref_id}. {summary}\n_Why this matters_: {description}") # No extra empty line needed here if we add newline to topic title return "\n".join(message_parts) # ๐Ÿ“ค Send via Gupshup WhatsApp API def send_to_whatsapp(message_text: str) -> dict: # Function expects the full message text # Validate critical environment variables for sending a message if not WHATSAPP_TOKEN or \ not WHATSAPP_TO_NUMBER or \ not GUPSHUP_SOURCE_NUMBER or \ not GUPSHUP_APP_NAME: error_msg = "โŒ Missing one or more critical WhatsApp API environment variables (WHATSAPP_TOKEN, WHATSAPP_TO_NUMBER, GUPSHUP_SOURCE_NUMBER, GUPSHUP_APP_NAME)." logging.error(error_msg) return {"status": "failed", "error": error_msg, "code": 500} headers = { "Content-Type": "application/x-www-form-urlencoded", "apikey": WHATSAPP_TOKEN, # API key in 'apikey' header "Cache-Control": "no-cache" # Add Cache-Control header } whatsapp_message_content = { "type": "text", "text": message_text # This is the full formatted text } payload = { "channel": "whatsapp", "source": GUPSHUP_SOURCE_NUMBER, "destination": WHATSAPP_TO_NUMBER, "src.name": GUPSHUP_APP_NAME, "message": json.dumps(whatsapp_message_content) # 'message' parameter with JSON string for text type } try: logging.info(f"Attempting to send standard text WhatsApp message to {WHATSAPP_TO_NUMBER} via Gupshup. API URL: {WHATSAPP_API_URL}") response = requests.post( WHATSAPP_API_URL, headers=headers, data=payload, ) response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx) return {"status": "success", "details": response.json()} except requests.exceptions.RequestException as e: logging.error(f"โŒ Failed to send WhatsApp message: {e}") return {"status": "failed", "error": str(e), "code": e.response.status_code if e.response else 500} except Exception as e: logging.error(f"โŒ An unexpected error occurred during WhatsApp send: {e}") return {"status": "failed", "error": str(e), "code": 500} # Removed the FastAPI app instance and endpoint from here, # as it will be defined in routes/api/wa_headlines.py # and then included in app.py. # Removed the if __name__ == "__main__": block from here, # as it will be in routes/api/wa_headlines.py for local testing.