
    Pmjd0                    $   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mZm	Z	 ddl
Z
 ej        e          ZdZdZdedd	i d
dedd	ddid
gZded<   ddgZded<   d/d0dZ G d de
j                  Zd1dZd2dZd3dZd4d$Zd5d%Zd6d*Zd7d.ZdS )8aY  Telegram-specific network helpers.

Provides a hostname-preserving fallback transport for networks where
api.telegram.org resolves to an endpoint that is unreachable from the current
host. The transport keeps the logical request host and TLS SNI as
api.telegram.org while retrying the TCP connection against one or more fallback
IPv4 addresses.
    )annotationsN)IterableOptionalzapi.telegram.orgg      @zhttps://dns.google/resolveA)nametype)urlparamsheadersz$https://cloudflare-dns.com/dns-queryAcceptzapplication/dns-jsonz
list[dict]_DOH_PROVIDERSz149.154.166.110z149.154.167.220	list[str]_SEED_FALLBACK_IPSreturn
str | Nonec                (    ddl m}  |d|           S )Nr   )resolve_proxy_urlTELEGRAM_PROXYtarget_hosts)gateway.platforms.baser   )r   r   s     Q/home/thesage/.hermes/hermes-agent/plugins/platforms/telegram/telegram_network.py_resolve_proxy_urlr   .   s+    888888-LIIII    c                  ^    e Zd ZdZ ej        dd          ZddZddZddZ	ddZ
ddZdS )TelegramFallbackTransportad  Retry Telegram Bot API requests via fallback IPs while preserving TLS/SNI.

    Requests continue to target https://api.telegram.org/... logically, but on
    connect failures the underlying TCP connection is retried against a known
    reachable IP. This is effectively the programmatic equivalent of
    ``curl --resolve api.telegram.org:443:<ip>``.
          )max_connectionsmax_keepalive_connectionsfallback_ipsIterable[str]c                   t          t                              t          |                              | _        t          t          g| j                  }|r	d|vr||d<   |                    d| j                   || _	        t          j        di || _        i | _        t          j                    | _        d | _        t          j                    | _        d S )Nr   proxylimits )listdictfromkeys_normalize_fallback_ips_fallback_ipsr   _TELEGRAM_API_HOST
setdefault_POOL_LIMITS_transport_kwargshttpxAsyncHTTPTransport_primary
_fallbacksasyncioLock_fallback_lock
_sticky_ip_sticky_lock)selfr!   transport_kwargs	proxy_urls       r   __init__z"TelegramFallbackTransport.__init__B   s    !$--0G0U0U"V"VWW&5G4]$J\4]^^^	 	2(888(1W%##Hd.?@@@!10DD3CDD?A%lnn)-#LNNr   ipstrr   httpx.AsyncHTTPTransportc                   K   | j         4 d {V  | j                            |          }| t          j        di | j        }|| j        |<   |cd d d           d {V  S # 1 d {V swxY w Y   d S )Nr&   )r6   r3   getr0   r1   r/   )r9   r=   	transports      r   _get_fallbackz'TelegramFallbackTransport._get_fallbackP   s     & 	 	 	 	 	 	 	 	++B//I !4NNt7MNN	&/#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   >A!!
A+.A+Nonec                H  K   | j         4 d{V  | j                            |d          }ddd          d{V  n# 1 d{V swxY w Y   |dS 	 |                                 d{V  dS # t          $ r'}t
                              d||           Y d}~dS d}~ww xY w)a|  Discard a failed fallback pool so its dead sockets are released.

        A connect that reaches ESTABLISHED and is then closed by the peer leaves
        its socket in CLOSE_WAIT inside the pool. Retaining the poisoned pool
        leaks one descriptor per retry until the process hits its file limit and
        can no longer accept connections or resolve DNS (#63311).
        Nz2[Telegram] Error closing fallback transport %s: %s)r6   r3   popaclose	Exceptionloggerdebug)r9   r=   rB   excs       r   _reset_fallbackz)TelegramFallbackTransport._reset_fallbackX   s_      & 	6 	6 	6 	6 	6 	6 	6 	6++B55I	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6F	X""$$$$$$$$$$$ 	X 	X 	XLLMrSVWWWWWWWWW	Xs'   >
