§
    øžyjÓU  ã                   óD  — 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mZ ddlmZmZ ddlmZmZmZmZ ddlmZmZ ddlmZ ddlmZ dd	„Z ej        e¦  «        Z d
Z!dZ"dZ#dZ$dZ%de&fd„Z' G d„ de¦  «        Z(de)de)fd„Z*ddddœd„Z+de&fd„Z,d„ Z-dd„Z.dS )uý  SMS (Twilio) platform adapter.

Connects to the Twilio REST API for outbound SMS and runs an aiohttp
webhook server to receive inbound messages.

Shares credentials with the optional telephony skill â€” same env vars:
  - TWILIO_ACCOUNT_SID
  - TWILIO_AUTH_TOKEN
  - TWILIO_PHONE_NUMBER  (E.164 from-number, e.g. +15551234567)

Gateway-specific env vars:
  - SMS_WEBHOOK_PORT     (default 8080)
  - SMS_WEBHOOK_HOST     (default 127.0.0.1)
  - SMS_WEBHOOK_URL      (public URL for Twilio signature validation â€” required)
  - SMS_INSECURE_NO_SIGNATURE  (true to disable signature validation â€” dev only)
  - SMS_ALLOWED_USERS    (comma-separated E.164 phone numbers)
  - SMS_ALLOW_ALL_USERS  (true/false)
  - SMS_HOME_CHANNEL     (phone number for cron delivery)
é    N)ÚAnyÚDictÚOptional)ÚPlatformÚPlatformConfig)ÚBasePlatformAdapterÚMessageEventÚMessageTypeÚ
SendResult)Úredact_phoneÚstrip_markdown)ÚUnscopedSecretError)Ú
get_secretc                 óz   — 	 t          | |¦  «        }n$# t          $ r t          j        | ¦  «        }Y nw xY w|�|n|S )a¹  Scope-aware credential read with the default-profile startup fallback.

    Secondary profiles construct their adapters under a profile secret
    scope -- the scope is authoritative and a scoped miss returns ``default``
    (no cross-profile borrow from ``os.environ``, which may hold another
    profile's value). The DEFAULT profile's adapter constructs and sends
    *unscoped* under multiplexing, where a bare ``get_secret`` would raise
    ``UnscopedSecretError`` and crash this path; there ``os.environ`` is that
    profile's own value, so fall back to it. Same pattern as the Slack
    ``SLACK_APP_TOKEN`` read (#59739) and
    ``gateway/platforms/whatsapp_common.py::_get_wsecret``.
    )Ú_scoped_get_secretÚ_UnscopedSecretErrorÚosÚgetenv)ÚnameÚdefaultÚvals      úC/home/ragecks/.hermes/hermes-agent/plugins/platforms/sms/adapter.pyÚ_get_scoped_secretr   +   sR   € ðÝ   wÑ/Ô/ˆˆøÝð ð ð ÝŒi˜‰oŒoˆˆˆðøøøà�/ˆ3ˆ3 wÐ.s   ‚ “4³4z*https://api.twilio.com/2010-04-01/Accountsi@  i�  z	127.0.0.1i   Úreturnc                  ó†   — 	 ddl } n# t          $ r Y dS w xY wt          t          d¦  «        ot          d¦  «        ¦  «        S )z0Check if SMS adapter dependencies are available.r   NFÚTWILIO_ACCOUNT_SIDÚTWILIO_AUTH_TOKEN)ÚaiohttpÚImportErrorÚboolr   )r   s    r   Úcheck_sms_requirementsr!   H   s[   € ðØˆˆˆˆøÝð ð ð ØˆuˆuðøøøåÕ"Ð#7Ñ8Ô8ÐdÕ=OÐPcÑ=dÔ=dÑeÔeÐes   ‚ ‡
