
    +VjB                   &   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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 ddlmZmZmZmZmZ ddlmZmZ  ej        e          ZdZ 	 ddl!Z!d	Z"n# e#$ r dZ!d
Z"Y nw xY w	 ddl$m%Z% ddl&m'Z'm(Z(m)Z) d	Z*n# e#$ r dZ%dZ'dZ(dZ)d
Z*Y nw xY wddl+m,Z,m-Z- ddl.m/Z/ ddl0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7 ddl8m9Z9 ddl:m;Z; dZ<dZ=dZ>dZ?dZ@dZAdZBdZCdZDdZEdZFdZGdZHdZId ZJdZKd!ZLd"ZMd#ZNd$ZOd%ZPd&ZQdd.ZRd/ZSd"ZTd!ZUd0ZVi ZWd1eXd2<   dd4ZYd/ZZd"Z[d!Z\d0Z]d5Z^d/Z_d"Z`d"Zad/Zbd"Zc e
jd        d6          Ze e
jd        d7          Zf e
jd        d8          Zg e
jd        d9          Zhdd:ZidddAZjddCZkdddHZlddKZmddMZnddOZoddPZpddQZqddUZrddXZsddZZtd[d\dd`ZuddbZv G dc dd          Zw G de df          ZxddiZyddlZzddnZ{ddqZ|ddvZ}ddwZ~ddyZdd~ZddZddZddZddZddddZ eh d          ZdeXd<   ddZddZddZddZddZddZddZddZddZddZddZddZddZddZddZ	 dddZdddZddZddZddZddZddZdddddZ G d de1          ZddÜddȄZdS )a{  
Weixin platform adapter.

Connects Hermes Agent to WeChat personal accounts via Tencent's iLink Bot API.

Design notes:
- Long-poll ``getupdates`` drives inbound delivery.
- Every outbound reply must echo the latest ``context_token`` for the peer.
- Media files move through an AES-128-ECB encrypted CDN protocol.
- QR login is exposed as a helper for the gateway setup wizard.
    )annotationsN)datetime)Path)AnyDictListOptionalTuple)quoteurlparsex   TF)default_backend)Cipher
