Ë
    Èœ©j÷*  ã                  óÄ  — U 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
Z
ddlmZ ddlmZ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 dd	lmZ dd
lmZ dZdZdZdZdZ dZ!d=d„Z" e"e«        ejF                  dd«      jI                  «       Z% ejF                  dd«      jI                  «       Z& ejF                  dd«      jI                  «       Z'e%re&s1ejP                  jS                  de› d�«        ejT                  d«       de%› �dddœZ+ ee«      jX                  j[                  dd¬«        ej\                  d«      Z/e/ja                  ejb                  «       d e/_2        e/jf                  sŽ eed!d"d#¬$«      Z4e4jk                   ejl                  d%d&«      «       e/jo                  e4«        ejp                  ejr                  «      Z:e:jk                   ejl                  d%d&«      «       e/jo                  e:«       e/jv                  Z<d>d'„Z= e=«       Z> e«       Z?d(e@d)<    e	j‚                  «       ZBd?d*„ZCd@d+„ZDdAd,„ZEdBd-„ZFdCd.„ZGdDd/„ZHdEd0„ZI eeJ«      ZKeKj™                  d1«      d2„ «       ZMeKj�                  d3«      d4„ «       ZOeJd5k(  rN eP ejF                  d6d7«      «      ZQ ejF                  d8d9«      ZR e<d:eR› d;eQ› �«       eKj§                  eReQ¬<«       yy)Fu÷  
Receiver de webhooks GHL para PX3 Lab.

Fluxo:
  Workflow GHL (Task Created / Completed / Updated)
    -> POST https://receiver-ghl-px3.agentesclimb.us/webhook/task
    -> extrai contactId
    -> busca oportunidades do contato
    -> recalcula farol (mesma lÃ³gica do farois_pipeline)
    -> faz PUT no custom field Status Tarefa

Endpoint auxiliar:
  GET /health -> {"ok": true}

Rate limit: 100 req/min (janela deslizante em memÃ³ria).
Log: /opt/mia/logs/receiver_ghl_px3.log (rotacionado 10MB x 3).
é    )ÚannotationsN)Údeque)ÚdatetimeÚtimezone)ÚRotatingFileHandler)ÚPath)ÚFlaskÚjsonifyÚrequest)ÚHTTPAdapter)ÚRetryz/opt/mia/config/farois_px3.envz"/opt/mia/logs/receiver_ghl_px3.logz$https://services.leadconnectorhq.comÚcvrJJ7rlAuIGgCL42NEHéd   é<   c                óÈ  — t         j                  j                  | «      sy t        | dd¬«      5 }|D ]™  }|j	                  «       }|r|j                  d«      sd|vrŒ+|j                  d«      \  }}}t         j                  j                  |j	                  «       |j	                  «       j	                  d«      j	                  d«      «       Œ› 	 d d d «       y # 1 sw Y   y xY w)NÚrúutf-8)Úencodingú#ú=ú"ú')	ÚosÚpathÚisfileÚopenÚstripÚ
startswithÚ	partitionÚenvironÚ
setdefault)r   ÚfÚrawÚlineÚkeyÚ_Úvals          ú6/opt/mia/workspace/clientes/px3lab/receiver_ghl/app.pyÚ	_load_envr)   6   s¹   € Ü�7‰7�>‰>˜$ÔØÜ	ˆd�C 'Ô	*ð R¨aØò 	RˆCØ—9‘9“;ˆDÙ˜4Ÿ?™?¨3Ô/°3¸d±?ØØŸ.™.¨Ó-‰KˆC��CÜ�J‰J×!Ñ! #§)¡)£+¨s¯y©y«{×/@Ñ/@ÀÓ/E×/KÑ/KÈCÓ/PÕQñ	R÷R÷ Rñ Rús   ¯BCÃC!Ú	GHL_TOKENÚ ÚGHL_LOCATIONÚGHL_WEBHOOK_SECRETz)[FATAL] Faltam GHL_TOKEN/GHL_LOCATION no ú
é   zBearer z
2021-07-28zapplication/json)ÚAuthorizationÚVersionzContent-TypeT)ÚparentsÚexist_okÚreceiver_ghl_px3Fi    é   r   )ÚmaxBytesÚbackupCountr   z%%(asctime)s %(levelname)s %(message)sz%Y-%m-%d %H:%M:%Sc            	     ó  — t        j                  «       } t        dddt        g d¢«      dd¬«      }t	        |dd¬	«      }| j                  d
|«       | j                  d|«       | j                  j                  t        «       | S )Nr5   g      ð?)é­  iô  éö  i÷  iø  )ÚGETÚPUTÚPOSTTF)ÚtotalÚbackoff_factorÚstatus_forcelistÚallowed_methodsÚrespect_retry_after_headerÚraise_on_statusé   )Úmax_retriesÚpool_connectionsÚpool_maxsizezhttps://zhttp://)	ÚrequestsÚSessionr   Ú	frozensetr   ÚmountÚheadersÚupdateÚHEADERS)ÚsÚretryÚadapters      r(   Ú_build_sessionrR   p   su   € Ü×ÑÓ€AÜØØØ2Ü!Ò"8Ó9Ø#'Øô€Eô  e¸bÈrÔR€GØ‡G�GˆJ˜Ô Ø‡G�GˆI�wÔØ‡I�I×Ñ”WÔØ€Hó    zdeque[float]Ú
_rate_hitsc                 ó\  — t        j                  «       } t        5  t        rA| t        d   z
  t        kD  r.t        j                  «        t        r| t        d   z
  t        kD  rŒ.t        t        «      t        k\  r
	 d d d «       yt        j                  | «       	 d d d «       y# 1 sw Y   y xY w)Nr   FT)	ÚtimeÚ	monotonicÚ