”c                   ó.  ‡ — e Zd ZdZeZdefˆ fd„Zdefd„Z	ddœde
de
fd	„Zdd„Z	 	 ddededee         deeeef                  def
d„Zdedeeef         fd„Zdedefd„Zdededede
fd„Zdededede
fd„Zededed
z  fd„¦   «         Zdd„Zˆ xZS )Ú
SmsAdapterzÀ
    Twilio SMS <-> Hermes gateway adapter.

    Each inbound phone number gets its own Hermes session (multi-tenant).
    Replies are always sent from the configured TWILIO_PHONE_NUMBER.
    Úconfigc                 ó  •— t          ¦   «                              |t          j        ¦  «         t	          dd¦  «        | _        t	          dd¦  «        | _        t          j        dd¦  «        | _	        t          t          j        dt          t          ¦  «        ¦  «        ¦  «        | _        t          j        dt          ¦  «        | _        t          j        dd¦  «                             ¦   «         | _        d | _        d | _        d S )Nr   Ú r   ÚTWILIO_PHONE_NUMBERÚSMS_WEBHOOK_PORTÚSMS_WEBHOOK_HOSTÚSMS_WEBHOOK_URL)ÚsuperÚ__init__r   ÚSMSr   Ú_account_sidÚ_auth_tokenr   r   Ú_from_numberÚintÚstrÚDEFAULT_WEBHOOK_PORTÚ_webhook_portÚDEFAULT_WEBHOOK_HOSTÚ_webhook_hostÚstripÚ_webhook_urlÚ_runnerÚ_http_session)Úselfr$   Ú	__class__s     €r   r,   zSmsAdapter.__init__[   sÓ   ø€ Ý‰Œ×Ò˜¥¤Ñ.Ô.Ð.Ý!3Ð4HÈ"Ñ!MÔ!MˆÔÝ 2Ð3FÈÑ KÔ KˆÔÝ!#¤Ð+@À"Ñ!EÔ!EˆÔÝ"%ÝŒIÐ(­#Õ.BÑ*CÔ*CÑDÔDñ#
ô #
ˆÔõ #%¤)Ð,>Õ@TÑ"UÔ"UˆÔÝ!#¤Ð+<¸bÑ!AÔ!A×!GÒ!GÑ!IÔ!IˆÔØˆŒØ@DˆÔÐÐó    r   c                 ó¢   — | j         › d| j        › �}t          j        |                     d¦  «        ¦  «                             d¦  «        }d|› �S )z.Build HTTP Basic auth header value for Twilio.ú:ÚasciiúBasic )r.   r/   Úbase64Ú	b64encodeÚencodeÚdecode)r;   ÚcredsÚencodeds      r   Ú_basic_auth_headerzSmsAdapter._basic_auth_headerh   sS   € àÔ$Ð9Ð9 tÔ'7Ð9Ð9ˆÝÔ" 5§<¢<°Ñ#8Ô#8Ñ9Ô9×@Ò@ÀÑIÔIˆØ!˜Ð!Ð!Ð!r=   F)Úis_reconnectrI   c             ƒ   óD  ‡K  — dd l }ddl mŠ | j        s6d}t                               |¦  «         |                      d|d¬¦  «         dS t          j        dd¦  «                             ¦   «         d	k    }| j	        s8|s6d
}t                               |¦  «         |                      d|d¬¦  «         dS |r'| j	        s t           
                    d| j        ¦  «         ‰                     t          ¬¦  «        }|j                             d| j        ¦  «         |j                             dˆfd„¦  «         ‰                     |¦  «        | _        | j                             ¦   «         ƒ d {V —† ‰                     | j        | j        | j        ¦  «        }|                     ¦   «         ƒ d {V —† |                     |                     d¬¦  «        d¬¦  «        | _        d| _        t                               d| j        | j        t;          | j        ¦  «        ¦  «         dS )Nr   ©Úwebu9   [sms] TWILIO_PHONE_NUMBER not set â€” cannot send repliesÚsms_missing_phone_numberF)Ú	retryableÚSMS_INSECURE_NO_SIGNATUREr&   Útruea+  [sms] Refusing to start: SMS_WEBHOOK_URL is required for Twilio signature validation. Set it to the public URL configured in your Twilio console (e.g. https://example.com/webhooks/twilio). For local development without validation, set SMS_INSECURE_NO_SIGNATURE=true (NOT recommended for production).Úsms_missing_webhook_urlu§   [sms] SMS_INSECURE_NO_SIGNATURE=true â€” Twilio signature validation is DISABLED. Any client that can reach port %d can inject messages. Do NOT use this in production.)Úclient_max_sizez/webhooks/twilioz/healthc                 ó0   •— ‰                      d¬¦  «        S )NÚok)Útext)ÚResponse)Ú_rL   s    €r   ú<lambda>z$SmsAdapter.connect.<locals>.<lambda>—   s   ø€ °·²À$°Ñ0GÔ0G€ r=   é   ©ÚtotalT©ÚtimeoutÚ	trust_envz8[sms] Twilio webhook server listening on %s:%d, from: %s)r   rL   r0   ÚloggerÚerrorÚ_set_fatal_errorr   r   Úlowerr8   Úwarningr4   ÚApplicationÚ_TWILIO_WEBHOOK_MAX_BODY_BYTESÚrouterÚadd_postÚ_handle_webhookÚadd_getÚ	AppRunnerr9   ÚsetupÚTCPSiter6   ÚstartÚClientSessionÚClientTimeoutr:   Ú_runningÚinfor   )r;   rI   r   ÚmsgÚinsecure_no_sigÚappÚsiterL   s          @r   ÚconnectzSmsAdapter.connectr   sQ  øè è € ØˆˆˆØÐÐÐÐÐàÔ ð 	ØMˆCÝ�LŠL˜ÑÔÐØ×!Ò!Ð"<¸cÈUÐ!ÑSÔSÐSØ�5åœ)Ð$?ÀÑDÔD×JÒJÑLÔLÐPVÒVˆàÔ ð 
	¨ð 
	ðSð õ �LŠL˜ÑÔÐØ×!Ò!Ð";¸SÈEÐ!ÑRÔRÐRØ�5àð 	 4Ô#4ð 	Ý�NŠNð1ð Ô"ñ	ô ð ð �oŠoÕ.LˆoÑMÔMˆØŒ
×ÒÐ.°Ô0DÑEÔEÐEØŒ
×Ò˜9Ð&GÐ&GÐ&GÐ&GÑHÔHÐHà—}’} SÑ)Ô)ˆŒØŒl× Ò Ñ"Ô"Ð"Ð"Ð"Ð"Ð"Ð"Ð"Ø�{Š{˜4œ<¨Ô);¸TÔ=OÑPÔPˆØ�jŠj‰lŒlÐÐÐÐÐÐÐØ$×2Ò2Ø×)Ò)°Ð)Ñ3Ô3Øð 3ñ 
ô 
ˆÔð ˆŒå�ŠØFØÔØÔÝ˜Ô*Ñ+Ô+ñ		
ô 	
ð 	
ð ˆtr=   Nc              ƒ   ó   K  — | j         r&| j                              ¦   «         ƒ d {V —† d | _         | j        r&| j                             ¦   «         ƒ d {V —† d | _        d| _        t
                               d¦  «         d S )NFz[sms] Disconnected)r:   Úcloser9   Úcleanuprp   r_   rq   )r;   s    r   Ú
