Ë
    šdŒj‘J  ã                  óž
  — d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
m
Z
mZ ddlmZ ddlmZ ddlmZ ddlZddlmZmZmZ  ee«      j1                  «       j2                  Z ed	«      Zej2                  j9                  d
d
¬«        ej:                  d«      Z eeddd¬«      ZejA                  e«        ejB                  ejD                  «      Z#e#jA                  e«        ejH                  ejJ                  ee#g¬«        ejL                  d«      Z'dZ(	 ddl)m*Z*  e*e(«       ejn                  js                  dd«      jc                  «       Z:ejn                  js                  dd«      jc                  «       Z;ejn                  js                  dd«      jy                  d«      Z=ejn                  js                  dd «      jc                  «       Z>ejn                  js                  d!d«      jc                  «       Z?ejn                  js                  d"d«      jc                  «       Z@ejn                  js                  d#d«      jc                  «       ZAejn                  js                  d$d«      jc                  «       ZBejn                  js                  d%d«      jc                  «       ZCejn                  js                  d&d«      jc                  «       ZDejn                  js                  d'd(«      jc                  «       d)v ZEe:re;re?s8e'j�                  d* eGe:«       eGe;«       eGe?«      «        ej�                  d«       e?e@eAeBeCd+œZIeIj•                  «       D � �ci c]  \  } }|sŒ	| |“Œ c}} ZIed,z  ZKd-ZLd.ZM ejœ                  «       ZOd/„ ZPdMd0„ZQdNd1„ZRdOd2„ZSdPd3„ZTdQd4„ZUdRd5„ZVdSd6„ZWdTd7„ZXdUd8„ZYdVdWd:„ZZdXd;„Z[dYd<„Z\ ee]«      Z^e^js                  d=«      d>„ «       Z_e^jÁ                  d?«      d@„ «       Zae^jÅ                  dAdBdCg¬D«      dZdE„«       Zc eQ«        e]dFk(  ri edejn                  js                  dGdH«      «      Zeejn                  js                  dIdJ«      Zfe'jÏ                  dKefeeeEe;«       e^jÑ                  efeed9d
¬L«       yy# e+$ r¼ ejX                  j[                  e(«      r� e.e(d¬«      5 Z/e/D ]w  Z0e0jc                  «       Z2e2re2jg                  d«      sde2vrŒ+e2ji                  dd«      \  Z5Z6ejn                  jq                  e5jc                  «       e6jc                  «       «       Œy 	 ddd«       n# 1 sw Y   nxY wY �Œw xY wc c}} w )[u
  
GHL UTM Enricher - PX3 Lab
==========================

Micro-serviÃ§o webhook que enriquece contatos do Linkia PX3 com UTM baseado
no attributionSource NATIVO do GoHighLevel.

Fluxo:
  1. Recebe webhook (POST /webhook) do painel Linkia (evento Contact Create/Update).
  2. Extrai contactId do payload (aceita vÃ¡rios formatos).
  3. GET /contacts/{id} na GHL API -> pega attributionSource + customFields.
  4. Se contato JÃ� tem custom field utm_source preenchido -> skip.
  5. Se attributionSource sinaliza origem paga/orgÃ¢nica identificÃ¡vel ->
     mapeia pra utm_source/utm_medium/... e faz PUT /contacts/{id}.
  6. O workflow existente do Linkia ("Aplicar tag de origem") dispara
     naturalmente com o campo agora preenchido e aplica origem_meta/google/etc.

Dedupe:
  - SQLite local (dedupe.db) marca contact_id processado com TTL de 6h.
  - Evita reprocessar quando o Linkia dispara mÃºltiplos eventos (Create + Update)
    do mesmo contato em curto perÃ­odo.

Endpoints:
  GET  /health              -> status + config resumida
  POST /webhook             -> recebe evento do Linkia
  POST /enrich/{contact_id} -> forÃ§a enriquecimento manual de um contato
  GET  /enrich/{contact_id} -> mesmo mas Ãºtil pra teste em browser