_rate_lockrT   ÚRATE_LIMIT_WINDOWÚpopleftÚlenÚRATE_LIMIT_MAXÚappend)Únows    r(   Ú_within_rate_limitr_   Œ   sˆ   € Ü
�.‰.Ó
€CÜ	ñ Ý˜S¤:¨a¡=Ñ0Ô3DÒDÜ×ÑÔ õ ˜S¤:¨a¡=Ñ0Ô3DÓDäŒz‹?œnÒ,Ø÷	ð ô
 	×Ñ˜#ÔØ÷÷ ñ ús   ›AB"Á#B"ÂB"Â"B+c                ór  — t         syt        j                  j                  d«      xs# t        j                  j                  d«      xs dj	                  «       }|syt        j                  t         j                  d«      | t        j                  «      j                  «       }t        j                  ||«      S )u˜   
    Se GHL_WEBHOOK_SECRET estiver setado, exige header X-Wh-Signature
    (HMAC-SHA256 hex). Se nÃ£o estiver, aceita tudo (comportamento default).
    TzX-Wh-SignaturezX-Ghl-Signaturer+   Fr   )r-   r   rL   Úgetr   ÚhmacÚnewÚencodeÚhashlibÚsha256Ú	hexdigestÚcompare_digest)Ú
body_bytesÚprovidedÚexpecteds      r(   Ú_valid_signaturerl   ›   s”   € õ
 Øä�‰×ÑÐ,Ó-ò 	Ü�?‰?×ÑÐ0Ó1ò	àß�eƒgð	 ñ
 ØÜ�x‰xÜ×!Ñ! 'Ó*¨J¼¿¹óç�iƒkð ô ×Ñ˜x¨Ó2Ð2rS   c           
     óÔ  — 	 t         j                  t        › d| › d�d¬«      }|j                  dk(  rd	g fS |j                  d
k7  r/t        d| › d|j                  › d|j                  d d
 › �«       dg fS 	 |j                  «       j                  d«      xs g }|rd|fS d	|fS # t        j                  $ r}t        d| › d|›�«       dg fcY d }~S d }~ww xY w# t        $ r dg fcY S w xY w)Nz
