from fastapi import FastAPI, Request import logging import json from datetime import datetime, timedelta from typing import Any, Dict, List, Tuple app = FastAPI() logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) log = logging.getLogger("supabase-receiver") def extract_records(payload: Any) -> List[Dict]: """ نحاول نطلع records من البودي بأي شكل محتمل: - { "record": {...} } - { "records": [ {...}, {...} ] } - [ {...}, {...} ] - أو dict واحد نعتبره row واحد. """ if isinstance(payload, dict): if "record" in payload and isinstance(payload["record"], dict): return [payload["record"]] if "records" in payload and isinstance(payload["records"], list): return [r for r in payload["records"] if isinstance(r, dict)] return [payload] elif isinstance(payload, list): return [r for r in payload if isinstance(r, dict)] else: return [] def parse_lead_timestamp(raw_ts: str) -> Tuple[str, str]: """ يحوّل lead_timestamp إلى: - deadline: تاريخ ووقت +24 ساعة (string) - due_date: تاريخ فقط (string) لو مفيش timestamp هنستخدم الوقت الحالي. """ if not raw_ts: now = datetime.utcnow() deadline = now + timedelta(hours=24) else: ts = raw_ts.replace("Z", "+00:00") dt = datetime.fromisoformat(ts) deadline = dt + timedelta(hours=24) deadline_str = deadline.strftime("%Y-%m-%d %H:%M:%S") due_date = deadline.date().isoformat() return deadline_str, due_date def build_odoo_payload_from_lead(lead: Dict) -> Dict: """ نفس المابّينج تقريبًا اللي هنعمله في السيرفر الحقيقي، بس هنا عشان نطبع كود جاهز للتست لوكال. """ full_name = lead.get("full_name") or lead.get("name") or "Lead" email = lead.get("email") or "" phone = lead.get("phone") or "" city_from_lead = lead.get("city") or False platform = lead.get("platform") or "" lead_ts = lead.get("lead_timestamp") deadline_str, due_date = parse_lead_timestamp(lead_ts) body = { "name": full_name, "date_deadline": deadline_str, "user_id": lead.get("lead_id"), # زي ما اتفقنا "active": True, "kanban_state": "grey", "type": "opportunity", "stage_id": 18, "tag_ids": [], "color": 0, "adgroup_name": lead.get("campaign_name"), "adset_name": lead.get("adset_name"), "ad_name": lead.get("ad_name", ""), "lead_timestamp": lead_ts, "source_id": None, "source_company": "Jizan", "due_date": due_date, "call_logs": "", "call_log_result": "", "age": None, "specialty_id": False, "gender_id": False, "city_ar": city_from_lead or "غير محدد", "email_from": email, "city": False, "phone": phone, "platform": platform, } return body def build_python_snippet_for_odoo(payload: Dict) -> str: """ يبني كود Python جاهز تقدر تاخده كوبي-بيست، وتستعمله لوكال عشان يبعته لـ Odoo. """ payload_json = json.dumps(payload, ensure_ascii=False, indent=4) snippet = f'''import requests ODOO_LEAD_URL = "https://odoo.binrushd.care/api/crm.lead" token = "PASTE_TOKEN_HERE" # حط التوكن اللي جالك من /api/auth/token payload = {payload_json} headers = {{ "Authorization": f"Bearer {{token}}", "Content-Type": "application/json", }} resp = requests.post(ODOO_LEAD_URL, json=payload, headers=headers, timeout=30) print(resp.status_code) print(resp.text) ''' return snippet @app.post("/") async def receive_from_supabase(request: Request): """ Endpoint رئيسي تستقبله Supabase Webhook. - يطبع البودي بالكامل في اللوج - يطلع أول record - يبني منه payload لـ Odoo - يطبع كود Python في اللوج تقدر تاخده كوبي-بيست وتجرّبه لوكال. """ try: body = await request.json() except Exception as e: log.error("Failed to parse JSON body: %s", e) return {"status": "error", "detail": f"Invalid JSON: {e}"} # 1) طباعة البودي الخام log.info("=== RAW WEBHOOK BODY FROM SUPABASE ===") log.info(json.dumps(body, ensure_ascii=False, indent=2)) # 2) استخراج records records = extract_records(body) if not records: log.warning("No records found in webhook payload.") return {"status": "ok", "note": "no records in payload"} first = records[0] log.info("=== FIRST RECORD (lead) ===") log.info(json.dumps(first, ensure_ascii=False, indent=2)) # 3) بناء payload لـ Odoo odoo_payload = build_odoo_payload_from_lead(first) log.info("=== ODOO PAYLOAD (dict) ===") log.info(json.dumps(odoo_payload, ensure_ascii=False, indent=2)) # 4) توليد كود Python جاهز python_snippet = build_python_snippet_for_odoo(odoo_payload) log.info("=== PYTHON SNIPPET TO SEND THIS LEAD TO ODOO ===") log.info("\n%s", python_snippet) # 5) نرجّع response بسيط return { "status": "ok", "records_received": len(records), "preview_odoo_payload": odoo_payload, }