disconnectzSmsAdapter.disconnect«   s—   è è € ØÔð 	&ØÔ$×*Ò*Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ø!%ˆDÔØŒ<ð 	 Ø”,×&Ò&Ñ(Ô(Ð(Ð(Ð(Ð(Ð(Ð(Ð(ØˆDŒLØˆŒÝ�ŠÐ(Ñ)Ô)Ð)Ð)Ð)r=   Úchat_idÚcontentÚreply_toÚmetadatac           	   ƒ   ó  K  — dd l }|                      |¦  «        }|                      |¦  «        }t          d¬¦  «        }t          › d| j        › d�}	d|                      ¦   «         i}
| j        p*|                     | 	                    d¬¦  «        d¬	¦  «        }	 |D �]}| 
                    ¦   «         }|                     d
| j        ¦  «         |                     d|¦  «         |                     d|¦  «         	 |                     |	||
¬¦  «        4 ƒd {V —†}|                     ¦   «         ƒ d {V —†}|j        dk    r§|                     dt#          |¦  «        ¦  «        }t$                               dt)          |¦  «        |j        |¦  «         t          dd|j        › d|› �¬¦  «        cd d d ¦  «        ƒd {V —† c | j        s|r|                     ¦   «         ƒ d {V —† S S S |                     dd¦  «        }t          d|¬¦  «        }d d d ¦  «        ƒd {V —† n# 1 ƒd {V —†swxY w Y   �Œ–# t,          $ rx}t$                               dt)          |¦  «        |¦  «         t          dt#          |¦  «        ¬¦  «        cY d }~c | j        s|r|                     ¦   «         ƒ d {V —† S S S d }~ww xY w	 | j        s|r|                     ¦   «         ƒ d {V —† n*# | j        s|r|                     ¦   «         ƒ d {V —† w w w xY w|S )Nr   T)Úsuccessú/ú/Messages.jsonÚAuthorizationrY   rZ   r\   ÚFromÚToÚBody©ÚdataÚheadersé�  Úmessagez[sms] send failed to %s: %s %sFzTwilio z: )r€   r`   Úsidr&   )r€   Ú
message_idz[sms] send error to %s: %s)r   Úformat_messageÚtruncate_messager   ÚTWILIO_API_BASEr.   rH   r:   rn   ro   ÚFormDataÚ	add_fieldr0   ÚpostÚjsonÚstatusÚgetr2   r_   r`   r   rx   Ú	Exception)r;   r{   r|   r}   r~   r   Ú	formattedÚchunksÚlast_resultÚurlr‰   ÚsessionÚchunkÚ	form_dataÚrespÚbodyÚ	error_msgÚmsg_sidÚes                      r   ÚsendzSmsAdapter.sendµ   s§  è è € ð 	ˆˆˆà×'Ò'¨Ñ0Ô0ˆ	Ø×&Ò& yÑ1Ô1ˆÝ ¨Ð.Ñ.Ô.ˆå ÐDÐD 4Ô#4ÐDÐDÐDˆà˜T×4Ò4Ñ6Ô6ð
ˆð Ô$ð 
¨×(=Ò(=Ø×)Ò)°Ð)Ñ3Ô3Øð )>ñ )
ô )
ˆð	&Øð Cñ C�Ø#×,Ò,Ñ.Ô.�	Ø×#Ò# F¨DÔ,=Ñ>Ô>Ð>Ø×#Ò# D¨'Ñ2Ô2Ð2Ø×#Ò# F¨EÑ2Ô2Ð2ðCØ&Ÿ|š|¨C°iÈ˜|ÑQÔQð Sð Sð Sð Sð Sð Sð SÐUYØ%)§Y¢Y¡[¤[Ð0Ð0Ð0Ð0Ð0Ð0˜Øœ;¨#Ò-Ð-Ø(,¯ª°½CÀ¹I¼IÑ(FÔ(F˜IÝ"ŸLšLØ @Ý ,¨WÑ 5Ô 5Ø $¤Ø )ñ	ô ð õ $.Ø(-Ø&J°´Ð&JÐ&J¸yÐ&JÐ&Jð$ñ $ô $ðSð Sð Sð Sñ Sô Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð* Ô%ð &¨'ð &Ø—m’m‘o”oÐ%Ð%Ð%Ð%Ð%Ð%Ð%Ð%ð&ð &ð #'§(¢(¨5°"Ñ"5Ô"5˜Ý&0¸È'Ð&RÑ&RÔ&R˜ðSð Sð Sñ Sô Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Sð Søøøð Sð Sð Sð Sùøõ  !ð Cð Cð CÝ—L’LÐ!=½|ÈGÑ?TÔ?TÐVWÑXÔXÐXÝ%¨e½3¸q¹6¼6ÐBÑBÔBÐBÐBÐBÐBÐBÐBÐBð Ô%ð &¨'ð &Ø—m’m‘o”oÐ%Ð%Ð%Ð%Ð%Ð%Ð%Ð%ð&ð &øøøøðCøøøð/Cð8 Ô%ð &¨'ð &Ø—m’m‘o”oÐ%Ð%Ð%Ð%Ð%Ð%Ð%øøð Ô%ð &¨'ð &Ø—m’m‘o”oÐ%Ð%Ð%Ð%Ð%Ð%Ð%Ð%ð&ð &øøøð Ðsu   ÂA K Ã=H4ÄBH!Æ.H4Ç K Ç('H!ÈH4È!
H+	È+H4È.H+	È/H4È2K È4
J6È>AJ1ÊJ6ÊK Ê1J6Ê6K Ë'Lc              ƒ   ó   K  — |ddœS )NÚdm)r   Útype© )r;   r{   s     r   Úget_chat_infozSmsAdapter.get_chat_infoí   s   è è € Ø¨Ð.Ð.Ð.r=   c                 ó    — t          |¦  «        S )õ8   Strip markdown â€” SMS renders it as literal characters.)r   )r;   r|   s     r   rŽ   zSmsAdapter.format_messageô   s   € å˜gÑ&Ô&Ð&r=   r›   Úpost_paramsÚ	signaturec                 ó˜   — |                       |||¦  «        rdS |                      |¦  «        }|r|                       |||¦  «        rdS dS )a  Validate ``X-Twilio-Signature`` header (HMAC-SHA1, base64).

        Tries both with and without the default port for the URL scheme,
        since Twilio may sign with either variant.

        Algorithm: https://www.twilio.com/docs/usage/security#validating-requests
        TF)Ú_check_signatureÚ_port_variant_url)r;   r›   r¬   r­   Úvariants        r   Ú_validate_twilio_signaturez%SmsAdapter._validate_twilio_signatureü   sa   € ð × Ò   k°9Ñ=Ô=ð 	Ø�4à×(Ò(¨Ñ-Ô-ˆØð 	�t×,Ò,¨W°kÀ9ÑMÔMð 	Ø�4àˆur=   c                 óä  — |}t          |                     ¦   «         ¦  «        D ]}||||         z   z  }Œt          j        | j                             d¦  «        |                     d¦  «        t          j        ¦  «        }t          j	        | 
                    ¦   «         ¦  «                             d¦  «        }t          j        |                     ¦   «         |                     ¦   «         ¦  «        S )z.Compute and compare a single Twilio signature.úutf-8)ÚsortedÚkeysÚhmacÚnewr/   rD   ÚhashlibÚsha1rB   rC   ÚdigestrE   Úcompare_digest)r;   r›   r¬   r­   Údata_to_signÚkeyÚmacÚcomputeds           r   r¯   zSmsAdapter._check_signature  sÐ   € ð ˆÝ˜+×*Ò*Ñ,Ô,Ñ-Ô-ð 	3ð 	3ˆCØ˜C +¨cÔ"2Ñ2Ñ2ˆLˆLÝŒhØÔ×#Ò# GÑ,Ô,Ø×Ò Ñ(Ô(ÝŒLñ
ô 
ˆõ
 Ô# C§J¢J¡L¤LÑ1Ô1×8Ò8¸ÑAÔAˆõ Ô" 8§?¢?Ñ#4Ô#4°i×6FÒ6FÑ6HÔ6HÑIÔIÐIr=   c                 óÈ  — t           j                             | ¦  «        }dddœ}|                     |j        ¦  «        }|€dS |j        |k    rCt           j                             |j        |j        |j        |j	        |j
        |j        f¦  «        S |j        €J|j        › d|› �}t           j                             |j        ||j        |j	        |j
        |j        f¦  «        S dS )z°Return the URL with the default port toggled, or None.

        Only toggles default ports (443 for https, 80 for http).
        Non-standard ports are never modified.
        i»  éP   )ÚhttpsÚhttpNr?   )ÚurllibÚparseÚurlparser–   ÚschemeÚportÚ
urlunparseÚhostnameÚpathÚparamsÚqueryÚfragment)r›   ÚparsedÚdefault_portsÚdefault_portÚnetlocs        r   r°   zSmsAdapter._port_variant_url   sé   € õ ”×&Ò& sÑ+Ô+ˆØ"%¨rÐ2Ð2ˆØ$×(Ò(¨¬Ñ7Ô7ˆØÐØ�4àŒ;˜,Ò&Ð&å”<×*Ò*Ø” ¤°´Ø” ¤¨f¬oð?ñô ð ð Œ[Ð àœÐ8Ð8¨,Ð8Ð8ˆFÝ”<×*Ò*Ø” ¨¬Ø” ¤¨f¬oð?ñô ð ð ˆtr=   úaiohttp.web.Responsec              ƒ   ó   K  — ddl m} 	 |j        }|�#|t          k    r|                     ddd¬¦  «        S |                     ¦   «         ƒ d {V —†}t          |¦  «        t          k    r|                     ddd¬¦  «        S t          j         	                    | 
                    d¦  «        d¬	¦  «        }nJ# t          $ r=}t                               d
|¦  «         |                     ddd¬¦  «        cY d }~S d }~ww xY w| j        r»|j                             dd¦  «        }|s2t                               d¦  «         |                     ddd¬¦  «        S d„ |                     ¦   «         D ¦   «         }|                      | j        ||¦  «        s2t                               d¦  «         |                     ddd¬¦  «        S |                     ddg¦  «        d                              ¦   «         }	|                     ddg¦  «        d                              ¦   «         }
|                     ddg¦  «        d                              ¦   «         }|                     ddg¦  «        d                              ¦   «         }|	r|s|                     dd¬¦  «        S |	| j        k    r?t                               dt/          |	¦  «        ¦  «         |                     dd¬¦  «        S t                               dt/          |	¦  «        t/          |
¦  «        |d d…         ¦  «         |                      |	|	d|	|	¬¦  «        }t5          |t6          j        |||¬¦  «        }t;          j        |                      |¦  «        ¦  «        }| j          !                    |¦  «         | "                    | j         j#        ¦  «         |                     dd¬¦  «        S )Nr   rK   z;<?xml version="1.0" encoding="UTF-8"?><Response></Response>zapplication/xmli�  )rU   Úcontent_typer•   r´   T)Úkeep_blank_valuesz[sms] webhook parse error: %srŠ   zX-Twilio-Signaturer&   z1[sms] Rejected: missing X-Twilio-Signature headeri“  c                 ó*   — i | ]\  }}|¯||d          “ŒS )r   r¨   )Ú.0ÚkÚvs      r   ú
<dictcomp>z.SmsAdapter._handle_webhook.<locals>.<dictcomp>h  s'   € ÐAÐAÐA¡t q¨!¸qÐA˜1˜a œdÐAÐAÐAr=   z([sms] Rejected: invalid Twilio signaturer„   r…   r†   Ú
MessageSid)rU   rÖ   z&[sms] ignoring echo from own number %sz[sms] inbound from %s -> %s: %srÂ   r¦   )r{   Ú	chat_nameÚ	chat_typeÚuser_idÚ	user_name)rU   Úmessage_typeÚsourceÚraw_messager�   )$r   rL   Úcontent_lengthre   rV   ÚreadÚlenrÅ   rÆ   Úparse_qsrE   r—   r_   r`   r8   r‰   r–   rc   Úitemsr²   r7   r0   Údebugr   rq   Úbuild_sourcer	   r
   ÚTEXTÚasyncioÚcreate_taskÚhandle_messageÚ_background_tasksÚaddÚadd_done_callbackÚdiscard)r;   ÚrequestrL   rå   ÚrawÚformr£   Ú