/contacts/z/tasksé   )Útimeoutú[z] falha ao ler tarefas: Úfaili”  ÚemptyéÈ   z] tarefas HTTP ú - ÚtasksÚok)
ÚSESSIONra   ÚGHL_BASErH   ÚRequestExceptionÚlogÚstatus_codeÚtextÚjsonÚ
ValueError)Ú
contact_idr   ÚeÚtarefass       r(   Úbuscar_tarefas_contator‚   ³   s  € ðÜ�K‰Kœ8˜* J¨z¨l¸&ÐAÈ2ˆKÓNˆð 	‡}�}˜ÒØ˜ˆ}ÐØ‡}�}˜ÒÜˆa�
ˆ|˜?¨1¯=©=¨/¸¸Q¿V¹VÀDÀS¸\¸NÐKÔLØ˜ˆ|ÐðØ—&‘&“(—,‘,˜wÓ'Ò-¨2ˆñ ˆD¨'Ð2Ð2 ¨'Ð2Ð2øô ×$Ñ$ò Üˆa�
ˆ|Ð3°A°5Ð9Ô:Ø˜ˆ|Õûðûô ò Ø˜ˆ|Òðús/   ‚!B" Á5#C Â"CÂ5CÃ	CÃCÃC'Ã&C'c           
     ó   — t         | dddœ}	 t        j                  t        › d�|d¬«      }|j                  d
k7  r-t        d| › d|j                  › d|j                  d	d
 › �«       g S 	 |j                  «       j                  d«      xs g S # t        j
                  $ r}t        d| › d|›�«       g cY d	}~S d	}~ww xY w# t        $ r g cY S w xY w)z$Oportunidades abertas de um contato.r   r   )Úlocation_idr   ÚstatusÚlimitz/opportunities/searchrn   )Úparamsro   rp   z] falha ao buscar opps: Nrs   z] opps HTTP rt   Úopportunities)r,   rw   ra   rx   rH   ry   rz   r{   r|   r}   r~   )r   r‡   r   r€   s       r(   Úbuscar_oportunidades_contator‰   Å   sÜ   € ô $Ø ØØñ	€FðÜ�K‰Kœ8˜*Ð$9Ð:À6ÐSUˆKÓVˆð 	‡}�}˜ÒÜˆa�
ˆ|˜<¨¯© °c¸!¿&¹&ÀÀ#¸,¸ÐHÔIØˆ	ðØ�v‰v‹x�|‰|˜OÓ,Ò2°Ð2øô ×$Ñ$ò Üˆa�
ˆ|Ð3°A°5Ð9Ô:Ø�	ûðûô ò ØŠ	ðús/   �B Á)"B? ÂB<ÂB7Â1B<Â7B<Â?CÃCc                óÚ  — | D �cg c]  }|j                  d«      rŒ|‘Œ }}|syt        j                  t        j                  «      }|D ]‹  }|j                  d«      }|sŒ	 t        |t        t        f«      r)t        j                  |dz  t        j                  ¬«      }n.t        j                  t        |«      j                  dd«      «      }||k  r yŒ� y	c c}w # t        $ r Y ŒŸw xY w)
NÚ	completedu   ðŸŸ¡ÚdueDateiè  )ÚtzÚZz+00:00u   ðŸ”´u   ðŸŸ¢)ra   r   r^   r   ÚutcÚ
isinstanceÚintÚfloatÚfromtimestampÚfromisoformatÚstrÚreplaceÚ	Exception)r�   ÚtÚ	pendentesÚagoraÚdueÚdts         r(   Úcalcular_farolr�   Û   sÖ   € Ø#Ö>�q¨1¯5©5°Õ+=’Ð>€IÐ>ÙØÜ�L‰LœŸ™Ó&€EØò ˆØ�e‰e�IÓˆÙØð	Ü˜#¤¤U˜|Ô,Ü×+Ñ+¨C°$©J¼8¿<¹<ÔH‘ä×+Ñ+¬C°«H×,<Ñ,<¸SÀ(Ó,KÓL�Ø�EŠzÙð ðð ùò# ?øô ò 	Ùð	ús   …CœCÁ"A2CÃ	C*Ã)C*c           
     ó6  — dt         |dœgi}	 t        j                  t        › d| › �|d¬«      }|j                  d	v ry
t        d| › d|j                  › d|j                  d d › �«       y# t        j
                  $ r}t        d| › d|›�«       Y d }~yd }~ww xY w)NÚcustomFields)ÚidÚvaluez/opportunities/rn   )r}   ro   rp   z] falha ao atualizar: F)rs   éÉ   Tz] update HTTP rt   rs   )	ÚCAMPO_STATUS_IDrw   Úputrx   rH   ry   rz   r{   r|   )Úopp_idÚfarolÚbodyr   r€   s        r(   Úatualizar_oppr¨   ð   s§   € Ø¤O¸eÑDÐEÐF€DðÜ�K‰Kœ8˜* O°F°8Ð<À4ÐQSˆKÓTˆð 	‡}�}˜