algorithmsmodes)PlatformPlatformConfig)MessageDeduplicator)BasePlatformAdapterMessageEventMessageType
SendResultcache_audio_from_bytescache_document_from_bytescache_image_from_bytes)get_hermes_home)atomic_json_writezhttps://ilinkai.weixin.qq.comz%https://novac2c.cdn.weixin.qq.com/c2cbotz2.2.0i  zilink/bot/getupdateszilink/bot/sendmessagezilink/bot/sendtypingzilink/bot/getconfigzilink/bot/getuploadurlzilink/bot/get_bot_qrcodezilink/bot/get_qrcode_statusi  i:  i'           ii,  ret'Optional[int]'errcodeerrmsg'Optional[str]'returnboolc                f    | t           k    r|t           k    rdS |pd                                dk    S )zTrue when iLink returns ret=-2 / errcode=-2 with 'unknown error',
    which is a stale-session signal (same as errcode=-14) rather than
    a genuine rate limit.F unknown error)RATE_LIMIT_ERRCODElower)r#   r%   r&   s      6/root/.hermes/hermes-agent/gateway/platforms/weixin.py_is_stale_session_retr0   c   s;        W0B%B%BuLb!!_44          Dict[str, Any]_LIVE_ADAPTERS Optional['aiohttp.TCPConnector']c                     	 ddl } ddl}n# t          $ r Y dS w xY wt          sdS |                     |                                          }t          j        |          S )a  Return a TCPConnector with a certifi CA bundle, or None if certifi is unavailable.

    Tencent's iLink server (``ilinkai.weixin.qq.com``) is not verifiable against
    some system CA stores (notably Homebrew's OpenSSL on macOS Apple Silicon).
    When ``certifi`` is installed, use its Mozilla CA bundle to guarantee
    verification. Otherwise fall back to aiohttp's default (which honors
    ``SSL_CERT_FILE`` env var via ``trust_env=True``).
    r   N)cafile)ssl)r9   certifiImportErrorAIOHTTP_AVAILABLEcreate_default_contextwhereaiohttpTCPConnector)r9   r:   ssl_ctxs      r/   _make_ssl_connectorrB   v   s|    


   tt t(((@@GG,,,,s    
   z^(#{1,6})\s+(.+?)\s*$z3^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$z^```([^\n`]*)\s*$z\[([^\]]+)\]\(([^)]+)\)c                     t           ot          S )z?Return True when runtime dependencies for Weixin are available.)r<   CRYPTO_AVAILABLE r1   r/   check_weixin_requirementsrG      s    1!11r1      valueOptional[str]keepintstrc                    t          | pd                                          }|sdS t          |          |k    r|S |d |         S )Nr+   ?)rM   striplen)rI   rK   raws      r/   _safe_idrS      sP    
ekr


 
 
"
"C s
3xx4
uu:r1   payloadc                0    t          j        | dd          S )NF),:)ensure_ascii
separators)jsondumps)rT   s    r/   _json_dumpsr\      s    :gEjIIIIr1      databytes
block_sizec                X    |t          |           |z  z
  }| t          |g|z            z   S N)rQ   r_   )r^   r`   pad_lens      r/   
_pkcs7_padrd      s1    CII
23G%	G+,,,,r1   	plaintextkeyc                   t          t          j        |          t          j                    t                                }|                                }|                    t          |                     |	                                z   S )Nbackend)
r   r   AESr   ECBr   	encryptorupdaterd   finalize)re   rf   cipherrl   s       r/   _aes128_ecb_encryptrp      sj    JN3''o>O>OPPPF  ""IJy1122Y5G5G5I5IIIr1   
ciphertextc                   t          t          j        |          t          j                    t                                }|                                }|                    |           |                                z   }|s|S |d         }d|cxk    rdk    r4n n1|	                    t          |g          |z            r|d |          S |S )Nrh   r2   r]   )r   r   rj   r   rk   r   	decryptorrm   rn   endswithr_   )rq   rf   ro   rt   paddedrc   s         r/   _aes128_ecb_decryptrw      s    JN3''o>O>OPPPF  ""Ij))I,>,>,@,@@F RjGGrfooeWI.>.>.HIIixi  Mr1   sizec                    | dz   dz   dz  dz  S )Nr2      r]   rF   )rx   s    r/   _aes_padded_sizer{      s    AX]r!R''r1   c                     t          j        dt          j        d                    d         } t	          j        t          |                               d                                        d          S )Nz>Ir3   r   utf-8ascii)	structunpacksecretstoken_bytesbase64	b64encoderM   encodedecoderI   s    r/   _random_wechat_uinr      sV    M$ 3A 6 677:ECJJ--g6677>>wGGGr1   c                     dt           iS )Nchannel_version)CHANNEL_VERSIONrF   r1   r/   
_base_infor      s    //r1   tokenbodyDict[str, str]c           	         ddt          t          |                    d                              t                      t          t          t
                    d}| rd|  |d<   |S )Nzapplication/jsonilink_bot_tokenr}   )Content-TypeAuthorizationTypezContent-LengthzX-WECHAT-UINiLink-App-IdiLink-App-ClientVersionzBearer Authorization)rM   rQ   r   r   ILINK_APP_IDILINK_APP_CLIENT_VERSION)r   r   headerss      r/   _headersr      sl    *.c$++g"6"67788*,,$#&'?#@#@ G  5#4U#4#4 Nr1   hermes_homer   c                ^    t          |           dz  dz  }|                    dd           |S )NweixinaccountsT)parentsexist_ok)r   mkdir)r   paths     r/   _account_dirr      s4    x'*4DJJtdJ+++Kr1   
account_idc                ,    t          |           | dz  S )Nz.jsonr   r   r   s     r/   _account_filer      s    $$*';';';;;r1   r+   )user_idbase_urlr   Nonec                   |||t          j        dt          j                              d}t          | |          }t	          ||           	 |                    d           dS # t          $ r Y dS w xY w)z,Persist account credentials for later reuse.z%Y-%m-%dT%H:%M:%SZ)r   r   r   saved_ati  N)timestrftimegmtimer   r   chmodOSError)r   r   r   r   r   rT   r   s          r/   save_weixin_accountr      s     M"6FF	 G j11DdG$$$

5   s   A$ $
A21A2Optional[Dict[str, Any]]c                    t          | |          }|                                sdS 	 t          j        |                    d                    S # t
          $ r Y dS w xY w)z#Load persisted account credentials.Nr}   encoding)r   existsrZ   loads	read_text	Exceptionr   r   r   s      r/   load_weixin_accountr     sk    j11D;;== tz$..'.::;;;   tts   'A 
AAc                  J    e Zd ZdZddZddZdd
ZddZddZddZ	ddZ
dS )ContextTokenStorez<Disk-backed ``context_token`` cache keyed by account + peer.r   rM   c                <    t          |          | _        i | _        d S rb   )r   _root_cache)selfr   s     r/   __init__zContextTokenStore.__init__  s    !+..
&(r1   r   r(   r   c                    | j         | dz  S )Nz.context-tokens.json)r   )r   r   s     r/   _pathzContextTokenStore._path  s    zz?????r1   r   c                    | d| S )NrW   rF   r   r   r   s      r/   _keyzContextTokenStore._key  s    ((w(((r1   r   c                8   |                      |          }|                                sd S 	 t          j        |                    d                    }nA# t
          $ r4}t                              dt          |          |           Y d }~d S d }~ww xY wd}|	                                D ]?\  }}t          |t                    r%|r#|| j        |                     ||          <   |dz  }@|r+t                              d|t          |                     d S d S )Nr}   r   z3weixin: failed to restore context tokens for %s: %sr   r2   z+weixin: restored %d context token(s) for %s)r   r   rZ   r   r   r   loggerwarningrS   items
isinstancerM   r   r   info)r   r   r   r^   excrestoredr   r   s           r/   restorezContextTokenStore.restore  s5   zz*%%{{}} 	F	:dnngn>>??DD 	 	 	NNPRZ[eRfRfhklllFFFFF	 "jjll 	 	NGU%%% % >CDIIj'::;A 	gKKExQYZdQeQefffff	g 	gs   (A 
B )BBrJ   c                ^    | j                             |                     ||                    S rb   )r   getr   r   s      r/   r   zContextTokenStore.get*  s$    {tyyW==>>>r1   r   c                l    || j         |                     ||          <   |                     |           d S rb   )r   r   _persist)r   r   r   r   s       r/   setzContextTokenStore.set-  s4    6;DIIj'223j!!!!!r1   c                &   | dfd| j                                         D             }	 t          |                     |          |           d S # t          $ r4}t
                              dt          |          |           Y d }~d S d }~ww xY w)NrW   c                p    i | ]2\  }}|                               |t                    d          |3S rb   )
startswithrQ   ).0rf   rI   prefixs      r/   
<dictcomp>z.ContextTokenStore._persist.<locals>.<dictcomp>3  sP     
 
 
U~~f%%
F
 
 
r1   z3weixin: failed to persist context tokens for %s: %s)r   r   r   r   r   r   r   rS   )r   r   rT   r   r   s       @r/   r   zContextTokenStore._persist1  s    !!!
 
 
 
"k//11
 
 

	mdjj44g>>>>> 	m 	m 	mNNPRZ[eRfRfhklllllllll	ms   #A 
B)BBN)r   rM   )r   rM   r(   r   )r   rM   r   rM   r(   rM   )r   rM   r(   r   )r   rM   r   rM   r(   rJ   )r   rM   r   rM   r   rM   r(   r   )__name__
__module____qualname____doc__r   r   r   r   r   r   r   rF   r1   r/   r   r     s        FF) ) ) )@ @ @ @) ) ) )g g g g"? ? ? ?" " " "
m 
m 
m 
m 
m 
mr1   r   c                  ,    e Zd ZdZdddZdd
ZddZdS )TypingTicketCachez3Short-lived typing ticket cache from ``getconfig``.     @ttl_secondsfloatc                "    || _         i | _        d S rb   )_ttl_secondsr   )r   r   s     r/   r   zTypingTicketCache.__init__A  s    '46r1   r   rM   r(   rJ   c                    | j                             |          }|sd S t          j                    |d         z
  | j        k    r| j                             |d            d S |d         S )Nr2   r   )r   r   r   r   pop)r   r   entrys      r/   r   zTypingTicketCache.getE  sc    (( 	49;;q!T%666KOOGT***4Qxr1   ticketr   c                @    |t          j                     f| j        |<   d S rb   )r   r   )r   r   r   s      r/   r   zTypingTicketCache.setN  s     &	4Gr1   N)r   )r   r   )r   rM   r(   rJ   )r   rM   r   rM   r(   r   )r   r   r   r   r   r   r   rF   r1   r/   r   r   >  s[        ==7 7 7 7 7   5 5 5 5 5 5r1   r   cdn_base_urlencrypted_query_paramc                T    |                      d           dt          |d           S )N/z /download?encrypted_query_param=r+   saferstripr   )r   r   s     r/   _cdn_download_urlr   R  s3    !!#&&ooNcjlHmHmHmooor1   upload_paramfilekeyc                x    |                      d           dt          |d           dt          |d           S )Nr   z/upload?encrypted_query_param=r+   r   z	&filekey=r   )r   r   r   s      r/   _cdn_upload_urlr   V  sV    s## 	. 	."'2">">">	. 	.'+++	. 	.r1   aes_key_b64c                R   t          j        |           }t          |          dk    r|S t          |          dk    rL|                    dd          }|r3t	          d |D                       rt
                              |          S t          dt          |           d          )	Nr]       r~   ignore)errorsc              3     K   | ]}|d v V  	dS )0123456789abcdefABCDEFNrF   )r   chs     r/   	<genexpr>z!_parse_aes_key.<locals>.<genexpr>d  s(      FF266FFFFFFr1   zunexpected aes_key format (z decoded bytes))r   	b64decoderQ   r   allr_   fromhex
ValueError)r   decodedtexts      r/   _parse_aes_keyr
  ^  s    {++G
7||r
7||r~~gh~77 	'CFFFFFFF 	'==&&&
P3w<<PPP
Q
QQr1   messageTuple[str, str]c                   t          |                     d          p|                     d          pd                                          }t          |                     d          pd                                          }t          |          p"|o |o||k    o|                     d          dk    }|r*d|p%|p#t          |                     d          pd          fS d	t          |                     d          pd          fS )
Nroom_idchat_room_idr+   
to_user_idmsg_typer2   groupfrom_user_iddm)rM   r   rP   r)   )r  r   r  r  is_groups        r/   _guess_chat_typer  i  s    '++i((MGKK,G,GM2NNTTVVGW[[..4"55;;==JG}}y!x
!xzZ?W!x\c\g\ghr\s\swx\xH XW:WW[[5P5P5VTV1W1WWWW[[006B7777r1   session'aiohttp.ClientSession'endpoint
timeout_msc                 K   t          i |dt                      i          }|                    d           d| }t          j        |dz            }|                     ||t          ||          |          4 d {V }	|	                                 d {V }
|	j        s%t          d| d|	j
         d|
d d	                    t          j        |
          cd d d           d {V  S # 1 d {V swxY w Y   d S )
N	base_infor     total)r^   r   timeoutziLink POST  HTTP :    )r\   r   r   r?   ClientTimeoutpostr   r	  okRuntimeErrorstatusrZ   r   )r  r   r  rT   r   r  r   urlr   responserR   s              r/   	_api_postr+  r  s      ='=;
==>>D__S!!
.
.H
.
.C#*t*;<<<G||CdHUD4I4ISZ|[[       _gMMOO######{ 	][X[[X_[[PSTXUXTXPY[[\\\z#	                             s   AC00
C:=C:c                 K   |                     d           d| }t          t          t                    d}t	          j        |dz            }|                     |||          4 d {V }|                                 d {V }|j        s%t          d| d|j
         d|d d	                    t          j        |          cd d d           d {V  S # 1 d {V swxY w Y   d S )
Nr   )r   r   r  r  )r   r   z
iLink GET r!  r"  r#  )r   r   rM   r   r?   r$  r   r	  r&  r'  r(  rZ   r   )	r  r   r  r  r)  r   r   r*  rR   s	            r/   _api_getr-    s      __S!!
.
.H
.
.C$#&'?#@#@ G #*t*;<<<G{{3{AA       XMMOO######{ 	\ZHZZHOZZsSWTWSWyZZ[[[z#	                             s   /AC
C&)C&sync_bufc                  K   	 t          | |t          d|i||           d {V S # t          j        $ r	 dg |dcY S w xY w)Nget_updates_bufr   r  rT   r   r  r   )r#   msgsr0  )r+  EP_GET_UPDATESasyncioTimeoutError)r  r   r   r.  r  s        r/   _get_updatesr6    s      
C#&1!
 
 
 
 
 
 
 
 
 	
  C C C"BBBBBCs   !& >>tor	  context_token	client_idc                  K   |r|                                 st          d          d||t          t          t          d|idgd}|r||d<   t          | |t          d|i|t                     d	{V S )
zSend a text message via iLink sendmessage API.

    Returns the raw API response dict (may contain error codes like
    ``errcode: -14`` for session expiry that the caller can inspect).
    z%_send_message: text must not be emptyr+   r	  )type	text_itemr  r  r9  message_typemessage_state	item_listr8  msgr1  N)rP   r  MSG_TYPE_BOTMSG_STATE_FINISH	ITEM_TEXTr+  EP_SEND_MESSAGEAPI_TIMEOUT_MS)r  r   r   r7  r	  r8  r9  r  s           r/   _send_messagerG    s        Btzz|| B@AAA$)(~FFG G  1#0   !         r1   r  typing_ticketr(  c          	     \   K   t          | |t          |||d|t                     d {V  d S )N)ilink_user_idrH  r(  r1  )r+  EP_SEND_TYPINGCONFIG_TIMEOUT_MS)r  r   r   r  rH  r(  s         r/   _send_typingrM    sk       '*
 

 $           r1   c               f   K   d|i}|r||d<   t          | |t          ||t                     d {V S )NrJ  r8  r1  )r+  EP_GET_CONFIGrL  )r  r   r   r   r8  rT   s         r/   _get_configrP    sk        /8G 1#0 $         r1   
media_typerawsize
rawfilemd5filesize
aeskey_hexc       	        b   K   t          | |t          ||||||d|	d|t                     d {V S )NT)r   rQ  r  rR  rS  rT  no_need_thumbaeskeyr1  )r+  EP_GET_UPLOAD_URLrF  )
r  r   r   r  rQ  r   rR  rS  rT  rU  s
             r/   _get_upload_urlrZ    sp       "$$$ ! 	
 	
 !         r1   
upload_urlc               d    K   d fd}t          j         |            d           d{V S )u   Upload encrypted media to the CDN.

    Accepts either a constructed CDN URL (from upload_param) or a direct
    upload_full_url — both use POST with the raw ciphertext as the body.
    r(   rM   c            	       K                        ddi          4 d {V } | j        dk    r~| j                            d          }|r.|                                  d {V  |cd d d           d {V  S |                                  d {V }t          d|d d                    |                                  d {V }t          d| j         d|d d                    # 1 d {V swxY w Y   d S )	Nr   application/octet-stream)r^   r   r#  zx-encrypted-paramz-CDN upload missing x-encrypted-param header: zCDN upload HTTP r"  )r%  r(  r   r   readr	  r'  )r*  encrypted_paramrR   rq   r  r[  s      r/   
_do_uploadz&_upload_ciphertext.<locals>._do_upload-  s     <<
nVpEq<rr 		R 		R 		R 		R 		R 		R 		Rv~#%%"*"2"6"67J"K"K" +"--//)))))))*		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R %MMOO++++++"#^SVW[X[W[S\#^#^___ ''''''CP(/PPS#YPPQQQ		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		R 		Rs   AC*:A0C**
C47C4r   r   Nr(   rM   r4  wait_for)r  rq   r[  ra  s   ``` r/   _upload_ciphertextrf    si      
R 
R 
R 
R 
R 
R 
R 
R !**,,<<<<<<<<<<r1         N@)timeout_secondsr)  rh  r   c               `    K   d fd}t          j         |            |           d {V S )Nr(   r_   c                    K                                  4 d {V } |                                  |                                  d {V cd d d           d {V  S # 1 d {V swxY w Y   d S rb   )r   raise_for_statusr_  )r*  r  r)  s    r/   _do_downloadz%_download_bytes.<locals>._do_downloadC  s     ;;s## 	) 	) 	) 	) 	) 	) 	)x%%'''!((((((	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)s   .A  
A*-A*rb  )r(   r_   rd  )r  r)  rh  rl  s   ``  r/   _download_bytesrm  ;  s[      ) ) ) ) ) ) ) !,,../JJJJJJJJJJr1   >   wx.qlogo.cnmmbiz.qpic.cnres.wx.qq.commmbiz.qlogo.cnthirdwx.qlogo.cnilinkai.weixin.qq.comnovac2c.cdn.weixin.qq.comzfrozenset[str]_WEIXIN_CDN_ALLOWLISTc                   	 t          |           }|j                                        }|j        pd}n%# t          $ r}t          d|           |d}~ww xY w|dvrt          d|d          |t          vrt          d|d          dS )	zDRaise ValueError if *url* does not point at a known WeChat CDN host.r+   zUnparseable media URL: N)httphttpsz Media URL has disallowed scheme z ; only http/https are permitted.zMedia URL host zG is not in the WeChat CDN allowlist. Refusing to fetch to prevent SSRF.)r   schemer.   hostnamer   r  ru  )r)  parsedry  hostr   s        r/   _assert_weixin_cdn_urlr}  W  s    E#$$&&$" E E E:3::;;DE &&&YvYYY
 
 	
 (((1d 1 1 1
 
 	
 )(s   14 
AAAitemc                Z    |                      |          pi                      d          pi S )Nmedia)r   )r~  rf   s     r/   _media_referencer  k  s*    HHSMMR$$W--33r1   full_urlc                 K   |r't          | t          ||          |           d {V }n9|r(t          |           t          | ||           d {V }nt          d          |rt	          |t          |                    }|S )N)r)  rh  z7media item had neither encrypt_query_param nor full_url)rm  r   r}  r'  rw   r
  )r  r   r   r   r  rh  rR   s          r/   _download_and_decrypt_mediar  o  s        
V#!,0EFF+
 
 
 
 
 
 
 
 

 
 Vx(((#G?[[[[[[[[[TUUU D!#~k'B'BCCJr1   filenamec                :    t          j        |           d         pdS )Nr   r^  )	mimetypes
guess_type)r  s    r/   _mime_from_filenamer    s    ))!,J0JJr1   line	List[str]c                    |                                  }|                    d          r
|dd          }|                    d          r
|d d         }d |                    d          D             S )N|r2   rs   c                6    g | ]}|                                 S rF   rP   )r   cells     r/   
<listcomp>z$_split_table_row.<locals>.<listcomp>  s     444TDJJLL444r1   )rP   r   ru   split)r  rows     r/   _split_table_rowr    so    
**,,C
~~c !""g
||C #2#h44SYYs^^4444r1   c                   t                               |           }|s|                                 S t          |                    d                    }|                    d                                          }|dk    rd| dS d| dS )Nr2   r       【u   】z**)
_HEADER_REmatchr   rQ   r  rP   )r  r  leveltitles       r/   _rewrite_headers_for_weixinr    s    T""E {{}}AEKKNN  ""EzzU>>>r1   linesc                   t          |           dk     rd                    |           S t          | d                   }d | dd          D             }|r|sd                    |           S g }|D ]A}g }t          |          D ]W\  }}|t          |          k    r n>|pd|dz    }||                                         }	|	r|                    ||	f           X|sot          |          dk    r'|d         \  }}	|                    d| d|	            t          |          dk    rN|d         \  }}	|d         \  }
}|                    d| d|	            |                    d	|
 d|            
d
                    d |D                       }|                    d|            C|rd                    |          nd                    |           S )Nr    
r   c                T    g | ]%}|                                 t          |          &S rF   )rP   r  r   r  s     r/   r  z3_rewrite_table_block_for_weixin.<locals>.<listcomp>  s.    NNNDN!$''NNNr1   zColumn r2   z- r"  z   | c              3  *   K   | ]\  }}| d | V  dS )r"  NrF   )r   labelrI   s      r/   r  z2_rewrite_table_block_for_weixin.<locals>.<genexpr>  s4      KK\UE0000KKKKKKr1   )rQ   joinr  	enumeraterP   append)r  r   	body_rowsformatted_rowsr  pairsidxheaderr  rI   other_labelother_valuesummarys                r/   _rewrite_table_block_for_weixinr    s,   
5zzA~~yyuQx((GNNE!""INNNI  )  yy "N . .$W-- 	- 	-KCc#hh11a11EHNN$$E -eU^,,, 	u::?? 8LE5!!"7u"7"7"7"7888u::?? 8LE5',Qx$K!!"7u"7"7"7"7888!!"C{"C"Ck"C"CDDD**KKUKKKKKn7nn----(6L499^$$$DIIe<L<LLr1   contentc                   |                                  }g }d}d}|D ]}|                                }t                              |                                          r| }|                    |           d}]|r|                    |           u|                                s!|dz  }|dk    r|                    d           d}|                    |           d                    |                                          S )NFr   r2   r+   r  
splitlinesr   	_FENCE_REr  rP   r  r  )r  r  resultin_code_block	blank_runraw_liner  s          r/   _normalize_markdown_blocksr    s
     EFMI    ??4::<<(( 	 --MMM$I 	MM$zz|| 	NIA~~b!!!	d99V""$$$r1   c           	     t   | s| S g }d}|                                  D ]}|                                }|                                }t                              |          r| }|                    |           ]|sIt          |          t          k    s1|r/|                    d          st                              |          r|                    |           t          j        |t          dddd          }|                    |p|g           d                    |                                          S )z@Wrap long display lines that are hard to copy in WeChat clients.Fr  T)widthbreak_long_wordsbreak_on_hyphensreplace_whitespacedrop_whitespacer  )r  r   rP   r  r  r  rQ   WEIXIN_COPY_LINE_WIDTHr   _TABLE_RULE_REtextwrapwrapextendr  )r  wrappedr  r  r  strippedwrapped_liness          r/   $_wrap_copy_friendly_lines_for_weixinr    sN    GM&&(( 0 0  ::<<??8$$ 	 --MNN4    	4yy222 3""3'' 3 ##H-- 3
 NN4    (""$ 
 
 
 	}.////99W##%%%r1   c                   | sg S g }|                                  }g }d}|D ][}|                                }t                              |                                          r|s>|r<|                    d                    |                                                     g }|                    |           | }|s<|                    d                    |                                                     g }|r|                    |           |                                s@|r<|                    d                    |                                                     g }F|                    |           ]|r:|                    d                    |                                                     d |D             S )NFr  c                    g | ]}||S rF   rF   )r   blocks     r/   r  z*_split_markdown_blocks.<locals>.<listcomp>,  s    ///e/E///r1   r  )r  blocksr  currentr  r  r  s          r/   _split_markdown_blocksr  	  s    	F  EGM    ??4::<<(( 		  W dii006688999NN4    --M  dii006688999 	NN4   zz|| 	 dii006688999t 2dii((..00111//v////r1   c                h   g }t          |           D ]}t                              |                                d                                                   r|                    |           ]g }|                                D ]}|                                }|                                s?|r<|                    d                    |                                                     g }it          |          o|	                    d          }|r|                    |           |r:|                    d                    |                                                     |g}|r:|                    d                    |                                                     d |D             S )aQ  Split formatted content into chat-friendly delivery units.

    Weixin can render Markdown, but chat readability is better when top-level
    line breaks become separate messages. Keep fenced code blocks intact and
    attach indented continuation lines to the previous top-level line so nested
    list items do not get torn apart.
    r   r   	c                    g | ]}||S rF   rF   )r   units     r/   r  z4_split_delivery_units_for_weixin.<locals>.<listcomp>S  s    +++Td+D+++r1   )
r  r  r  r  rP   r  r   r  r)   r   )r  unitsr  r  r  r  is_continuations          r/    _split_delivery_units_for_weixinr  /  s    E'00 5 5??5++--a0668899 	LL((** 	 	H??$$D::<<  !LL7!3!3!9!9!;!;<<< G"7mmP0C0CK0P0PO t$$$ 9TYYw//5577888fGG 	5LL7++1133444++U++++r1   c                P   |                                  }|sdS t          |          dk    rdS |                     d          rdS |                    d          rdS t                              |          rdS t          j        d|          rdS t          j        d|          rdS dS )z?Return True when a line looks like a standalone chat utterance.F0   r  )>-*r  #r  z^\*\*[^*]+\*\*$z^\d+\.\sT)rP   rQ   r   r  r  rer  r  s     r/   "_looks_like_chatty_line_for_weixinr  V  s    zz||H u
8}}ru{## u;<< uH%% u	x"H-- u	xX&& u4r1   c                    |                                  }|sdS t                              |          rdS t          |          dk    o|                    d          S )z5Return True when a short line behaves like a heading.FT   )rW   u   ：)rP   r  r  rQ   ru   r  s     r/   #_looks_like_heading_line_for_weixinr  j  s[    zz||H u!! tx==BB8#4#4\#B#BBr1   r  c                    d |                                  D             }dt          |          cxk    rdk    sn dS t          |d                   rdS t          d |D                       S )z<Split only chat-like multiline blocks into separate bubbles.c                :    g | ]}|                                 |S rF   r  r  s     r/   r  z=_should_split_short_chat_block_for_weixin.<locals>.<listcomp>v  s%    AAAdDJJLLATAAAr1   r       Fr   c              3  4   K   | ]}t          |          V  d S rb   )r  r  s     r/   r  z<_should_split_short_chat_block_for_weixin.<locals>.<genexpr>{  s+      JJD1$77JJJJJJr1   )r  rQ   r  r  )r  r  s     r/   )_should_split_short_chat_block_for_weixinr  t  s    AAe..00AAAEE

au*5844 uJJEJJJJJJr1   
max_lengthc                |   t          |           |k    r| gS g }d}t          |           D ]z}|s|n| d| }t          |          |k    r|}#|r|                    |           d}t          |          |k    r|}R|                    t	          j        ||                     {|r|                    |           |S )Nr+   z

)rQ   r  r  r  r   truncate_message)r  r  packedr  r  	candidates         r/    _pack_markdown_blocks_for_weixinr  ~  s    
7||z!!yFG'00 O O!(EEE.E.Ee.E.E	y>>Z''G 	MM'"""Gu::##G):5*MMNNNN gMr1   split_per_linec                   | sg S |rt          |           |k    rd| vr| gS g }t          |           D ]N}t          |          |k    r|                    |           +|                    t	          ||                     Od |D             p| gS t          |           |k    r+t          |           rd t          |           D             n| gS t	          | |          p| gS )u  Split content into sequential Weixin messages.

    *compact* (default): Keep everything in a single message whenever it fits
    within the platform limit, even when the author used explicit line breaks.
    Only fall back to block-aware packing when the payload exceeds
    ``max_length``.

    *per_line* (``split_per_line=True``): Legacy behavior — top-level line
    breaks become separate chat messages; oversized units still use
    block-aware packing.

    The active mode is controlled via ``config.yaml`` ->
    ``platforms.weixin.extra.split_multiline_messages`` (``true`` / ``false``)
    or the env var ``WEIXIN_SPLIT_MULTILINE_MESSAGES``.
    r  c                    g | ]}||S rF   rF   r   cs     r/   r  z3_split_text_for_weixin_delivery.<locals>.<listcomp>  s    '''aQ''''r1   c                    g | ]}||S rF   rF   )r   us     r/   r  z3_split_text_for_weixin_delivery.<locals>.<listcomp>  s    GGG1QGQGGGr1   )rQ   r  r  r  r  r  )r  r  r  chunksr  s        r/   _split_text_for_weixin_deliveryr    s    $  	 
5w<<:%%$g*=*=94W== 	N 	ND4yyJ&&d###MM:4LLMMMM''6'''4G94
 7||z!! 9AAGG8AAGGGG	

 ,GZ@@MWIMr1   r   defaultc                   | |S t          | t                    r| S t          | t          t          f          rt          |           S t	          |                                                                           }|s|S |dv rdS |dv rdS |S )zBCoerce a config value to bool, tolerating strings like ``"true"``.N>   1onyestrueT>   0noofffalseF)r   r)   rL   r   rM   rP   r.   )rI   r  r	  s      r/   _coerce_boolr    s    }% %#u&& E{{u::##%%D )))t***uNr1   r@  List[Dict[str, Any]]c                   | D ]}|                     d          t          k    rpt          |                     d          pi                      d          pd          }|                     d          pi }|                     d          pi }|                     d          }|t          t          t
          t          fv r;|                     d          pd}|rd| d	nd
}| |                                 c S |rg }|                     d          r(|                    t          |d                              t          |g          }	|	r|                    |	           |r/dd
                    |           d	|                                 c S |c S | D ]_}|                     d          t          k    r?t          |                     d          pi                      d          pd          }
|
r|
c S `dS )Nr;  r<  r	  r+   ref_msgmessage_itemr  u   [引用媒体: z]
u   [引用媒体]
u	   [引用: r  
voice_item)r   rD  rM   
ITEM_IMAGE
ITEM_VIDEO	ITEM_FILE
ITEM_VOICErP   r  _extract_textr  )r@  r~  r	  refref_itemref_typer  r   partsref_text
voice_texts              r/   r  r    s&     88Fy((--388@@FBGGD((9%%+Cww~..4"H||F++HJ
IzJJJ((.B9>V555555DV ($((..00000 L#%777## 4LLS\!2!2333((44 +LL*** LCuzz%'8'8CCTCCIIKKKKKKKK% )&  " "88Fz))dhh|44:??GGM2NNJ "!!!!2r1   media_typesr   c                V   t          d | D                       rt          j        S t          d | D                       rt          j        S t          d | D                       rt          j        S | rt          j        S |                    d          rt          j        S t          j        S )Nc              3  @   K   | ]}|                     d           V  dS )image/Nr   r   ms     r/   r  z+_message_type_from_media.<locals>.<genexpr>  .      
7
7a1<<!!
7
7
7
7
7
7r1   c              3  @   K   | ]}|                     d           V  dS )video/Nr  r  s     r/   r  z+_message_type_from_media.<locals>.<genexpr>  r  r1   c              3  @   K   | ]}|                     d           V  dS )audio/Nr  r  s     r/   r  z+_message_type_from_media.<locals>.<genexpr>  r  r1   r   )	anyr   PHOTOVIDEOVOICEDOCUMENTr   COMMANDTEXT)r  r	  s     r/   _message_type_from_mediar&    s    

7
7;
7
7
777 !  

7
7;
7
7
777 !  

7
7;
7
7
777 !   $##s #""r1   c                ,    t          |           | dz  S )Nz
.sync.jsonr   r   s     r/   _sync_buf_pathr(    s    $$*'@'@'@@@r1   c                    t          | |          }|                                sdS 	 t          j        |                    d                                        dd          S # t          $ r Y dS w xY w)Nr+   r}   r   r0  )r(  r   rZ   r   r   r   r   r   s      r/   _load_sync_bufr*    s}    +z22D;;== rz$..'.::;;??@QSUVVV   rrs   ;A$ $
A21A2c                J    t          | |          }t          |d|i           d S )Nr0  )r(  r   )r   r   r.  r   s       r/   _save_sync_bufr,    s-    +z22Dd.9:::::r1   3i  )bot_typerh  r.  Optional[Dict[str, str]]c               d  K   t           st          d          t          j        dt	                                4 d{V }	 t          |t          t           d| t                     d{V }nD# t          $ r7}t                              d|           Y d}~ddd          d{V  dS d}~ww xY wt          |                    d          pd	          }t          |                    d
          pd	          }|s.t                              d           	 ddd          d{V  dS |r|n|}t          d           |rt          |           	 ddl}	|	                                }