twilio_sigÚflat_paramsÚfrom_numberÚ	to_numberrU   Úmessage_sidrã   ÚeventÚtasks                   r   rh   zSmsAdapter._handle_webhookB  sF  è è € ØÐÐÐÐÐð	Ø$Ô3ˆNØÐ)¨nÕ?]Ò.]Ð.]Ø—|’|ØVØ!2Øð $ñ ô ð ð
  Ÿš™œÐ&Ð&Ð&Ð&Ð&Ð&ˆCÝ�3‰xŒxÕ8Ò8Ð8Ø—|’|ØVØ!2Øð $ñ ô ð õ ”<×(Ò(¨¯ª°GÑ)<Ô)<ÐPTÐ(ÑUÔUˆDˆDøÝð 	ð 	ð 	Ý�LŠLÐ8¸!Ñ<Ô<Ð<Ø—<’<ØRØ.Øð  ñ ô ð ð ð ð ð ð øøøøð	øøøð Ôð 	Ø œ×,Ò,Ð-AÀ2ÑFÔFˆJØð Ý—’ÐRÑSÔSÐSØ—|’|ØVØ!2Øð $ñ ô ð ð
 BÐA¨t¯zªz©|¬|ÐAÑAÔAˆKØ×2Ò2ØÔ! ;°
ñô ð õ —’ÐIÑJÔJÐJØ—|’|ØVØ!2Øð $ñ ô ð ð —x’x ¨¨Ñ-Ô-¨qÔ1×7Ò7Ñ9Ô9ˆØ—X’X˜d R DÑ)Ô)¨1Ô-×3Ò3Ñ5Ô5ˆ	Ø—’˜ " Ñ&Ô&¨Ô*×0Ò0Ñ2Ô2ˆØ—x’x ¨r¨dÑ3Ô3°QÔ7×=Ò=Ñ?Ô?ˆàð 	 $ð 	Ø—<’<ØRØ.ð  ñ ô ð ð ˜$Ô+Ò+Ð+Ý�LŠLÐAÅ<ÐP[ÑC\ÔC\Ñ]Ô]Ð]Ø—<’<ØRØ.ð  ñ ô ð õ
 	�ŠØ-Ý˜Ñ%Ô%Ý˜Ñ#Ô#Ø��"�ŒIñ		
