
    `gj#-                      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mZ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 	 ddlZdZ	 ddlZdZdd
lm Z m!Z! ddl"m#Z# ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* ddl+m,Z,  ejZ                  e.      Z/dZ0dZ1dZ2dZ3dZ4dZ5dZ6dZ7dZ8dZ9dZ:e2e3hZ;e;e4hz  Z<dZ=dZ>dZ?dZ@g dZAdZBdZCdZDd ZEd!ZFeFZGd"ZHd#ZId$hZJd:d%ZKd;d&ZLd<d'ZMd=d(ZN G d) d*e%      ZOd+ZPd,ZQd-ZRd.ZSd/ZTeTd0	 	 	 d>d1ZUddd	d2d3ZVd?d4ZWd:d5ZXd:d6ZYd7 ZZd8 Z[d?d9Z\y# e$ r d	ZdZY w xY w# e$ r d	ZdZY w xY w)@a8  
WeCom (Enterprise WeChat) platform adapter.

Uses the WeCom AI Bot WebSocket gateway for inbound and outbound messages.
The adapter focuses on the core gateway path:

- authenticate via ``aibot_subscribe``
- receive inbound ``aibot_msg_callback`` events
- send outbound markdown messages via ``aibot_send_msg``
- upload outbound media via ``aibot_upload_media_*`` and send native attachments
- best-effort download of inbound image/file attachments for agent context

Configuration in config.yaml:
    platforms:
      wecom:
        enabled: true
        extra:
          bot_id: "your-bot-id"          # or WECOM_BOT_ID env var
          secret: "your-secret"          # or WECOM_SECRET env var
          websocket_url: "wss://openws.work.weixin.qq.com"
          dm_policy: "pairing"           # open | allowlist | disabled | pairing
          allow_from: ["user_id_1"]
          group_policy: "pairing"        # open | allowlist | disabled | pairing
          group_allow_from: ["group_id_1"]
          groups:
            group_id_1:
              allow_from: ["user_id_1"]
    )annotationsN)datetimetimezone)Path)AnyDictListOptionalTuple)unquoteurlparseTF)PlatformPlatformConfig)MessageDeduplicator)BasePlatformAdapterMessageEventMessageType
SendResultcache_document_from_bytescache_image_from_bytes)	env_floatzwss://openws.work.weixin.qq.comaibot_subscribeaibot_msg_callbackaibot_callbackaibot_event_callbackaibot_send_msgaibot_respond_msgpingaibot_upload_media_initaibot_upload_media_chunkaibot_upload_media_finish  g      4@g      .@      >@)      
      <   i  i   i    i  @i   d   	audio/amrc                     t         xr t        S )z2Check if WeCom runtime dependencies are available.)AIOHTTP_AVAILABLEHTTPX_AVAILABLE     R/root/.hermes/venv/lib/python3.12/site-packages/plugins/platforms/wecom/adapter.pycheck_wecom_requirementsr1   m   s    00r/   c                   | g S t        | t              r>| j                  d      D cg c]#  }|j                         s|j                         % c}S t        | t        t
        t        f      rA| D cg c]5  }t        |      j                         st        |      j                         7 c}S t        |       j                         rt        |       j                         gS g S c c}w c c}w )z0Coerce config values into a trimmed string list.,)
isinstancestrsplitstriplisttupleset)valueitems     r0   _coerce_listr=   r   s    }	%).S)9JTZZ\

JJ%$s+,.3Ids4y7HD	!II#&u:#3#3#5CJ=2= KIs   C$C$2C)C)c                    t        |       j                         }t        j                  dd|t        j                        }t        j                  dd|t        j                        }|j                         S )z7Normalize allowlist entries such as ``wecom:user:foo``.z^wecom: )flagsz^(user|group):)r5   r7   resub
IGNORECASE)rawr;   s     r0   _normalize_entryrE   }   sO    HNNEFF:r5>EFF$b%r}}EE;;=r/   c                    t        |      j                         j                         }| D ]'  }t        |      j                         }|dk(  s||k(  s' y y)z4Case-insensitive allowlist match with ``*`` support.*TF)r5   r7   lowerrE   )entriestargetnormalized_targetentry
normalizeds        r0   _entry_matchesrN      sX    F))+113 %e,224

.? ? r/   c                      e Zd ZdZeZdZdZdE fdZdddFdZdGdZ	dGdZ
dGd	ZdHd
ZdGdZdGdZdGdZdIdZdJdZdIdZefdKdZeef	 	 	 	 	 	 	 	 	 dLdZedMd       ZedNd       ZedOd       ZdIdZdPdZdQdZdRdZedSd       ZdTdZ dUdZ!edVd       Z"edWd       Z#edXdYd       Z$edZd        Z%ed[d!       Z&ed\d"       Z'e(d]d#       Z)d]d$Z*d^d%Z+d^d&Z,d_d'Z-d`d(Z.dad)Z/dbd*Z0dcd+Z1eddd,       Z2eded-       Z3edfd.       Z4edgdhd/       Z5edid0       Z6e7djd1       Z8edkd2       Z9	 	 	 	 	 	 dld3Z:edmd4       Z;	 dg	 	 	 	 	 dnd5Z<	 dg	 	 	 	 	 dod6Z=dpd7Z>dqd8Z?drd9Z@	 	 	 	 	 	 	 	 dsd:ZA	 dg	 	 	 	 	 	 	 dtd;ZB	 	 	 du	 	 	 	 	 	 	 	 	 	 	 dvd<ZC	 	 dw	 	 	 	 	 	 	 	 	 dxd=ZD	 	 	 du	 	 	 	 	 	 	 	 	 	 	 dyd>ZE	 	 dw	 	 	 	 	 	 	 	 	 dzd?ZF	 	 	 du	 	 	 	 	 	 	 	 	 	 	 d{d@ZG	 	 dw	 	 	 	 	 	 	 	 	 d|dAZH	 	 dw	 	 	 	 	 	 	 	 	 d}dBZIdgd~dCZJd`dDZK xZLS )WeComAdapterzAWeCom AI Bot adapter backed by a persistent WebSocket connection.Fi<  c                0   t         |   |t        j                         |j                  xs i }t        |j                  d      xs t        j                  dd            j                         | _
        t        |j                  d      xs t        j                  dd            j                         | _        t        |j                  d      xs- |j                  d      xs t        j                  dt                    j                         xs t        | _        t        |j                  d	      xs t        j                  d
d            j                         j                         | _        t!        |j                  d      xs) |j                  d      xs t        j                  dd            | _        t        |j                  d      xs t        j                  dd            j                         j                         | _        t!        |j                  d      xs |j                  d            | _        t)        |j                  d      t*              r|j                  d      ni | _        d | _        d | _        d | _        d | _        d | _        i | _        t;        t<              | _        i | _         tC        dd      | _"        tC        dd      | _#        i | _$        i | _%        tM        jN                         jP                  | _)        i | _*        y )Nbot_idWECOM_BOT_IDr?   secretWECOM_SECRETwebsocket_urlwebsocketUrlWECOM_WEBSOCKET_URL	dm_policyWECOM_DM_POLICYpairing
allow_from	allowFromWECOM_ALLOWED_USERSgroup_policyWECOM_GROUP_POLICYgroup_allow_fromgroupAllowFromgroups)max_size%HERMES_WECOM_TEXT_BATCH_DELAY_SECONDSg333333?+HERMES_WECOM_TEXT_BATCH_SPLIT_DELAY_SECONDSg       @)+super__init__r   WECOMextrar5   getosgetenvr7   _bot_id_secretDEFAULT_WS_URL_ws_urlrH   
_dm_policyr=   _allow_from_group_policy_group_allow_fromr4   dict_groups_session_ws_http_client_listen_task_heartbeat_task_pending_responsesr   DEDUP_MAX_SIZE_dedup_reply_req_idsr   _text_batch_delay_seconds_text_batch_split_delay_seconds_pending_text_batches_pending_text_batch_tasksuuiduuid4hex
_device_id_last_chat_req_ids)selfconfigrj   	__class__s      r0   rh   zWeComAdapter.__init__   sh   0"599X.O"))NB2OPVVX599X.O"))NB2OPVVXIIo& @yy(@yy.?
 %'	$ $	 	 eii4_		BSU^8_`ffhnnp
 (IIl# 4yy%4yy.3
 !>!:!hbiiH\^g>hiooqwwy!-eii8J.K.juyyYiOj!k.889Ld.Suyy*Y[;?@D;?487;=?)>B.0 *33Z\_)`&/89fhk/l,>@"BD&**,**24r/   )is_reconnectc                 K   t         s8d}| j                  d|d       t        j                  d| j                  |       yt
        s8d}| j                  d|d       t        j                  d| j                  |       y| j                  r| j                  s8d	}| j                  d
|d       t        j                  d| j                  |       y	 ddlm	} t        j                  dd |             | _        | j                          d{    | j                          t        j                   | j#                               | _        t        j                   | j'                               | _        t        j+                  d| j                  | j,                         y7 # t.        $ r}d| }| j                  d|d       t        j1                  d| j                  |d       | j3                          d{  7   | j                  r*| j                  j5                          d{  7   d| _        Y d}~yd}~ww xY ww)z$Connect to the WeCom AI Bot gateway.z+WeCom startup failed: aiohttp not installedwecom_missing_dependencyT)	retryablez![%s] %s. Run: pip install aiohttpFz)WeCom startup failed: httpx not installedz[%s] %s. Run: pip install httpxz@WeCom startup failed: WECOM_BOT_ID and WECOM_SECRET are requiredwecom_missing_credentialsz[%s] %sr   )platform_httpx_limitsr#   )timeoutfollow_redirectslimitsNz[%s] Connected to %szWeCom startup failed: wecom_connect_errorz[%s] Failed to connect: %s)exc_info)r,   _set_fatal_errorloggerwarningnamer-   rn   ro   %gateway.platforms._http_client_limitsr   httpxAsyncClientrz   _open_connection_mark_connectedasynciocreate_task_listen_loopr{   _heartbeat_loopr|   inforq   	Exceptionerror_cleanup_wsaclose)r   r   messager   excs        r0   connectzWeComAdapter.connect   s     CG!!"<gQU!VNN>		7SAG!!"<gQU!VNN<diiQ||4<<XG!!"=wRV!WNN9dii9	S % 1 1t<Q<S!D '')))  " ' 3 3D4E4E4G HD#*#6#6t7K7K7M#ND KK.		4<<H *  	.se4G!!"7D!QLL5tyy#PTLU""$$$  ''..000$(!	s\   CH?;F FBF H?F 	H<'AH76G97.H7%H(&H72H?7H<<H?c                  K   d| _         | j                          | j                  r6| j                  j                          	 | j                   d{    d| _        | j                  r6| j                  j                          	 | j                   d{    d| _        | j                  t        d             | j                          d{    | j                  r)| j                  j                          d{    d| _
        | j                  j                          t        j                  d| j                          y7 # t        j
                  $ r Y w xY w7 # t        j
                  $ r Y w xY w7 7 w)zDisconnect from WeCom.FNzWeCom adapter disconnectedz[%s] Disconnected)_running_mark_disconnectedr{   cancelr   CancelledErrorr|   _fail_pending_responsesRuntimeErrorr   rz   r   r   clearr   r   r   r   s    r0   
disconnectzWeComAdapter.disconnect   sB    !$$&'''' !%D  '')**** $(D $$\2N%OP   ##**,,, $D'3+ ())  +)) 
 	! -s   >E;E E E -E;E EE 4E;E7-E;9E9:AE; E EE;EE;E E41E;3E44E;9E;c                @  K   | j                   r8| j                   j                  s"| j                   j                          d{    d| _         | j                  r8| j                  j                  s"| j                  j                          d{    d| _        y7 W7 w)z)Close the live websocket/session, if any.N)ry   closedcloserx   r   s    r0   r   zWeComAdapter._cleanup_ws  sm     88DHHOO((.."""==!5!5--%%''' # (s%   A BBA
BBBBc                T  K   | j                          d{    t        j                  d      | _        | j                  j	                  | j
                  t        dz  t               d{   | _        | j                  d      }| j                  t        d|i| j                  | j                  | j                  dd	       d{    | j                  |       d{   }|j!                  d
d      }|dvr#|j!                  dd      }t#        | d| d      y7 	7 7 \7 Ew)z-Open and authenticate a websocket connection.NT)	trust_envr$   )	heartbeatr   	subscribereq_id)rR   rT   	device_idcmdheadersbodyerrcoder      Nr   errmsgzauthentication failedz
 (errcode=))r   aiohttpClientSessionrx   
ws_connectrq   HEARTBEAT_INTERVAL_SECONDSCONNECT_TIMEOUT_SECONDSry   _new_req_id
_send_jsonAPP_CMD_SUBSCRIBErn   ro   r   _wait_for_handshakerk   r   )r   r   auth_payloadr   r   s        r0   r   zWeComAdapter._open_connection  s$       --=11LL014+ 2 
 
 !!+.oo($f-"ll"ll!%

 
	
 
	
 "55f==""9a0)#!%%h0GHF&G9A>?? $/ 	!

	
 >sG   D(DAD(,D"-AD(D$D( D&!?D("D($D(&D(c                  K   | j                   st        d      t        j                         j	                         t
        z   }	 |t        j                         j	                         z
  }|dk  rt        d      t        j                  | j                   j                         |       d{   }|j                  t        j                  j                  k(  r~| j                  |j                        }|s|j                  d      t         k(  r| j#                  |      |k(  r|S t$        j'                  d| j(                  |j                  d             nd|j                  t        j                  j*                  t        j                  j,                  t        j                  j.                  hv rt        d      w7 w)	z'Wait for the subscribe acknowledgement.zWebSocket not initializedr   z5Timed out waiting for WeCom subscribe acknowledgementr   Nr   z"[%s] Ignoring pre-auth payload: %sz,WeCom websocket closed during authentication)ry   r   r   get_running_looptimer   TimeoutErrorwait_forreceivetyper   	WSMsgTypeTEXT_parse_jsondatark   APP_CMD_PING_payload_req_idr   debugr   CLOSEDCLOSEERROR)r   r   deadline	remainingmsgpayloads         r0   r   z WeComAdapter._wait_for_handshake7  sV    xx:;;++-2247NN 7#;#;#=#B#B#DDIA~"#Z[[(()9)9);YOOCxx7,,111**3884;;u%5''0F:"NA499gkkZ_N`ag//668I8I8O8OQXQbQbQhQhii"#QRR! 
 Ps   B)F>+F;,DF>c                  K   d}| j                   r)	 | j                          d{    d}| j                   r(yy7 # t        j                  $ r Y yt        $ r!}| j                   sY d}~yt
        j                  d| j                  |       | j                  t        d             t        t        |t        t              dz
           }|dz  }t        j                  |       d{  7   	 | j                          d{  7   d}| j                          t
        j!                  d| j                         n7# t        $ r+}t
        j                  d| j                  |       Y d}~nd}~ww xY wY d}~Kd}~ww xY ww)z6Read websocket events forever, reconnecting on errors.r   Nz[%s] WebSocket error: %szWeCom connection interrupted   z[%s] Reconnectedz[%s] Reconnect failed: %s)r   _read_eventsr   r   r   r   r   r   r   r   RECONNECT_BACKOFFminlensleepr   r   r   )r   backoff_idxr   delayreconnect_excs        r0   r   zWeComAdapter._listen_loopO  s/    mmZ''))) mm)))  Z}}9499cJ,,\:X-YZ)#k3?P;QTU;U*VWq mmE***Z//111"#K((*KK 2DII>  ZNN#>		=YYZZs   E?= ;= E?E?= E<E?	E<E7'E?,A;E7'C*(E7.D:D7D:9E7:	E.!E)$E7)E..E71E?7E<<E?c                
  K   | j                   st        d      | j                  rV| j                   rH| j                   j                  s0| j                   j	                          d{   }|j
                  t        j                  j                  k(  r7| j                  |j                        }|r| j                  |       d{    n}|j
                  t        j                  j                  t        j                  j                  t        j                  j                  t        j                  j                  hv rt        d      | j                  r&| j                   r| j                   j                  s-yyyyyy7 7 w)z2Read websocket frames until the connection closes.zWebSocket not connectedNzWeCom websocket closed)ry   r   r   r   r   r   r   r   r   r   r   _dispatch_payloadr   r   r   CLOSING)r   r   r   s      r0   r   zWeComAdapter._read_eventsj  s    xx899mm((**Cxx7,,111**388400999g//55w7H7H7O7OQXQbQbQhQhjqj{j{  kD  kD  E  E"#;<< mmmm* :s,   A&F(E>)AFFB0F8FFc                  K   	 | j                   rt        j                  t               d{    | j                  r| j                  j
                  rP	 | j                  t        d| j                  d      ii d       d{    | j                   ryy7 i7 # t        $ r+}t        j                  d| j                  |       Y d}~Bd}~ww xY w# t        j                  $ r Y yw xY ww)z)Send lightweight application-level pings.Nr   r   r   z[%s] Heartbeat send failed: %s)r   r   r   r   ry   r   r   r   r   r   r   r   r   r   )r   r   s     r0   r   zWeComAdapter._heartbeat_loopx  s     	--mm$>???xx488??	S//#/(0$2B2B62J'K$&   --? ! SLL!A499cRRS%% 		st   C*(C B'C -B BB C C*C B 	C#!C	C 	CC C'$C*&C''C*c                  K   | j                  |      }t        |j                  d      xs d      }|rU|| j                  v rG|t        vr?| j                  j                  |      }|r!|j                         s|j                  |       y|t        v r| j                  |       d{    y|t        t        hv ryt        j                  d| j                  |xs |       y7 :w)z!Route inbound websocket payloads.r   r?   Nz#[%s] Ignoring websocket payload: %s)r   r5   rk   r}   NON_RESPONSE_COMMANDSdone
set_resultCALLBACK_COMMANDS_on_messager   APP_CMD_EVENT_CALLBACKr   r   r   )r   r   r   r   futures        r0   r   zWeComAdapter._dispatch_payload  s     %%g.'++e$*+f 7 77CG\<\,,008Ffkkm!!'*##""7+++<!788:DIIs~gV ,s   B#C"%C &;C"c                    t        | j                  j                               D ]B  \  }}|j                         s|j	                  |       | j                  j                  |d       D y)z%Fail all outstanding request futures.N)r8   r}   itemsr   set_exceptionpop)r   r   r   r   s       r0   r   z$WeComAdapter._fail_pending_responses  sV    "4#:#:#@#@#BC 	6NFF;;=$$S)##''5	6r/   c                   K   | j                   r| j                   j                  rt        d      | j                   j                  |       d{    y7 w)z0Send a raw JSON frame over the active websocket. WeCom websocket is not connectedN)ry   r   r   	send_json)r   r   s     r0   r   zWeComAdapter._send_json  s;     xx488??ABBhh  )))s   AAAAc                  K   | j                   r| j                   j                  rt        d      | j                  |      }t	        j
                         j                         }|| j                  |<   	 | j                  |d|i|d       d{    t	        j                  ||       d{   }|| j                  j                  |d       S 7 A7 $# | j                  j                  |d       w xY ww)z6Send a JSON request and await the correlated response.r  r   r   Nr   )ry   r   r   r   r   r   create_futurer}   r   r   r  )r   r   r   r   r   r   responses          r0   _send_requestzWeComAdapter._send_request  s     xx488??ABB!!#&))+99;*0'	6//#8V:LVZ"[\\\$--fgFFH##''5	 ]F ##''5sB   A0C43C CC ,C-C 2C4C C C11C4c                  K   | j                   r| j                   j                  rt        d      t        |xs d      j	                         }|st        d      t        j                         j                         }|| j                  |<   	 | j                  |d|i|d       d{    t        j                  ||       d{   }|| j                  j                  |d       S 7 A7 $# | j                  j                  |d       w xY ww)z<Send a reply frame correlated to an inbound callback req_id.r  r?   zreply_req_id is requiredr   r   Nr   )ry   r   r   r5   r7   
ValueErrorr   r   r  r}   r   r   r  )r   reply_req_idr   r   r   normalized_req_idr   r  s           r0   _send_reply_requestz WeComAdapter._send_reply_request  s      xx488??ABB 2399; 788))+99;5; 12	A//3D(EtT   %--fgFFH##''(94@ G ##''(94@sB   B	DC, &C('C, C*C, D(C, *C, ,D