Config: /opt/mia/config/ghl_utm_enricher_px3.env
Log:    /opt/mia/logs/ghl_utm_enricher_px3.log
DB:     ./dedupe.db

Handler modular: `enrich_contact()` Ã© chamado tanto pelo webhook quanto pelo
backfill_enrichment.py. No futuro (Meta CAPI Purchase), plugar outro handler
em `run_handlers(contact, changes)`.
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚRotatingFileHandler)ÚPath)ÚAny)ÚFlaskÚjsonifyÚrequestz&/opt/mia/logs/ghl_utm_enricher_px3.logT)ÚparentsÚexist_okz1%(asctime)s [%(levelname)s] %(name)s: %(message)si    é   zutf-8)ÚmaxBytesÚbackupCountÚencoding)ÚlevelÚhandlersÚutm_enricher_px3z(/opt/mia/config/ghl_utm_enricher_px3.env)Úload_dotenv)r   ú#ú=é   Ú	GHL_TOKENÚ ÚGHL_LOCATION_IDÚGHL_API_BASEz$https://services.leadconnectorhq.comú/ÚGHL_API_VERSIONz
2021-07-28ÚUTM_FIELD_SOURCEÚUTM_FIELD_MEDIUMÚUTM_FIELD_CAMPAIGNÚUTM_FIELD_CONTENTÚUTM_FIELD_TERMÚWEBHOOK_SECRETÚDRY_RUNÚ0©Ú1ÚtrueÚyesz:Config incompleta: GHL_TOKEN=%s LOC=%s UTM_FIELD_SOURCE=%s)Ú
utm_sourceÚ
utm_mediumÚutm_campaignÚutm_contentÚutm_termz	dedupe.dbi`T  i �' c                 óp   — t        j                  t        t        «      d¬«      } | j	                  d«       | S )Ng      @)ÚtimeoutzPRAGMA journal_mode=WAL)Úsqlite3ÚconnectÚstrÚDB_PATHÚexecute)Úconns    ú:/opt/mia/workspace/clientes/px3lab/ghl_utm_enricher/app.pyÚ_dbr9   ‡   s(   € Ü�?‰?œ3œw›<°Ô5€DØ‡L�LÐ*Ô+Ø€Kó    c                 óŽ  — t         5  t        «       5 } | j                  d«       | j                  d«       t        t	        j                  «       «      t
        z
  }| j                  d|f«      j                  }| j                  «        |rt        j                  d|«       d d d «       d d d «       y # 1 sw Y   ŒxY w# 1 sw Y   y xY w)NzÈ
            CREATE TABLE IF NOT EXISTS processed (
                contact_id TEXT PRIMARY KEY,
                ts         INTEGER NOT NULL,
                result     TEXT
            )
            z7CREATE INDEX IF NOT EXISTS idx_proc_ts ON processed(ts)z"DELETE FROM processed WHERE ts < ?z0dedupe cleanup: %d registros expirados removidos)
Ú_db_lockr9   r6   ÚintÚtimeÚCLEANUP_TTL_SECONDSÚrowcountÚcommitÚlogÚinfo)r7   ÚcutoffÚns      r8   Ú_init_dbrF   �   s§   € Ü	ñ L”3“5ð L˜DØ�‰ðô	
ð 	�‰ÐNÔOÜ”T—Y‘Y“[Ó!Ô$7Ñ7ˆØ�L‰LÐ=À¸yÓI×RÑRˆØ�‰ŒÙÜ�H‰HÐGÈÔK÷L÷ L÷ Lñ Lú÷ Lð Lús#   ‡B;’BB/ÂB;Â/B8	Â4B;Â;Cc                ó  — | syt        t        j                  «       «      t        z
  }t        5  t	        «       5 }|j                  d| |f«      j                  «       }d d d «       d d d «       d uS # 1 sw Y   ŒxY w# 1 sw Y   d uS xY w)NFz9SELECT ts FROM processed WHERE contact_id = ? AND ts >= ?)r=   r>   ÚDEDUPE_TTL_SECONDSr<   r9   r6   Úfetchone)Ú
contact_idrD   r7   Úrows       r8   Ú_is_recently_processedrL       s‰   € ÙØÜ”—‘“ÓÔ 2Ñ2€FÜ	ñ ”3“5ð ˜DØ�l‰lØGØ˜Ð ó
÷ ‰(‹*ð 	÷÷ ð
 �dˆ?Ð÷ð ú÷ ð
 �dˆ?Ðús"   ®A<¹#A0ÁA<Á0A9	Á5A<Á<Bc                ó  — | sy t        t        j                  «       «      }t        5  t        «       5 }|j	                  d| ||f«       |j                  «        d d d «       d d d «       y # 1 sw Y   ŒxY w# 1 sw Y   y xY w)NzJINSERT OR REPLACE INTO processed (contact_id, ts, result) VALUES (?, ?, ?))r=   r>   r<   r9   r6   rA   )rJ   ÚresultÚnowr7   s       r8   Ú_mark_processedrP   ¬   so   € ÙØÜ
Œd�i‰i‹kÓ
€CÜ	ñ ”3“5ð ˜DØ�‰ØXØ˜˜fÐ%ô	
ð 	�‰Œ÷÷ ÷ ñ ú÷ ð ús"   §A5²&A)ÁA5Á)A2	Á.A5Á5A>c                 ó&   — dt         › �t        dddœS )NzBearer zapplication/json)ÚAuthorizationÚVersionÚAcceptzContent-Type)r   r   © r:   r8   Ú_ghl_headersrV   »   s   € à"¤9 +Ð.Ü"Ø$Ø*ñ	ð r:   c                óÜ  — 	 t        j                  t        › d| › �t        «       d¬«      }|j                  dk(  rt
        j                  d| «       y |j                  dk\  r0t
        j                  d| |j                  |j                  d d «       y |j                  «       xs i j                  d	«      xs i S # t         j                  $ r!}t
        j                  d
| |«       Y d }~y d }~ww xY w)Nú
/contacts/é   )Úheadersr1   i”  zcontato %s nao encontrado (404)é,  zGET /contacts/%s falhou: %s %séÈ   ÚcontactzGET /contacts/%s excecao: %s)ÚrequestsÚgetr   rV   Ústatus_coderB   ÚwarningÚtextÚjsonÚRequestExceptionÚerror)rJ   ÚrÚes      r8   Úfetch_contactrh   Ä   sÊ   € ðÜ�L‰LÜˆn˜J z lÐ3Ü “NØô
ˆð
 �=‰=˜CÒÜ�K‰KÐ9¸:ÔFØØ�=‰=˜CÒÜ�K‰KØ0Ø˜AŸM™M¨1¯6©6°$°3¨<ôð Ø—‘“’˜B×#Ñ# IÓ.Ò4°"Ð4øÜ×$Ñ$ò Ü�	‰	Ð0°*¸aÔ@Üûðús$   ‚AB7 Á>B7 Â&B7 Â7C+Ã
C&Ã&C+c                óZ  — t         rt        j                  d| |«       y	 t        j                  t
        › d| › �t        «       d|id¬«      }d|j                  cxk  xr dk  nc }||j                  › d	|j                  d
d › �fS # t        j                  $ r}dd|› �fcY d
}~S d
}~ww xY w)zDPUT /contacts/{id} com customFields. Retorna (ok, resp_body_or_err).z*DRY_RUN: PUT /contacts/%s custom_fields=%s)TÚdry_runrX   ÚcustomFieldsrY   )rZ   rc   r1   r\   r[   ú NFzREQ_ERROR: )
r%   rB   rC   r^   Úputr   rV   r`   rb   rd   )rJ   Úfieldsrf   Úokrg   s        r8   Úupdate_contact_custom_fieldsrp   Ú   s«   € åÜ�‰Ð=¸zÈ6ÔRØð
(Ü�L‰LÜˆn˜J z lÐ3Ü “NØ  &Ð)Øô	
ˆð �A—M‘MÖ' CÔ'ˆØ�a—m‘m�_ A a§f¡f¨T¨c l ^Ð4Ð4Ð4øÜ×$Ñ$ò (Ø˜ A 3Ð'Ð'Õ'ûð(ús    A%B ÂB*ÂB%ÂB*Â%B*c                óÎ   — | j                  dg «      xs g D ]L  }|j                  d«      |k(  sŒ|j                  d«      }|€ y t        |«      j                  «       }|xs d c S  y )Nrk   ÚidÚvalue)r_   r4   Ústrip)r]   Úfield_idÚcfÚvÚss        r8   Ú_get_cf_valuery   ï   sd   € Ø�k‰k˜.¨"Ó-Ò3°ò ˆØ�6‰6�$‹<˜8Ó#Ø—‘�w“ˆAØˆyÙÜ�A“—‘“ˆAØ’9˜Òðð r:   c                ót  — | rt        | t        «      si S i }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       }| j                  d«      xs dj                  «       j	                  «       }| j                  d	«      xs dj                  «       }	| j                  d
«      xs dj                  «       }
|r?|j	                  «       |d<   |r|j	                  «       |d<   |r||d<   |r||d<   |r||d<   |S |j	                  «       }d|v sd|v r#d|d<   d|v rdnd|d<   |	r|	dd |d<   |
r|
|d<   |S d|v sd|v rd|d<   d|v rdnd|d<   |S d|v sd|v rd|d<   d|d<   |S d|v rd|d<   d|d<   |S d |v rd |d<   d |d<   |S d!|v r
d!|d<   d!|d<   |S )"u©  
    Regras de mapeamento (atribuiÃ§Ã£o nativa GHL -> UTMs lÃ³gicos).

    Prioridade:
    1. Se attribution jÃ¡ traz utmSource, usa direto (com utmMedium/Campaign/etc.).
    2. sessionSource == "Paid Social" -> utm_source=meta, utm_medium=whatsapp_ads
       (se medium=whatsapp) ou paid_social; extra: adName->utm_campaign, adId->utm_content.
    3. sessionSource == "Social media" / "Social Media" (orgÃ¢nico) -> utm_source=meta,
       utm_medium=whatsapp_organic (se medium=whatsapp) senÃ£o social.
    4. sessionSource contÃ©m "Paid Search" / "cpc" -> utm_source=google, utm_medium=cpc.
    5. sessionSource == "Organic Search" -> utm_source=google, utm_medium=organic.
    6. sessionSource == "Referral" -> utm_source=referral, utm_medium=referral.
    7. sessionSource == "Email" -> utm_source=email.
    8. Direct / CRM Workflows / CRM UI / Manual / vazio -> {} (deixa workflow
       classificar como origem_desconhecida).
    Ú	utmSourcer   Ú	utmMediumÚutmCampaignÚ