Ñ"ØÜˆ!ˆFˆ8�> !§-¡- °°A·F±F¸4¸C°L°>ÐBÔCØøô ×$Ñ$ò Üˆa�ˆxÐ-¨a¨UÐ3Ô4Üûðús   Ž!A* Á*BÁ=BÂBc                óÈ  — t        | t        «      sy| j                  d«      | j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      | j                  d«      xs i j                  d«      g	}|D ]5  }t        |t        «      sŒ|j	                  «       sŒ%|j	                  «       c S  y)	u@   Aceita vÃ¡rios formatos usados pelo GHL Workflow / Webhook Task.NÚ	contactIdr   Úcontactr    ÚtaskÚdataÚ
customData)r�   Údictra   r•   r   )ÚpayloadÚ
candidatosÚcs      r(   Ú_extract_contact_idr³   ý   s(  € ä�gœtÔ$Øð 	�‰�KÓ Ø�‰�LÓ!Ø	�‰�YÓ	Ò	% 2×*Ñ*¨4Ó0Ø	�‰�VÓ	Ò	" ×'Ñ'¨Ó4Ø	�‰�VÓ	Ò	" ×'Ñ'¨Ó5Ø	�‰�VÓ	Ò	" ×'Ñ'¨Ó4Ø	�‰�VÓ	Ò	" ×'Ñ'¨Ó5Ø	�‰�\Ó	"Ò	( b×-Ñ-¨kÓ:Ø	�‰�\Ó	"Ò	( b×-Ñ-¨lÓ;ð
€Jð ò ˆÜ�aœÕ !§'¡'¥)Ø—7‘7“9Òðð rS   z/healthc                 óB   — t        ddt        j                  «       ¬«      S )NTzreceiver-ghl-px3)rv   ÚserviceÚts)r
   rV   © rS   r(   Úhealthr¸     s   € ä�dÐ$6¼4¿9¹9»;ÔGÐGrS   z/webhook/taskc                 ó¨  — t        «       st        dd¬«      dfS t        j                  d¬«      } t	        | «      s5t        dt        t        j                  «      ›d�«       t        dd	¬«      d
fS 	 | r$t        j                  | j                  d«      «      ni }t        |«      }|s3t        dt        j                  |«      d d › �«       t        dd¬«      dfS t        d|› d�«       t        |«      \  }}|dk(  rt        dd¬«      dfS t        |«      }t!        |«      }|s t        d|› d|› �«       t        d||d¬«      S d\  }}|D ]-  }	|	j#                  d«      }
|
sŒt%        |
|«      r|dz  }Œ)|dz  }Œ/ t        d|› d|› dt'        |«      › d|› d|› �
«       t        d||t'        |«      ||¬«      S # t        $ r t        dd¬«      dfcY S w xY w) NFÚrate_limited)rv   Úerrorr9   T)Úcacheu   assinatura invÃ¡lida (headers=ú)Úinvalid_signaturei‘  r   Úinvalid_jsoni�  zpayload sem contactId: Úmissing_contact_idrp   z] webhook recebidorq   Úfail_read_tasksr:   z&] nenhuma opp aberta; farol calculado=r   )rv   rª   r¦   Úopps)r   r   r    é   z] farol=z opps=z atualizados=z falhas=)rv   rª   r¦   rÂ   ÚatualizadosÚfalhas)r_   r
   r   Úget_datarl   rz   r¯   rL   r}   ÚloadsÚdecoder~   r³   Údumpsr‚   r�   r‰   ra   r¨   r[   )r#   r°   r   r…   r�   r¦   rÂ   rÄ   rÅ   Úoppr¥   s              r(   Úwebhook_taskrË   $  sü  € äÔÜ˜% ~Ô6¸Ð;Ð;ä
×
Ñ
 Ô