ô 	
ð 	
ð ×"Ò"ØØ!ØØØ!ð #ñ 
ô 
ˆõ ØÝ$Ô)ØØØ"ð
ñ 
ô 
ˆõ Ô" 4×#6Ò#6°uÑ#=Ô#=Ñ>Ô>ˆØÔ×"Ò" 4Ñ(Ô(Ð(Ø×Ò˜tÔ5Ô=Ñ>Ô>Ð>ð �|Š|ØNØ*ð ñ 
ô 
ð 	
s)   Š+B5 ¶A	B5 Â 4B5 Â5
C<Â?2C7Ã1C<Ã7C<©r   N)NN)r   rÔ   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚMAX_SMS_LENGTHÚMAX_MESSAGE_LENGTHr   r,   r2   rH   r    rv   rz   r   r   r   r   r¤   r©   rŽ   Údictr²   r¯   Ústaticmethodr°   rh   Ú__classcell__)r<   s   @r   r#   r#   Q   s,  ø€ € € € € ðð ð (ÐðE˜~ð Eð Eð Eð Eð Eð Eð" Cð "ð "ð "ð "ð 5:ð 7ð 7ð 7¨Tð 7¸dð 7ð 7ð 7ð 7ðr*ð *ð *ð *ð #'Ø-1ð6ð 6àð6ð ð6ð ˜3”-ð	6ð
 ˜4  S œ>Ô*ð6ð 