utmContentÚutmTermÚsessionSourceÚmediumÚadNameÚadIdr+   r,   r-   r.   r/   zpaid socialÚpaid_socialÚmetaÚwhatsÚwhatsapp_adsNr\   zsocial mediazorganic socialÚwhatsapp_organicÚsocialzpaid searchÚcpcÚgooglezorganic searchÚorganicÚreferralÚemail)Ú
isinstanceÚdictr_   rt   Úlower)ÚattÚoutÚutm_srcÚutm_medÚutm_campÚutm_contr/   Úsess_srcr�   Úad_nameÚad_idÚ
sess_lowers               r8   Ú_map_attribution_to_utmrœ   ú   s¤  € ñ" ”j ¤dÔ+Øˆ	à€CØ�w‰w�{Ó#Ò) r×0Ñ0Ó2€GØ�w‰w�{Ó#Ò) r×0Ñ0Ó2€GØ—‘˜Ó&Ò,¨"×3Ñ3Ó5€HØ—‘˜Ó%Ò+¨×2Ñ2Ó4€HØ—‘˜	Ó"Ò( b×/Ñ/Ó1€HØ—‘˜Ó(Ò.¨B×5Ñ5Ó7€HØ�g‰g�hÓÒ% 2×,Ñ,Ó.×4Ñ4Ó6€FØ�w‰w�xÓ Ò& B×-Ñ-Ó/€GØ�W‰W�V‹_Ò" ×)Ñ)Ó+€Eñ Ø#ŸM™M›OˆˆLÑÙØ '§¡£ˆC�ÑÙØ"*ˆC�ÑÙØ!)ˆC�ÑÙØ&ˆC�
‰OØˆ
ð —‘Ó!€Jà˜
Ñ" m°zÑ&AØ"ˆˆLÑØ.5¸Ñ.?™NÀ]ˆˆLÑáØ")¨$¨3 -ˆC�ÑÙØ!&ˆC�Ñð$ €Jð# 
˜:Ñ	%Ð)9¸ZÑ)GØ"ˆˆLÑØ29¸VÑ2CÑ.ÈˆˆLÑð €Jð 
˜*Ñ	$¨°Ñ(;Ø$ˆˆLÑØ!ˆˆLÑð €Jð 
˜ZÑ	'Ø$ˆˆLÑØ%ˆˆLÑð €Jð 
�zÑ	!Ø&ˆˆLÑØ&ˆˆLÑð €Jð 
�JÑ	Ø#ˆˆLÑØ#ˆˆLÑð €Jr:   c                ó–   — g }| j                  «       D ]3  \  }}t        j                  |«      }|r|sŒ |j                  ||dœ«       Œ5 |S )zHConverte {utm_source: 'meta', ...} em [{id: <cfid>, value: 'meta'}, ...])rr   rs   )ÚitemsÚCF_MAPr_   Úappend)Úutmr“   ÚkeyÚvalÚcf_ids        r8   Ú_build_custom_fields_payloadr¥   E  sM   € à
€CØ—I‘I“Kò 0‰ˆˆSÜ—
‘
˜3“ˆÙ™CØØ�
‰
˜%¨#Ñ.Õ/ð	0ð
 €Jr:   Fc           	     ó6  — | sdddœS |st        | «      rdd| dœS t        | «      }|sdd| dœS t        |t        «      }|rt	        | d	«       dd
