import os import logging import socket import requests import dns.resolver from heyoo import WhatsApp from dotenv import load_dotenv from flask import Flask, request, make_response # Initialize Flask App app = Flask(__name__) # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) # Load .env file load_dotenv() # Configure DNS resolver with environment variables from Spaces nameserver1 = os.getenv('nameserver1', '8.8.8.8') nameserver2 = os.getenv('nameserver2', '8.8.4.4') # Configure global DNS resolution def setup_dns(): """Configure DNS resolution globally""" resolver = dns.resolver.Resolver() resolver.nameservers = [nameserver1, nameserver2] # Create a custom getaddrinfo function _orig_getaddrinfo = socket.getaddrinfo def new_getaddrinfo(*args, **kwargs): try: # If the host is graph.facebook.com, use our custom resolver if args and args[0] == 'graph.facebook.com': answers = resolver.resolve('graph.facebook.com', 'A') ip = str(answers[0]) return _orig_getaddrinfo(ip, *args[1:], **kwargs) except Exception as e: logger.error(f"DNS resolution failed: {e}") return _orig_getaddrinfo(*args, **kwargs) # Replace the global getaddrinfo function socket.getaddrinfo = new_getaddrinfo # Setup DNS resolution setup_dns() # Initialize WhatsApp try: messenger = WhatsApp( os.getenv("whatsapp_token"), phone_number_id=os.getenv("phone_number_id") ) if not os.getenv("whatsapp_token") or not os.getenv("phone_number_id"): raise ValueError("Missing required environment variables") except Exception as e: logger.error(f"Failed to initialize WhatsApp messenger: {str(e)}") raise VERIFY_TOKEN = "30cca545-3838-48b2-80a7-9e43b1ae8ce4" @app.get("/health") def health_check(): """Health check endpoint to verify DNS resolution""" try: resolver = dns.resolver.Resolver() resolver.nameservers = [nameserver1, nameserver2] ip = resolver.resolve('graph.facebook.com', 'A')[0] return { "status": "healthy", "dns_resolution": str(ip), "nameserver1": nameserver1, "nameserver2": nameserver2 } except Exception as e: return { "status": "unhealthy", "error": str(e) } @app.get("/") def verify_token(): """Webhook verification endpoint""" try: if request.args.get("hub.verify_token") == VERIFY_TOKEN: logger.info("Verified webhook") response = make_response(request.args.get("hub.challenge"), 200) response.mimetype = "text/plain" return response logger.error("Webhook Verification failed") return "Invalid verification token" except Exception as e: logger.error(f"Error in verify_token: {str(e)}") return "Internal server error", 500 @app.post("/") def hook(): """Main webhook handler""" try: data = request.get_json() logger.info("Received webhook data: %s", data) changed_field = messenger.changed_field(data) if changed_field != "messages": return "OK", 200 if not messenger.is_message(data): delivery = messenger.get_delivery(data) if delivery: logger.info(f"Message delivery status: {delivery}") return "OK", 200 # Extract message details mobile = messenger.get_mobile(data) name = messenger.get_name(data) message_type = messenger.get_message_type(data) logger.info(f"New Message; sender:{mobile} name:{name} type:{message_type}") # Handle different message types try: if message_type == "text": message = messenger.get_message(data) logger.info("Message: %s", message) messenger.send_message(f"Hi {name}, nice to connect with you", mobile) elif message_type == "interactive": message_response = messenger.get_interactive_response(data) interactive_type = message_response.get("type") message_id = message_response[interactive_type]["id"] message_text = message_response[interactive_type]["title"] logger.info(f"Interactive Message; {message_id}: {message_text}") elif message_type == "location": message_location = messenger.get_location(data) message_latitude = message_location["latitude"] message_longitude = message_location["longitude"] logger.info("Location: %s, %s", message_latitude, message_longitude) elif message_type in ["image", "video", "audio", "document"]: handle_media_message(messenger, data, message_type, mobile) else: logger.info(f"{mobile} sent unsupported message type: {message_type}") logger.debug(f"Full message data: {data}") except Exception as e: logger.error(f"Error processing message type {message_type}: {str(e)}") return "Internal server error", 500 return "OK", 200 except Exception as e: logger.error(f"Error in webhook handler: {str(e)}") return "Internal server error", 500 def handle_media_message(messenger, data, message_type, mobile): """Handle different types of media messages""" try: media_methods = { "image": messenger.get_image, "video": messenger.get_video, "audio": messenger.get_audio, "document": messenger.get_document } media = media_methods[message_type](data) media_id, mime_type = media["id"], media["mime_type"] try: media_url = messenger.query_media_url(media_id) media_filename = messenger.download_media(media_url, mime_type) logger.info(f"{mobile} sent {message_type} {media_filename}") except requests.exceptions.ConnectionError as ce: logger.error(f"Connection error while downloading {message_type}: {str(ce)}") except Exception as e: logger.error(f"Error downloading {message_type}: {str(e)}") except Exception as e: logger.error(f"Error processing {message_type} message: {str(e)}") raise def test_connection(): """Test DNS resolution and connection to Graph API""" try: resolver = dns.resolver.Resolver() resolver.nameservers = [nameserver1, nameserver2] ip = resolver.resolve('graph.facebook.com', 'A')[0] logger.info(f"DNS Resolution successful: {ip}") response = requests.get('https://graph.facebook.com') logger.info(f"HTTPS Connection successful: {response.status_code}") except Exception as e: logger.error(f"Connection test failed: {e}") if __name__ == "__main__": # Test connection before starting test_connection() # Start Flask app app.run(debug=True, host="0.0.0.0", port=7860)