|
                    |           |
                    d           |
                    d           n*# t          $ r}t          d| d           Y d}~nd}~ww xY wt)          j                    |z   }t          }d}t)          j                    |k     r	 t          ||t,           d| t                     d{V }nv# t.          j        $ r t/          j        d           d{V  Y mt          $ r?}t                              d|           t/          j        d           d{V  Y d}~d}~ww xY wt          |                    d          pd          }|dk    rt          dd	d           n|dk    rt          d           n|dk    r-t          |                    d          pd	          }|rd| }n|dk    r|dz  }|dk    r#t          d            	 ddd          d{V  dS t          d!| d"           	 t          |t          t           d| t                     d{V }t          |                    d          pd	          }t          |                    d
          pd	          }|r|n|}|rt          |           	 ddl}|                                }
|
                    |           |
                    d           |
                    d           n# t          $ r Y nw xY wnQ# t          $ r7}t                              d#|           Y d}~ddd          d{V  dS d}~ww xY w|d$k    rt          |                    d%          pd	          }t          |                    d&          pd	          }t          |                    d'          pt                    }t          |                    d(          pd	          }|r|s.t                              d)           	 ddd          d{V  dS t7          | ||||*           t          d+|            ||||d*cddd          d{V  S t/          j        d           d{V  t)          j                    |k     t          d,           	 ddd          d{V  dS # 1 d{V swxY w Y   dS )-z
    Run the interactive iLink QR login flow.

    Returns a credential dict on success, or ``None`` if login fails or times out.
    z'aiohttp is required for Weixin QR loginT	trust_env	connectorNz