AAA0 0
B!:BB!requesthttpx.Requesthttpx.Responsec           	       K   |j         j        t          k    s| j        s | j                            |           d {V S | j        }|r|gnd g}|r|                    d            | j        D ]}||k    r|                    |           d }|D ]}||nt          ||          }|| j        n| 	                    |           d {V }	 |                    |           d {V }|i| j        |k    r^| j
        4 d {V  | j        |k    r"|| _        t                              d|           d d d           d {V  n# 1 d {V swxY w Y   |c S # t          $ r}	|	}t          |	          s |i|| j        k    r^| j
        4 d {V  | j        |k    r"d | _        t                              d|           d d d           d {V  n# 1 d {V swxY w Y   |:t                              d|	d                    | j                             Y d }	~	t                              d||	           |                     |           d {V  Y d }	~	d }	~	ww xY w|t#          d          |)NzQ[Telegram] Primary api.telegram.org path unreachable; using sticky fallback IP %szF[Telegram] Sticky fallback IP %s failed; resetting to primary DNS pathzR[Telegram] Primary api.telegram.org connection failed (%s); trying fallback IPs %s, z$[Telegram] Fallback IP %s failed: %sz=All Telegram fallback IPs exhausted but no error was recorded)r	   hostr,   r+   r2   handle_async_requestr7   append_rewrite_request_for_iprC   r8   rI   warningrH   _is_retryable_connect_errorjoinrL   RuntimeError)
r9   rM   	sticky_ipattempt_orderr=   
last_error	candidaterB   responserK   s
             r   rS   z.TelegramFallbackTransport.handle_async_requesti   s     ;1119K1;;GDDDDDDDDDO	<E-Qi[[D6 	'  &&&$ 	) 	)BY$$R((('+
 #	 #	B#%:3J7TV3W3WI)+t?Q?QRT?U?U9U9U9U9U9U9UI !*!?!?	!J!JJJJJJJ>do&;&;#0        ?b00.0DO"NN s "                                  
2377 >bDO&;&;#0        ?b00.2DO"NN h "                             :NNl		$"455  
 HHHHEr3OOO**2.........+. ^___sf   
5E?.D?-E?
E			EE		E
I,I
.G
8I

GIG9I7IIc                f  K   | j                                          d {V  | j        4 d {V  t          | j                                                  }| j                                         d d d           d {V  n# 1 d {V swxY w Y   |D ]}|                                 d {V  d S N)r2   rG   r6   r'   r3   valuesclear)r9   
transportsrB   s      r   rG   z TelegramFallbackTransport.aclose   sU     m""$$$$$$$$$& 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$do446677JO!!###	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ $ 	% 	%I""$$$$$$$$$$	% 	%s   A B
BBN)r!   r"   )r=   r>   r   r?   )r=   r>   r   rD   )rM   rN   r   rO   )r   rD   )__name__
__module____qualname____doc__r0   Limitsr.   r<   rC   rL   rS   rG   r&   r   r   r   r   4   s           5<QOOOL+ + + +   X X X X"4 4 4 4l% % % % % %r   r   ra   r"   c                   g }| D ]}t          |                                          }|s&	 t          j        |          }n+# t          $ r t
                              d|           Y cw xY w|j        dk    rt
                              d|           |j        s|j	        s|j
        s|j        rt
                              d|           |                    t          |                     |S )Nz)Ignoring invalid Telegram fallback IP: %rr   z*Ignoring non-IPv4 Telegram fallback IP: %sz2Ignoring private/internal Telegram fallback IP: %s)r>   strip	ipaddress
ip_address
ValueErrorrI   rV   version
is_privateis_loopbackis_link_localis_unspecifiedrT   )ra   
normalizedvaluerawaddrs        r   r*   r*      s   J % %%jj   		',,DD 	 	 	NNFLLLH	 <1NNGMMM? 	d. 	$2D 	H[ 	NNOQTUUU#d))$$$$s   A%A)(A)rt   c                f    | sg S d |                      d          D             }t          |          S )Nc                6    g | ]}|                                 S r&   )rj   ).0parts     r   
<listcomp>z)parse_fallback_ip_env.<locals>.<listcomp>   s     777dTZZ\\777r   ,)splitr*   )rt   partss     r   parse_fallback_ip_envr      s<     	77ekk#&6&6777E"5)))r   set[str]c                     	 t          j        t          dt           j                  } d | D             S # t          $ r t                      cY S w xY w)zJReturn the IPv4 addresses that the OS resolver gives for api.telegram.org.i  c                *    h | ]}|d          d         S )r   r   r&   )ry   rv   s     r   	<setcomp>z&_resolve_system_dns.<locals>.<setcomp>   s     ///tQ
///r   )socketgetaddrinfor,   AF_INETrH   set)resultss    r   _resolve_system_dnsr      sZ    $%7fnMM//w////   uus   03 AAclienthttpx.AsyncClientproviderr(   c                `  K   	 |                      |d         |d         |d                    d{V }|                                 |                                }g }|                     dg           D ]~}|                     d          dk    r|                     d	d
                                          }	 t	          j        |           |                    |           o# t          $ r Y {w xY w|S # t          $ r.}t          
                    d|d         |           g cY d}~S d}~ww xY w)z/Query one DoH provider and return A-record IPs.r	   r
   r   )r
   r   NAnswerr      data zDoH query to %s failed: %s)rA   raise_for_statusjsonrj   rk   rl   rT   rm   rH   rI   rJ   )r   r   respr   ipsanswerru   rK   s           r   _query_doh_providerr      sg     ZZUOHX$6@S   
 
 
 
 
 
 
 
 	yy{{hhx,, 	 	Fzz&!!Q&&**VR((..00C$S)))

3   
   18E?CHHH						sB   B4C5 9)C#"C5 #
