# routes/voipms.py import json from datetime import datetime from flask import Blueprint, request, jsonify from config import YEASTAR_WEBHOOK_URL, YEASTAR_SECRET, ENDPOINT_OBSCURITY from routes.helpers import to_e164, create_yeastar_payload, generate_signature import requests import logging import uuid bp = Blueprint("voipms", __name__) @bp.route(f"/{ENDPOINT_OBSCURITY}/voipms-inbound/verbose", methods=["POST"]) @bp.route(f"/{ENDPOINT_OBSCURITY}/voipms-inbound", methods=["POST"]) def voipms_inbound(): request_id = uuid.uuid4().hex[:8] verbose = request.path.endswith("/verbose") def get_client_ip(): for header in ["X-Forwarded-For", "X-Real-IP"]: ip = request.headers.get(header) if ip: return ip.split(",")[0].strip() return request.remote_addr or "unknown" client_ip = get_client_ip() if verbose: print(f"[{request_id}] voipms_inbound called{' [VERBOSE]' if verbose else ''}") try: if verbose: print(f"[{request_id}] Headers: {dict(request.headers)}") raw_data = request.get_data(as_text=True) if verbose: print(f"[{request_id}] Raw Body: {raw_data}") data = request.get_json(force=True) payload = data.get("data", {}).get("payload", {}) from_number = payload.get("from", {}).get("phone_number") to_list = payload.get("to", []) text = payload.get("text", "") media_urls = [m["url"] for m in payload.get("media", [])] if payload.get("media") else [] from_number = to_e164(from_number) if from_number else "" to_numbers = [to_e164(entry.get("phone_number")) for entry in to_list if entry.get("phone_number")] if verbose: print(f"[{request_id}] Normalized From: {from_number}") print(f"[{request_id}] Normalized To: {to_numbers}") print(f"[{request_id}] Text: {text}") print(f"[{request_id}] Media URLs: {media_urls}") else: msg_type = "MMS" if media_urls else "SMS" now_str = datetime.now().isoformat() print(f"[{now_str}] IP:{client_ip} From Voip.ms | {msg_type} | From:{from_number} ? To:{', '.join(to_numbers)} | Images:{len(media_urls)}") ys_payload = create_yeastar_payload({ "from": from_number, "to": to_numbers, "text": text, "media_urls": media_urls }) if verbose: print(f"[{request_id}] Outbound Payload: {json.dumps(ys_payload, indent=2)}") body_str = json.dumps(ys_payload) signature = generate_signature(body_str) headers = { "Content-Type": "application/json", "X-Signature-256": signature } response = requests.post(YEASTAR_WEBHOOK_URL, data=body_str, headers=headers) if verbose: print(f"[{request_id}] Yeastar Response Code: {response.status_code}") if verbose: print(f"[{request_id}] Yeastar Response Body: {response.text}") return jsonify({"status": "ok"}), 200 except Exception as e: logging.error(f"[{request_id}] Error in voipms_inbound: {e}", exc_info=True) return jsonify({"error": "internal server error"}), 500