?bot_type=)r   r  r  z#weixin: failed to fetch QR code: %sqrcoder+   qrcode_img_contentz"weixin: QR response missing qrcodeu(   
请使用微信扫描以下二维码：r   )fit)invertu    （终端二维码渲染失败: u-   ，请直接打开上面的二维码链接）z?qrcode=r2   zweixin: QR poll error: %sr(  wait.)endflushscanedu%   
已扫码，请在微信里确认...scaned_but_redirectredirect_hosthttps://expiredr   u1   
二维码多次过期，请重新执行登录。u'   
二维码已过期，正在刷新... (z/3)zweixin: QR refresh failed: %s	confirmedilink_bot_id	bot_tokenbaseurlrJ  z:weixin: QR confirmed but credential payload was incomplete)r   r   r   r   u!   
微信连接成功，account_id=u   
微信登录超时。)r<   r'  r?   ClientSessionrB   r-  ILINK_BASE_URLEP_GET_BOT_QRQR_TIMEOUT_MSr   r   errorrM   r   printr4  QRCodeadd_datamakeprint_asciir   	monotonicEP_GET_QR_STATUSr4  r5  sleepr   r   )r   r.  rh  r  qr_respr   qrcode_value
qrcode_urlqr_scan_datar4  qr_qr_excdeadlinecurrent_base_urlrefresh_countstatus_respr(  r>  _qrcoder   r   r   r   s                          r/   qr_loginr]    s	       FDEEE$t?R?T?TUUU v v v v v v vY`		$')??X??(	        GG  	 	 	LL>DDD444v v v v v v v v v v v v v v	 7;;x006B77%9::@bAA
 	LL=>>>!v v v v v v v v v v v v v v( &0Azz\9::: 	*	mMMMBKK%%%GGGNN$N'''' 	m 	m 	mkWkkkllllllll	m >##o5)n))$,- 0HH,HH,	% % %       '   mA&&&&&&&&&   :C@@@mA&&&&&&&&&
 22<f==Fcr.....8##>????000 #KOOO$D$D$J K K  B'A-'A'A$9$$" 1$$NOOOIv v v v v v v v v v v v v vJ SSSSTTT $,!/$1!G!GX!G!G#0	% % %      G $'w{{8'<'<'B#C#CL!$W[[1E%F%F%L"!M!MJ1;#M::L! *j)))0000$^^--L111D)))d3333$          LL!@#FFF444yv v v v v v v v v v v v v vt  ;&& !@!@!FBGG
KOOK88>B??{y99K^LLkooo>>D"EE!    LL!]^^^Iv v v v v v v v v v v v v vJ $)%#    G:GGHHH"," (&	 [v v v v v v v v v v v v v vf -"""""""""Y n))\ 	'(((mv v v v v v v v v v v v v v v v v v v v v v v v v v v v v vs	  X-A10X1
B2;B-X-B22A(X-&XAF.-X.
G8GXG:X(H98X9)J,"X$	J,-5J'"X'J,,B-X,X BQ9AQ'&Q9'
Q41Q93Q44Q97X9
R:R5X5R::B>X,X
AX
X),X)c                  R    e Zd ZdZdZdZdG fdZedHd            ZdIdZ	dJdZ
dJdZdKdZdKdZdLdZdMdZdNdZdNdZdOd ZdNd!ZdPd$ZdQd&ZdRd*Z	 	 dSdTd0ZdUdVd1ZdWd2Z	 	 dSdXd5Z	 	 	 dYdZd7Z	 	 	 	 d[d\d:Z	 	 	 dYd]d<Z	 	 	 dYd^d>Zd_d@Z	 d`dadCZ d`dbdDZ!dcdEZ"dddFZ# xZ$S )eWeixinAdapterz3Native Hermes adapter for Weixin personal accounts.i  Fconfigr   c                ,
   t                                          |t          j                   |j        pi }t          t                                }|| _        t          |          | _	        t                      | _        d | _        d | _        d | _        t          t                     | _        t          |                    d          pt'          j        dd                                                    | _        t          |j        p)|                    d          pt'          j        dd                                                    | _        t          |                    d          pt'          j        dt2                                                                                  d	          | _        t          |                    d
          pt'          j        dt8                                                                                  d	          | _        t=          |                    d          pt'          j        dd                    | _        tA          |                    d          pt'          j        dd                    | _!        t=          |                    d          pt'          j        dd                    | _"        t          |                    d          pt'          j        dd                                                    #                                | _$        t          |                    d          pt'          j        dd                                                    #                                | _%        |                    d          }|t'          j        dd          }|                    d          }|t'          j        dd          }| &                    |          | _'        | &                    |          | _(        tS          |                    d          pt'          j        d           d!"          | _*        | j        r| j        stW          || j                  }|rt          |                    d          pd                                          | _        t          |                    d          p| j                                                                      d	          | _        d S d S d S d S )#N)r   r   WEIXIN_ACCOUNT_IDr+   r   WEIXIN_TOKENr   WEIXIN_BASE_URLr   r   WEIXIN_CDN_BASE_URLsend_chunk_delay_secondsWEIXIN_SEND_CHUNK_DELAY_SECONDSz1.5send_chunk_retriesWEIXIN_SEND_CHUNK_RETRIES4send_chunk_retry_delay_seconds%WEIXIN_SEND_CHUNK_RETRY_DELAY_SECONDSz1.0	dm_policyWEIXIN_DM_POLICYopengroup_policyWEIXIN_GROUP_POLICYdisabled
allow_fromWEIXIN_ALLOWED_USERSgroup_allow_fromWEIXIN_GROUP_ALLOWED_USERSsplit_multiline_messagesWEIXIN_SPLIT_MULTILINE_MESSAGESF)r  ),superr   r   WEIXINextrarM   r   _hermes_homer   _token_storer   _typing_cache_poll_session_send_session
_poll_taskr   MESSAGE_DEDUP_TTL_SECONDS_dedupr   osgetenvrP   _account_idr   _tokenrF  r   	_base_urlre  _cdn_base_urlr   _send_chunk_delay_secondsrL   _send_chunk_retries_send_chunk_retry_delay_secondsr.   
_dm_policy_group_policy_coerce_list_allow_from_group_allow_fromr  _split_multiline_messagesr   )r   r`  r{  r   rs  ru  	persisted	__class__s          r/   r   zWeixinAdapter.__init__  sV   111"/++,,'-k::.00>B>B26)6OPPPuyy66\")DWY[:\:\]]ccee&,]%))G*<*<]	.Z\@]@]^^ddffUYYz22bbi@QSa6b6bcciikkrrsvww IIn%%^3HJ])^)^
 

%''&&++ 	 */II011hRY?`bg5h5h*
 *
& $'II*++Zry9TVY/Z/Z$
 $
  05II677 Iy@%HH0
 0
, eii44]	BTV\8]8]^^ddffllnn >!:!:!jbiH]_i>j>jkkqqssyy{{YY|,,
#92>>J 99%788#!y)ErJJ,,Z88!%!2!23C!D!D)5II011 <y:;;*
 *
 *
&  	fDK 	f+K9IJJI f!)--"8"8">B??EEGG!$Y]]:%>%>%P$.!Q!Q!W!W!Y!Y!`!`ad!e!e		f 	f 	f 	ff fr1   rI   r   r(   r  c                X   | g S t          | t                    rd |                     d          D             S t          | t          t          t
          f          rd | D             S t          |                                           r"t          |                                           gng S )Nc                ^    g | ]*}|                                 |                                 +S rF   r  r   r~  s     r/   r  z.WeixinAdapter._coerce_list.<locals>.<listcomp>  s-    NNNTNDJJLLNNNr1   rV   c                    g | ]D}t          |                                          #t          |                                          ES rF   )rM   rP   r  s     r/   r  z.WeixinAdapter._coerce_list.<locals>.<listcomp>  s=    MMM$3t99??;L;LMCIIOO%%MMMr1   )r   rM   r  listtupler   rP   r   s    r/   r  zWeixinAdapter._coerce_list  s    =IeS!! 	ONNU[[-=-=NNNNedE3/00 	NMM%MMMM'*5zz'7'7'9'9AE

  ""##rAr1   r)   c                  K   t                      s=d}|                     d|d           t                              d| j        |           dS | j        s=d}|                     d|d           t                              d| j        |           dS | j        s=d}|                     d	|d           t                              d| j        |           dS 	 |                     d
| j        d          sdS n8# t          $ r+}t          	                    d| j        |           Y d }~nd }~ww xY wt          j        dt                                | _        t          j        d d d d           }t          j        dt                      |          | _        | j                            | j                   t%          j        |                                 d          | _        |                                  | t.          | j        <   t                              d| j        t3          | j                  | j                   | j        dk    r&t                              d| j        | j                   dS )Nz<Weixin startup failed: aiohttp and cryptography are requiredweixin_missing_dependencyF)	retryablez[%s] %sz/Weixin startup failed: WEIXIN_TOKEN is requiredweixin_missing_tokenz4Weixin startup failed: WEIXIN_ACCOUNT_ID is requiredweixin_missing_accountzweixin-bot-tokenzWeixin bot tokenz+[%s] Token lock unavailable (non-fatal): %sTr1  )r  connectsock_connect	sock_read)r2  r3  r   zweixin-poll)namez![%s] Connected account=%s base=%srr  a  [%s] WEIXIN_GROUP_POLICY=%s is set, but QR-login connects an iLink bot identity (e.g. ...@im.bot) which typically cannot be invited into ordinary WeChat groups. iLink usually does not deliver ordinary-group events for these accounts, so group messages may never reach Hermes regardless of this policy. If group delivery doesn't work, the limitation is on the iLink side, not in Hermes.)rG   _set_fatal_errorr   r   r  r  r  _acquire_platform_lockr   debugr?   rE  rB   r  r$  r  r}  r   r4  create_task
_poll_loopr  _mark_connectedr5   r   rS   r  r  )r   r  r   _no_aiohttp_timeouts       r/   r  zWeixinAdapter.connect  s     (** 	TG!!"=wRW!XXXNN9di9995{ 	GG!!"8'U!SSSNN9di9995 	LG!!":Gu!UUUNN9di9995	X../A4;Pbcc u 	X 	X 	XLLF	SVWWWWWWWW	X %2TM`MbMbccc
 &3$[_koppp$2TM`MbMbl  A  A  A!!$"2333!-doo.?.?mTTT&*t{#7HTM]D^D^`d`nooo++NN! 	"	 	 	 ts   C6 6
D+ !D&&D+r   c                  K   t                               | j        d            d| _        | j        rV| j                                        s=| j                                         	 | j         d {V  n# t          j        $ r Y nw xY wd | _        | j	        r+| j	        j
        s| j	                                         d {V  d | _	        | j        r+| j        j
        s| j                                         d {V  d | _        |                                  |                                  t                              d| j                   d S )NFz[%s] Disconnected)r5   r   r  _runningr  donecancelr4  CancelledErrorr  closedcloser  _release_platform_lock_mark_disconnectedr   r   r  )r   s    r/   
disconnectzWeixinAdapter.disconnect  ss     4;---? 	4?#7#7#9#9 	O""$$$o%%%%%%%%)    	-d&8&? 	-$**,,,,,,,,,! 	-d&8&? 	-$**,,,,,,,,,!##%%%!!!'33333s   $A2 2BBc           
       K   | j         J t          | j        | j                  }t          }d}| j        r	 t          | j         | j        | j        ||           d {V }|	                    d          }t          |t                    r|dk    r|}|	                    dd          }|	                    dd          }|dvs|dvr|t          k    s/|t          k    s$t          |||	                    d                    r>t                              d| j                   t#          j        d	           d {V  d}|d
z  }t                              d| j        |||	                    dd          |t(                     t#          j        |t(          k    rt*          nt,                     d {V  |t(          k    rd}d}t/          |	                    d          pd          }|r|}t1          | j        | j        |           |	                    d          pg D ])}	t#          j        |                     |	                     *n# t"          j        $ r Y d S t8          $ ru}
|d
z  }t                              d| j        |t(          |
           t#          j        |t(          k    rt*          nt,                     d {V  |t(          k    rd}Y d }
~
nd }
~
ww xY w| j        d S d S )Nr   )r   r   r.  r  longpolling_timeout_msr#   r%   )r   Nr&   z,[%s] Session expired; pausing for 10 minutesiX  r2   z:[%s] getUpdates failed ret=%s errcode=%s errmsg=%s (%d/%d)r+   r0  r2  z[%s] poll error (%d/%d): %s)r  r*  r|  r  LONG_POLL_TIMEOUT_MSr  r6  r  r  r   r   rL   SESSION_EXPIRED_ERRCODEr0   r   rI  r  r4  rQ  r   MAX_CONSECUTIVE_FAILURESBACKOFF_DELAY_SECONDSRETRY_DELAY_SECONDSrM   r,  r  _process_message_safer  r   )r   r.  r  consecutive_failuresr*  suggested_timeoutr#   r%   new_sync_bufr  r   s              r/   r  zWeixinAdapter._poll_loop!  s     !---!$"3T5EFF)
 m 4	-3-!-&!^+%)" " "       %-LL1I$J$J!/55 3:Ka:O:O!2Jll5!,,",,y!44i''7)+C+C666'E\:\:\4S'8<<PXCYCYZZ ;]%SUYU^___%mC000000000/0, (A-(NNT	 Xr22,0   "-AUYqAqAq(=(=  xK  L  L  L  L  L  L  L  L  L+/GGG/0,'($"8<<0A#B#B#HbII R+H"4#4d6FQQQ'||F339r M MG'(B(B7(K(KLLLLM)    - - -$)$:DIG[]uwz{{{m=QUm=m=m$9$9  tG  H  H  H  H  H  H  H  H  H'+CCC+,(-_ m 4	- 4	- 4	- 4	- 4	-s-   DI	 ?BI	 BI	 	K	K$A+KKr  r4   c           
        K   	 |                      |           d {V  d S # t          $ rO}t                              d| j        t          |                    d                    |d           Y d }~d S d }~ww xY w)Nz([%s] unhandled inbound error from=%s: %sr  T)exc_info)_process_messager   r   rI  r  rS   r   )r   r  r   s      r/   r  z#WeixinAdapter._process_message_safe]  s      	K''00000000000 	K 	K 	KLLCTYPXY`YdYdesYtYtPuPuwz  FJL  K  K  K  K  K  K  K  K  K	Ks   ! 
A:AA55A:c                  K   | j         J t          |                    d          pd                                          }|sd S || j        k    rd S t          |                    d          pd                                          }|r| j                            |          rd S |                    d          pg }t          |          }|r{d| dt          j	        |
                                                                           }| j                            |          r#t                              d| j        |           d S t          || j                  \  }}|dk    r$| j        d	k    rd S | j        d