C0-C5 /C00C5 5
D-?#D("D-(D-c                 r  K   t          j        t          j        t                              4 d{V fdt          D             } t          j        t          j        t                              }t          j	        | ddi d{V }ddd          d{V  n# 1 d{V swxY w Y   t                      }	 t          j        |t                     d{V }t          |t                    r|}n0# t          $ r# t                              dt                      Y nw xY wg }|D ],}t          |t"                    r|                    |           -t                      }g }|D ]0}	|	|vr*|                    |	           |                    |	           1t+          |          }
|
r0t                              dd                    |
                     |
S t                              d	d                    |          pd
d                    t0                               t#          t0                    S )a-  Auto-discover Telegram API IPs via DNS-over-HTTPS.

    Resolves api.telegram.org through Google and Cloudflare DoH and returns all
    unique A records.  IPs that match the local system resolver are kept rather
    than excluded: in many networks the system-DNS IP is the most reliable path
    to api.telegram.org and a transient primary-path failure should be retried
    against the same address via the IP-rewrite path before the seed list is
    consulted (#14520).  Falls back to a hardcoded seed list only when DoH
    yields no usable answers.
    )timeoutNc                0    g | ]}t          |          S r&   )r   )ry   pr   s     r   r{   z)discover_fallback_ips.<locals>.<listcomp>   s$    LLL(33LLLr   return_exceptionsTz5System-DNS resolution for %s did not complete in timez,Discovered Telegram fallback IPs via DoH: %srQ   zPDoH discovery yielded no usable IPs (system DNS: %s); using seed fallback IPs %sunknown)r0   AsyncClientTimeout_DOH_TIMEOUTr   r4   ensure_future	to_threadr   gatherr   wait_for
isinstancerH   rI   rJ   r,   r'   extendaddrT   r*   rX   infor   )	doh_taskssystem_dns_taskr   
system_ipssystem_resultdoh_ipsrseen
candidatesr=   	validatedr   s              @r   discover_fallback_ipsr      s       |)D)DEEE K K K K K K KLLLL^LLL	!/0ABU0V0VWW	JTJJJJJJJJK K K K K K K K K K K K K K K K K K K K K K K K K K K 55Jb%.UUUUUUUUUmS)) 	'&J b b bLN`aaaaab G  a 	NN1 UUDJ " "T>>HHRLLLb!!! (
33I CTYYyEYEYZZZ
KKZ		**		$%%  
 "###s$   AB
B(+B(>8C7 7*D$#D$rM   rN   r=   r>   c                   | j         j        pt          }| j                             |          }| j                                        }||d<   t          | j                  }||d<   t          j	        | j
        ||| j        |          S )N)rR   rR   sni_hostname)methodr	   r   stream
extensions)r	   rR   r,   	copy_withr   copyr(   r   r0   Requestr   r   )rM   r=   original_hostr	   r   r   s         r   rU   rU      s    K$:(:M
+

R

(
(Co""$$G#GFOg())J!.J~=~~   r   rK   rH   boolc                N    t          | t          j        t          j        f          S r`   )r   r0   ConnectTimeoutConnectError)rK   s    r   rW   rW   0  s    cE0%2DEFFFr   r`   )r   r   )ra   r"   r   r   )rt   r   r   r   )r   r   )r   r   r   r(   r   r   )r   r   )rM   rN   r=   r>   r   rN   )rK   rH   r   r   )rg   
__future__r   r4   rk   loggingr   typingr   r   r0   	getLoggerrd   rI   r,   r   r   __annotations__r   r   AsyncBaseTransportr   r*   r   r   r   r   rU   rW   r&   r   r   <module>r      s     # " " " " "        % % % % % % % % 		8	$	$'   ,-s;;  6-s;;45      "34E F  F F F FJ J J J Jq% q% q% q% q% 8 q% q% q%h   ** * * *      46$ 6$ 6$ 6$r    G G G G G Gr   