&€CÜ˜CÔ ÜÐ,¬T´'·/±/Ó-BÐ,EÀQÐGÔHÜ˜%Ð':Ô;¸SÐ@Ð@ð<Ù58”$—*‘*˜SŸZ™Z¨Ó0Ô1¸bˆô % WÓ-€JÙÜÐ%¤d§j¡j°Ó&9¸$¸3Ð&?Ð%@ÐAÔBÜ˜%Ð';Ô<¸cÐAÐAäˆ!ˆJˆ<Ð)Ð*Ô+ä,¨ZÓ8�O€FˆGØ�ÒÜ˜%Ð'8Ô9¸3Ð>Ð>ä˜7Ó#€EÜ'¨
Ó3€DÙÜˆa�
ˆ|ÐAÀ%ÀÐIÔJÜ˜$¨*¸EÈÔJÐJàÑ€K�Øò ˆØ—‘˜“ˆÙØÜ˜ Ô'Ø˜1Ñ‰Kà�a‰K‰Fðô ˆ!ˆJˆ<�x ˜w f¬S°«Y¨K°}À[ÀMÐQYÐZ`ÐYaÐbÔcÜØØØÜ�‹YØØôð øô? ò <Ü˜% ~Ô6¸Ð;Ò;ð<ús   Á1(F6 Æ6GÇGÚ__main__ÚPORTÚ8945ÚHOSTz0.0.0.0zreceiver-ghl-px3 subindo em ú:)ÚhostÚport)r   r•   ÚreturnÚNone)rÓ   zrequests.Session)rÓ   Úbool)ri   ÚbytesrÓ   rÕ   )r   r•   rÓ   ztuple[str, list[dict]])r   r•   rÓ   ú
list[dict])r�   r×   rÓ   r•   )r¥   r•   r¦   r•   rÓ   rÕ   )r°   r¯   rÓ   z
str | None)TÚ__doc__Ú
__future__r   re   rb   r}   Úloggingr   ÚsysÚ	threadingrV   Úcollectionsr   r   r   Úlogging.handlersr   Úpathlibr   rH   Úflaskr	   r
   r   Úrequests.adaptersr   Úurllib3.util.retryr   ÚENV_FILEÚLOG_PATHrx   r£   r\   rY   r)   Úgetenvr   r*   r,   r-   ÚstderrÚwriteÚexitrN   ÚparentÚmkdirÚ	getLoggerÚloggerÚsetLevelÚINFOÚ	propagateÚhandlersÚhandlerÚsetFormatterÚ	FormatterÚ
addHandlerÚStreamHandlerÚstdoutÚstreamÚinforz   rR   rw   rT   Ú__annotations__ÚLockrX   r_   rl   r‚   r‰   r�   r¨   r³   Ú__name__Úappra   r¸   ÚpostrË   r‘   rÒ   rÑ   Úrunr·   rS   r(   ú<module>rÿ      së  ðòõ$ #ã Û Û Û Û 	Û 
Û Û Ý ß 'Ý 0Ý ã ß )Ñ )Ý )Ý $ð ,€Ø/€à1€Ø(€à€ØÐ ó	Rñ 
ˆ(Ô àˆB�I‰I�k 2Ó&×,Ñ,Ó.€	Øˆr�y‰y˜¨Ó,×2Ñ2Ó4€Ø�R—Y‘YÐ3°RÓ8×>Ñ>Ó@Ð á™Ø‡J�J×ÑÐ@ÀÀ
È"ÐMÔNØ€C‡H�HˆQ„Kð ˜y˜kÐ*ØØ&ñ€ñ €Xƒ× Ñ × Ñ  D°4Ð Ô 8à	ˆ×	Ñ	Ð-Ó	.€Ø ‡��—‘Ô Ø€Ô Ø‡‚Ù!ØÐ+¸ÀWô€Gð ×ÑØˆ×ÑÐAÐCVÓWôð ×Ñ�gÔØ"ˆW×"Ñ" 3§:¡:Ó.€FØ
×ÑØˆ×ÑÐAÐCVÓWôð ×Ñ�fÔà‡k�k€óñ" Ó
€ñ !›7€
ˆLÓ "ØˆY�^‰^Ó€
óó3ó03ó$ó,ó*
óñ> ˆHƒo€ð ‡�ˆÓñHó ðHð ‡�ˆ/Óñ1ó ð1ðh ˆzÒÙˆyˆr�y‰y˜ Ó(Ó)€DØˆ2�9‰9�V˜YÓ'€DÙÐ
& t f¨A¨d¨VÐ4Ô5Ø‡G�G�˜D€GÕ!ð	 rS   