k    r|| j        vrd S n|                     |          sd S t          |                    d          pd                                          }	|	r!| j                            | j        ||	           t+          j        |                     ||	pd                      g }
g }|D ]}}|                     ||
|           d {V  |                    d          pi }|                    d          }t3          |t4                    r|                     ||
|           d {V  ~|s|
sd S |                     ||||          }t9          |t;          ||          |||pd |
|t=          j                              }t                               d| j        tC          |          |j"        tG          |
                     | $                    |           d {V  d S )Nr  r+   
message_idr@  zcontent:rW   z6[%s] Content-dedup: skipping duplicate message from %sr  rr  	allowlistr8  r  r  )chat_id	chat_typer   	user_name)r	  r>  sourceraw_messager  
media_urlsr  	timestampz%[%s] inbound from=%s type=%s media=%d)%r  rM   r   rP   r  r  is_duplicater  hashlibmd5r   	hexdigestr   r  r  r  r  r  _is_dm_allowedr}  r   r4  r  _maybe_fetch_typing_ticket_collect_mediar   dictbuild_sourcer   r&  r   nowr   rS   r  rQ   handle_message)r   r  	sender_idr  r@  r	  content_keyr  effective_chat_idr8  media_pathsr  r~  ref_messager  r  events                    r/   r  zWeixinAdapter._process_messagec  s     !---N339r::@@BB	 	F(((F\228b99??AA
 	$+22:>> 	F KK,,2	Y'' 	YYYYT[[]]1K1K1U1U1W1WYYK{''44 UW[W`bklll'7AQ'R'R$	$!Z//![005FdNd5d5d$$Y// 	FGKK88>B??EEGG 	N!!$"2I}MMMD;;I}G\X\]]^^^!#!# 	N 	ND%%dKEEEEEEEEE((9--3K"~66H(D)) N))(KMMMMMMMMM 	K 	F""%	 # 
 
 1+tDD!)T"#lnn	
 	
 	
 	;TYQZH[H[]c]mors~oo  	A  	A  	A!!%(((((((((((r1   r  rM   c                H    | j         dk    rdS | j         dk    r	|| j        v S dS )Nrr  Fr  T)r  r  )r   r  s     r/   r  zWeixinAdapter._is_dm_allowed  s4    ?j((5?k)) 000tr1   r~  r  r  c                  K   |                     d          }|t          k    rK|                     |           d {V }|r,|                    |           |                    d           d S d S |t          k    rK|                     |           d {V }|r,|                    |           |                    d           d S d S |t          k    rN|                     |           d {V \  }}|r,|                    |           |                    |           d S d S |t          k    rI| 	                    |           d {V }|r.|                    |           |                    d           d S d S d S )Nr;  z
image/jpegz	video/mp4z
audio/silk)
r   r  _download_imager  r	  _download_videor
  _download_filer  _download_voice)r   r~  r  r  	item_typer   mime
voice_paths           r/   r  zWeixinAdapter._collect_media  s     HHV$$	
""--d33333333D 1""4(((""<000001 1 *$$--d33333333D 0""4((("";/////0 0 )###22488888888JD$ )""4(((""4((((() ) *$$#33D99999999J 1"":...""<00000	 %$1 1r1   rJ   c                  K   t          |d          }	 t          | j        | j        |                    d          |                    d          pi                     d          rtt          j        t                              t          |                    d          pi                     d                                        
                    d          p|                    d          |                    d          d           d {V }t          |d	          S # t          $ r,}t                              d
| j        |           Y d }~d S d }~ww xY w)N
image_itemencrypt_query_paramrX  r~   aes_keyr  g      >@r   r   r   r  rh  .jpgz[%s] image download failed: %s)r  r  r  r  r   r   r   r_   r  rM   r   r   r   r   r   r  r   r~  r  r^   r   s        r/   r  zWeixinAdapter._download_image  s_      |44	4"!/&+ii0E&F&F!XXl339r>>xHH w$U]]38N8N8TRT7Y7YZb7c7c3d3d%e%effmmnuvv(99Y'':.. $	 	 	 	 	 	 	 	 	D *$777 	 	 	NN;TYLLL44444	s   DD   
E*!EEc           	     v  K   t          |d          }	 t          | j        | j        |                    d          |                    d          |                    d          d           d {V }t          |d          S # t          $ r,}t                              d| j	        |           Y d }~d S d }~ww xY w)	N
video_itemr  r  r  g      ^@r  z	video.mp4z[%s] video download failed: %s)
r  r  r  r  r   r   r   r   r   r  r  s        r/   r  zWeixinAdapter._download_video  s       |44	4"!/&+ii0E&F&F!IIi00:.. %        D -T;??? 	 	 	NN;TYLLL44444	s   A-B 
B8!B33B8Tuple[Optional[str], str]c           	     "  K   |                     d          pi }|                     d          pi }t          |                     d          pd          }t          |          }	 t          | j        | j        |                     d          |                     d          |                     d          d	           d {V }t          ||          |fS # t          $ r/}t          	                    d
| j
        |           d |fcY d }~S d }~ww xY w)N	file_itemr  	file_namezdocument.binr  r  r  rg  r  z[%s] file download failed: %s)r   rM   r  r  r  r  r   r   r   r   r  )r   r~  r  r  r  r  r^   r   s           r/   r  zWeixinAdapter._download_file  s9     HH[))/R	g&&,"y}}[11C^DD"8,,	4"!/&+ii0E&F&F!IIi00:.. $        D -T8<<dBB 	 	 	NN:DIsKKK:	s   %A/C 
D$D	D	Dc           	       K   |                     d          pi }|                     d          pi }|                     d          rd S 	 t          | j        | j        |                     d          |                     d          |                     d          d           d {V }t	          |d	          S # t
          $ r,}t                              d
| j        |           Y d }~d S d }~ww xY w)Nr  r  r	  r  r  r  rg  r  .silkz[%s] voice download failed: %s)	r   r  r  r  r   r   r   r   r  )r   r~  r  r  r^   r   s         r/   r  zWeixinAdapter._download_voice  s     XXl++1r
w''-2>>&!! 	4	4"!/&+ii0E&F&F!IIi00:.. $        D *$888 	 	 	NN;TYLLL44444	s   	A-B7 7
C-!C((C-r   r8  c                  K   | j         r| j        sd S | j                            |          rd S 	 t	          | j         | j        | j        ||           d {V }t          |                    d          pd          }|r| j                            ||           d S d S # t          $ r:}t          
                    d| j        t          |          |           Y d }~d S d }~ww xY w)N)r   r   r   r8  rH  r+   z [%s] getConfig failed for %s: %s)r  r  r~  r   rP  r  rM   r   r   r   r  r  rS   )r   r   r8  r*  rH  r   s         r/   r  z(WeixinAdapter._maybe_fetch_typing_ticket  s6     ! 	 	F!!'** 	F	`("k+        H  _ = = CDDM ?"&&w>>>>>? ? 	` 	` 	`LL;TYQXHYHY[^_________	`s   A*B 
C"(/CC"r  c                8    t          || j        | j                  S rb   )r  MAX_MESSAGE_LENGTHr  r   r  s     r/   _split_textzWeixinAdapter._split_text  s!    .T,d.L
 
 	
r1   r  chunkr9  c                 K   d}d}t          | j        dz             D ]}	 t          | j        | j        | j        ||||           d{V }|rt          |t                    r|                    d          }	|                    d          }
|	|	dvs|
|
dvr|	t          k    p.|
t          k    p#t          |	|
|                    d                    }|rv|st|rrd	}d}| j        j                            | j                            | j        |          d           t                               d
| j        t'          |                     2|	t(          k    p
|
t(          k    }|r|                    d          p|                    d          pd}t+          d|	 d|
 d|           }|| j        k    r n1| j        dz  }t                               d| j        t'          |          |           t/          j        |           d{V  |                    d          p|                    d          pd}t+          d|	 d|
 d|            dS # t2          $ r}|}|| j        k    rY d}~ nx| j        |dz   z  }t                               d| j        t'          |          |dz   | j        dz   ||           |dk    rt/          j        |           d{V  Y d}~d}~ww xY w|J |)uh  Send a single text chunk with per-chunk retry and backoff.

        On session-expired errors (errcode -14), automatically retries
        *without* ``context_token`` — iLink accepts tokenless sends as a
        degraded fallback, which keeps cron-initiated push messages working
        even when no user message has refreshed the session recently.
        NFr2   r   r   r7  r	  r8  r9  r#   r%   )r   r&   Tz;[%s] session expired for %s; retrying without context_tokenrA  zrate limitedz$iLink sendmessage rate limited: ret=z	 errcode=z errmsg=r   z8[%s] rate limited for %s; backing off %.1fs before retryr,   ziLink sendmessage error: ret=zA[%s] send chunk failed to=%s attempt=%d/%d, retrying in %.2fs: %sr   )ranger  rG  r  r  r  r   r  r   r  r0   r}  r   r   r   r  r   r   r  rS   r-   r'  r  r4  rQ  r   )r   r  r  r8  r9  