Dc                H    |  dt        j                         j                   S )N-)r   r   r   )prefixs    r0   r   zWeComAdapter._new_req_id  s     4::<++,--r/   c                    | j                  d      }t        |t              rt        |j                  d      xs d      S y)Nr   r   r?   )rk   r4   rv   r5   )r   r   s     r0   r   zWeComAdapter._payload_req_id  s6    ++i(gt$w{{8,233r/   c                    	 t        j                  |       }t        |t              r|S d S # t        $ r t        j	                  d|        Y y w xY w)Nz!Failed to parse WeCom payload: %r)jsonloadsr   r   r   r4   rv   )rD   r   s     r0   r   zWeComAdapter._parse_json  sM    	jjoG %Wd3w==  	LL<cB	s   + AAc                6  K   |j                  d      }t        |t              syt        |j                  d      xs1 | j	                  |      xs t        j                         j                        }| j                  j                  |      r"t        j                  d| j                  |       y| j                  || j	                  |             t        |j                  d      t              r|j                  d      ni }t        |j                  d      xs d      j                         }t        |j                  d      xs |      j                         }|s!t        j                  d	| j                         yt        |j                  d
      xs d      j                         dk(  }|r5| j!                  ||      sVt        j                  d| j                  ||       y| j#                  |      s"t        j                  d| j                  |       y| j%                  || j	                  |             | j'                  |      \  }}	|r'|r%t)        j*                  dd|      j                         }| j-                  |       d{   \  }
}| j/                  |||      }t1        |	xr |xs |
      }|s|	r|