| |dœS |j                  d«      xs i }|j                  d«      xs i }t        |«      }d}|s|rt        |«      }d}|st	        | d«       dd| ||dœS t        |«      }|st	        | d«       dd| |dœS t        | |«      \  }	}
|	rdnd}t	        | |«       |	| |j                  d«      ||||	s	|
t        dœS dt        dœS )uñ   
    Aplica enriquecimento UTM a um contato.

    Args:
        contact_id: ID GHL
        force: se True, ignora dedupe (mas ainda respeita 'jÃ¡ tem utm_source').

    Retorna dict com resultado (ok/skip/erro) â€” sempre serializÃ¡vel.
    FÚno_contact_id)ro   ÚreasonTÚdedupe)ro   ÚskiprJ   Úcontact_not_found)ro   r¨   rJ   Úskip_has_utm_sourceÚalready_has_utm_source)ro   rª   rJ   r+   ÚattributionSourceÚlastAttributionSourceÚskip_no_mappingÚno_attribution_mapping)ro   rª   rJ   ÚattributionÚlast_attributionÚskip_no_payloadÚempty_payload)ro   rª   rJ   r¡   ÚwrittenÚwrite_errorÚ	firstNamero   )ro   rJ   ÚnameÚused_attributionÚutm_writtenÚ