last_errorretried_without_tokenattemptrespr#   r%   is_session_expiredis_rate_limitedr&   r8  r   s                   r/   _send_text_chunkzWeixinAdapter._send_text_chunk$  s      +/
 %T59:: K	. K	.GJ.*&!^+"/'          .JtT22 .((5//C"hhy11GCtOOATY`hlYlYl#:: W&*AAW4S'488HCUCUVV + . 
%6K 
%P] 
%481,0M -488 $ 1 6 6t7G Q QSW   #NN ] $	8G+<+<   %  #55 =&*<< ( + %%)XXh%7%7%\488E??%\nF *6 ns n nU\ n nfl n n* *J  '$*BBB %#'#G!#KD"NN Z $	8G+<+<d   #*-"5"55555555$!%(!3!3!Ytxx!Y/*cCcc'cc[acc    . . . 
d666EEEEE;w{KWIW%%aK,q0   !88!----------.  %%%s4   D,IA'I:AIAI
K( K#3A*K##K(Nreply_tometadatar   r   c                   K    j         r j        st          dd          S  j                             j                  }d }                     |          \  }}                     |          \  }	}
                     |
          \  }}h dh dh ddd fd}	 |D ]Q\  }}	  |||           d {V  # t          $ r,}t                              d j        ||           Y d }~Jd }~ww xY w|D ]O}	  ||d           d {V  # t          $ r,}t                              d j        ||           Y d }~Hd }~ww xY wd                                           |                    D             }t          |          D ]\  }}dt!          j                    j         }                     |||           d {V  |}|t)          |          dz
  k     r* j        dk    rt-          j         j                   d {V  t          d|          S # t          $ rW}t                              d j        t3                    |           t          dt5          |                    cY d }~S d }~ww xY w)NFNot connectedsuccessrI  >   .m4a.mp3.ogg.wav.flac.opus>   .3gp.avi.mkv.mov.mp4.webm>   .gifr  .png.jpeg.webpr   rM   is_voicer)   r(   r   c                p  K   t          |           j                                        }|s|v r                     |            d {V  d S |v r                     |            d {V  d S |v r                     |            d {V  d S                     |            d {V  d S )N)r  
audio_pathr  )r  
video_pathr  )r  
image_pathr  )r  	file_pathr  )r   suffixr.   
send_voice
send_videosend_image_filesend_document)	r   r  ext_AUDIO_EXTS_IMAGE_EXTS_VIDEO_EXTSr  r  r   s	      r/   _deliver_mediaz*WeixinAdapter.send.<locals>._deliver_media  s     t**#))++C ]3+--oog$QYoZZZZZZZZZZZ##oog$QYoZZZZZZZZZZZ##**7tV^*___________((DS[(\\\\\\\\\\\r1   z%[%s] media delivery failed for %s: %s)r  z*[%s] local file delivery failed for %s: %sc                >    g | ]}||                                 |S rF   r  r  s     r/   r  z&WeixinAdapter.send.<locals>.<listcomp>  s0    iiiAYZi_`_f_f_h_hiaiiir1   hermes-weixin-)r  r  r8  r9  r2   r   Tr  r  z[%s] send failed to=%s: %sF)r   rM   r  r)   r(   r   )r  r  r   r}  r   r  extract_mediaextract_imagesextract_local_filesr   r   r   r  r  format_messager  uuiduuid4hexr  rQ   r  r4  rQ  rI  rS   rM   )r   r  r  r  r  r8  last_message_idmedia_filescleaned_content_image_cleanedlocal_filesfinal_contentr+  
media_pathr  r   r!  r  r  r  r9  r(  r)  r*  s   ``  `                 @@@r/   sendzWeixinAdapter.send  s      ! 	D 	De?CCCC)--d.>HH)- (,'9'9''B'B$_..??=%)%=%=m%L%L"]HHHGGG@@@		] 		] 		] 		] 		] 		] 		] 		] 		] 		] 		] 		]	=(3 h h$
Hh(.X>>>>>>>>>>  h h hNN#JDIWacfggggggggh ) l l	l(.UCCCCCCCCCCC  l l lNN#OQUQZ\egjkkkkkkkkl ji!1!1$2E2Em2T2T!U!UiiiF'// 
H 
H
U?TZ\\-=??	++#"/'	 ,          #,Vq((T-Ka-O-O!-(FGGGGGGGGGdGGGG 	= 	= 	=LL5ty(7BSBSUXYYYe3s88<<<<<<<<<	=st   +H% 3CH% 
C<"C72H% 7C<<H% DH% 
E""E	H% 	ECH% %
J/AJ;JJc                T  K   | j         r| j        sd S | j                            |          }|sd S 	 t	          | j         | j        | j        ||t                     d {V  d S # t          $ r:}t          	                    d| j
        t          |          |           Y d }~d S d }~ww xY w)Nr   r   r  rH  r(  z#[%s] typing start failed for %s: %s)r  r  r~  r   rM  r  TYPING_STARTr   r   r  r  rS   )r   r  r  rH  r   s        r/   send_typingzWeixinAdapter.send_typing  s      ! 	 	F*..w77 	F
	c"k"+#             	c 	c 	cLL>	8T[K\K\^abbbbbbbbb	c   /A# #
B'-/B""B'c                T  K   | j         r| j        sd S | j                            |          }|sd S 	 t	          | j         | j        | j        ||t                     d {V  d S # t          $ r:}t          	                    d| j
        t          |          |           Y d }~d S d }~ww xY w)NrA  z"[%s] typing stop failed for %s: %s)r  r  r~  r   rM  r  TYPING_STOPr   r   r  r  rS   )r   r  rH  r   s       r/   stop_typingzWeixinAdapter.stop_typing  s      ! 	 	F*..w77 	F
	b"k"+"             	b 	b 	bLL=ty(SZJ[J[]`aaaaaaaaa	brD  	image_urlcaptionc                  K   |                     d          r|                     |           d {V }d}nV|                    dd          }t          j                            |          st          j                            |          }d}	 |                     ||||           d {V 	 |rG|rFt          j                            |          r(	 t          j	        |           S # t          $ r Y S w xY wS S S # |rG|rFt          j                            |          r(	 t          j	        |           w # t          $ r Y w w xY ww w w xY w)N)zhttp://r?  Tzfile://r+   F)rI  r  )r   _download_remote_mediareplacer  r   isabsabspathr&  r   unlinkr   )r   r  rH  rI  r  r  r!  cleanups           r/   
send_imagezWeixinAdapter.send_image  s       788 	"99)DDDDDDDDIGG!)))R88I7==++ 7GOOI66	G	++GYZb+ccccccccc 9 	)B)B Ii((((   D  w 9 	)B)B Ii((((   D  sB   C8 C%%
C21C28$ED21E2
D?<E>D??Er   c                H   K   ~~|                      ||||           d {V S )N)r  r!  rI  r  )r&  )r   r  r   rI  r  r  kwargss          r/   r%  zWeixinAdapter.send_image_file  sT       f'' 	 ( 
 
 
 
 
 
 
 
 	
r1   r!  r  c                v  K   ~~~~| j         r| j        st          dd          S 	 |                     |||pd           d {V }t          d|          S # t          $ rW}	t
                              d| j        t          |          |	           t          dt          |	                    cY d }	~	S d }	~	ww xY w)NFr	  r
  r+   Tr.  z#[%s] send_document failed to=%s: %s