ð6ð 6ð 6ð 6ðp/¨3ð /°4¸¸S¸´>ð /ð /ð /ð /ð' cð '¨cð 'ð 'ð 'ð 'ðØðØ%)ðØ69ðà	ðð ð ð ð&JØðJØ%)ðJØ69ðJà	ðJð Jð Jð Jð" ð˜sð  s¨T¡zð ð ð ñ „\ððBd
ð d
ð d
ð d
ð d
ð d
ð d
ð d
r=   r#   r‹   c                 óš  — t                                dd| t           j        ¬¦  «        } t                                dd| t           j        ¬¦  «        } t                                dd| t           j        ¬¦  «        } t                                dd| t           j        ¬¦  «        } t                                dd| ¦  «        } t                                d	d| ¦  «        } t                                d
d| t           j        ¬¦  «        } t                                dd| ¦  «        } t                                dd| ¦  «        } |                      ¦   «         S )r«   z\*\*(.+?)\*\*z\1)Úflagsz	\*(.+?)\*z	__(.+?)__z_(.+?)_z```[a-z]*\n?r&   z`(.+?)`z
^#{1,6}\s+z\[([^\]]+)\]\([^\)]+\)z\n{3,}z

)ÚreÚsubÚDOTALLÚ	MULTILINEr7   )r‹   s    r   Ú_strip_markdown_for_smsr  µ  sí   € å�fŠfÐ% u¨g½R¼YˆfÑGÔG€GÝ�fŠf�\ 5¨'½¼ˆfÑCÔC€GÝ�fŠf�\ 5¨'½¼ˆfÑCÔC€GÝ�fŠf�Z ¨µr´yˆfÑAÔA€GÝ�fŠf�_ b¨'Ñ2Ô2€GÝ�fŠf�Z ¨Ñ0Ô0€GÝ�fŠf�] B¨µr´|ˆfÑDÔD€GÝ�fŠfÐ.°°wÑ?Ô?€GÝ�fŠf�Y ¨Ñ0Ô0€GØ�=Š=‰?Œ?Ðr=   F)Ú	thread_idÚmedia_filesÚforce_documentc          	   ƒ   óH  K  — t          | dd¦  «        pt          dd¦  «        }	 ddl}n# t          $ r ddicY S w xY wddl}t          dd¦  «        }	t          j        d	d¦  «        }
|	r|r|
sdd
iS t          |¦  «        }d„ }	 ddlm	}m
}  |¦   «         } ||¦  «        \  }}|	› d|› �} |j        |                     d¦  «        ¦  «                             d¦  «        }d|	› d�}dd|› �i} |j        d#d|                     d¬¦  «        i|¤Ž4 ƒd{V —†}|                     ¦   «         }|                     d|
¦  «         |                     d|¦  «         |                     d|¦  «          |j        |f||dœ|¤Ž4 ƒd{V —†}|                     ¦   «         ƒ d{V —†}|j        dk    r]|                     dt-          |¦  «        ¦  «        } |d|j        › d|› �¦  «        cddd¦  «        ƒd{V —† cddd¦  «        ƒd{V —† S dd||                     d d¦  «        d!œcddd¦  «        ƒd{V —† cddd¦  «        ƒd{V —† S # 1 ƒd{V —†swxY w Y   	 ddd¦  «        ƒd{V —† dS # 1 ƒd{V —†swxY w Y   dS # t.          $ r} |d"|› �¦  «        cY d}~S d}~ww xY w)$zŒOut-of-process SMS delivery via the Twilio REST API. Implements the
    standalone_sender_fn contract; replaces the legacy _send_sms helper.Úapi_keyNr   r&   r   r`   z/aiohttp not installed. Run: pip install aiohttpr   r'   zXSMS not configured (TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE_NUMBER required)c                 óN   — 	 ddl m}  || ¦  «        S # t          $ r d| icY S w xY w)Nr   )Ú_errorr`   )Útools.send_message_toolr  r—   )rU   Ú_es     r   Ú_redacted_errorz)_standalone_send.<locals>._redacted_errorÜ  sQ   € ð	#Ø<Ð<Ð<Ð<Ð<Ð<Ø�2�d‘8”8ˆOøÝð 	#ð 	#ð 	#Ø˜T�?Ð"Ð"Ð"ð	#øøøs   ‚ “$£$)Úresolve_proxy_urlÚproxy_kwargs_for_aiohttpr?   r@   z+https://api.twilio.com/2010-04-01/Accounts/r‚   rƒ   rA   r]   rY   rZ   r„   r…   r†   r‡   rŠ   r‹   zTwilio API error (z): TÚsmsrŒ   )r€   Úplatformr{   r�   zSMS send failed: r¨   )Úgetattrr   r   r   rB   r   r   r  Úgateway.platforms.baser  r  rC   rD   rE   rn   ro   r‘   r’   r“   r”   r•   r–   r2   r—   )Úpconfigr{   r‹   r  r  r  Ú