cf_payloadÚresprj   )
rL   rh   ry   r   rP   r_   rœ   r¥   rp   r%   )rJ   Úforcer]   Úcurrent_utm_sourcer’   Úlast_attr¡   Úusedr¼   ro   r½   rN   s               r8   Úenrich_contactrÂ   S  s‚  € ñ Ø Ñ7Ð7áÔ+¨JÔ7Ø H¸JÑGÐGä˜JÓ'€GÙØÐ':È*ÑUÐUô ' wÔ0@ÓAÐÙÜ˜
Ð$9Ô:àÐ 8Ø$Ð4Fñ
ð 	
ð
 �+‰+Ð)Ó
*Ò
0¨b€CØ�{‰{Ð2Ó3Ò9°r€Hô " #Ó
&€CØ€DÙ‘8Ü% hÓ/ˆØ&ˆáÜ˜
Ð$5Ô6àÐ 8Ø$Ø°Hñ
ð 	
ô .¨cÓ2€JÙÜ˜
Ð$5Ô6à Ø$¨Sñ
ð 	
ô
 ,¨J¸
ÓC�H€BˆÙ‰Y -€FÜ�J Ô'ð Ø Ø—‘˜KÓ(Ø ØØ Ù�Üñ	ð 	ð %)Üñ	ð 	r:   c                óF   — g }|j                  ddit        | «      ¥«       |S )u‚   
    Executa todos os handlers pra este contact_id/evento.
    Por ora sÃ³ o enricher; no futuro plug outros (ex: Meta CAPI).
    ÚhandlerÚutm_enricher)r    rÂ   )rJ   Ú
