Files
META-LEAD/main.py
T
2026-06-26 12:16:15 +02:00

323 lines
7.7 KiB
Python

"""
Synchronisation Meta Lead Gen vers SQL Server local.
Récupère les formulaires actifs et leurs leads via l'API Graph Meta,
puis les stocke dans SQL Server sans créer de doublons.
L'injection vers OscarCRM n'est pas gérée à ce stade.
"""
import os
from datetime import datetime, timezone
import pyodbc
import requests
from dotenv import load_dotenv
load_dotenv()
META_ACCESS_TOKEN = os.getenv("META_ACCESS_TOKEN")
META_API_VERSION = os.getenv("META_API_VERSION", "v25.0")
META_PAGE_ID = os.getenv("META_PAGE_ID")
DB_DRIVER = os.getenv("DB_DRIVER", "SQL Server")
DB_SERVER = os.getenv("DB_SERVER", "localhost")
DB_PORT = os.getenv("DB_PORT", "1433")
DB_NAME = os.getenv("DB_NAME", "MetaLeadDb")
DB_USER = os.getenv("DB_USER", "sa")
DB_PASSWORD = os.getenv("DB_PASSWORD")
FORM_CAMPUSES = {
"1016111097572602": "Marseille",
"1032481262797041": "Nantes",
"941574848893640": "Cergy",
"1012012785055468": "Cergy",
}
def get_database_connection():
"""Ouvre une connexion vers SQL Server."""
connection_string = (
f"DRIVER={{{DB_DRIVER}}};"
f"SERVER={DB_SERVER},{DB_PORT};"
f"DATABASE={DB_NAME};"
f"UID={DB_USER};"
f"PWD={DB_PASSWORD};"
"TrustServerCertificate=yes;"
)
return pyodbc.connect(connection_string)
def call_meta_api(path, params=None):
"""Exécute un appel GET vers l'API Graph Meta."""
url = f"https://graph.facebook.com/{META_API_VERSION}/{path}"
request_params = params.copy() if params else {}
request_params["access_token"] = META_ACCESS_TOKEN
response = requests.get(url, params=request_params, timeout=30)
response.raise_for_status()
return response.json()
def parse_meta_datetime(value):
"""Convertit une date Meta en datetime compatible SQL Server."""
if not value:
return None
parsed_date = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S%z")
utc_date = parsed_date.astimezone(timezone.utc)
return utc_date.replace(tzinfo=None)
def normalize_field_data(field_data):
"""Convertit field_data Meta en dictionnaire simple."""
normalized = {}
for field in field_data or []:
name = field.get("name")
values = field.get("values", [])
if name:
normalized[name] = values[0] if values else None
return normalized
def get_email_from_fields(fields):
"""Récupère l'email en gérant les variantes possibles du champ Meta."""
return (
fields.get("adresse_e-mail")
or fields.get("adresse-e-mail")
or fields.get("adresse_email")
or fields.get("email")
)
def fetch_active_forms():
"""Récupère les formulaires Lead Gen actifs autorisés."""
result = call_meta_api(
f"{META_PAGE_ID}/leadgen_forms",
{
"fields": "id,name,status,locale",
"limit": 100,
},
)
forms = result.get("data", [])
return [
form for form in forms
if form.get("status") == "ACTIVE"
and form.get("id") in FORM_CAMPUSES
]
def fetch_form_leads(form_id):
"""Récupère tous les leads d'un formulaire Meta avec pagination."""
all_leads = []
result = call_meta_api(
f"{form_id}/leads",
{
"fields": "id,created_time,field_data",
"limit": 100,
},
)
all_leads.extend(result.get("data", []))
next_url = result.get("paging", {}).get("next")
while next_url:
response = requests.get(next_url, timeout=30)
response.raise_for_status()
result = response.json()
all_leads.extend(result.get("data", []))
next_url = result.get("paging", {}).get("next")
return all_leads
def upsert_leadgen_form(connection, form):
"""Insère ou met à jour un formulaire Meta dans leadgen_forms."""
update_query = """
UPDATE dbo.leadgen_forms
SET
page_id = ?,
name = ?,
locale = ?,
status = ?,
updated_at = SYSDATETIME()
WHERE meta_form_id = ?
"""
insert_query = """
INSERT INTO dbo.leadgen_forms (
meta_form_id,
page_id,
name,
locale,
status
)
VALUES (?, ?, ?, ?, ?)
"""
cursor = connection.cursor()
cursor.execute(
update_query,
(
META_PAGE_ID,
form.get("name"),
form.get("locale"),
form.get("status"),
form.get("id"),
),
)
if cursor.rowcount == 0:
cursor.execute(
insert_query,
(
form.get("id"),
META_PAGE_ID,
form.get("name"),
form.get("locale"),
form.get("status"),
),
)
connection.commit()
cursor.close()
def upsert_lead_data(connection, form, lead):
"""Insère ou met à jour un lead Meta dans lead_data."""
fields = normalize_field_data(lead.get("field_data", []))
campus = FORM_CAMPUSES.get(form.get("id"))
update_query = """
UPDATE dbo.lead_data
SET
meta_form_id = ?,
created_time = ?,
je_suis = ?,
nom = ?,
first_name = ?,
adresse_email = ?,
phone_number = ?,
city = ?,
campus = ?,
updated_at = SYSDATETIME()
WHERE meta_lead_id = ?
"""
insert_query = """
INSERT INTO dbo.lead_data (
meta_lead_id,
meta_form_id,
created_time,
je_suis,
nom,
first_name,
adresse_email,
phone_number,
city,
campus,
status
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, N'RECEIVED')
"""
values_for_update = (
form.get("id"),
parse_meta_datetime(lead.get("created_time")),
fields.get("je_suis"),
fields.get("nom"),
fields.get("first_name"),
get_email_from_fields(fields),
fields.get("phone_number"),
fields.get("city"),
campus,
lead.get("id"),
)
values_for_insert = (
lead.get("id"),
form.get("id"),
parse_meta_datetime(lead.get("created_time")),
fields.get("je_suis"),
fields.get("nom"),
fields.get("first_name"),
get_email_from_fields(fields),
fields.get("phone_number"),
fields.get("city"),
campus,
)
cursor = connection.cursor()
cursor.execute(update_query, values_for_update)
if cursor.rowcount == 0:
cursor.execute(insert_query, values_for_insert)
connection.commit()
cursor.close()
def synchronize_meta_to_sql_server():
"""Synchronise les formulaires et les leads Meta vers SQL Server."""
connection = get_database_connection()
total_forms = 0
total_leads = 0
try:
forms = fetch_active_forms()
for form in forms:
upsert_leadgen_form(connection, form)
total_forms += 1
leads = fetch_form_leads(form.get("id"))
for lead in leads:
upsert_lead_data(connection, form, lead)
total_leads += 1
print("Synchronisation terminée.")
print(f"Formulaires traités : {total_forms}")
print(f"Leads traités : {total_leads}")
finally:
connection.close()
if __name__ == "__main__":
if not META_ACCESS_TOKEN:
raise RuntimeError("META_ACCESS_TOKEN est manquant dans le fichier .env.")
if not META_PAGE_ID:
raise RuntimeError("META_PAGE_ID est manquant dans le fichier .env.")
if not DB_PASSWORD:
raise RuntimeError("DB_PASSWORD est manquant dans le fichier .env.")
synchronize_meta_to_sql_server()