s|	}|s#|
s!t        j                  d| j                         y| j3                  ||rdnd|xs d|xs d      }t5        ||||||
||rd| nd|r|	ndt7        j8                  t:        j<                        
      }|t>        j@                  k(  r!| jB                  dkD  r| jE                  |       y| jG                  |       d{    y7 7 w)z0Process an inbound WeCom message callback event.r   Nmsgidz![%s] Duplicate message %s ignoredfromuseridr?   chatidz&[%s] Missing chat id, skipping messagechattypegroupz+[%s] Group %s / sender %s blocked by policyz#[%s] DM sender %s blocked by policyz^@\S+\s*z [%s] Empty WeCom message skippeddm)chat_id	chat_typeuser_id	user_namequote:)tz)
textmessage_typesourceraw_message
message_id
media_urlsmedia_typesreply_to_message_idreply_to_text	timestampr   )$rk   r4   rv   r5   r   r   r   r   r   is_duplicater   r   r   _remember_reply_req_idr7   rH   _is_group_allowed_is_dm_intake_allowed_remember_chat_req_id_extract_textrA   rB   _extract_media_derive_message_typeboolbuild_sourcer   r   nowr   utcr   r   r   _enqueue_text_eventhandle_message)r   r   r   msg_idsender	sender_idr  is_groupr#  
reply_textr(  r)  r$  has_reply_contextr%  events                   r0   r   zWeComAdapter._on_message  s    {{6"$%TXXg&[$*>*>w*G[4::<K[K[\;;##F+LL<diiP##FD,@,@,IJ%/0@$%G&!R

8,2399;	dhhx(5I6<<>LLA499Mtxx
+1r288:gE))'9=JDIIW^`ij++I6LL>		9U
 	""7D,@,@,IJ--d3j 66+r40668D(,(;(;D(A"A
K00t[I !D1CE
:DJLL;TYYG""!)gt%'4	 # 
 %!#5F& 1D(9*tllhll3
 ;+++0N0NQR0R$$U+%%e,,,I #BH -s%   JNN	DNNNNc                    ddl m}  ||j                  | j                  j                  j                  dd      | j                  j                  j                  dd      |j                  j                        S )z-Session-scoped key for text message batching.r   )build_session_keygroup_sessions_per_userTthread_sessions_per_userF)rD  rE  profile)gateway.sessionrC  r%  r   rj   rk   rF  )r   rA  rC  s      r0   _text_batch_keyzWeComAdapter._text_batch_key>  s^    5 LL$(KK$5$5$9$9:SUY$Z%)[[%6%6%:%:;UW\%]LL((	
 	
r/   c                   | j                  |      }| j                  j                  |      }t        |j                  xs d      }|||_        || j                  |<   n|j                  r8|j                  r|j                   d|j                   n|j                  |_        ||_        |j                  rJ|j                  j                  |j                         |j                  j                  |j                         | j                  j                  |      }|r |j                         s|j                          t        j                  | j                  |            | j                  |<   y)zBuffer a text event and reset the flush timer.

        When WeCom splits a long user message at 4000 chars, the chunks
        arrive within a few hundred milliseconds.  This merges them into
        a single event before dispatching.
        r?   N
)rH  r   rk   r   r#  _last_chunk_lenr(  extendr)  r   r   r   r   r   _flush_text_batch)r   rA  keyexisting	chunk_len
prior_tasks         r0   r9  z WeComAdapter._enqueue_text_eventH  s)    ""5)--11#6

(b)	$-E!.3D&&s+zzDLMM8==/EJJ< @W\WaWa'0H$##**5+;+;<$$++E,=,=> 3377<
joo/.5.A.A""3'/
&&s+r/   c                  K   t        j                         }	 | j                  j                  |      }|rt	        |dd      nd}|| j
                  k\  r| j                  }n| j                  }t        j                  |       d{    | j                  j                  |      |ur<	 | j                  j                  |      |u r| j                  j                  |d       yy| j                  j                  |d      }|s<	 | j                  j                  |      |u r| j                  j                  |d       yyt        j                  d|t        |j                  xs d             | j                  |       d{    | j                  j                  |      |u r| j                  j                  |d       yy7 :7 B# | j                  j                  |      |u r| j                  j                  |d       w w xY ww)zWait for the quiet period then dispatch the aggregated text.

        Uses a longer delay when the latest chunk is near WeCom's 4000-char
        split point, since a continuation chunk is almost certain.
        rK  r   Nz)[WeCom] Flushing text batch %s (%d chars)r?   )r   current_taskr   rk   getattr_SPLIT_THRESHOLDr   r   r   r   r  r   r   r   r#  r:  )r   rN  rS  pendinglast_lenr   rA  s          r0   rM  zWeComAdapter._flush_text_batchf  s     ++-	>0044S9GAHww(91=aH4000<<66--&&& --11#6lJ --11#6,F..223= G ..223=E --11#6,F..223= G KK;S)r* %%e,,,--11#6,F..223= G- '( ---11#6,F..223= GsZ   HA,G F>!G ';H"G ;H<AG >G?G ;H>G G <G??Hc                   g }d}t        | j                  d      xs d      j                         }|dk(  r| j                  d      }t        |t              r|ni }|j                  d      }t        |t
              r|ng }|D ]  }t        |t              st        |j                  d      xs d      j                         dk(  sD|j                  d      }	t        |	t              r|	ni }
t        |
j                  d      xs d      j                         }|s|j                  |        n]t        | j                  d      t              r| j                  d      ni }
t        |
j                  d      xs d      j                         }|r|j                  |       |dk(  rqt        | j                  d      t              r| j                  d      ni }t        |j                  d      xs d      j                         }|r|j                  |       |d	k(  rqt        | j                  d	      t              r| j                  d	      ni }t        |j                  d
      xs d      j                         }|r|j                  |       t        | j                  d      t              r| j                  d      ni }t        |j                  d      xs d      j                         }|dk(  rct        |j                  d      t              r|j                  d      ni }t        |j                  d      xs d      j                         xs d}ng|dk(  rbt        |j                  d      t              r|j                  d      ni }t        |j                  d      xs d      j                         xs d}dj                  d |D              j                         |fS )z;Extract plain text and quoted text from a callback payload.Nmsgtyper?   mixedmsg_itemr#  contentvoiceappmsgtitlequoterJ  c              3  &   K   | ]	  }|s|  y wNr.   ).0parts     r0   	<genexpr>z-WeComAdapter._extract_text.<locals>.<genexpr>  s     =$=s   )	r5   rk   rH   r4   rv   r8   r7   appendjoin)r   