event_typeÚresultss      r8   Úrun_handlersrÈ   �  s)   € ð
 €Gà‡N�N�I˜~ÐL´À
Ó1KÐLÔMØ€Nr:   c                óä  — t        | t        «      syt        | j                  d«      xs | j                  d«      xs d«      j	                  «       }d}dD ]G  }| j                  |«      }|sŒt        |t        «      sŒ(t        |«      dk\  sŒ7|j	                  «       } n |sM| j                  d«      }t        |t        «      r,t        |j                  d«      xs d«      j	                  «       }||fS )	a  
    Retorna (contact_id, event_type). O Linkia/GHL manda formatos variados:
    - {"type": "ContactCreate", "contact_id": "..."}
    - {"type": "ContactCreate", "id": "..."}
    - {"contactId": "...", "type": "..."}
    - {"contact": {"id": "..."}, "type": "..."}
    )r   r   ÚtypeÚeventr   )rJ   Ú	contactIdrr   é
   r]   rr   )r�   r�   r4   r_   rt   Úlen)ÚpayloadÚetÚcidr¢   rw   Úcs         r8   Ú_extract_contact_idrÓ   «  sÌ   € ô �gœtÔ$ØÜ	ˆW�[‰[˜Ó Ò> G§K¡K°Ó$8Ò>¸BÓ	?×	EÑ	EÓ	G€BØ
€CØ0ò ˆØ�K‰K˜ÓˆÚ”˜AœsÕ#¬¨A«°"«Ø—'‘'“)ˆCÙð	ñ
 Ø�K‰K˜	Ó"ˆÜ�aœÔÜ�a—e‘e˜D“kÒ' RÓ(×.Ñ.Ó0ˆCØ�ˆ7€Nr:   z/healthc            
     ó†   — t        ddt        t        t        j	                  «       «      t
        t        t        «      dœ«      S )NTÚghl_utm_enricher_px3)ro   ÚserviceÚlocationÚ	cf_fieldsrj   Úwebhook_secret_required)r
   r   ÚlistrŸ   Úkeysr%   Úboolr$   rU   r:   r8   ÚhealthrÝ   É  s6   € äàØ-Ü'ÜœfŸk™k›mÓ,ÜÜ'+¬NÓ';ñ	
ó	ð 	r:   z/webhookc            
     ó  — t         rkt        j                  j                  dd«      j	                  «       } | t         k7  r4t
        j                  dt        j                  «       t        dddœ«      dfS t        j                  d¬	«      xs i }t        |«      \  }}t
        j                  d
