Ë
    îì½jÉ  ã                   óê  — d 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 ej                  j                  ej                  j                  e«      «      Zej                  j#                  ed«      ZdZdZdZej,                  j/                  dd	«      Z eej,                  j/                  d
d«      «      Zej,                  j/                  dd«      dk(  ZdZd\  ZZi Z ej@                  «       Z!d„ Z" e"ed«      Z# e"ed«      xs	  e"ed«      Z$ e"ed«      Z%d„ Z&d„ Z'd„ Z( G d„ de
«      Z)e*dk(  r> ejV                  e«        eeefe)«      Z, e-deeefz  d¬«       e,j]                  «        yy)a/  DLRI Lead Engine API.

- POST /lead   : public lead capture (landing page). Appends to leads.jsonl,
                 fires a Telegram alert, best-effort creates a person in Twenty.
- GET  /health : liveness
- everything else : static files served from this directory (landing.html, privacy.html, terms.html)

Design notes:
- Public endpoint (browsers post it), so it is protected with a honeypot field,
  a per-IP rate limit, and a payload size cap -- NOT with the dashboard token.
- Secrets loaded from ~/.openclaw/credentials/*.env (0600), never hardcoded.
é    N)ÚSimpleHTTPRequestHandlerÚThreadingHTTPServerzleads.jsonlz-/home/openclaw/.openclaw/credentials/dlri.envz3/home/openclaw/.openclaw/credentials/bot-tokens.envz4/home/openclaw/.openclaw/credentials/twenty-dlri.envÚDLRI_LEAD_BINDz0.0.0.0ÚDLRI_LEAD_PORTÚ9096ÚTWENTY_ENABLEÚ0Ú1i    )é<   é   c                 ó  — 	 t        | «      D ]f  }|j                  «       }|sŒ|j                  d«      rŒ(d|v sŒ-|j                  dd«      \  }}|j                  «       |k(  sŒV|j                  «       c S  	 y # t        $ r Y y w xY w)Nú#ú=é   )ÚopenÚstripÚ
startswithÚsplitÚ	Exception)ÚpathÚkeyÚlineÚkÚvs        ú!insurance/lead-engine/lead-api.pyÚ_credr   !   sƒ   € ðÜ˜“Jò 	%ˆDØ—:‘:“<ˆDÚ˜DŸO™O¨CÕ0°S¸D²[Ø—z‘z # qÓ)‘��1Ø—7‘7“9 Ó#ØŸ7™7›9Ò$ñ	%ð øô ò ØØðús,   ‚ A8 £A8 µA8 º(A8 Á#A8 Á5A8 Á8	BÂBÚMRPABLO_BOT_TOKENÚDLRI_LEADS_CHATÚDLRI_TELEGRAM_CHATÚDLRI_LEADS_THREADc                 ót  — t         rt        syt        | ddœ}t        r	t        |d<   	 t        j                  j                  |«      j                  «       }t        j                  j                  t        j                  j                  dt         z  |¬«      d¬«      j                  «        y	# t        $ r Y yw xY w)
NFÚtrue)Úchat_idÚtextÚdisable_web_page_previewÚmessage_thread_idz*https://api.telegram.org/bot%s/sendMessage)Údataé
   ©ÚtimeoutT)ÚTELEGRAM_TOKENÚTELEGRAM_CHATÚTELEGRAM_THREADÚurllibÚparseÚ	urlencodeÚencodeÚrequestÚurlopenÚRequestÚreadr   )r$   Úpayloadr'   s      r   Úsend_telegramr7   4   s¤   € Ý�}ØÜ'°Ø+1ñ3€GåÜ'6ˆÐ#Ñ$ðÜ�|‰|×%Ñ% gÓ.×5Ñ5Ó7ˆÜ�‰×ÑœvŸ~™~×5Ñ5Ø8¼>ÑIÐPTð  6ó  VØ_að 	ó 	cßcgÑcgÔciØøÜò Ùðús   ¨BB+ Â+	B7Â6B7c                 ó¬  — t         syt        t        d«      }t        t        d«      }|r|sy| j                  d«      xs dj	                  «       }|r|d   nddj                  |d	d «      xs d
}}t        j                  ||dœd| j                  dd«      i| j                  dd«      ddœdœ«      j                  «       }t        j                  j                  |j                  d«      dz   |dd|z   dœ¬«      }	 t        j                  j                  |d¬«      j                  «       j                  «       dd S # t         $ r}d|z  cY d}~S d}~ww xY w)zFBest-effort. Requires TWENTY_ENABLE=1 and a matching workspace schema.NÚTWENTY_API_URLÚTWENTY_API_KEYÚnameÚ r   ÚLeadú r   ú-)Ú	firstNameÚlastNameÚprimaryEmailÚemailÚphoneÚUS)ÚprimaryPhoneNumberÚprimaryPhoneCountryCode)r;   ÚemailsÚphonesú/z/rest/peopleúapplication/jsonzBearer )úContent-TypeÚAuthorization)r'   Úheadersr(   r)   éÈ   zerr: %s)r   r   ÚCRED_TWENTYÚgetr   ÚjoinÚjsonÚdumpsr1   r.   r2   r4   Úrstripr3   r5   Údecoder   )	ÚpÚbaser   ÚpartsÚfirstÚlastÚbodyÚreqÚes	            r   Úcreate_twenty_personr_   D   sH  € åØÜ”Ð.Ó/€DÜ
”Ð-Ó
.€CÙ‘SØØ�U‰U�6‹]Ò ˜b×'Ñ'Ó)€EÙ$�5˜’8¨&°C·H±H¸UÀ1À2¸YÓ4GÒ4NÈ3ˆ4€EÜ�:‰:Ø#°Ñ6Ø! 1§5¡5¨°"Ó#5Ð6Ø)*¯©¨w¸Ó);ÐX\Ñ]ñó ÷ �vƒxð	 	ô
 �.‰.×
 Ñ
  §¡¨SÓ!1°NÑ!BÈØ:LØ;DÀs¹?ñ*Lð !ó M€CðÜ�~‰~×%Ñ% c°2Ð%Ó6×;Ñ;Ó=×DÑDÓFÀtÈÐLÐLøÜò Ø˜1‰}Õûðús   Ã;?D; Ä;	EÅEÅEÅEc                 ó$  — t        j                   «       }t        5  t        j                  | g «      D �cg c]  }||z
  t        k  sŒ|‘Œ }}|j                  |«       |t        | <   t        |«      t        k  cd d d «       S c c}w # 1 sw Y   y xY w©N)ÚtimeÚ_lockÚ_hitsrQ   ÚRATE_WINDOWÚappendÚlenÚRATE_MAX)ÚipÚnowÚtÚarrs       r   Úallowedrm   \   sv   € Ü
�)‰)‹+€CÜ	ñ $ÜŸ)™) B¨Ó+ÖE�Q¨s°Q©w¼Ó/DŠqÐEˆÐEØ�
‰
�3ŒØŒˆb‰	Ü�3‹xœ8Ñ#÷	$ñ $ùÚE÷$ð $ús"   ›B´BÁBÁ
-BÂBÂBc                   ó<   ‡ — e Zd Zd„ Zd„ Zd„ Zd„ Zˆ fd„Zd„ Zˆ xZ	S )ÚHandlerc                  ó   — y ra   © )ÚselfÚas     r   Úlog_messagezHandler.log_messagef   s   € Øó    c                 óR  — t        j                  |«      j                  «       }| j                  |«       | j	                  dd«       | j	                  dd«       | j	                  dt        t        |«      «      «       | j                  «        | j                  j                  |«       y )NrL   rK   úAccess-Control-Allow-OriginÚ*úContent-Length)
rS   rT   r1   Úsend_responseÚsend_headerÚstrrg   Úend_headersÚwfileÚwrite)rr   ÚcodeÚobjÚbs       r   Ú_jsonzHandler._jsoni   s   € Ü�J‰J�s‹O×"Ñ"Ó$ˆØ×Ñ˜4Ô Ø×Ñ˜Ð);Ô<Ø×ÑÐ6¸Ô<Ø×ÑÐ)¬3¬s°1«v«;Ô7Ø×ÑÔØ�
‰
×Ñ˜Õru   c                 ó²   — | j                  d«       | j                  dd«       | j                  dd«       | j                  dd«       | j                  «        y )NéÌ   rw   rx   zAccess-Control-Allow-HeadersrL   zAccess-Control-Allow-MethodszPOST, OPTIONS)rz   r{   r}   )rr   s    r   Ú
do_OPTIONSzHandler.do_OPTIONSr   sO   € Ø×Ñ˜3ÔØ×ÑÐ6¸Ô<Ø×ÑÐ7¸ÔHØ×ÑÐ7¸ÔIØ×ÑÕru   c                 óz  — | j                   d   }t        |«      s| j                  dddi«      S t        | j                  j                  dd«      xs d«      }|t        kD  r| j                  dddi«      S 	 t        j                  | j                  j                  |«      xs d«      }|t        j                  j                  t        j                  j                  «      j!                  «       |j                  d	«      |j                  d
«      |j                  d«      |j                  d«      |j                  d«      dœ}t"        5  t%        t&        j(                  j+                  t,        d«      d«      5 }|j/                  t        j0                  |«      dz   «       ddd«       ddd«       | j                  j                  d«      dk7  r4t3        d|d	   xs d›d|d   xs d›d|d
   xs d›d|d   xs d›d�	«       | j                  dddi«      S # t        $ r i }Y �Œyw xY w# 1 sw Y   Œ‰xY w# 1 sw Y   Œ�xY w)zBBrowser calls this when a /lead submission fails, so we can alert.r   é­  Úerrorú	slow downry   é�  útoo bigó   {}r;   rC   rD   ÚinterestÚsource)Ú_ipÚ_received_atr;   rC   rD   rŽ   r�   zlead_errors.jsonlrs   ú
NúX-Testr
   u&   âš ï¸� WEBSITE FORM SUBMISSION FAILED
ú?ú | r?   zS
Visitor saw a 'contact me directly' fallback. Check the lead endpoint + follow up.rO   ÚokT)Úclient_addressrm   rƒ   ÚintrN   rQ   ÚMAX_BODYrS   ÚloadsÚrfiler5   r   Údatetimerj   ÚtimezoneÚutcÚ	isoformatrc   r   Úosr   rR   ÚHEREr   rT   r7   )rr   ri   ÚnrW   ÚrecÚfs         r   Ú_lead_errorzHandler._lead_errory   sî  € à× Ñ  Ñ#ˆÜ�rŒ{Ø—:‘:˜c G¨[Ð#9Ó:Ð:Ü�—‘× Ñ Ð!1°1Ó5Ò:¸Ó;ˆØŒxŠ<Ø—:‘:˜c G¨YÐ#7Ó8Ð8ð	Ü—
‘
˜4Ÿ:™:Ÿ?™?¨1Ó-Ò6°Ó7ˆAð ¬(×*;Ñ*;×*?Ñ*?Ä×@QÑ@Q×@UÑ@UÓ*V×*`Ñ*`Ó*bØ—u‘u˜V“}¨q¯u©u°W«~ÈÏÉÈgËØŸ5™5 Ó,¸¿¹¸h»ñIˆô ñ 	0Ü”b—g‘g—l‘l¤4Ð)<Ó=¸sÓCð 0ÀqØ—‘œŸ
™
 3›¨$Ñ.Ô/÷0÷	0ð �<‰<×Ñ˜HÓ%¨Ò,ÝØ�F‘Ò"˜sÑ" C¨¡LÒ$7°CÑ$7¸¸W¹Ò9LÈÑ9LÈcÐR\ÉoÒNdÐadÑNdðfô gà�z‰z˜#  d˜|Ó,Ð,øô ò 	Ø‹Að	ú÷0ð 0ú÷	0ð 	0ús6   Á62H Å/H1Å5(H%ÆH1ÈH"È!H"È%H.	È*H1È1H:c           
      ó  •— | j                   j                  d«      d   dk(  r]| j                  ddt        j                   j	                  t
        «      r%t        d„ t        t
        «      D «       «      dœ«      S ddœ«      S t        ‰| �%  «       S )Nr”   r   z/healthrO   Tc              3   ó    K  — | ]  }d –— Œ y­w)r   Nrq   )Ú.0Ú_s     r   ú	<genexpr>z!Handler.do_GET.<locals>.<genexpr>’   s   è ø€ Ò<TÀ1¼QÑ<Tùs   ‚)r–   Úleads)
r   r   rƒ   r    ÚexistsÚLEADSÚsumr   ÚsuperÚdo_GET)rr   Ú	__class__s    €r   r°   zHandler.do_GET�   sp   ø€ Ø�9‰9�?‰?˜3Ó Ñ" iÒ/Ø—:‘:˜c¨$ÔXZ×X_ÑX_×XfÑXfÔglÔXm¼Ñ<TÌÌUËÔ<TÓ9TÑ#uÓvÐvÐstÑ#uÓvÐvÜ‰w‰~ÓÐru   c                 óŒ  — | j                   j                  d«      d   }|dk(  r| j                  «       S |dk7  r| j                  dddi«      S | j                  d   }t        |«      s| j                  ddd	i«      S t        | j                  j                  d
d«      xs d«      }|t        kD  r| j                  dddi«      S 	 t        j                  | j                  j                  |«      xs d«      }|j                  d«      r| j                  dddi«      S ||d<   t        j                  j!                  t        j"                  j$                  «      j'                  «       |d<   t(        5  t+        t,        d«      5 }|j/                  t        j0                  |«      dz   «       d d d «       d d d «       |j                  dd«      }|j                  dd«      ›d|j                  dd«      ›d|j                  dd«      ›d|j                  dd«      ›d|j                  d d!«      ›�	}| j                  j                  d"«      d#k7  r0t3        d$|›d|›d%|j                  d&d'«      ›d(�«       t5        |«       | j                  dddi«      S # t        $ r | j                  dddi«      cY S w xY w# 1 sw Y   �Œ	xY w# 1 sw Y   �ŒxY w))Nr”   r   z/lead-errorz/leadi”  r‰   z	not foundrˆ   rŠ   ry   r‹   rŒ   r�   i�  zbad jsonÚcompanyrO   r–   Tr�   r‘   rs   r’   r;   rD   r•   rC   z | age Úagez | zip ÚzipÚhas_coverager<   r“   r
   u   ðŸ“¥ NEW WEBSITE LEAD â€” z	
Source: r�   Úlandingu   
â†’ call within 5 min)r   r   r¥   rƒ   r—   rm   r˜   rN   rQ   r™   rS   rš   r›   r5   r   rœ   rj   r�   rž   rŸ   rc   r   r­   r   rT   r7   r_   )rr   r   ri   r¢   rW   r¤   ÚwhoÚnotes           r   Údo_POSTzHandler.do_POST•   sd  € Ø�y‰y�‰˜sÓ# AÑ&ˆØ�=Ò Ø×#Ñ#Ó%Ð%Ø�7Š?Ø—:‘:˜c G¨[Ð#9Ó:Ð:Ø× Ñ  Ñ#ˆÜ�rŒ{Ø—:‘:˜c G¨[Ð#9Ó:Ð:Ü�—‘× Ñ Ð!1°1Ó5Ò:¸Ó;ˆØŒxŠ<Ø—:‘:˜c G¨YÐ#7Ó8Ð8ð	:Ü—
‘
˜4Ÿ:™:Ÿ?™?¨1Ó-Ò6°Ó7ˆAð �5‰5�ÔØ—:‘:˜c D¨$ <Ó0Ð0àˆˆ%‰Ü$×-Ñ-×1Ñ1´(×2CÑ2C×2GÑ2GÓH×RÑRÓTˆˆ.ÑÜñ 	.Ü”e˜SÓ!ð . QØ—‘œŸ
™
 1›¨Ñ,Ô-÷.÷	.ð �e‰e�F˜CÓ ˆà�E‰E�'˜3Õ §¡ w°Õ!4°a·e±e¸EÀ3Õ6GÈÏÉÈuÐVYÕIZØ�E‰E�. "Ô%ð'ˆð �<‰<×Ñ˜HÓ%¨Ò,ÝÚ’T˜1Ÿ5™5 ¨9Õ5ð7ô 8ä  Ô#Ø�z‰z˜#  d˜|Ó,Ð,øô' ò 	:Ø—:‘:˜c G¨ZÐ#8Ó9Ò9ð	:ú÷.ñ .ú÷	.ñ 	.ús6   Ã2J	 Å,J9Å=(J,Æ%J9Ê	J)Ê(J)Ê,J6	Ê1J9Ê9K)
Ú__name__Ú
__module__Ú__qualname__rt   rƒ   r†   r¥   r°   rº   Ú__classcell__)r±   s   @r   ro   ro   e   s!   ø„ òòòò-ô. ö
!-ru   ro   Ú__main__z"DLRI lead API on %s:%d (twenty=%s)T)Úflush)/Ú__doc__r    rS   rb   Ú	threadingrœ   Úurllib.requestr.   Úurllib.parseÚhttp.serverr   r   r   ÚdirnameÚabspathÚ__file__r¡   rR   r­   Ú	CRED_DLRIÚ	CRED_BOTSrP   ÚenvironrQ   ÚBINDr˜   ÚPORTr   r™   re   rh   rd   ÚLockrc   r   r+   r,   r-   r7   r_   rm   ro   r»   ÚchdirÚsrvÚprintÚserve_foreverrq   ru   r   ú<module>rÓ      sq  ðñ÷ +× *Ó *ß #ß Eà	‡w�w‡��r—w‘w—‘ xÓ0Ó1€Ø
�‰�‰�T˜=Ó)€Ø;€	ØA€	ØD€Ø	‡z�z‡~�~Ð&¨	Ó2€Ù
ˆ2�:‰:�>‰>Ð*¨FÓ3Ó4€à—
‘
—‘˜°Ó4¸Ñ;€Ø€ØÑ €ˆXØ
€Øˆ	�‰Ó€ò
ñ �yÐ"5Ó6€á�iÐ!2Ó3Ò]±u¸YÐH\Ó7]€Ù˜	Ð#6Ó7€òò ò0$ôQ-Ð&ô Q-ðh ˆzÒØ€B‡H�HˆT„NÙ
˜t T˜l¨GÓ
4€CÙ	Ð
.°$¸¸mÐ1LÑ
LÐTXÕYØ×ÑÕð	 ru   