text_partsr?  rY  
_raw_mixedrZ  
_raw_itemsr   r<   	_raw_text
text_blockr\  voice_block
voice_textr^  r_  r`  
quote_type
quote_textquote_voices                       r0   r2  zWeComAdapter._extract_text  s    !#
$(
dhhy)/R0668g'*J",Z">JBE:.J",Z">JBE 3!$-txx	*0b1779VC $ 0I.8D.IrJ!*..";"ArBHHJG"))'23 .88H$-O&)UWJ*..39r:@@BG!!'*'!3=dhhw>OQU3Vdhhw/\^ !;!ArBHHJ
%%j1 ("/9$((8:Ld/S(+Y[FJJw/526<<>%%e,%/0A4%H!b9-34::<
.869JD.Q6*WYJZ^^I6<"=CCEMJ7"0:599W;Mt0T%))G,Z\K[__Y7=2>DDFN$Jyy=*==CCEzQQr/   c                d  K   g }g }g }t        |j                  d      xs d      j                         }|dk(  r|j                  d      }t        |t              r|ni }|j                  d      }t        |t
              r|ng }	|	D ]{  }
t        |
t              st        |
j                  d      xs d      j                         }|dk(  sFt        |
j                  d      t              sf|j                  d|
d   f       } nt        |j                  d      t              r|j                  d|d   f       |dk(  r5t        |j                  d      t              r|j                  d|d   f       |dk(  rt        |j                  d      t              rp|d   }t        |j                  d      t              r|j                  d|d   f       n5t        |j                  d      t              r|j                  d|d   f       t        |j                  d      t              r|j                  d      ni }t        |j                  d      xs d      j                         }|dk(  r6t        |j                  d      t              r|j                  d|d   f       n:|dk(  r5t        |j                  d      t              r|j                  d|d   f       |D ]I  \  }}| j                  ||       d	{   }|s#|\  }}|j                  |       |j                  |       K ||fS 7 4w)
z=Best-effort extraction of inbound media to local cache paths.rY  r?   rZ  r[  imagefiler^  r`  N)r5   rk   rH   r4   rv   r8   rf  _cache_media)r   r   media_pathsr)  refsrY  ri  rZ  rj  r   r<   	item_typer^  r`  ro  kindrefcachedpathcontent_types                       r0   r3  zWeComAdapter._extract_media  s    !#!#13dhhy)/R0668g'*J",Z">JBE:.J",Z">JBE :!$- 3 9r:@@B	'Jtxx7H$,OKK$w- 89: $((7+T2Wd7m45& Z0@$%GVT&\23("z$((82Dd'Khfjj0$7KK 89

7 3T:KK&/ :;%/0A4%H!b9-34::<
 Z		'0BD%IKK%.126!j61BD&IKKv/0 	1ID#,,T377F%+"l""4(""<0	1 K'' 8s%   C	L0L0,HL09L.:L0.L0c                X  K   d|v r|j                  d      r	 | j                  |d         }|dk(  r1| j                  |      }	 t        ||      | j                  |d      fS t        |j                  d      xs |j                  d	      xs d
      }t        ||      t        j                  |      d   xs dfS t        |j                  d      xs d      j                         }|sy	 | j!                  |t"               d{   \  }}t        |j                  d      xs d      j                         }	|	r	 | j%                  ||	      }t        |j                  d      xs d      j'                  dd      d   j                         xs d}
|dk(  rG| j)                  ||
| j                  |            }	 t        ||      |
xs | j                  |d      fS | j+                  ||j                  d      |
      }t        ||      |
fS # t        $ r,}t        j	                  d| j
                  ||       Y d}~yd}~ww xY w# t        $ r+}t        j                  d| j
                  |       Y d}~yd}~ww xY w7 w# t        $ r-}t        j	                  d| j
                  |||       Y d}~yd}~ww xY w# t        $ r-}t        j	                  d| j
                  |||       Y d}~yd}~ww xY w# t        $ r,}t        j                  d| j
                  ||       Y d}~yd}~ww xY ww)z=Cache an inbound image/file/media reference to local storage.base64z)[%s] Failed to decode %s base64 media: %sNrs  z
image/jpeg)fallbackz![%s] Rejected non-image bytes: %sfilenamer   
wecom_filer   application/octet-streamurlr?   	max_bytesz&[%s] Failed to download %s from %s: %saeskeyz%[%s] Failed to decrypt %s from %s: %scontent-type;r   z)[%s] Rejected non-image bytes from %s: %scontent-disposition)rk   _decode_base64r   r   r   r   _detect_image_extr   _mime_for_extr
  r   r5   r   	mimetypes
guess_typer7   _download_remote_bytesABSOLUTE_MAX_BYTES_decrypt_file_bytesr6   _guess_extension_guess_filename)r   ry  mediarD   r   extr  r  r   aes_keyr}  s              r0   ru  zWeComAdapter._cache_media  s    u8!4))%/:
 w,,S1 1#s;T=O=OPS^j=O=kkk
 599Z0UEIIf4EUVH,S(;Y=Q=QRZ=[\]=^=|b|||%))E"(b)//1	!%!<!<SL^!<!__LC
 eii)/R0668..sG<
 7;;~6<"=CCCKANTTVtZt7?''\DDZDZ[^D_'`C-c379wI[I[\_jvI[Iwww
 ''W[[9N-OQ]^(h7EEW  H$))UY[^_ "  NN#F		SVW  ` 	LLA499dTWY\]	  DdiiQUWZ\_`  JDIIWZ\_`s   L*H L*I "B	L*,J  I=J  .L*=J9 A+L*;"K2 1L*	I"H>9L*>IL*	I:!I50L*5I::L*=J   	J6	#J1,L*1J66L*9	K/#K*%L**K//L*2	L';"L"L*"L''L*c                r    | j                  dd      d   j                         }t        j                  |      S )Nr3   r   )r6   r7   r  	b64decode)r   r   s     r0   r  zWeComAdapter._decode_base64!  s1    **S!$R(..0((r/   c                    | j                  d      ry| j                  d      ry| j                  d      ry| j                  d      r	| dd	 d
k(  ryy)Ns   PNG

z.pngs   z.jpg)s   GIF87as   GIF89az.gifs   RIFF      s   WEBPz.webp
startswith)r   s    r0   r  zWeComAdapter._detect_image_ext&  sP    ??/0???+??12??7#Qr
g(=r/   c                ^    t         j                  j                  | j                         |      S rb  )r  	types_maprk   rH   )r  r  s     r0   r  zWeComAdapter._mime_for_ext2  s!    ""&&syy{H==r/   c                    |rt        j                  |      nd }|r|S t        t        |       j                        j
                  }|r|S |S rb  )r  guess_extensionr   r   r|  suffix)r  r}  r  r  path_exts        r0   r  zWeComAdapter._guess_extension6  sE    9Ei''54J**+22Or/   c                    |r)t        j                  d|      }|r|j                  d      S t        t	        |       j
                        j                  xs d}d|vrt        j                  |      xs d}| | }|S )Nzfilename="?([^";]+)"?r   document.z.bin)	rA   searchr  r   r   r|  r   r  r  )r  content_dispositionr}  matchr   r  s         r0   r  zWeComAdapter._guess_filename@  su    II68KLE{{1~%HSM&&',,:
d?++L9CVCVC5>Dr/   c                L   t        d |D              rt        j                  S t        d |D              r"|rt        j                  S t        j                  S t        | j                  d      xs d      j                         dk(  rt        j                  S t        j                  S )z+Choose the normalized inbound message type.c              3  >   K   | ]  }|j                  d         yw))zapplication/ztext/Nr  rc  mtypes     r0   re  z4WeComAdapter._derive_message_type.<locals>.<genexpr>P  s     Tuu 9:T   c              3  >   K   | ]  }|j                  d         yw)image/Nr  r  s     r0   re  z4WeComAdapter._derive_message_type.<locals>.<genexpr>R  s     Ceu)Cr  rY  r?   r]  )	anyr   DOCUMENTr   PHOTOr5   rk   rH   VOICE)r   r#  r)  s      r0   r4  z!WeComAdapter._derive_message_typeM  s     TTT'''C{CC'+;##B1B1BBtxx	"(b)//1W<$$$r/   c                     y)zAWeCom gates DM/group access at intake via dm_policy/group_policy.Tr.   r   s    r0   enforces_own_access_policyz'WeComAdapter.enforces_own_access_policy\  s     r/   c                    t        j                  dd      j                         dv ryt        j                  dd      j                         dv S )NGATEWAY_ALLOW_ALL_USERSr?   >   1yestrueTWECOM_ALLOW_ALL_USERS)rl   rm   rH   r   s    r0   _open_dm_opted_inzWeComAdapter._open_dm_opted_ina  sD    99.399;?SSyy0"5;;=AUUUr/   c                    | j                   dk(  ry| j                   dk(  rt        | j                  |      S | j                   dk(  r| j                         S y)NdisabledF	allowlistopen)rr   rN   rs   r  )r   r=  s     r0   _is_dm_allowedzWeComAdapter._is_dm_allowedf  sN    ??j(??k)!$"2"2I>>??f$))++r/   c                   t        |xs d      j                         }|sy| j                  dk(  ry| j                  dk(  rt        | j                  |      S | j                  dk(  ry| j                  dk(  r| j                         S y)Nr?   Fr  r  r[   Tr  )r5   r7   rr   rN   rs   r  )r   r=  	principals      r0   r0  z"WeComAdapter._is_dm_intake_allowedo  sz    	R(..0	??j(??k)!$"2"2I>>??i'??f$))++r/   c                (   | j                   dk(  ry| j                   dk(  ry| j                   dk(  rt        | j                  |      sy| j                  |      }t	        |j                  d      xs |j                  d            }|rt        ||      S y)Nr  Fr[   r  r\   r]   T)rt   rN   ru   _resolve_group_cfgr=   rk   )r   r  r=  	group_cfgsender_allows        r0   r/  zWeComAdapter._is_group_allowed}  s    +*,^DDZDZ\c5d++G4	#IMM,$?$]9==Q\C]^!,	::r/   c                   t        | j                  t              si S || j                  v r,t        | j                  |   t              r| j                  |   S |j                         }| j                  j	                         D ]>  \  }}t        |t
              s|j                         |k(  s+t        |t              s<|c S  | j                  j                  d      }t        |t              r|S i S )NrG   )r4   rw   rv   rH   r   r5   rk   )r   r  loweredrN  r;   wildcards         r0   r  zWeComAdapter._resolve_group_cfg  s    $,,-Idll"z$,,w2G'N<<((--/,,,,. 	JC#s#		w(>:eUYCZ	 <<##C(%h5x=2=r/   c                   t        |xs d      j                         }t        |xs d      j                         }|r|sy || j                  |<   t        | j                        t        kD  rU| j                  j                  t        t        | j                                     t        | j                        t        kD  rTy y )Nr?   )r5   r7   r   r   r~   r  nextiter)r   r'  r   normalized_message_idr  s        r0   r.  z#WeComAdapter._remember_reply_req_id  s     #J$4" 5 ; ; ="-335$,=5F12$%%&7##Dd.A.A)B$CD $%%&7r/   c                   t        |xs d      j                         }t        |xs d      j                         }|r|sy|| j                  |<   t        | j                        t        kD  rU| j                  j                  t        t        | j                                     t        | j                        t        kD  rTyy)u  Cache the most recent inbound req_id per chat.

        Used as a fallback reply target when we need to send into a group
        without an explicit ``reply_to`` — WeCom AI Bots are blocked from
        APP_CMD_SEND in groups and must use APP_CMD_RESPONSE bound to some
        prior req_id. Bounded like _reply_req_ids so long-running gateways
        don't leak memory across many chats.
        r?   N)r5   r7   r   r   r~   r  r  r  )r   r  r   normalized_chat_idr  s        r0   r1  z"WeComAdapter._remember_chat_req_id  s     !B/557"-335!):6G 23$))*^;##''T$2I2I-J(KL $))*^;r/   c                    t        |xs d      j                         }|r|j                  d      ry | j                  j	                  |      S )Nr?   r!  )r5   r7   r  r   rk   )r   reply_torM   s      r0   _reply_req_id_for_messagez&WeComAdapter._reply_req_id_for_message  sC    R(..0