r  r  r   
_send_filer   r   rI  r  rS   rM   )
r   r  r!  rI  r  r  r  rS  r  r   s
             r/   r&  zWeixinAdapter.send_document  s       x6! 	D 	De?CCCC	=#w	7=bQQQQQQQQJdzBBBB 	= 	= 	=LL>	8T[K\K\^abbbe3s88<<<<<<<<<	=   /A 
B8!AB3-B83B8r  c                n  K   | j         r| j        st          dd          S 	 |                     |||pd           d {V }t          d|          S # t          $ rW}t
                              d| j        t          |          |           t          dt          |                    cY d }~S d }~ww xY w)NFr	  r
  r+   Tr.  z [%s] send_video failed to=%s: %srU  )r   r  r  rI  r  r  r  r   s           r/   r$  zWeixinAdapter.send_video'  s       ! 	D 	De?CCCC	=#w
GMrRRRRRRRRJdzBBBB 	= 	= 	=LL;TYQXHYHY[^___e3s88<<<<<<<<<	=s   /A 
B4AB/)B4/B4r  c                v  K   | j         r| j        st          dd          S |pd}	 |                     |||d           d {V }t          d|          S # t          $ rW}t
                              d| j        t          |          |           t          dt          |                    cY d }~S d }~ww xY w)	NFr	  r
  z[voice message as attachment]Tforce_file_attachmentr.  z [%s] send_voice failed to=%s: %srU  )	r   r  r  rI  r  r  fallback_captionr  r   s	            r/   r#  zWeixinAdapter.send_voice8  s      ! 	D 	De?CCCC
 #E&E
	=# &*	  /          J dzBBBB 	= 	= 	=LL;TYQXHYHY[^___e3s88<<<<<<<<<	=rW  r)  c                   K   ddl m}  |          st          d            j        J  fd}t	          j         |            d           d {V }t                              dd          d                   j        pd	}t          j
        d
|          5 }|                    |           |j        cd d d            S # 1 swxY w Y   d S )Nr   )is_safe_urlz&Blocked unsafe URL (SSRF protection): c                    K   j                                       4 d {V } |                                  |                                  d {V cd d d           d {V  S # 1 d {V swxY w Y   d S rb   )r  r   rk  r_  )r*  r   r)  s    r/   	_do_fetchz7WeixinAdapter._download_remote_media.<locals>._do_fetch\  s     )--c22 - - - - - - -h))+++%]]__,,,,,,- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -s   .A%%
A/2A/r!   rb  rO   r2   z.binF)deleter"  )tools.url_safetyr^  r  r  r4  re  r   r  r"  tempfileNamedTemporaryFilewriter  )r   r)  r^  r`  r^   r"  handles   ``     r/   rK  z$WeixinAdapter._download_remote_mediaS  sV     000000{3 	MKcKKLLL!---	- 	- 	- 	- 	- 	- %iikk2>>>>>>>>>ciiQ''*++2<f(fEEE 	LL;	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   !C

CCr   r[  c                d  K   | j         | j        J t          |                                          }|                     ||          \  }}t          j        d          }t          j        d          }	t          |          }
t          j
        |                                          }t          | j         | j        | j        ||||
|t          |
          |	                                
  
         d {V }t!          |                    d          pd          }t!          |                    d          pd          }t%          ||	          }|r|}n+|rt'          | j        ||          }nt+          d|           t-          | j         ||           d {V }| j                            | j        |          }t3          j        |	                                                    d	                                        d	          }||t          |          |
t          |          j        |d
}|t<          k    r$|                    d          rd|d<   d|d<   d|d<    |di |}d }|rYdtA          j!                    j         }tE          | j         | j        | j        || #                    |          ||           d {V  dtA          j!                    j         }tI          | j         | j        tJ          dd||tL          tN          |gd|rd|ini i| j        tP                     d {V  |S )NrZ  r]   )	r   r   r  rQ  r   rR  rS  rT  rU  r   r+   upload_full_urlz@getUploadUrl returned neither upload_param nor upload_full_url: )rq   r[  r~   )r  aes_key_for_apiciphertext_sizeplaintext_sizer  rS  r  r  encode_typei]  sample_ratebits_per_sampler-  r  rA  r=  r8  r1  rF   ))r  r  r   
read_bytes_outbound_media_builderr   	token_hexr   rQ   r  r  r  rZ  r  r{   r6  rM   r   rp   r   r  r'  rf  r}  r  r   r   r   r   r  MEDIA_VOICEru   r4  r5  rG  r3  r+  rE  rB  rC  rF  )r   r  r   rI  r[  re   rQ  item_builderr   r  rR  rS  upload_responser   rh  rq   r[  r   r8  ri  item_kwargs
media_itemr7  s                          r/   rV  zWeixinAdapter._send_filef  s      !-$+2I2I2IJJ))++	#'#?#?\q#?#r#r 
L#B''%b))i..[++5577
 /^+!!%g..{{}}!
 !
 !
 
 
 
 
 
 
 ?..~>>D"EEo112CDDJKK(G<<

  	u(JJ 	u();\7SSJJsbqssttt&8!!'
 '
 '
 !
 !
 !
 !
 !
 !

 )--d.>HH !*7;;==+?+?+H+HIIPPQXYY#8.":%T

$
 
 $$w)?)?$)*K&).K&-/K)*!\00K00
 
	Atz||/?AAO"k((11+)          >4:<<+;==^$$&")!0$0%5",  <IP77b
 +%!
 
 
 	
 	
 	
 	
 	
 	
 	
$ r1   c                J   t          j        |          d         pd}|                    d          r
t          d fS |                    d          r
t          d fS |                    d          r|s
t          d fS |                    d	          r
t          d
 fS t          d fS )Nr   r^  r  c                 H    t           | d         | d         dd| d         ddS )Nr  ri  r2   r  r  encrypt_typerj  )r  mid_size)r;  r  )r  kws    r/   <lambda>z7WeixinAdapter._outbound_media_builder.<locals>.<lambda>  sG    " 022G/H#%&7#8() 
 !##4 5 
. 
. r1   r  c                     t           | d         | d         dd| d         |                     dd          |                     dd	          d
dS )Nr  ri  r2   ry  rj  play_lengthr   rS  r+   )r  
video_sizer  	video_md5)r;  r  )r	  r   r|  s    r/   r~  z7WeixinAdapter._outbound_media_builder.<locals>.<lambda>  si    " 022G/H#%&7#8() 
 #%%6"7#%66-#;#;!#b!9!9	 	. . r1   r  c            	         t           | d         | d         dd|                     d          |                     d          |                     d          |                     dd	          d
dS )Nr  ri  r2   ry  rl  rn  rm  playtimer   )r  rl  rn  rm  r  )r;  r  )r  r   r|  s    r/   r~  z7WeixinAdapter._outbound_media_builder.<locals>.<lambda>  s}    " 022G/H#%&7#8() 
 $&66-#8#8')vv.?'@'@#%66-#8#8 "z1 5 5
 
. . r1   r  c                 p    t           | d         | d         dd| d         t          | d                   ddS 	Nr  ri  r2   ry  r  rk  )r  r  rQ   )r;  r  r
  rM   r|  s    r/   r~  z7WeixinAdapter._outbound_media_builder.<locals>.<lambda>  sV    ! 022G/H#%&7#8() 
 "$Jr"2344 - - r1   c                 p    t           | d         | d         dd| d         t          | d                   ddS r  r  r|  s    r/   r~  z7WeixinAdapter._outbound_media_builder.<locals>.<lambda>   sV     ,..C+D!"34$% 
  
^2./00 )
 )
 r1   )r  r  r   MEDIA_IMAGEMEDIA_VIDEOru   rr  
MEDIA_FILE)r   r   r[  r  s       r/   rp  z%WeixinAdapter._outbound_media_builder  s    #D))!,J0J??8$$ 	 
! 
! 
 
 ??8$$ 	 ! !   ==!! 	*? 	 ! !   ??8$$ 	        
 
 
 	
r1   c                D   K   |                     d          rdnd}|||dS )Nz	@chatroomr  r  )r  r;  r  )ru   )r   r  r  s      r/   get_chat_infozWeixinAdapter.get_chat_info  s2      &//<<FGG$	wGGGr1   c                B    |dS t          t          |                    S )Nr+   )r  r  r  s     r/   r3  zWeixinAdapter.format_message  s#    ?234Nw4W4WXXXr1   )r`  r   )rI   r   r(   r  r(   r)   )r(   r   )r  r4   r(   r   )r  rM   r(   r)   )r~  r4   r  r  r  r  r(   r   )r~  r4   r(   rJ   )r~  r4   r(   r  )r   rM   r8  rJ   r(   r   r  rM   r(   r  )
r  rM   r  rM   r8  rJ   r9  rM   r(   r   )NN)
r  rM   r  rM   r  rJ   r  r   r(   r   rb   )r  rM   r  r   r(   r   )r  rM   r(   r   )r  rM   rH  rM   rI  rM   r  rJ   r  r   r(   r   )NNN)r  rM   r   rM   rI  rJ   r  rJ   r  r   r(   r   )NNNN)r  rM   r!  rM   rI  rJ   r  rJ   r  rJ   r  r   r(   r   )r  rM   r  rM   rI  rJ   r  rJ   r  r   r(   r   )r  rM   r  rM   rI  rJ   r  rJ   r  r   r(   r   )r)  rM   r(   rM   r/  )
r  rM   r   rM   rI  rM   r[  r)   r(   rM   )r   rM   r[  r)   )r  rM   r(   r4   )r  rJ   r(   rM   )%r   r   r   r   r  SUPPORTS_MESSAGE_EDITINGr   staticmethodr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r?  rC  rG  rQ  r%  r&  r$  r#  rK  rV  rp  r  r3  __classcell__)r  s   @r/   r_  r_    s?       ==  %0f 0f 0f 0f 0f 0fd B B B \B. . . .`4 4 4 4(:- :- :- :-xK K K KA) A) A) A)F   1 1 1 1.   $       &   &` ` ` `&
 
 
 