|xs d|xs dt        j                  j                  dt        j                  xs d«      «       |sTt
        j                  dt        j                  |«      d d «       t        ddt        |j                  «       «      dœ«      dfS t!        ||«      }t
        j                  d|t        j                  |d¬«      d d «       t        d||dœ«      S )NzX-Webhook-Secretr   z*webhook rejeitado: secret invalido (ip=%s)FÚunauthorized)ro   re   i‘  T)Úsilentz.webhook recebido: event=%s contact_id=%s ip=%sú?z(vazio)zX-Forwarded-Foru%   webhook sem contact_id parseÃ¡vel: %sé�  r§   )ro   re   Úpayload_keysz)webhook processado: contact=%s results=%s©Úensure_asciiiô  )ro   rJ   rÇ   )r$   r   rZ   r_   rt   rB   ra   Úremote_addrr
   Úget_jsonrÓ   rC   rc   ÚdumpsrÚ   rÛ   rÈ   )ÚgotrÏ   rJ   rÆ   rÇ   s        r8   Úwebhookrê   ×  sO  € õ Ü�o‰o×!Ñ!Ð"4°bÓ9×?Ñ?ÓAˆØ”.Ò Ü�K‰KÐDÄg×FYÑFYÔZÜ %°.ÑAÓBÀCÐGÐGä×Ñ dÔ+Ò1¨r€GÜ0°Ó9Ñ€J�
ä‡H�HØ8ØÒ�cØÒ�iÜ�‰×ÑÐ-¬w×/BÑ/BÒ/IÀcÓJô	ñ Ü�‰Ð;¼T¿Z¹ZÈÓ=PÐQUÐRUÐ=VÔWÜ˜e¨oÌtÐT[×T`ÑT`ÓTbÓOcÑdÓeÐgjÐjÐjä˜: zÓ2€GÜ‡H�HØ3Ø”D—J‘J˜w°UÔ;¸D¸SÐAôô ˜$¨jÀWÑMÓNÐNr:   z/enrich/<contact_id>ÚGETÚPOST)Úmethodsc                óÔ   — t         j                  j                  dd«      dv }t        | |¬«      }t        j                  d| t        j                  |d¬«      dd	 «       t        |«      S )
u<   Manual: forÃ§a enriquecimento de um contato (ignora dedupe).r¾   r(   r'   )r¾   z"manual enrich contact=%s result=%sFrä   Nrâ   )	r   Úargsr_   rÂ   rB   rC   rc   rè   r
   )rJ   r¾   rN   s      r8   Úenrich_endpointrð   ö  s\   € ô �L‰L×Ñ˜W cÓ*Ð.BÐB€EÜ˜J¨eÔ4€FÜ‡H�HÐ1°:¼t¿z¹zÈ&Ð_dÔ?eÐfjÐgjÐ?kÔlÜ�6‹?Ðr:   Ú__main__ÚPORTÚ8919ÚHOSTz0.0.0.0zDghl_utm_enricher_px3 subindo: host=%s port=%s dry_run=%s location=%s)ÚhostÚportÚdebugÚthreaded)ÚreturnÚNone)rJ   r4   rù   rÜ   )rJ   r4   rN   r4   rù   rú   )rù   údict[str, str])rJ   r4   rù   zdict | None)rJ   r4   rn   ú
list[dict]rù   ztuple[bool, str])r]   r�   ru   r4   rù   z
str | None)r’   r�   rù   rû   )r¡   rû   rù   rü   )F)rJ   r4   r¾   rÜ   rù   r�   )rJ   r4   rÆ   r4   rù   rü   )rÏ   r�   rù   ztuple[str, str])rJ   r4   )iÚ__doc__Ú
__future__r   rc   ÚloggingÚosr2   ÚsysÚ	threadingr>   r   r   Úlogging.handlersr   Úpathlibr   Útypingr   r^   Úflaskr	   r
   r   Ú__file__ÚresolveÚparentÚBASE_DIRÚLOG_FILEÚmkdirÚ	FormatterÚ_fmtÚ_fhÚsetFormatterÚStreamHandlerÚstdoutÚ_shÚbasicConfigÚINFOÚ	getLoggerrB   ÚENV_FILEÚdotenvr   ÚImportErrorÚpathÚexistsÚopenÚfÚrawrt   ÚlineÚ