Z228<""&&z22r/   c                    t        j                  |       d   }|r|S t        |       j                  j	                         dk(  ryy)Nr   z.amrr*   r  )r  r  r   r  rH   )r  	mime_types     r0   _guess_mime_typezWeComAdapter._guess_mime_type  sA    ((215	>  &&(F2)r/   c                    t        | xs d      j                  dd      d   j                         j                         }t        j                  |      }|s|S |dv r|S |S )Nr?   r  r   r   >   
text/plainr  )r5   r6   r7   rH   rP   r  )r}  r  rM   guesseds       r0   _normalize_content_typez$WeComAdapter._normalize_content_type  sa    +,223:1=CCEKKM
//9NCCNr/   c                    t        | xs d      j                         j                         }|j                  d      ry|j                  d      ry|j                  d      s|dk(  ryy	)
Nr?   r  rs  zvideo/videozaudio/zapplication/oggr]  rt  )r5   r7   rH   r  )r}  r  s     r0   _detect_wecom_media_typez%WeComAdapter._detect_wecom_media_type  sa    *+11399;	)))Y:K-Kr/   c                   | dz  }t        |xs d      j                         }t        |xs d      j                         j                         }| t        kD  r|dd|dddd d	S |d
k(  r| t        kD  rddd dd|ddd	S |dk(  r| t
        kD  rddd dd|ddd	S |dk(  r,|r|t        vrddd dd| dd	S | t        kD  rddd dd|ddd	S |dd dd d	S )Ni   rt  r?   Tu   文件大小 z.2fuq   MB 超过了企业微信允许的最大限制 20MB，无法发送。请尝试压缩文件或减小文件大小。F)
final_typerejectedreject_reason
downgradeddowngrade_noters  u   图片大小 u3   MB 超过 10MB 限制，已转为文件格式发送r  u   视频大小 r]  u   语音格式 uE    不支持，企微仅支持 AMR 格式，已转为文件格式发送u   语音大小 u2   MB 超过 2MB 限制，已转为文件格式发送)r5   rH   r7   r  IMAGE_MAX_BYTESVIDEO_MAX_BYTESVOICE_SUPPORTED_MIMESVOICE_MAX_BYTES)	file_sizedetected_typer}  file_size_mbnormalized_typenormalized_content_types         r0   _apply_file_size_limitsz$WeComAdapter._apply_file_size_limits  sx    K0m5v6<<>"%l&8b"9"?"?"A"G"G"I))- #L#5 6D D $"&	 	 g%)o*E$!!%"$1,s1CCv"w  g%)o*E$!!%"$1,s1CCv"w  g%&+BJ_+_"( %%)"&'(?'@  AF  G  ?*"( %%)"&(5l35GGy&z  *!"
 	
r/   c                |    | j                  dd      }|dv ry t        | j                  d      xs d      }d| d| S )Nr   r   r   r   unknown errorzWeCom errcode z: )rk   r5   )r  r   r   s      r0   _response_errorzWeComAdapter._response_error  sH    ,,y!,iX\\(+>?y6(33r/   c                J    | j                  |      }|rt        | d|       y )N	 failed: )r  r   )clsr  	operationr   s       r0   _raise_for_wecom_errorz#WeComAdapter._raise_for_wecom_error!  s0    ##H-)IeW=>> r/   c                  
 | st        d      |st        d      |ddt        |      dz  z
  dz  z  z   }t        j                  |      }t        |      dk7  rt        dt        |             	 ddlm}m}m}  ||j                  |      |j                  |d d
             }|j                         }|j                  |       |j                         z   }	|	d   

dk  s
dkD  s
t        |	      kD  rt        d
       t        
fd|	
 d  D              rt        d      |	d 
  S # t        $ r}t        d	      |d }~ww xY w)Nzencrypted_data is emptyzaes_key is required=       z5Invalid WeCom AES key length: expected 32 bytes, got r   )Cipher
algorithmsmodesz3cryptography is required for WeCom media decryption   r  r   zInvalid PKCS#7 padding value: c              3  (   K   | ]	  }|k7    y wrb  r.   )rc  bytepad_lens     r0   re  z3WeComAdapter._decrypt_file_bytes.<locals>.<genexpr>@  s     @4tw@s   z.Invalid PKCS#7 padding: padding bytes mismatch)r
  r   r  r  &cryptography.hazmat.primitives.ciphersr  r  r  ImportErrorr   AESCBC	decryptorupdatefinalizer  )encrypted_datar  rN  r  r  r  r   cipherr  	decryptedr  s             @r0   r  z WeComAdapter._decrypt_file_bytes'  sb   677233 CAGq(8$8A#=>>w's8r>TUXY\U]T^_``	_XX 
s+UYYs3Bx-@A$$&	$$^4y7I7I7KK	B-Q;'B,'C	N*B=gYGHH@9gXY+?@@MNN7(##  	_TU[^^	_s   1
D+ +	E4E  Ec           	     0  K   ddl m}  ||      st        d|d d        t        st	        d      | j
                  xs t        j                  dd      }|| j
                  u}	 |j                  d	|d
dd      4 d {   }|j                          |j                  j                         D ci c]  \  }}|j                         | }	}}|	j                  d      }
|
r9|
j                         r)t        |
      |kD  rt        dt        |
       d| d      t!               }|j#                         2 3 d {   }|j%                  |       t'        |      |kD  s)t        dt'        |       d| d      7 c c}}w 7 G6 t)        |      |	fcd d d       d {  7   |r|j+                          d {  7   S S # 1 d {  7  sw Y   nxY w	 |r|j+                          d {  7   y y # |r|j+                          d {  7   w w xY ww)Nr   )is_safe_urlz&Blocked unsafe URL (SSRF protection): P   z*httpx is required for WeCom media downloadr#   T)r   r   GETHermesAgent/1.0z*/*)
User-AgentAcceptr   zcontent-lengthz"Remote media exceeds WeCom limit: z	 bytes > z bytesz4Remote media exceeds WeCom limit while downloading: )tools.url_safetyr  r
  r-   r   rz   r   r   streamraise_for_statusr   r   rH   rk   isdigitint	bytearrayaiter_bytesrL  r   bytesr   )r   r  r  r  clientcreated_clientr  rN  r;   r   content_lengthr   chunks                r0   r  z#WeComAdapter._download_remote_bytesE  s"    
 	13Ec#2hZPQQKLL""\e&7&7W[&\t'8'88	&}}"3# %  , , ))+@H@P@P@V@V@XY*#u399;-YY!(-=!>!n&<&<&>3~CVYbCb$<S=P<QQZ[dZeekl  !{#+#7#7#9  %KK&4y9,(RSVW[S\R]]fgpfqqwx ), Z#9 T{G+1, , ,4 mmo%% 5, , , ,4 mmo%% ~mmo%% s   A$H'G5 E<G5 .G4E>A)G8F<F=F  G!G<G5 >GFGG5 F" G5 %H:F=;HGG	GG5 H-G0.H5HHHHc                N    t        t        | xs d            }|j                  dv S )Nr?   >   httphttps)r   r5   scheme)media_sourceparseds     r0   _looks_like_urlzWeComAdapter._looks_like_urlq  s&    #l0b12}} 111r/   c                  K   t        |xs d      j                         }|st        d      t        j                  d|      rt        d|       t        |      }|j                  dv r| j                  |t               d {   \  }}|j                  d      }|xs# | j                  |||j                  dd            }| j                  |j                  dd      |      }	||	|fS |j                  d	k(  r-t        t        |j                              j                         }