auth_tokenr   rB   Úaccount_sidrù   r  r  r  Ú_proxyÚ_sess_kwÚ_req_kwrF   rG   r›   r‰   rœ   rž   rŸ   r    r¡   r£   s                              r   Ú_standalone_sendr%  Ã  sž  è è € õ ˜ )¨TÑ2Ô2ÐaÕ6HÐI\Ð^`Ñ6aÔ6a€JðLØˆˆˆˆøÝð Lð Lð LØÐJÐKÐKÐKÐKðLøøøà€M€M€Må$Ð%9¸2Ñ>Ô>€KÝ”)Ð1°2Ñ6Ô6€KØð u˜jð u°ð uØÐsÐtÐtå% gÑ.Ô.€Gð#ð #ð #ð8ØVÐVÐVÐVÐVÐVÐVÐVØ"Ð"Ñ$Ô$ˆØ4Ð4°VÑ<Ô<Ñˆ�'ØÐ-Ð- Ð-Ð-ˆØ"�&Ô" 5§<¢<°Ñ#8Ô#8Ñ9Ô9×@Ò@ÀÑIÔIˆØW¸KÐWÐWÐWˆØ"Ð$6¨WÐ$6Ð$6Ð7ˆØ(�7Ô(Ð]Ð]°×1FÒ1FÈRÐ1FÑ1PÔ1PÐ]ÐT\Ð]Ð]ð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sÐahØ×(Ò(Ñ*Ô*ˆIØ×Ò ¨Ñ4Ô4Ð4Ø×Ò  gÑ.Ô.Ð.Ø×Ò ¨Ñ0Ô0Ð0Ø#�w”| CÐT¨iÀÐTÐTÈGÐTÐTð sð sð sð sð sð sð sÐX\Ø!ŸYšY™[œ[Ð(Ð(Ð(Ð(Ð(Ð(�Ø”; #Ò%Ð%Ø $§¢¨µC¸±I´IÑ >Ô >�IØ*˜?Ð+[ÀÄÐ+[Ð+[ÐPYÐ+[Ð+[Ñ\Ô\ð	sð sð sð sñ sô sð sð sð sð sð sð sð sð
	sð 
	sð 
	sð 
	sñ 
	sô 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð $(°UÀwÐ^b×^fÒ^fÐglÐnpÑ^qÔ^qÐrÐrðsð sð sð sñ sô sð sð sð sð sð sð sð sð
	sð 
	sð 
	sð 
	sñ 
	sô 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð
sð sð sð sð sð sð sð sð sð søøøð sð sð sð sð sð
	sð 
	sð 
	sñ 
	sô 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	sð 
	søøøð 
	sð 
	sð 
	sð 
	sð 
	sð 
	søõ ð 8ð 8ð 8ØˆÐ6°1Ð6Ð6Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7øøøøð8øøøs�   ¥* ª;º;ÂBI? ÄA.I,ÆAIÇ#I,Ç5I? ÈIÈ"I,È4I? É
I	ÉI,ÉI	ÉI,ÉI? É,
I6É6I? É9I6É:I? É?
J!Ê	JÊJ!ÊJ!c                 óz   — ddl m} t          |                     d¦  «        pd                     ¦   «         ¦  «        S )z“SMS is connected when Twilio credentials are present. Mirrors the legacy
    _PLATFORM_CONNECTED_CHECKERS[Platform.SMS] = bool(TWILIO_ACCOUNT_SID).r   Nr   r&   )Úhermes_cli.gatewayÚgatewayr    Úget_env_valuer7   )r$   Úgateway_mods     r   Ú_is_connectedr+  ú  sD   € ð -Ð,Ð,Ð,Ð,Ð,Ý�×*Ò*Ð+?Ñ@Ô@ÐFÀB×MÒMÑOÔOÑPÔPÐPr=   c                 ó    — t          | ¦  «        S )zAFactory wrapper that constructs SmsAdapter from a PlatformConfig.)r#   )r$   s    r   Ú_build_adapterr-    s   € å�fÑÔÐr=   c                 ó„   — |                       ddt          t          t          g d¢ddddt          t
          dd	d¬
¦  «         dS )u:   Plugin entry point â€” called by the Hermes plugin system.r  zSMS (Twilio))r   r   r'   zpip install aiohttpÚSMS_ALLOWED_USERSÚSMS_ALLOW_ALL_USERSÚSMS_HOME_CHANNELTu   ðŸ“±)r   ÚlabelÚadapter_factoryÚcheck_fnÚis_connectedÚrequired_envÚinstall_hintÚallowed_users_envÚallow_all_envÚcron_deliver_env_varÚstandalone_sender_fnÚmax_message_lengthÚpii_safeÚemojiÚallow_update_commandN)Úregister_platformr-  r!   r+  r%  r  )Úctxs    r   ÚregisterrB    s\   € à×ÒØØÝ&Ý'Ý"ØWÐWÐWØ*Ø-Ø+Ø/Ý-Ý)ØØØ!ð ñ ô ð ð ð r=   )Nrþ   )/r  rí   rB   r¹   r·   Úloggingr   Úurllib.parserÅ   Útypingr   r   r   Úgateway.configr   r   r  r   r	   r
   r   Úgateway.platforms.helpersr   r   Úagent.secret_scoper   r   r   r   r   Ú	getLoggerrÿ   r_   r�   r  r3   r5   re   r    r!   r#   r2   r  r%  r+  r-  rB  r¨   r=   r   ú<module>rJ     s0  ððð ð( €€€Ø €€€Ø €€€Ø €€€Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &Ð &à 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3ðð ð ð ð ð ð ð ð ð ð ð ð CÐ BÐ BÐ BÐ BÐ BÐ BÐ Bà JÐ JÐ JÐ JÐ JÐ JØ ?Ð ?Ð ?Ð ?Ð ?Ð ?ð/ð /ð /ð /ð( 
ˆÔ	˜8Ñ	$Ô	$€à>€Ø€ØÐ Ø"Ð Ø!'Ð ðf ð fð fð fð fðU
ð U
ð U
ð U
ð U
Ð$ñ U
ô U
ð U
ðH Sð ¨Sð ð ð ð ð& ØØð48ð 48ð 48ð 48ð 48ðnQ˜Tð Qð Qð Qð Qðð ð ð
ð ð ð ð ð r=   