startswithÚsplitÚkrw   ÚenvironÚ
setdefaultr_   r   r   Úrstripr   r   r   r    r!   r"   r#   r$   r%   ÚcriticalrÜ   ÚexitrŸ   rž   r5   rH   r?   ÚLockr<   r9   rF   rL   rP   rV   rh   rp   ry   rœ   r¥   rÂ   rÈ   rÓ   Ú__name__ÚapprÝ   Úpostrê   Úrouterð   r=   rö   rõ   rC   Úrun)r"  rw   s   00r8   ú<module>r.     s×  ðñ#õJ #ã Û Û 	Û Û 
Û Û ß 'Ý 0Ý Ý ã ß )Ñ )á�‹>×!Ñ!Ó#×*Ñ*€ñ
 Ð8Ó9€Ø ‡�× Ñ ˜d¨TÐ Ô 2à€w×ÑÐLÓM€ÙØÐ'°QÀô€ð × Ñ �Ô Ø€g×Ñ˜CŸJ™JÓ'€Ø × Ñ �Ô Ø €× Ñ ˜'Ÿ,™,°#°s°Õ <Ø€g×ÑÐ*Ó+€ð
 6€ð<Ý"á�Ôð �J‰J�N‰N˜;¨Ó+×1Ñ1Ó3€	Ø—*‘*—.‘.Ð!2°BÓ7×=Ñ=Ó?€Ø�z‰z�~‰~˜nÐ.TÓU×\Ñ\Ð]`Óa€Ø—*‘*—.‘.Ð!2°LÓA×GÑGÓI€à—:‘:—>‘>Ð"4°bÓ9×?Ñ?ÓAÐ Ø—:‘:—>‘>Ð"4°bÓ9×?Ñ?ÓAÐ Ø—Z‘Z—^‘^Ð$8¸"Ó=×CÑCÓEÐ Ø—J‘J—N‘NÐ#6¸Ó;×AÑAÓCÐ Ø—‘—‘Ð 0°"Ó5×;Ñ;Ó=€à—‘—‘Ð 0°"Ó5×;Ñ;Ó=€Ø
�*‰*�.‰.˜ CÓ
(×
.Ñ
.Ó
0Ð4HÐ
H€á™Ñ/?Ø‡L�LØDÙˆY‹™˜oÓ.±Ð5EÓ0Fôð €C‡H�HˆQ„Kð #Ø"Ø&Ø$Øñ
€ð "Ÿ<™<›>×	/‘4�1�aªQˆ!ˆQ‰$Ó	/€ð �[Ñ
 €Ø Ð Ø'Ð àˆ9�>‰>Ó€òóLó&	ó	óóó,(ó*óHóVôDóTóñ6 ˆHƒo€ð ‡�ˆÓñ
ó ð
ð ‡�ˆ*ÓñOó ðOð< ‡�Ð!¨E°6¨?€Ó;òó <ðñ 	„
àˆzÒÙˆr�z‰z�~‰~˜f fÓ-Ó.€DØ�:‰:�>‰>˜& )Ó,€DØ‡H�HØNØˆd�G˜_ôð ‡G�G�˜D¨¸€GÕ=ð øðe ò <Ø	‡w�w‡~�~�hÔÙ�( WÔ-ð 	<°Øò <�Ø—y‘y“{�Ù˜tŸ™¨sÔ3°sÀ$±ØØ—z‘z # qÓ)‘��1Ø—
‘
×%Ñ% a§g¡g£i°·±³Õ;ñ<÷	<÷ 	<ñ 	<ýð<üóP 
0s7   Ä#R Í
U	Í&U	Ò*UÒ/A=T6Ô-	UÔ6T?	Ô;UÕU