nt        |      j                         }
|
j!                         s%t        j"                         |
z  j%                         }
|
j'                         r|
j)                         st+        d
|
       |
j-                         }|xs |
j.                  }| j                  d|      }	||	|fS 7 Vw)Nr?   zmedia source is requiredz	<[^>\n]+>z:Media placeholder was not replaced with a real file path: >   r#  r$  r  r  r  rt  zMedia file not found: )r5   r7   r
  rA   	fullmatchr   r%  r  r  rk   r  r  r   r   r|  
expanduseris_absolutecwdresolveexistsis_fileFileNotFoundError
read_bytesr   )r   r&  	file_namer%  r'  r   r   r  resolved_namer}  
local_paths              r0   _load_outbound_mediaz!WeComAdapter._load_outbound_mediav  s    
 \'R(..0788<<f-YZ`Yabcc&!==--"&"="=fPb"="ccMD'")++.C"D%{)=)=fFY[b[f[fguwy[z){M77NTV8WYfgL}44==F"gfkk23>>@Jf002J%%'((*z1::<J  "**<*<*>#&<ZL$IJJ$$&!4Z__33BF\=00) ds   BGGEGc                   K   | j                  ||       d {   \  }}}| j                  |      }| j                  t        |      ||      }||||d|S 7 >w)Nr3  )r   r}  r3  r  )r6  r  r  r   )r   r&  r3  r   r}  r4  r  
size_checks           r0   _prepare_outbound_mediaz$WeComAdapter._prepare_outbound_media  sv     
 372K2KLdm2K2n,n)lM55lC11#d)]LY
(&*	

 
 	
 -os   AA?Ac           
       K   |st        d      t        |      }|t        z   dz
  t        z  }|t        kD  rt        d| dt         d      | j	                  t
        ||||t        j                  |      j                         d       d {   }| j                  |d       t        |j                  d      t              r|j                  d      ni }t        |j                  d	      xs d
      j                         }|st        d|       t!        t#        d|t                    D ]j  \  }	}
||
|
t        z    }| j	                  t$        ||	t'        j(                  |      j+                  d      d       d {   }| j                  |d|	        l | j	                  t,        d	|i       d {   }| j                  |d       t        |j                  d      t              r|j                  d      ni }t        |j                  d      xs d
      j                         }|st        d|       t        |j                  d      xs |      ||j                  d      dS 7 7 7 w)NzCannot upload empty mediar   zFile too large: z chunks exceeds maximum of z chunks)r   r  
total_sizetotal_chunksmd5zmedia upload initr   	upload_idr?   z8media upload init failed: missing upload_id in response r   ascii)r?  chunk_indexbase64_datazmedia upload chunk zmedia upload finishmedia_idz9media upload finish failed: missing media_id in response r   
created_at)r   rC  rD  )r
  r   UPLOAD_CHUNK_SIZEMAX_UPLOAD_CHUNKSr  APP_CMD_UPLOAD_MEDIA_INIThashlibr>  	hexdigestr  r4   rk   rv   r5   r7   r   	enumeraterangeAPP_CMD_UPLOAD_MEDIA_CHUNKr  	b64encodedecodeAPP_CMD_UPLOAD_MEDIA_FINISH)r   r   
media_typer  r<  r=  init_response	init_bodyr?  rA  startr!  chunk_responsefinish_responsefinish_bodyrC  s                   r0   _upload_media_bytesz WeComAdapter._upload_media_bytes  s{    899Y
"%66:?PP++"<.0KL]K^^ef  #00%"$( ,{{4(224	
 	
 	##M3FG1;M<M<Mf<UW[1\M%%f-bd		k28b9??A	!YZgYhijj"+E!ZAR,S"T 	]K):!:;E#'#5#5*!*#.#)#3#3E#:#A#A'#J	$ N '':Mk]8[\	] !% 2 2')$!
 
 	##O5JK5?@S@STZ@[]a5bo))&1hj{z28b9??A!Z[jZklmm /=:> %//,7
 	
S	
&
s8   BI1I*C/I1=I->6I14I/5B6I1-I1/I1c           
        K   | j                  t        d|d||d|ii       d {   }| j                  |d       |S 7 w)Nr  rY  rC  zsend media message)r  APP_CMD_SENDr  )r   r  rP  rC  r  s        r0   _send_media_messagez WeComAdapter._send_media_message  sT     ++':Z2
 
 	##H.BC
s   "?=?c                   K   | j                  |dd|d | j                   id       d {   }| j                  |d       |S 7 w)Nmarkdownr\  )rY  r\  zsend reply markdown)r  MAX_MESSAGE_LENGTHr  )r   r  r\  r  s       r0   _send_reply_markdownz!WeComAdapter._send_reply_markdown  sY     11%&0H1H1H(IJ
 
 	##H.CD
s   (AAAc                v   K   | j                  |d||d|ii       d {   }| j                  |d       |S 7 w)NrY  rC  zsend reply media message)r  r  )r   r  rP  rC  r  s        r0   _send_reply_media_messagez&WeComAdapter._send_reply_media_message  sQ      11:Z2
 
 	##H.HI
s   979c                   K   |sy | j                  |||       d {   }|j                  s+t        j                  d| j                  |j
                         |S 7 =w)Nr  r\  r  z'[%s] Follow-up markdown send failed: %s)sendsuccessr   r   r   r   )r   r  r\  r  results        r0   _send_followup_markdownz$WeComAdapter._send_followup_markdown
  sS      yy'HyUU~~NNDdiiQWQ]Q]^ Vs   AA>Ac                  K   |st        dd      S 	 | j                  ||       d {   }|d   r2| j                  |d|d    |	       d {    t        d|d         S | j                  |      }|s|| j                  v r| j                  |   }	 | j                  |d
   |d   |d          d {   }	|r"| j                  ||d   |	d          d {   }
n!| j                  ||d   |	d          d {   }
d }d }|r| j                  |||	       d {   }|d   r'|d   r"| j                  |d|d    |	       d {   }t        d| j!                  |
      xs! t#        j$                         j&                  d d |	|