^ ^ ^ ^H #'-1?= ?= ?= ?= ?=Bc c c c c$b b b b. #'-1    : "&"&-1
 
 
 
 
* "&#'"&-1= = = = =0 "&"&-1= = = = =* "&"&-1= = = = =6   0 ',` ` ` ` `DC
 C
 C
 C
 C
JH H H HY Y Y Y Y Y Y Yr1   r_  )r8  r{  r  r8   Optional[List[Tuple[str, bool]]]c                h	  K   t          |                     d          pt          j        dd                                                    }t          |                     d          pt          j        dt
                                                                                  d          }t          |                     d          pt          j        dt                                                                                  d          }t          |p)|                     d	          pt          j        d
d                                                    }|sddiS |sddiS t          t          t                                          }	|	
                    |           |	                    ||          }
t                              |          }t          |dd          }||
|j        s|j        t          j                    u rd}|                    |          }|r/|                    ||           d{V }|j        sdd|j         iS |pg D ]}\  }}t+          |          j                                        }|dv r|                    ||           d{V }n|                    ||           d{V }|j        sdd|j         ic S ~dd||r|j        ndt7          |
          dS t9          j        dt=                                4 d{V }t?          tA          d|i tC          | pi           |||d                    }||_"        ||_#        ||_$        ||_%        ||_&        ||_'        |	|_(        d}|                    |          }|rA|                    ||           d{V }|j        sdd|j         icddd          d{V  S |pg D ]\  }}t+          |          j                                        }|dv r|                    ||           d{V }n|                    ||           d{V }|j        s dd|j         ic cddd          d{V  S dd||r|j        ndt7          |
          dcddd          d{V  S # 1 d{V swxY w Y   dS )z
    One-shot send helper for ``send_message`` and cron delivery.

    This bypasses the long-poll adapter lifecycle and uses the raw API directly.
    r   rb  r+   r   rd  r   r   re  r   rc  rI  zGWeixin token missing. Configure WEIXIN_TOKEN or platforms.weixin.token.z\Weixin account ID missing. Configure WEIXIN_ACCOUNT_ID or platforms.weixin.extra.account_id.r  NzWeixin send failed: >   .bmpr  r  r  r  r  zWeixin media send failed: Tr   )r  platformr  r  context_token_usedr1  )r   r   r   )enabledr   r{  ))rM   r   r  r  rP   rF  r   re  r   r   r   r5   getattrr  _loopr4  get_running_loopr3  r?  r  rI  r   r"  r.   r%  r&  r  r)   r?   rE  rB   r_  r   r  r  _sessionr  r  r  r  r}  )r{  r   r  r  r8  r   r   r   resolved_tokentoken_storer8  live_adaptersend_sessionlast_resultcleanedr>  	_is_voicer'  r  adapters                       r/   send_weixin_directr    sy      UYY|,,R	:Mr0R0RSSYY[[J599Z((XBI6G,X,XYY__aahhilmmHuyy00iBI>SUh4i4ijjpprryyz}~~LU%))G"4"4U	.RT8U8UVV\\^^N dbcc ywxx#C(9(9$:$:;;K
###OOJ88M!%%n55L<$??L \%= ' &>"g&>&@&@@@,0--g66 	M , 1 1'7 C CCCCCCCK& M!K8I!K!KLL%0%6B 	S 	S!J	z"")//11CHHH$0$@$@*$U$UUUUUUU$0$>$>w
$S$SSSSSSS& S!Qk>O!Q!QRRRRS  4?I+00T"&}"5"5
 
 	
 $t?R?T?TUUU +
 +
 +
 +
 +
 +
 +
Y`$5;B''", ($0	  	 	 	
 
 !("'($ ,*,0((11 	M 'Wg > >>>>>>>K& M!K8I!K!KL5+
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
8 &1%6B 	S 	S!J	z"")//11CHHH$+$;$;GZ$P$PPPPPPP$+$9$9':$N$NNNNNNN& S!Qk>O!Q!QRRRG+
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
DS  4?I+00T"&}"5"5
 
K+
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
s    B/R!B R!0R!!
R+.R+)r#   r$   r%   r$   r&   r'   r(   r)   )r(   r6   r  )rH   )rI   rJ   rK   rL   r(   rM   )rT   r4   r(   rM   )r]   )r^   r_   r`   rL   r(   r_   )re   r_   rf   r_   r(   r_   )rq   r_   rf   r_   r(   r_   )rx   rL   r(   rL   rc  )r(   r4   )r   rJ   r   rM   r(   r   )r   rM   r(   r   )r   rM   r   rM   r(   r   )r   rM   r   rM   r   rM   r   rM   r   rM   r(   r   )r   rM   r   rM   r(   r   )r   rM   r   rM   r(   rM   )r   rM   r   rM   r   rM   r(   rM   )r   rM   r(   r_   )r  r4   r   rM   r(   r  )r  r  r   rM   r  rM   rT   r4   r   rJ   r  rL   r(   r4   )
r  r  r   rM   r  rM   r  rL   r(   r4   )r  r  r   rM   r   rM   r.  rM   r  rL   r(   r4   )r  r  r   rM   r   rM   r7  rM   r	  rM   r8  rJ   r9  rM   r(   r4   )r  r  r   rM   r   rM   r  rM   rH  rM   r(  rL   r(   r   )r  r  r   rM   r   rM   r   rM   r8  rJ   r(   r4   )r  r  r   rM   r   rM   r  rM   rQ  rL   r   rM   rR  rL   rS  rM   rT  rL   rU  rM   r(   r4   )r  r  rq   r_   r[  rM   r(   rM   )r  r  r)  rM   rh  r   r(   r_   )r)  rM   r(   r   )r~  r4   rf   rM   r(   r4   )r  r  r   rM   r   rJ   r   rJ   r  rJ   rh  r   r(   r_   )r  rM   r(   rM   )r  rM   r(   r  )r  rM   r(   rM   )r  r  r(   rM   )r  rM   r(   rM   r  )r  rM   r(   r)   )r  rM   r(   r)   )r  rM   r  rL   r(   r  r/  )r  rM   r  rL   r  r)   r(   r  )T)rI   r   r  r)   r(   r)   )r@  r  r(   rM   )r  r  r	  rM   r(   r   )r   rM   r   rM   r(   rM   )r   rM   r   rM   r.  rM   r(   r   )r   rM   r.  rM   rh  rL   r(   r/  )r{  r4   r   rJ   r  rM   r  rM   r8  r  r(   r4   )r   
__future__r   r4  r   r  rZ   loggingr  r  r  r   r   rc  r  r   r4  r   pathlibr   typingr   r   r   r	   r
   urllib.parser   r   	getLoggerr   r   r  r?   r<   r;   cryptography.hazmat.backendsr   &cryptography.hazmat.primitives.ciphersr   r   r   rE   gateway.configr   r   gateway.platforms.helpersr   gateway.platforms.baser   r   r   r   r   r   r   hermes_constantsr   utilsr   rF  re  r   r   r   r3  rE  rK  rO  rY  rG  rP  r  rF  rL  rH  r  r  r  r  r-   r  r0   r  r  r  rr  r5   __annotations__rB   rD  r  r  r
  r	  MSG_TYPE_USERrB  rC  rB  rF  compiler  r  r  _MARKDOWN_LINK_RErG   rS   r\   rd   rp   rw   r{   r   r   r   r   r   r   r   r   r   r   r   r
  r  r+  r-  r6  rG  rM  rP  rZ  rf  rm  	frozensetru  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r&  r(  r*  r,  r]  r_  r  rF   r1   r/   <module>r     sO	  
 
 
 # " " " " "          				 				                   3 3 3 3 3 3 3 3 3 3 3 3 3 3 ( ( ( ( ( ( ( (		8	$	$ NNN   G
<<<<<<PPPPPPPPPP   OFJE 4 3 3 3 3 3 3 3 9 9 9 9 9 9                  - , , , , , # # # # # #0= 3 ')'%, *0         5 5 5 5 
!# # # # #- - - -& 	

	
 RZ011
RSSBJ+,,	BJ9:: 2 2 2 2
    J J J J- - - - -
J J J J	 	 	 	( ( ( (H H H H
0 0 0 0      < < < <      .   /m /m /m /m /m /m /m /md5 5 5 5 5 5 5 5(p p p p   R R R R8 8 8 8   &   (C C C C*" " " "J   .   *   @= = = =@ "	K K K K K K )2	  
) 
)  
 
 
 

 
 
 
(4 4 4 4   2K K K K5 5 5 5    M  M  M  MF% % % %<%& %& %& %&P#0 #0 #0 #0L$, $, $, $,N   (C C C CK K K K   0 ;@)N )N )N )N )NX    $   :   A A A A   ; ; ; ; 	D D D D D DN|Y |Y |Y |Y |Y' |Y |Y |YJ 59b
 b
 b
 b
 b
 b
 b
 b
s$   7A> >	B
	B
B! !B32B3