|r|j(                  nd |r|j*                  s|j                  nd |r|j(                  nd |r|j*                  s|j                  nd d      S 7 # t        $ r }t        dt        |            cY d }~S d }~wt        $ rB}t
        j                  d| j                  ||       t        dt        |            cY d }~S d }~ww xY w7 7 7 7 k# t        j                  $ r t        dd      cY S t        $ rB}t
        j                  d| j                  ||       t        dt        |            cY d }~S d }~ww xY w7 7 w)NFchat_id is requiredrd  r   r8  z,[%s] Failed to prepare outbound media %s: %sr  u   ⚠️ r  )r  r   r  r3  rC  zTimeout sending media to WeComz [%s] Failed to send media %s: %sr  r  u   ℹ️ Tr  )uploadr  captioncaption_error	downgradedowngrade_errorrd  r'  raw_response)r   r:  r1  r5   r   r   r   r   rf  r  r   rW  r`  rZ  r   r   r   r   r   r   rp  rd  )r   r  r&  rk  r3  r  preparedr   r  upload_resultmedia_responsecaption_resultdowngrade_results                r0   _send_media_sourcezWeComAdapter._send_media_source  s     e3HII	=!99,R[9\\H J..(?345! /   
 e8O3LMM55h?4+B+B B227;L	="&":": &%# M
 '+'E'E \*!*-( " (,'?'?\*!*-( " #'#?#?! $@ $ N
 L!h/?&@%)%A%A(#3456! &B &   ++N;Ttzz|?O?OPSQS?T'':H>66d9GP^PfPf!5!5lp>N-::TX=MVfVnVn#3#9#9tx
 	
u ]  	=e3s8<< 	=LLGT`befe3s8<<	=
""
 ## 	Ue3STT 	=LL;TYYVYZe3s8<<	= s   KG' G$G' "KIAKI( 5I6"I( I"!I( :I%;I( ?KK+KK	BK$G' '	I0HIKI7IIKIKI( "I( %I( ( KK
K7K	K
KKKKc           	       K   ~|st        dd      S 	 | j                  |      }|s|| j                  v r| j                  |   }|r| j                  ||       d{   }n1| j	                  t
        |dd|d| j                   id       d{   }| j                  |      }|rt        d|      S t        d
| j                  |      xs! t!        j"                         j$                  dd |      S 7 7 f# t        j                  $ r t        dd      cY S t        $ rA}t        j                  d	| j                  |       t        dt        |            cY d}~S d}~ww xY ww)z?Send markdown to a WeCom chat via proactive ``aibot_send_msg``.Frh  ri  Nr\  r\  )r  rY  r\  z Timeout sending message to WeComz[%s] Send failed: %sTr  ro  )r   r  r   r^  r  rY  r]  r   r   r   r   r   r   r5   r  r   r   r   r   )	r   r  r\  r  metadatar  r  r   r   s	            r0   rc  zWeComAdapter.sendj  s]     e3HII	=99(CLGt/F/F$F#66w?!%!:!:<!QQ!%!3!3 ")#-%.8P9P9P0Q$R"  $$X.e599++H5N9I9I#29N!
 	
) R ## 	We3UVV 	=LL/C@e3s8<<	=sf   E$AC6 C21C6 C4C6 A E$2C6 4C6 6 E!E$E! 6EE!E$E!!E$c                2  K   ~| j                  ||||       d {   }|j                  s| j                  |      s|S t        j	                  d| j
                  ||j                         |r| d| n|}| j                  |||       d {   S 7 v7 w)Nr  r&  rk  r  z3[%s] Falling back to text send for image URL %s: %srJ  rb  )rv  rd  r(  r   r   r   r   rc  )r   r  	image_urlrk  r  rx  re  fallback_texts           r0   
send_imagezWeComAdapter.send_image  s      .."	 / 
 
 >>!5!5i!@MLdiiYbdjdpdpq5<7)2i[1)YYwPXYYYY
 Zs"   BBA1BBBBc                J   K   ~| j                  ||||       d {   S 7 wNrz  rv  )r   r  
image_pathrk  r  kwargss         r0   send_image_filezWeComAdapter.send_image_file  9      ,,#	 - 
 
 	
 
   #!#c                L   K   ~| j                  |||||       d {   S 7 w)N)r  r&  rk  r3  r  r  )r   r  	file_pathrk  r3  r  r  s          r0   send_documentzWeComAdapter.send_document  s<      ,," - 
 
 	
 
s   $"$c                J   K   ~| j                  ||||       d {   S 7 wr  r  )r   r  
audio_pathrk  r  r  s         r0   
send_voicezWeComAdapter.send_voice  r  r  c                J   K   ~| j                  ||||       d {   S 7 wr  r  )r   r  
video_pathrk  r  r  s         r0   
send_videozWeComAdapter.send_video  r  r  c                   K   ~~yw)z8WeCom does not expose typing indicators in this adapter.Nr.   )r   r  rx  s      r0   send_typingzWeComAdapter.send_typing  s     Xs   c                ^   K   ||r#|j                         j                  d      rddS ddS w)zReturn minimal chat info.r  r  )r   r   )rH   r  )r   r  s     r0   get_chat_infozWeComAdapter.get_chat_info  s:      &7==?+E+Eg+NG
 	
TX
 	
s   +-)r   r   )r   r5  returnr5  r  None)r   r5   r  Dict[str, Any])r   r  r  r  )r   r   r  r  )r   r5   r   r  r   floatr  r  )
r  r5   r   r  r   r5   r   r  r  r  )r  r5   r  r5   )r   r  r  r5   )rD   r   r  Optional[Dict[str, Any]])rA  r   r  r5   )rA  r   r  r  )rN  r5   r  r  )r   r  r  zTuple[str, Optional[str]])r   r  r  zTuple[List[str], List[str]])ry  r5   r  r  r  zOptional[Tuple[str, str]])r   r5   r  r  )r   r  r  r5   )r  )r  r5   r  r5   r  r5   )r  r5   r}  r5   r  r5   r  r5   )r  r5   r  Optional[str]r}  r5   r  r5   )r   r  r#  r5   r)  	List[str]r  r   r  r5  )r=  r5   r  r5  )r  r5   r=  r5   r  r5  )r  r5   r  r  )r'  r5   r   r5   r  r  )r  r5   r   r5   r  r  )r  r  r  r  )r  r5   r  r5   )r}  r5   r  r5   r  r5   )r}  r5   r  r5   rb  )r  r  r  r5   r}  r  r  r  )r  r  r  r  )r  r  r  r5   r  r  )r  r  r  r5   r  r  )r  r5   r  r  r  zTuple[bytes, Dict[str, str]])r&  r5   r  r5  )r&  r5   r3  r  r  zTuple[bytes, str, str])r&  r5   r3  r  r  r  )r   r  rP  r5   r  r5   r  r  )r  r5   rP  r5   rC  r5   r  r  )r  r5   r\  r5   r  r  )r  r5   rP  r5   rC  r5   r  r  )r  r5   r\  r5   r  r  r  zOptional[SendResult])NNN)r  r5   r&  r5   rk  r  r3  r  r  r  r  r   )NN)
r  r5   r\  r5   r  r  rx  r  r  r   )r  r5   r{  r5   rk  r  r  r  rx  r  r  r   )
r  r5   r  r5   rk  r  r  r  r  r   )r  r5   r  r5   rk  r  r3  r  r  r  r  r   )
r  r5   r  r5   rk  r  r  r  r  r   )
r  r5   r  r5   rk  r  r  r  r  r   )r  r5   r  r  )M__name__
__module____qualname____doc__r]  SUPPORTS_MESSAGE_EDITINGrU  rh   r   r   r   r   r   r   r   r   r   r   r   REQUEST_TIMEOUT_SECONDSr  APP_CMD_RESPONSEr  staticmethodr   r   r   r   rH  r9  rM  r2  r3  ru  r  r  r  r  r  r4  propertyr  r  r  r0  r/  r  r.  r1  r  r  r  r  r  r  classmethodr  r  r  r(  r6  r:  rW  rZ  r^  r`  rf  rv  rc  r}  r  r  r  r  r  r  __classcell__)r   s   @r0   rP   rP      sE   K+$ +5b 5: &P4>@:S0Z6=(W&6* Tk 6& $0AA A 	A
 A 
A4 . .   > >K-b

<%>N 0R 0Rd-(^0Fd ) ) 	 	 > >   
 
      V

>EM"3 * *     =
 =
~ 4 4 ? ?
 $ $:*&*& *& 
&	*&X 2 2 $(!1!1 !!1 
 	!1L $(

 !
 
	
 8
t
	  	
 
( #'	   	
 
" "&#'"&Q
Q
 Q
 	Q

 !Q
  Q
 
Q
n #'-1,
,
 ,
  	,

 +,
 
,
d "&"&-1ZZ Z 	Z
  Z +Z 
Z6 "&"&

 
 	

  
 

( "&#'"&

 
 	

 !
  
 

, "&"&

 
 	

  
 

( "&"&

 
 	

  
 

 
r/   rP   z)https://work.weixin.qq.com/ai/qc/generatez-https://work.weixin.qq.com/ai/qc/query_resultz9https://work.weixin.qq.com/ai/qc/gen?source=hermes&scode=   i,  )timeout_secondsc                	   	 ddl }ddl}t
         d}t        ddd       	 |j                  j                  |d	d
i      }|j                  j                  |d      5 }t        j                  |j                         j                  d            }ddd       j                  d      xs i }t!        |j                  d      xs d      j#                         }t!        |j                  d      xs d      j#                         }	|r|	s"t        j	                  d|       t        d       yt        d       t                d}
	 ddl}|j'                         }|j)                  |	       |j+                  d       |j-                  d       d}
t.         |j0                  j3                  |       }|
rt        d|        nt        d| d       t        d       t                t        ddd       t5        j6                         | z   }t8         d|j0                  j3                  |       }d}t5        j6                         |k  r	 |j                  j                  |d	d
i      }|j                  j                  |d       5 }t        j                  |j                         j                  d            }ddd       |d"z  }t        d#dd       j                  d      xs i }t!        |j                  d$      xs d      jA                         }|d%k(  rt                |j                  d&      xs i }t!        |j                  d'      xs |j                  d(      xs d      j#                         }t!        |j                  d)      xs d      j#                         }|r|r||d*S t        jC                  d+|       t        d,       yt5        j<                  t>               t5        j6                         |k  rt                t        d-| d.z   d/       y# t        $ r t        j	                  d       Y yw xY w# 1 sw Y   xY w# t        $ r.}t        j	                  d|       t        d|        Y d}~yd}~ww xY w# t        $ r Y t        $ r Y w xY w# 1 sw Y   xY w# t        $ r:}t        j;                  d!|       t5        j<                  t>               Y d}~d}~ww xY w)0uF  Run the WeCom QR scan flow to obtain bot_id and secret.

    Fetches a QR code from WeCom, renders it in the terminal, and polls
    until the user scans it or the timeout expires.

    Returns ``{"bot_id": ..., "secret": ...}`` on success, ``None`` on
    failure or timeout.

    Note: the ``work.weixin.qq.com/ai/qc/{generate,query_result}`` endpoints
    used here are not part of WeCom's public developer API — they back the
    admin-console web UI's bot-creation flow and may change without notice.
    The same pattern is used by the feishu/dingtalk QR setup wizards.
    r   Nz$urllib is required for WeCom QR scanz?source=hermesz  Connecting to WeCom...r?   T)endflushr  r  r     r   zutf-8z%WeCom QR: failed to fetch QR code: %sr  r   scodeauth_urlz(WeCom QR: unexpected response format: %sz# failed: unexpected response formatz done.F)fit)invertz8
  Scan the QR code above, or open this URL directly:
  z+  Open this URL in WeCom on your phone:

  rJ  zH  Tip: pip install qrcode  to display a scannable QR code here next timez#  Fetching configuration results...z?scode=r&   zWeCom QR poll error: %sr   r  statusrd  bot_infobotidrR   rT   )rR   rT   zFWeCom QR: scan reported success but bot_info missing or incomplete: %sz  QR scan reported success but no bot credentials were returned.
  This usually means the bot was not actually created on the WeCom side.
  Falling back to manual credential entry.z  QR scan timed out (r(   z minutes). Please try again.)"urllib.requesturllib.parser  r   r   _QR_GENERATE_URLprintrequestRequesturlopenr  r  readrN  r   rk   r5   r7   qrcodeQRCodeadd_datamakeprint_ascii_QR_CODE_PAGEparser`  r   	monotonic_QR_QUERY_URLr   r   _QR_POLL_INTERVALrH   r   )r  urllibgenerate_urlreqresprD   r   r   r  r  qr_rendered_qrcodeqrpage_urlr   	query_url
poll_countre  result_datar  r  rR   rT   s                          r0   qr_scan_for_bot_infor    sT   "
 ''~6L 

$"D9nn$$\LJ[;\$]^^##C#4 	:**TYY[//89C	: 776? bD!'R(..0E488J'-2.446H?E34	(O 
GK
 ^^
H
D
d#  !3!3E!: ;<HJ8*UV=hZrJKXY	G	
/RtD ~~/1H /););E)B(CDIJ
..
X
%	..((\K\<](^C''R'8 ADDIIK$6$6w$?@A 	a
cr&jj(.B[__X.4"5;;=YG"z28bHg.N(,,x2HNBOUUWFh/526<<>F&"(F;;NNX =
 

$%G ..
X
%J 
G	!/R"7!88T
UVA  ;<	: 	: <cB	# 4   (A A 	LL2C8JJ()	s   P <Q 3QQ AR
 <R2 3R%:R2 Q ?Q QQ 	R$RR
	R"R"!R"%R/*R2 2	S5;/S00S5)	thread_idmedia_filesforce_documentc                 K   t               sddiS 	 t        |       }|j                          d{   }|sddt        |dd      xs d iS 	 |j	                  ||       d{   }|j
                  s)dd|j                   i|j                          d{    S dd	||j                  d
|j                          d{    S 7 7 d7 27 # |j                          d{  7   w xY w# t        $ r}	dd|	 icY d}	~	S d}	~	ww xY ww)aE  Out-of-process WeCom delivery via the adapter's WebSocket send pipeline.

    Implements the standalone_sender_fn contract so deliver=wecom cron jobs
    succeed when cron runs separately from the gateway. Opens an ephemeral
    WeComAdapter, connects, sends, and disconnects. Replaces the legacy
    _send_wecom helper.
    r   z?WeCom requirements not met. Need aiohttp + WECOM_BOT_ID/SECRET.NzWeCom: failed to connect - fatal_error_messager  zWeCom send failed: Twecom)rd  platformr  r'  )
r1   rP   r   rT  rc  rd  r   r   r'  r   )
pconfigr  r   r  r  r  adapter	connectedre  es
             r0   _standalone_sendr    s      $%Z[[4w'!//++	:77Lacg;h;{l{:|}~~	'"<<99F>>#6v||n!EF $$&&&  #"$//	 $$&&& , : '&'$$&&& 4.qc2334s   D	C, CC, D	C #C$ C C, C
C, D	C -C,  CC, D	C, C 
C, C, C)"C%#C))C, ,	D5D;D<D	DD	c                 (   ddl m} m} ddlm} ddlm}m}m}m	}m
}m}m}	  |d        | d      }
 | d      }|
r|r |d        |d	d
      sy |dddgd      }d}d}|dk(  rI	 t               }|r,|j!                  dd      }|j!                  dd      } |d       |r|s |d       d}d}|r|sJ |d        |d        |d        |d        |dd
      }|s	 |d       y |dd      }|s	 |d       y |d|        |d|        |d         |d!        |d"d
      }|r" |d#|j#                  d$d              |d%       nj |d&g d'd(      }|dk(  r |d)d*        |d+d,        |d-       n>|d(k(  r |d)d.        |d/        |d0       n|d1k(  r |d)d2        |d3       n |d4        |d5d
      }|r |d6|        |d7|         |d8       y# t        $ r  |d       Y yt        $ r} |d|        d}Y d}~d}~ww xY w)9u   Interactive setup for WeCom — QR scan or manual credential input.

    Replaces hermes_cli/gateway.py::_setup_wecom and the static
    _PLATFORMS["wecom"] dict. CLI helpers are lazy-imported.
    r   )get_env_valuesave_env_value)prompt_choice)promptprompt_yes_noprint_header
print_infoprint_successprint_warningprint_errorWeCom (Enterprise WeChat)rS   rU   zWeCom is already configured.zReconfigure WeCom?FNz#How would you like to set up WeCom?zDScan QR code to obtain Bot ID and Secret automatically (recommended)z)Enter existing Bot ID and Secret manuallyzWeCom setup cancelled.zQR scan failed: rR   r?   rT   u3   ✔ QR scan successful! Bot ID and Secret obtained.z7QR scan did not complete. Continuing with manual input.uO   1. Go to WeCom Application → Workspace → Smart Robot -> Create smart robotsz2. Select API Modez=3. Copy the Bot ID and Secret from the bot's credentials infou?   4. The bot connects via WebSocket — no public endpoint neededzBot ID)passwordu.   Skipped — WeCom won't work without a Bot ID.SecretTu.   Skipped — WeCom won't work without a Secret.z5The gateway DENIES all users by default for security.z6Enter user IDs to create an allowlist, or leave empty.z,Allowed user IDs (comma-separated, or empty)r^    u5   Saved — only these users can interact with the bot.z)How should unauthorized users be handled?)z/Enable open access (anyone can message the bot)zXUse DM pairing (unknown users request access, you approve with 'hermes pairing approve')zDisable direct messagesz7Skip for now (bot will deny all users until configured)r   rZ   r  r  r  u0   Open access enabled — anyone can use your bot!r[   u@   DM pairing mode — users will receive a code to request access.z6Approve with: hermes pairing approve <platform> <code>r$   r  zDirect messages disabled.u7   Skipped — configure later with 'hermes gateway setup'z/Home chat ID (optional, for cron/notifications)WECOM_HOME_CHANNELzHome channel set to u   💬 WeCom configured!)hermes_cli.configr  r  hermes_cli.setupr  hermes_cli.cli_outputr  r  r  r  r  r  r  r  KeyboardInterruptr   rk   replace)r  r  r  r  r  r  r  r  r  r  existing_bot_idexisting_secret
method_idxrR   rT   credentialsr   allowed
access_idxhomes                       r0   interactive_setupr    s    @.   ,-#N3O#N3O?45159-R7	
 	
J FFQ	.0K  __Xr2F __Xr2FOPVPQFFde'(RSTU51JK40JK>6*>6*FGGHCeTG,gooc2.FGMN"7 	

 ?,f54f=LM1_,i8\]OP1_,j956PQCeTD+T2,TF34*+I ! 	23 	,SE23K	s   %
G H1H9HHc                X    t        | di       xs i }t        |j                  d            S )zWeCom (Smart Robot) is connected when a bot_id is configured. Mirrors the
    legacy _PLATFORM_CONNECTED_CHECKERS[Platform.WECOM] entry.rj   rR   rT  r5  rk   r   rj   s     r0   _is_connectedr  $  s+     FGR(.BE		(#$$r/   c                ~    t        | di       xs i }t        |j                  d      xs |j                  d            S )zWeCom callback mode is connected when corp_id (or a multi-app `apps`
    block) is configured. Mirrors the legacy
    _PLATFORM_CONNECTED_CHECKERS[Platform.WECOM_CALLBACK] entry.rj   corp_idappsr  r  s     r0   _callback_is_connectedr  +  s9     FGR(.BE		)$9		&(9::r/   c                    t        |       S )zCFactory wrapper that constructs WeComAdapter from a PlatformConfig.)rP   )r   s    r0   _build_adapterr  3  s    r/   c                    ddl m}  ||       S )zKFactory wrapper that constructs WecomCallbackAdapter from a PlatformConfig.r   )WecomCallbackAdapter)(plugins.platforms.wecom.callback_adapterr  )r   r  s     r0   _build_callback_adapterr  8  s    M''r/   c                    | j                  ddt        t        t        t        ddgdt        dddt
        d	d
d       ddlm} | j                  ddt        |t        t        ddgdddd
d       y)u6   Plugin entry point — registers both WeCom platforms.r  r  rS   rU   z!pip install 'hermes-agent[wecom]'r^   r  r  r"   u   💼T)r   labeladapter_factorycheck_fnis_connectedvalidate_configrequired_envinstall_hintsetup_fnallowed_users_envallow_all_envcron_deliver_env_varstandalone_sender_fnmax_message_lengthemojiallow_update_commandr   )!check_wecom_callback_requirementswecom_callbackz WeCom Callback (self-built apps)WECOM_CALLBACK_CORP_IDWECOM_CALLBACK_CORP_SECRETWECOM_CALLBACK_ALLOWED_USERSWECOM_CALLBACK_ALLOW_ALL_USERS)r   r
  r  r  r  r  r  r  r  r  r  r  N)
register_platformr  r1   r  r  r  r  r  r  r  )ctxr  s     r0   registerr!  >  s    )&)"%$n58"/-1-!!  & [0/2+..0LM886!  r/   r  )r;   r   r  r  )rD   r5   r  r5   )rI   r  rJ   r5   r  r5  )r  r  r  zOptional[Dict[str, str]]r  )]r  
__future__r   r   r  rH  r  loggingr  rl   rA   r   r   r   r   pathlibr   typingr   r   r	   r
   r   r  r   r   r   r,   r  r   r-   gateway.configr   r   gateway.platforms.helpersr   gateway.platforms.baser   r   r   r   r   r   utilsr   	getLoggerr  r   rp   r   APP_CMD_CALLBACKAPP_CMD_LEGACY_CALLBACKr   rY  r  r   rG  rL  rO  r   r   r]  r   r  r   r   r~   r  r  r  FILE_MAX_BYTESr  rE  rF  r  r1   r=   rE   rN   rP   r  r  r  r  _QR_POLL_TIMEOUTr  r  r  r  r  r  r  r!  r.   r/   r0   <module>r/     s  : #       	 	   '  3 3 *
O
 4 9  			8	$2% ' * / & 5 7 9 %'>? )-C,DD    ! & ""!!#   $ 1
>k
& k
d+ ? ?K  
 ,tt tT $4Nm,`%; 
(#Y8  G  OEs$   D& D5 &	D21D25	E E