
    epj3                      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n# e$ r d	ZdZY nw xY w	 ddlZdZn# e$ r d	ZdZY nw xY w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, ddl-m.Z/ ddl-m0Z1 dJdZ2 ej3        e4          Z5dZ6dZ7dZ8dZ9dZ:dZ;dZ<dZ=dZ>dZ?dZ@e8e9hZAeAe:hz  ZBdZCdZDdZEdZFg d ZGd!ZHd"ZId"ZJd#ZKd$ZLeLZMd%ZNd&ZOd'hZPdKd*ZQdLd.ZRdMd1ZSdNd4ZT G d5 d6e%          ZUd7ZVd8ZWd9ZXd:ZYd;ZZeZd<dOd@Z[ddd	dAdBZ\dPdDZ]dKdEZ^dKdFZ_dG Z`dH ZadPdIZbdS )Qa8  
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_float)UnscopedSecretError)
get_secretc                z    	 t          | |          }n$# t          $ r t          j        |           }Y nw xY w||n|S )a  Scope-aware credential read with the default-profile startup fallback.

    Secondary profiles construct their adapters under a profile secret
    scope -- the scope is authoritative and a scoped miss returns ``default``
    (no cross-profile borrow from ``os.environ``, which may hold another
    profile's value). The DEFAULT profile's adapter constructs and sends
    *unscoped* under multiplexing, where a bare ``get_secret`` would raise
    ``UnscopedSecretError`` and crash this path; there ``os.environ`` is that
    profile's own value, so fall back to it. Same pattern as the Slack
    ``SLACK_APP_TOKEN`` read (#59739) and
    ``gateway/platforms/whatsapp_common.py::_get_wsecret``.
    )_scoped_get_secret_UnscopedSecretErrorosgetenv)namedefaultvals      E/home/thesage/.hermes/hermes-agent/plugins/platforms/wecom/adapter.py_get_scoped_secretr#   M   sR     w//   ioo/33w.s    44z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/amrreturnboolc                     t           ot          S )z2Check if WeCom runtime dependencies are available.)AIOHTTP_AVAILABLEHTTPX_AVAILABLE     r"   check_wecom_requirementsr>      s    00r=   valuer   	List[str]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 )z0Coerce config values into a trimmed string list.Nc                ^    g | ]*}|                                 |                                 +S r<   )strip.0items     r"   
<listcomp>z _coerce_list.<locals>.<listcomp>   s-    JJJTZZ\\J

JJJr=   ,c                    g | ]D}t          |                                          #t          |                                          ES r<   )strrC   rD   s     r"   rG   z _coerce_list.<locals>.<listcomp>   s=    IIIds4yy7H7HID		!!IIIr=   )
isinstancerJ   splitlisttuplesetrC   )r?   s    r"   _coerce_listrP      s    }	% KJJS)9)9JJJJ%$s+,, JIIeIIII#&u::#3#3#5#5=CJJ2=r=   rawrJ   c                    t          |                                           }t          j        dd|t          j                  }t          j        dd|t          j                  }|                                S )z7Normalize allowlist entries such as ``wecom:user:foo``.z^wecom: )flagsz^(user|group):)rJ   rC   resub
IGNORECASE)rQ   r?   s     r"   _normalize_entryrX      sZ    HHNNEF:r5>>>EF$b%r}EEEE;;==r=   entriestargetc                    t          |                                                                          }| D ]2}t          |                                          }|dk    s||k    r dS 3dS )z4Case-insensitive allowlist match with ``*`` support.*TF)rJ   rC   lowerrX   )rY   rZ   normalized_targetentry
normalizeds        r"   _entry_matchesra      sv    F))++1133  %e,,2244

.? ? ?44 !@5r=   c                      e Zd ZdZeZdZdZd fdZddddZddZ	ddZ
ddZddZddZddZddZddZddZddZefdd!Zeefdd#Zedd%            Zedd&            Zedd*            Zdd+Zdd.Zdd/Zdd1Zedd3            Zdd5Z dd9Z!edd<            Z"edd=            Z#edddA            Z$eddD            Z%eddG            Z&eddL            Z'e(ddM            Z)ddNZ*ddPZ+ddQZ,ddSZ-ddTZ.ddVZ/ddWZ0ddYZ1edd[            Z2edd\            Z3edd]            Z4edddb            Z5eddd            Z6e7ddf            Z8eddi            Z9ddlZ:eddn            Z;	 dddqZ<	 dddrZ=ddtZ>ddvZ?ddxZ@ddyZA	 ddd{ZB	 	 	 ddd~ZC	 	 dddZD	 	 	 dddZE	 	 dddZF	 	 	 dddZG	 	 dddZH	 	 dddZIdddZJddZK xZLS )WeComAdapterzAWeCom AI Bot adapter backed by a persistent WebSocket connection.Fi<  configr   c                   t                                          |t          j                   |j        pi }t          |                    d          pt          j        dd                    	                                | _
        t          |                    d          pt          dd                    	                                | _        t          |                    d          p.|                    d          pt          j        dt                              	                                pt          | _        t          |                    d	          pt          j        d
d                    	                                                                | _        t#          |                    d          p)|                    d          pt          j        dd                    | _        t          |                    d          pt          j        dd                    	                                                                | _        t#          |                    d          p|                    d                    | _        t+          |                    d          t,                    r|                    d          ni | _        d | _        d | _        d | _        d | _        d | _        i | _        t=          t>                    | _         i | _!        tE          dd          | _#        tE          dd          | _$        i | _%        i | _&        tO          j(                    j)        | _*        i | _+        d S )Nbot_idWECOM_BOT_IDrS   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extrarJ   getr   r   rC   _bot_idr#   _secretDEFAULT_WS_URL_ws_urlr]   
_dm_policyrP   _allow_from_group_policy_group_allow_fromrK   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)selfrd   r~   	__class__s      r"   r|   zWeComAdapter.__init__   s   000"599X..O")NB2O2OPPVVXX599X..X2D^UW2X2XYY__aaIIo&& @yy((@y.??
 
 %''	$ $	 	 eii44_	BSU^8_8_``ffhhnnpp
 (IIl## 4yy%%4y.33
 
 !>!:!:!hbiH\^g>h>hiiooqqwwyy!-eii8J.K.K.juyyYiOjOj!k!k.889L9Ld.S.S[uyy***Y[;?@D;?487;=?)>BBB.0 *33Z\_)`)`&/89fhk/l/l,>@"BD&*,,*24r=   )is_reconnectr   r8   r7   c                 K   t           s=d}|                     d|d           t                              d| j        |           dS t
          s=d}|                     d|d           t                              d| j        |           dS | j        r| j        s=d	}|                     d
|d           t                              d| j        |           dS 	 ddlm	} ddl
m} ddlm}  |ddd|gi |                      | _        |                                  d{V  |                                  t#          j        |                                           | _        t#          j        |                                           | _        t                              d| j        | j                   dS # t2          $ r}d| }|                     d|d           t                              d| j        |d           |                                  d{V  | j        r&| j                                         d{V  d| _        Y d}~dS d}~ww xY 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_limits_ssrf_redirect_guard)create_ssrf_safe_async_clientr/   response)timeoutfollow_redirectsevent_hookslimitsNz[%s] Connected to %szWeCom startup failed: wecom_connect_errorz[%s] Failed to connect: %s)exc_info)r:   _set_fatal_errorloggerwarningr   r;   r   r   %gateway.platforms._http_client_limitsr   gateway.platforms.baser   tools.url_safetyr   r   _open_connection_mark_connectedasynciocreate_task_listen_loopr   _heartbeat_loopr   infor   	Exceptionerror_cleanup_wsaclose)r   r   messager   r   r   excs          r"   connectzWeComAdapter.connect   s       	CG!!"<gQU!VVVNN>	7SSS5 	AG!!"<gQU!VVVNN<diQQQ5| 	4< 	XG!!"=wRV!WWWNN9di9995	SSSSSSCCCCCCFFFFFF = =!%'*>)?@,,..	! ! !D '')))))))))  """ ' 3D4E4E4G4G H HD#*#6t7K7K7M7M#N#ND KK.	4<HHH4 	 	 	4s44G!!"7D!QQQLL5ty#PTLUUU""$$$$$$$$$  )'..000000000$(!55555	s   CF4 4
I>BIINonec                  K   d| _         |                                  | j        rD| j                                         	 | j         d{V  n# t          j        $ r Y nw xY wd| _        | j        rD| j                                         	 | j         d{V  n# t          j        $ r Y nw xY wd| _        |                     t          d                     | 	                                 d{V  | j
        r&| j
                                         d{V  d| _
        | j                                         t                              d| j                   dS )zDisconnect from WeCom.FNzWeCom adapter disconnectedz[%s] Disconnected)_running_mark_disconnectedr   cancelr   CancelledErrorr   _fail_pending_responsesRuntimeErrorr   r   r   r   clearr   r   r   r   s    r"   
disconnectzWeComAdapter.disconnect  s     !!! 	%$$&&&''''''''')    $D 	( '')))*********)   #'D $$\2N%O%OPPP          	%#**,,,,,,,,, $D'33333s#   A AA
B B*)B*c                   K   | j         r+| j         j        s| j                                          d{V  d| _         | j        r+| j        j        s| j                                         d{V  d| _        dS )z)Close the live websocket/session, if any.N)r   closedcloser   r   s    r"   r   zWeComAdapter._cleanup_ws.  s      8 	#DHO 	#(.."""""""""= 	(!5 	(-%%'''''''''r=   c                B  K   |                                   d{V  t          j        d          | _        | j                            | j        t          dz  t                     d{V | _        | 	                    d          }| 
                    t          d|i| j        | j        | j        dd	           d{V  |                     |           d{V }|                    d
d          }|dvr+|                    dd          }t#          | d| d          dS )z-Open and authenticate a websocket connection.NT)	trust_envr0   )	heartbeatr   	subscribereq_id)rf   rh   	device_idcmdheadersbodyerrcoder      Nr   errmsgzauthentication failedz
 (errcode=))r   aiohttpClientSessionr   
ws_connectr   HEARTBEAT_INTERVAL_SECONDSCONNECT_TIMEOUT_SECONDSr   _new_req_id
_send_jsonAPP_CMD_SUBSCRIBEr   r   r   _wait_for_handshaker   r   )r   r   auth_payloadr   r   s        r"   r   zWeComAdapter._open_connection8  s              -===11L014+ 2 
 
 
 
 
 
 
 
 !!+..oo($f-"l"l!%  

 

 
	
 
	
 
	
 
	
 
	
 
	
 
	
 "55f========""9a00)##!%%h0GHHF&>>G>>>??? $#r=   r   rJ   Dict[str, Any]c                N  K   | j         st          d          t          j                                                    t
          z   }	 |t          j                                                    z
  }|dk    rt          d          t          j        | j                                         |           d{V }|j	        t          j        j        k    r|                     |j                  }|s|                    d          t           k    r|                     |          |k    r|S t$                              d| j        |                    d                     nH|j	        t          j        j        t          j        j        t          j        j        hv rt          d	          `)
z'Wait for the subscribe acknowledgement.zWebSocket not initializedTr   z5Timed out waiting for WeCom subscribe acknowledgementr   Nr   z"[%s] Ignoring pre-auth payload: %sz,WeCom websocket closed during authentication)r   r   r   get_running_looptimer   TimeoutErrorwait_forreceivetyper   	WSMsgTypeTEXT_parse_jsondatar   APP_CMD_PING_payload_req_idr   debugr   CLOSEDCLOSEERROR)r   r   deadline	remainingmsgpayloads         r"   r   z WeComAdapter._wait_for_handshakeU  s     x 	<:;;;+--22447NN	S 7#;#=#=#B#B#D#DDIA~~"#Z[[[()9)9););YOOOOOOOOOCx7,111**3844 ;;u%%55''00F::"NA49gkkZ_N`N`aaaag/68I8OQXQbQhiii"#QRRR!	Sr=   c                  K   d}| j         r{	 |                                  d{V  d}nR# t          j        $ r Y dS t          $ r3}| j         sY d}~dS t
                              d| j        |           |                     t          d                     t          t          |t          t                    dz
                     }|dz  }t          j        |           d{V  	 |                                  d{V  d}|                                  t
                              d| j                   n8# t          $ r+}t
                              d| j        |           Y d}~nd}~ww xY wY d}~nd}~ww xY w| j         ydS dS )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        r"   r   zWeComAdapter._listen_loopm  s     m 	ZZ''))))))))))    Z Z Z} FFFFF949cJJJ,,\:X-Y-YZZZ)#k3?P;Q;QTU;U*V*VWq mE*********Z//111111111"#K((***KK 2DI>>>>  Z Z ZNN#>	=YYYYYYYYZZ m 	Z 	Z 	Z 	Z 	ZsL   , E;
E;E6BE6(AD98E69
E.!E)$E6)E..E66E;c                D  K   | j         st          d          | j        r| j         r| j         j        s| j                                          d{V }|j        t          j        j        k    r8| 	                    |j
                  }|r|                     |           d{V  nX|j        t          j        j        t          j        j        t          j        j        t          j        j        hv rt          d          | j        r| j         r| j         j        dS dS dS dS dS dS )z2Read websocket frames until the connection closes.zWebSocket not connectedNzWeCom websocket closed)r   r   r   r   r   r   r   r   r   r   r   _dispatch_payloadr   r   r   CLOSING)r   r   r   s      r"   r  zWeComAdapter._read_events  sa     x 	:8999m 	= 	= 	=((********Cx7,111**3844 :00999999999g/5w7H7OQXQbQhjqj{  kD  E  E  E"#;<<< m 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=r=   c                  K   	 | j         rt          j        t                     d{V  | j        r| j        j        r:	 |                     t          d|                     d          ii d           d{V  n8# t          $ r+}t                              d| j        |           Y d}~nd}~ww xY w| j         dS dS # t          j        $ r Y dS w xY w)z)Send lightweight application-level pings.Nr   r*   r   z[%s] Heartbeat send failed: %s)r   r   r  r   r   r   r   r   r   r   r   r   r   r   )r   r   s     r"   r   zWeComAdapter._heartbeat_loop  sO     	- Sm$>?????????x 48? 	S//#/(0$2B2B62J2J'K$&           ! S S SLL!A49cRRRRRRRRS - S S S S S % 	 	 	DD	s:   :B< 9A9 8B< 9
B.!B)$B< )B..
B< <CCr   c                  K   |                      |          }t          |                    d          pd          }|rY|| j        v rP|t          vrG| j                            |          }|r)|                                s|                    |           dS |t          v r|                     |           d{V  dS |t          t          hv rdS t                              d| j        |p|           dS )z!Route inbound websocket payloads.r   rS   Nz#[%s] Ignoring websocket payload: %s)r   rJ   r   r   NON_RESPONSE_COMMANDSdone
set_resultCALLBACK_COMMANDS_on_messager   APP_CMD_EVENT_CALLBACKr   r   r   )r   r   r   r   futures        r"   r
  zWeComAdapter._dispatch_payload  s     %%g..'++e$$*++ 	f 777CG\<\<\,0088F +fkkmm +!!'***F###""7+++++++++F<!7888F:DIs~gVVVVVr=   r   r   c                    t          | j                                                  D ]I\  }}|                                s|                    |           | j                            |d           JdS )z%Fail all outstanding request futures.N)rM   r   itemsr  set_exceptionpop)r   r   r   r  s       r"   r   z$WeComAdapter._fail_pending_responses  sv    "4#:#@#@#B#BCC 	6 	6NFF;;== *$$S)))#''5555	6 	6r=   c                   K   | j         r| j         j        rt          d          | j                             |           d{V  dS )z0Send a raw JSON frame over the active websocket. WeCom websocket is not connectedN)r   r   r   	send_json)r   r   s     r"   r   zWeComAdapter._send_json  sZ      x 	C48? 	CABBBh  )))))))))))r=   r   r   r   floatc                  K   | j         r| j         j        rt          d          |                     |          }t	          j                                                    }|| j        |<   	 |                     |d|i|d           d{V  t	          j	        ||           d{V }|| j        
                    |d           S # | j        
                    |d           w xY w)z6Send a JSON request and await the correlated response.r  r   r   Nr   )r   r   r   r   r   r   create_futurer   r   r   r  )r   r   r   r   r   r  r   s          r"   _send_requestzWeComAdapter._send_request  s     x 	C48? 	CABBB!!#&&)++99;;*0'	6//#8V:LVZ"["[\\\\\\\\\$-fgFFFFFFFFFH#''5555D#''5555s   *>C C!reply_req_idc                  K   | j         r| j         j        rt          d          t          |pd                                          }|st          d          t          j                                                    }|| j	        |<   	 | 
                    |d|i|d           d{V  t          j        ||           d{V }|| j	                            |d           S # | j	                            |d           w xY w)z<Send a reply frame correlated to an inbound callback req_id.r  rS   zreply_req_id is requiredr   r   Nr   )r   r   r   rJ   rC   
ValueErrorr   r   r  r   r   r   r  )r   r   r   r   r   normalized_req_idr  r   s           r"   _send_reply_requestz WeComAdapter._send_reply_request  sB      x 	C48? 	CABBB 23399;;  	97888)++99;;5; 12	A//3D(EtTT         %-fgFFFFFFFFFH#''(94@@@@D#''(94@@@@s   	>C# #D prefixc                <    |  dt          j                    j         S )N-)r   r   r   )r%  s    r"   r   zWeComAdapter._new_req_id  s    --4:<<+---r=   c                    |                      d          }t          |t                    r$t          |                     d          pd          S dS )Nr   r   rS   )r   rK   r   rJ   )r   r   s     r"   r   zWeComAdapter._payload_req_id  sI    ++i((gt$$ 	4w{{8,,2333rr=   rQ   r   Optional[Dict[str, Any]]c                    	 t          j        |           }n,# t          $ r t                              d|            Y d S w xY wt          |t                    r|nd S )Nz!Failed to parse WeCom payload: %r)jsonloadsr   r   r   rK   r   )rQ   r   s     r"   r   zWeComAdapter._parse_json  sh    	jooGG 	 	 	LL<cBBB44	 %Wd33=ww=s    %A A c                  K   |                     d          }t          |t                    sdS t          |                     d          p,|                     |          pt          j                    j                  }| j        	                    |          r#t                              d| j        |           dS |                     ||                     |                     t          |                     d          t                    r|                     d          ni }t          |                     d          pd                                          }t          |                     d          p|                                          }|s"t                              d	| j                   dS t          |                     d
          pd                                          dk    }|r;|                     ||          s$t                              d| j        ||           dS n8|                     |          s#t                              d| j        |           dS |                     ||                     |                     |                     |          \  }}	|r*|r(t)          j        dd|                                          }|                     |           d{V \  }
}|                     |||          }t1          |	o|p|
          }|s|	r|
s|	}|s$|
s"t                              d| j                   dS |                     ||rdnd|pd|pd          }t5          ||||||
||rd| nd|r|	ndt7          j        t:          j                  
  
        }|t>          j         k    r"| j!        dk    r| "                    |           dS | #                    |           d{V  dS )z0Process an inbound WeCom message callback event.r   Nmsgidz![%s] Duplicate message %s ignoredfromuseridrS   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   )$r   rK   r   rJ   r   r   r   r   r   is_duplicater   r   r   _remember_reply_req_idrC   r]   _is_group_allowed_is_dm_intake_allowed_remember_chat_req_id_extract_textrU   rV   _extract_media_derive_message_typer8   build_sourcer   r   nowr   utcr   r   r   _enqueue_text_eventhandle_message)r   r   r   msg_idsender	sender_idr5  is_groupr;  
reply_textr@  rA  r<  has_reply_contextr=  events                   r"   r  zWeComAdapter._on_message  s      {{6""$%% 	FTXXg&&[$*>*>w*G*G[4:<<K[\\;##F++ 	LL<diPPPF##FD,@,@,I,IJJJ%/0@0@$%G%GO&!!!R

8,,23399;;	dhhx((5I66<<>> 	LLA49MMMFtxx
++1r2288::gE 	))'9== JDIW^`ijjj ++I66 	LL>	9UUUF
 	""7D,@,@,I,IJJJ--d33j  	9 	96+r4006688D(,(;(;D(A(A"A"A"A"A"A"A
K00t[II !D1CEE 	
 	: 	D 	J 	LL;TYGGGF""!)3ggt%'4	 # 
 
 %!#5F P 1 1 1 1D(9C**tlhl333
 
 
 ;+++0NQR0R0R$$U+++++%%e,,,,,,,,,,,r=   rX  r   c                    ddl m}  ||j        | j        j                            dd          | 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)r[  r\  profile)gateway.sessionrZ  r=  rd   r~   r   r]  )r   rX  rZ  s      r"   _text_batch_keyzWeComAdapter._text_batch_key\  so    555555  L$(K$5$9$9:SUY$Z$Z%)[%6%:%:;UW\%]%]L(	
 
 
 	
r=   c                   |                      |          }| j                            |          }t          |j        pd          }|||_        || j        |<   nw|j        r$|j        r|j         d|j         n|j        |_        ||_        |j        r>|j                            |j                   |j                            |j                   | j	                            |          }|r(|
                                s|                                 t          j        |                     |                    | j	        |<   dS )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.
        rS   N
)r_  r   r   r  r;  _last_chunk_lenr@  extendrA  r   r  r   r   r   _flush_text_batch)r   rX  keyexisting	chunk_len
prior_tasks         r"   rP  z WeComAdapter._enqueue_text_eventf  sN    ""5))-11#66
(b))	$-E!.3D&s++z bDLM a8= @ @EJ @ @ @W\Wa'0H$ ?#**5+;<<<$++E,=>>> 377<<
 	 joo// 	 .5.A""3''/
 /
&s+++r=   re  c                  K   t          j                    }	 | j                            |          }|rt	          |dd          nd}|| j        k    r| j        }n| j        }t          j        |           d{V  | j	                            |          |ur<	 | j	                            |          |u r| j	        
                    |d           dS dS | j        
                    |d          }|s<	 | j	                            |          |u r| j	        
                    |d           dS dS t                              d|t          |j        pd                     |                     |           d{V  | j	                            |          |u r| j	        
                    |d           dS dS # | j	                            |          |u r| j	        
                    |d           w w xY 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.
        rb  r   Nz)[WeCom] Flushing text batch %s (%d chars)rS   )r   current_taskr   r   getattr_SPLIT_THRESHOLDr   r   r  r   r  r   r   r  r;  rQ  )r   re  rj  pendinglast_lenr  rX  s          r"   rd  zWeComAdapter._flush_text_batch  s=      +--	>044S99GAHOww(91===aH4000<6-&&&&&&&&& -11#66lJJ -11#66,FF.223===== GF .223==E  -11#66,FF.223===== GF KK;S)r**   %%e,,,,,,,,,-11#66,FF.223===== GFt-11#66,FF.223==== Gs   A?F1 F1 +AF1 1:G+Tuple[str, Optional[str]]c                	   g }d}t          |                     d          pd                                          }|dk    r.|                     d          }t          |t                    r|ni }|                    d          }t          |t
                    r|ng }|D ]}t          |t                    st          |                    d          pd                                          dk    r{|                    d          }	t          |	t                    r|	ni }
t          |
                    d          pd                                          }|r|                    |           ΐnt          |                     d          t                    r|                     d          ni }
t          |
                    d          pd                                          }|r|                    |           |dk    rt          |                     d          t                    r|                     d          ni }t          |                    d          pd                                          }|r|                    |           |d	k    rt          |                     d	          t                    r|                     d	          ni }t          |                    d
          pd                                          }|r|                    |           t          |                     d          t                    r|                     d          ni }t          |                    d          pd                                          }|dk    rxt          |                    d          t                    r|                    d          ni }t          |                    d          pd                                          pd}n}|dk    rwt          |                    d          t                    r|                    d          ni }t          |                    d          pd                                          pd}d                    d |D                                                       |fS )z;Extract plain text and quoted text from a callback payload.NmsgtyperS   mixedmsg_itemr;  contentvoiceappmsgtitlequotera  c              3     K   | ]}||V  	d S Nr<   )rE   parts     r"   	<genexpr>z-WeComAdapter._extract_text.<locals>.<genexpr>  s'      ==$=======r=   )	rJ   r   r]   rK   r   rM   rC   appendjoin)r   
text_partsrV  rq  
_raw_mixedrr  
_raw_itemsr  rF   	_raw_text
text_blockrt  voice_block
voice_textrv  rw  rx  
quote_type
quote_textquote_voices                       r"   rJ  zWeComAdapter._extract_text  sH    !#
$(
dhhy))/R006688g'**J",Z">">FJJBE:..J",Z">">FJJBE 3 3!$-- txx	**0b117799VCC $ 0 0I.8D.I.I!QrJ!*..";";"ArBBHHJJG 3"))'2223 .88H8H$-O-OW&)))UWJ*..339r::@@BBG +!!'***'!!3=dhhw>O>OQU3V3V^dhhw///\^ !;!;!ArBBHHJJ
 2%%j111 (""/9$((8:L:Ld/S/S[(+++Y[FJJw//5266<<>> -%%e,,,%/0A0A4%H%HP!!!b9--344::<<
.869J9JD.Q.QY6***WYJZ^^I66<"==CCEEMJJ7""0:599W;M;Mt0T0T\%))G,,,Z\K[__Y77=2>>DDFFN$Jyy==*=====CCEEzQQr=   Tuple[List[str], List[str]]c                  K   g }g }g }t          |                    d          pd                                          }|dk    r|                    d          }t          |t                    r|ni }|                    d          }t          |t
                    r|ng }	|	D ]}
t          |
t                    st          |
                    d          pd                                          }|dk    rEt          |
                    d          t                    r|                    d|
d         f           nQt          |                    d          t                    r|                    d|d         f           |dk    rEt          |                    d          t                    r|                    d|d         f           |dk    rt          |                    d          t                    r|d         }t          |                    d          t                    r|                    d|d         f           nEt          |                    d          t                    r|                    d|d         f           t          |                    d          t                    r|                    d          ni }t          |                    d          pd                                          }|dk    rFt          |                    d          t                    r|                    d|d         f           nK|dk    rEt          |                    d          t                    r|                    d|d         f           |D ]R\  }}|                     ||           d	{V }|r/|\  }}|                    |           |                    |           S||fS )
z=Best-effort extraction of inbound media to local cache paths.rq  rS   rr  rs  imagefilerv  rx  N)rJ   r   r]   rK   r   rM   r}  _cache_media)r   r   media_pathsrA  refsrq  r  rr  r  r  rF   	item_typerv  rx  r  kindrefcachedpathcontent_types                       r"   rK  zWeComAdapter._extract_media  s     !#!#13dhhy))/R006688g'**J",Z">">FJJBE:..J",Z">">FJJBE : :!$--  3 3 9r::@@BB	''Jtxx7H7H$,O,O'KK$w- 8999: $((7++T22 6Wd7m4555&  Z0@0@$%G%G VT&\2333(""z$((82D2Dd'K'K"hfjj00$77 <KK 89999

7 3 3T:: <KK&/ :;;;%/0A0A4%H%HP!!!b9--344::<<
  Z		'0B0BD%I%I KK%.122226!!j61B1BD&I&I!KKv/000 	1 	1ID#,,T377777777F 1%+"l""4(((""<000K''r=   r  mediaOptional[Tuple[str, str]]c                @  K   d|v rN|                     d          r8	 |                     |d                   }n:# t          $ r-}t                              d| j        ||           Y d}~dS d}~ww xY w|dk    rv|                     |          }	 t          ||          |                     |d          fS # t          $ r,}t          
                    d| j        |           Y d}~dS d}~ww xY wt          |                     d          p|                     d	          pd
          }t          ||          t          j        |          d         pdfS t          |                     d          pd                                          }|sdS 	 |                     |t"                     d{V \  }}n;# t          $ r.}t                              d| j        |||           Y d}~dS d}~ww xY wt          |                     d          pd                                          }	|	rS	 |                     ||	          }n;# t          $ r.}t                              d| j        |||           Y d}~dS d}~ww xY wt          |                     d          pd                              dd          d                                         pd}
|dk    r|                     ||
|                     |                    }	 t          ||          |
p|                     |d          fS # t          $ r-}t          
                    d| j        ||           Y d}~dS d}~ww xY w|                     ||                     d          |
          }t          ||          |
fS )z=Cache an inbound image/file/media reference to local storage.base64z)[%s] Failed to decode %s base64 media: %sNr  z
image/jpeg)fallbackz![%s] Rejected non-image bytes: %sfilenamer   
wecom_filer   application/octet-streamurlrS   	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)r   _decode_base64r   r   r   r   _detect_image_extr   _mime_for_extr"  r   rJ   r   	mimetypes
guess_typerC   _download_remote_bytesABSOLUTE_MAX_BYTES_decrypt_file_bytesrL   _guess_extension_guess_filename)r   r  r  rQ   r   extr  r  r   aes_keyr  s              r"   r  zWeComAdapter._cache_media  s      u8!4!4))%/::   H$)UY[^___ttttt w,,S11 1#s;;T=O=OPS^j=O=k=kkk!      NN#F	SVWWW44444  599Z00UEIIf4E4EUVVH,S(;;Y=QRZ=[=[\]=^=|b|||%))E""(b))//11 	4	!%!<!<SL^!<!_!_______LC 	 	 	LLA49dTWY\]]]44444	 eii))/R006688 	..sG<<   DdiQUWZ\_```ttttt 7;;~66<"==CCCKKANTTVVtZt7??''\DDZDZ[^D_D_'``C-c3779wI[I[\_jvI[IwIwww   JDIWZ\_```ttttt ''W[[9N-O-OQ]^^(h77EEsu   ; 
A2"A--A2&B8 8
C.!C))C.%F7 7
G/#G**G/+I 
I:#I55I:(L* *
M!4"MM!r   bytesc                    |                      dd          d                                         }t          j        |          S )NrH   r   )rL   rC   r  	b64decode)r   r   s     r"   r  zWeComAdapter._decode_base64?  s7    **S!$$R(..00(((r=   c                    |                      d          rdS |                      d          rdS |                      d          rdS |                      d          r| dd	         d
k    rdS dS )Ns   PNG

z.pngs   z.jpg)s   GIF87as   GIF89az.gifs   RIFF      s   WEBPz.webp
startswith)r   s    r"   r  zWeComAdapter._detect_image_extD  s    ??/00 	6???++ 	6??122 	6??7## 	QrT
g(=(=7vr=   r  r  r  c                f    t           j                            |                                 |          S rz  )r  	types_mapr   r]   )r  r  s     r"   r  zWeComAdapter._mime_for_extP  s#    "&&syy{{H===r=   r  r  c                    |rt          j        |          nd }|r|S t          t          |           j                  j        }|r|S |S rz  )r  guess_extensionr   r   r  suffix)r  r  r  r  path_exts        r"   r  zWeComAdapter._guess_extensionT  sT    9EOi'5554 	J*++2 	Or=   content_dispositionOptional[str]c                    |r,t          j        d|          }|r|                    d          S t          t	          |           j                  j        pd}d|vrt          j        |          pd}| | }|S )Nzfilename="?([^";]+)"?r   document.z.bin)	rU   searchr3  r   r   r  r   r  r  )r  r  r  matchr   r  s         r"   r  zWeComAdapter._guess_filename^  s     	&I68KLLE &{{1~~%HSMM&'',:
d??+L99CVC>C>>Dr=   r;  rA  r@   r   c                V   t          d |D                       rt          j        S t          d |D                       r|rt          j        nt          j        S t          |                     d          pd                                          dk    rt          j        S t          j        S )z+Choose the normalized inbound message type.c              3  @   K   | ]}|                     d           V  dS ))zapplication/ztext/Nr  rE   mtypes     r"   r|  z4WeComAdapter._derive_message_type.<locals>.<genexpr>n  s1      TTuu 9::TTTTTTr=   c              3  @   K   | ]}|                     d           V  dS )image/Nr  r  s     r"   r|  z4WeComAdapter._derive_message_type.<locals>.<genexpr>p  s0      CCeu))CCCCCCr=   rq  rS   ru  )	anyr   DOCUMENTr   PHOTOrJ   r   r]   VOICE)r   r;  rA  s      r"   rL  z!WeComAdapter._derive_message_typek  s     TTTTTTT 	(''CC{CCCCC 	C'+B;##1BBtxx	""(b))//11W<<$$r=   c                    dS )zAWeCom gates DM/group access at intake via dm_policy/group_policy.Tr<   r   s    r"   enforces_own_access_policyz'WeComAdapter.enforces_own_access_policyz  s	     tr=   c                    t          j        dd                                          dv rdS t          j        dd                                          dv S )NGATEWAY_ALLOW_ALL_USERSrS   >   1yestrueTWECOM_ALLOW_ALL_USERS)r   r   r]   r   s    r"   _open_dm_opted_inzWeComAdapter._open_dm_opted_in  sQ    9.3399;;?SSS4y0"55;;==AUUUr=   rT  c                    | j         dk    rdS | j         dk    rt          | j        |          S | j         dk    r|                                 S dS )NdisabledF	allowlistopen)r   ra   r   r  )r   rT  s     r"   _is_dm_allowedzWeComAdapter._is_dm_allowed  sX    ?j((5?k))!$"2I>>>?f$$))+++ur=   c                   t          |pd                                          }|sdS | j        dk    rdS | j        dk    rt          | j        |          S | j        dk    rdS | j        dk    r|                                 S dS )NrS   Fr  r  ro   Tr  )rJ   rC   r   ra   r   r  )r   rT  	principals      r"   rH  z"WeComAdapter._is_dm_intake_allowed  s    	R((..00	 	5?j((5?k))!$"2I>>>?i''4?f$$))+++ur=   r5  c                :   | j         dk    rdS | j         dk    rdS | j         dk    rt          | j        |          sdS |                     |          }t	          |                    d          p|                    d                    }|rt          ||          S dS )Nr  Fro   r  rp   rq   T)r   ra   r   _resolve_group_cfgrP   r   )r   r5  rT  	group_cfgsender_allows        r"   rG  zWeComAdapter._is_group_allowed  s    ++5**5,,^DDZ\c5d5d,5++G44	#IMM,$?$?$]9==Q\C]C]^^ 	;!,	:::tr=   c                   t          | j        t                    si S || j        v r-t          | j        |         t                    r| j        |         S |                                }| j                                        D ]K\  }}t          |t
                    r1|                                |k    rt          |t                    r|c S L| j                            d          }t          |t                    r|ni S )Nr\   )rK   r   r   r]   r  rJ   r   )r   r5  loweredre  r?   wildcards         r"   r  zWeComAdapter._resolve_group_cfg  s    $,-- 	Idl""z$,w2G'N'N"<((--//,,,.. 	 	JC#s## 		w(>(>:eUYCZCZ(><##C((%h55=xx2=r=   r?  c                   t          |pd                                          }t          |pd                                          }|r|sd S || j        |<   t          | j                  t          k    rX| j                            t          t          | j                                       t          | j                  t          k    Vd S d S )NrS   )rJ   rC   r   r  r   r  nextiter)r   r?  r   normalized_message_idr#  s        r"   rF  z#WeComAdapter._remember_reply_req_id  s     #J$4" 5 5 ; ; = ="--3355$ 	,= 	F5F12$%&&77##Dd.A)B)B$C$CDDD $%&&777777r=   c                   t          |pd                                          }t          |pd                                          }|r|sdS || j        |<   t          | j                  t          k    rX| j                            t          t          | j                                       t          | j                  t          k    VdS dS )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.
        rS   N)rJ   rC   r   r  r   r  r  r  )r   r5  r   normalized_chat_idr#  s        r"   rI  z"WeComAdapter._remember_chat_req_id  s     !B//5577"--3355! 	): 	F6G 23$)**^;;#''T$2I-J-J(K(KLLL $)**^;;;;;;r=   reply_toc                    t          |pd                                          }|r|                    d          rd S | j                            |          S )NrS   r9  )rJ   rC   r  r   r   )r   r  r`   s      r"   _reply_req_id_for_messagez&WeComAdapter._reply_req_id_for_message  sX    R((..00
 	Z228<< 	4"&&z222r=   r  c                    t          j        |           d         }|r|S t          |           j                                        dk    rdS dS )Nr   z.amrr6   r  )r  r  r   r  r]   )r  	mime_types     r"   _guess_mime_typezWeComAdapter._guess_mime_type  sP    (2215	 	>> &&((F22;))r=   c                    t          | pd                              dd          d                                                                         }t                              |          }|s|S |dv r|S |S )NrS   r  r   r   >   
text/plainr  )rJ   rL   rC   r]   rc   r  )r  r  r`   guesseds       r"   _normalize_content_typez$WeComAdapter._normalize_content_type  sx    +,,223::1=CCEEKKMM
//99 	NCCCNr=   c                   t          | pd                                                                          }|                    d          rdS |                    d          rdS |                    d          s|dk    rdS d	S )
NrS   r  r  zvideo/videozaudio/zapplication/oggru  r  )rJ   rC   r]   r  )r  r  s     r"   _detect_wecom_media_typez%WeComAdapter._detect_wecom_media_type  s    *++113399;;	)) 	7)) 	7)) 	Y:K-K-K7vr=   N	file_sizeintdetected_typec                   | dz  }t          |pd                                          }t          |pd                                                                          }| t          k    r|dd|dddd d	S |d
k    r| t          k    rddd dd|ddd	S |dk    r| t
          k    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          k    rddd dd|ddd	S |dd dd d	S )Ni   r  rS   Tu   文件大小 z.2fuq   MB 超过了企业微信允许的最大限制 20MB，无法发送。请尝试压缩文件或减小文件大小。F)
final_typerejectedreject_reason
downgradeddowngrade_noter  u   图片大小 u3   MB 超过 10MB 限制，已转为文件格式发送r  u   视频大小 ru  u   语音格式 uE    不支持，企微仅支持 AMR 格式，已转为文件格式发送u   语音大小 u2   MB 超过 2MB 限制，已转为文件格式发送)rJ   r]   rC   r  IMAGE_MAX_BYTESVIDEO_MAX_BYTESVOICE_SUPPORTED_MIMESVOICE_MAX_BYTES)r  r  r  file_size_mbnormalized_typenormalized_content_types         r"   _apply_file_size_limitsz$WeComAdapter._apply_file_size_limits  s    K0m5v66<<>>"%l&8b"9"9"?"?"A"A"G"G"I"I)))- DLz D D D $"&	 	 	 g%%)o*E*E$!!%""w,"w"w"w"w   g%%)o*E*E$!!%""w,"w"w"w"w   g%%& 	+BJ_+_+_"( %%)"& G(?  G  G  G   ?**"( %%)"&&zl&z&z&z&z   *!"
 
 	
r=   r   c                    |                      dd          }|dv rd S t          |                      d          pd          }d| d| S )Nr   r   r   r   unknown errorzWeCom errcode z: )r   rJ   )r   r   r   s      r"   _response_errorzWeComAdapter._response_error7  sY    ,,y!,,i4X\\(++>??3336333r=   	operationc                \    |                      |          }|rt          | d|           d S )N	 failed: )r  r   )clsr   r  r   s       r"   _raise_for_wecom_errorz#WeComAdapter._raise_for_wecom_error?  sC    ##H-- 	?)==e==>>>	? 	?r=   encrypted_datar  c                J  
 | st          d          |st          d          |ddt          |          dz  z
  dz  z  z   }t          j        |          }t          |          dk    rt          dt          |                     	 ddlm}m}m} n"# t          $ r}t          d	          |d }~ww xY w ||
                    |          |                    |d d
                             }|                                }|                    |           |                                z   }	|	d         

dk     s
dk    s
t          |	          k    rt          d
           t          
fd|	
 d          D                       rt          d          |	d 
          S )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   | ]
}|k    V  d S rz  r<   )rE   bytepad_lens     r"   r|  z3WeComAdapter._decrypt_file_bytes.<locals>.<genexpr>^  s'      @@4tw@@@@@@r=   z.Invalid PKCS#7 padding: padding bytes mismatch)r"  r  r  r  &cryptography.hazmat.primitives.ciphersr  r  r  ImportErrorr   AESCBC	decryptorupdatefinalizer  )r  r  re  r  r  r  r   cipherr$  	decryptedr  s             @r"   r  z WeComAdapter._decrypt_file_bytesE  s    	86777 	42333 CAGq(8$8A#=>>w''s88r>>_UXY\U]U]__```	_XXXXXXXXXXX 	_ 	_ 	_TUU[^^	_ 
s++UYYs3B3x-@-@AA$$&&	$$^44y7I7I7K7KK	B-Q;;'B,,'C	NN*B*BGgGGHHH@@@@9gXYY+?@@@@@ 	OMNNN7(##s   	
B 
B3B..B3r  Tuple[bytes, Dict[str, str]]c           	     f  K   ddl m} ddlm}m}  ||          st          d|d d                    t          st          d          | j        p |ddd	|gi
          }|| j        u}	 |	                    d|ddd          4 d {V 	 }|
                                 d |j                                        D             }	|	                    d          }
|
rJ|
                                r6t          |
          |k    r#t          dt          |
           d| d          t!                      }|                                2 3 d {V }|                    |           t'          |          |k    r#t          dt'          |           d| d          S6 t)          |          |	fcd d d           d {V  |r|                                 d {V  S S # 1 d {V swxY w Y   	 |r|                                 d {V  d S d S # |r|                                 d {V  w w xY w)Nr   r   )r   is_safe_urlz&Blocked unsafe URL (SSRF protection): P   z*httpx is required for WeCom media downloadr/   Tr   )r   r   r   GETHermesAgent/1.0z*/*)
User-AgentAcceptr   c                >    i | ]\  }}|                                 |S r<   )r]   )rE   re  r?   s      r"   
<dictcomp>z7WeComAdapter._download_remote_bytes.<locals>.<dictcomp>  s&    YYY*#u399;;YYYr=   zcontent-lengthz"Remote media exceeds WeCom limit: z	 bytes > z bytesz4Remote media exceeds WeCom limit while downloading: )r   r   r   r   r+  r"  r;   r   r   streamraise_for_statusr   r  r   isdigitr  	bytearrayaiter_bytesrc  r  r  r   )r   r  r  r   r   r+  clientcreated_clientr   r   content_lengthr   chunks                r"   r  z#WeComAdapter._download_remote_bytesc  s     
 	@?????OOOOOOOO{3 	RPc#2#hPPQQQ 	MKLLL" 
&C&C!#&:%;<'
 '
 '

  t'88	&}}"3#  %   , , , , , , , , ))+++YY@P@V@V@X@XYYY!(-=!>!>! n&<&<&>&> 3~CVCVYbCbCb$lS=P=Pll[dlll   !{{#+#7#7#9#9       %KK&&&4yy9,,(xSVW[S\S\xxgpxxx   - $: T{{G+1, , , , , , , , , , , , ,4  &mmoo%%%%%%%%&5, , , , , , , , , , , , , , ,4  &mmoo%%%%%%%%%& &~ &mmoo%%%%%%%%&s>   .!H B;GFAG/H 
G))H ,G)-H H0media_sourcec                P    t          t          | pd                    }|j        dv S )NrS   >   httphttps)r   rJ   scheme)r=  parseds     r"   _looks_like_urlzWeComAdapter._looks_like_url  s*    #l0b1122} 111r=   	file_nameTuple[bytes, str, str]c                4  K   t          |pd                                          }|st          d          t          j        d|          rt          d|           t          |          }|j        dv r|                     |t                     d {V \  }}|	                    d          }|p*| 
                    |||	                    dd                    }|                     |	                    dd          |          }	||	|fS |j        d	k    r4t          t          |j                                                            }
n!t          |                                          }
|
                                s(t          j                    |
z                                  }
|
                                r|
                                st+          d
|
           |
                                }|p|
j        }|                     d|          }	||	|fS )NrS   zmedia source is requiredz	<[^>\n]+>z:Media placeholder was not replaced with a real file path: >   r?  r@  r  r  r  r  zMedia file not found: )rJ   rC   r"  rU   	fullmatchr   rA  r  r  r   r  r  r   r   r  
expanduseris_absolutecwdresolveexistsis_fileFileNotFoundError
read_bytesr   )r   r=  rD  r=  rB  r   r   r  resolved_namer  
local_paths              r"   _load_outbound_mediaz!WeComAdapter._load_outbound_media  s!     
 \'R((..00 	97888<f-- 	dbZ`bbccc&!!=---"&"="=fPb"="c"cccccccMD'")++.C"D"D%{)=)=fFY[b[f[fguwy[z[z){){M77NTV8W8WYfggL}44=F""gfk2233>>@@JJf0022J%%'' 	=(**z1::<<J  "" 	K**<*<*>*> 	K#$IZ$I$IJJJ$$&&!4Z_33BFF\=00r=   c                   K   |                      ||           d {V \  }}}|                     |          }|                     t          |          ||          }||||d|S )NrD  )r   r  rD  r  )rR  r  r  r  )r   r=  rD  r   r  rP  r  
size_checks           r"   _prepare_outbound_mediaz$WeComAdapter._prepare_outbound_media  s      
 372K2KLdm2K2n2n,n,n,n,n,n,n)lM55lCC11#d))]LYY
(&*	
 

 
 	
r=   
media_typec           
       K   |st          d          t          |          }|t          z   dz
  t          z  }|t          k    rt          d| dt           d          |                     t
          ||||t          j        |                                          d           d {V }| 	                    |d           t          |                    d          t                    r|                    d          ni }t          |                    d	          pd
                                          }|st          d|           t!          t#          d|t                              D ]z\  }	}
||
|
t          z            }|                     t$          ||	t'          j        |                              d          d           d {V }| 	                    |d|	            {|                     t,          d	|i           d {V }| 	                    |d           t          |                    d          t                    r|                    d          ni }t          |                    d          pd
                                          }|st          d|           t          |                    d          p|          ||                    d          dS )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_idrS   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   r`  ra  )r"  r  UPLOAD_CHUNK_SIZEMAX_UPLOAD_CHUNKSr  APP_CMD_UPLOAD_MEDIA_INIThashlibr[  	hexdigestr  rK   r   r   rJ   rC   r   	enumeraterangeAPP_CMD_UPLOAD_MEDIA_CHUNKr  	b64encodedecodeAPP_CMD_UPLOAD_MEDIA_FINISH)r   r   rW  r  rY  rZ  init_response	init_bodyr\  r^  startr<  chunk_responsefinish_responsefinish_bodyr`  s                   r"   _upload_media_bytesz WeComAdapter._upload_media_bytes  sT      	:8999YY
"%66:?PP+++f<ffL]fff   #00%"$( ,{4((2244 	
 	
 	
 	
 	
 	
 	
 	
 	##M3FGGG1;M<M<Mf<U<UW[1\1\dM%%f---bd		k228b99??AA	 	kiZgiijjj"+E!ZAR,S,S"T"T 	] 	]K):!::;E#'#5#5*!*#.#)#3E#:#:#A#A'#J#J	 $ $      N ''8[k8[8[\\\\ $ 2 2')$!
 !
 
 
 
 
 
 
 	##O5JKKK5?@S@STZ@[@[]a5b5bjo))&111hj{z228b99??AA 	nl[jllmmm //=:>> %//,77
 
 	
r=   r`  c           
        K   |                      t          d|d||d|ii           d {V }|                     |d           |S )Nr1  rq  r`  zsend media message)r  APP_CMD_SENDr  )r   r5  rW  r`  r   s        r"   _send_media_messagez WeComAdapter._send_media_message  ss      ++':Z2
 
 
 
 
 
 
 
 	##H.BCCCr=   rt  c                   K   |                      |dd|d | j                 id           d {V }|                     |d           |S )Nmarkdownrt  )rq  rx  zsend reply markdown)r$  MAX_MESSAGE_LENGTHr  )r   r   rt  r   s       r"   _send_reply_markdownz!WeComAdapter._send_reply_markdown  s}      11%&0H1H0H(IJ 
 
 
 
 
 
 
 
 	##H.CDDDr=   c                z   K   |                      |d||d|ii           d {V }|                     |d           |S )Nrq  r`  zsend reply media message)r$  r  )r   r   rW  r`  r   s        r"   _send_reply_media_messagez&WeComAdapter._send_reply_media_message  sp       11:Z2
 
 
 
 
 
 
 
 	##H.HIIIr=   Optional[SendResult]c                   K   |sd S |                      |||           d {V }|j        s&t                              d| j        |j                   |S )Nr5  rt  r  z'[%s] Follow-up markdown send failed: %s)sendsuccessr   r   r   r   )r   r5  rt  r  results        r"   _send_followup_markdownz$WeComAdapter._send_followup_markdown.  sk        	4yy'HyUUUUUUUU~ 	_NNDdiQWQ]^^^r=   captionr   c                  K   |st          dd          S 	 |                     ||           d {V }n# t          $ r(}t          dt          |                    cY d }~S d }~wt          $ rJ}t
                              d| j        ||           t          dt          |                    cY d }~S d }~ww xY w|d         r>|                     |d|d          |	           d {V  t          d|d                   S | 	                    |          }|s|| j
        v r| j
        |         }	 |                     |d
         |d         |d                    d {V }	|r*|                     ||d         |	d                    d {V }
n)|                     ||d         |	d                    d {V }
nx# t          j        $ r t          dd          cY S t          $ rJ}t
                              d| j        ||           t          dt          |                    cY d }~S d }~ww xY wd }d }|r|                     |||	           d {V }|d         r/|d         r'|                     |d|d          |	           d {V }t          d|                     |
          p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 )NFchat_id is requiredr  r   rT  z,[%s] Failed to prepare outbound media %s: %sr   u   ⚠️ r  )r  r   r  rD  r`  zTimeout sending media to WeComz [%s] Failed to send media %s: %sr  r  u   ℹ️ Tr  )uploadr  r  caption_error	downgradedowngrade_errorr  r?  raw_response)r   rV  rN  rJ   r   r   r   r   r  r  r   rs  r|  rv  r   r   r   r   r   r   r  r  )r   r5  r=  r  rD  r  preparedr   r   upload_resultmedia_responsecaption_resultdowngrade_results                r"   _send_media_sourcezWeComAdapter._send_media_source;  s       	Je3HIIII	=!99,R[9\\\\\\\\HH  	= 	= 	=e3s88<<<<<<<<< 	= 	= 	=LLGT`befffe3s88<<<<<<<<<	= J 	N..5(?355! /         
 e8O3LMMMM55h?? 	<4+B B B27;L	="&":": &%# #      M
  '+'E'E \*!*-( ( " " " " " " (,'?'?\*!*-( ( " " " " " "
 # 	U 	U 	Ue3STTTTTT 	= 	= 	=LL;TYVYZZZe3s88<<<<<<<<<	=  	#'#?#?! $@ $ $      N
 L! 	h/?&@ 	%)%A%A6(#3466! &B & &             ++N;;Ttz||?OPSQSPS?T'':HR>66d9G!pP^Pf!p!5!5lp>NX-::TX=M#xVfVn#x#3#9#9tx 
 
 
 	
sQ   5 
B9A"B9"B9/?B4.B94B90BF5 5 H*	H* ?H%H*%H*metadatac           	       K   ~|st          dd          S 	 |                     |          }|s|| j        v r| j        |         }|r|                     ||           d{V }n4|                     t
          |dd|d| j                 id           d{V }nw# t          j        $ r t          dd          cY S t          $ rI}t                              d	| j        |           t          dt          |                    cY d}~S d}~ww xY w|                     |          }|rt          d|          S t          d
|                     |          pt!          j                    j        dd         |          S )z?Send markdown to a WeCom chat via proactive ``aibot_send_msg``.Fr  r  Nrx  rt  )r1  rq  rx  z Timeout sending message to WeComz[%s] Send failed: %sTr  r  )r   r  r   rz  r  ru  ry  r   r   r   r   r   r   rJ   r  r   r   r   r   )	r   r5  rt  r  r  r   r   r   r   s	            r"   r  zWeComAdapter.send  s       	Je3HIIII	=99(CCL @Gt/F$F$F#6w? 
!%!:!:<!Q!QQQQQQQ!%!3!3 ")#-%.8P9P8P0Q$R " "       # 	W 	W 	We3UVVVVVV 	= 	= 	=LL/C@@@e3s88<<<<<<<<<	= $$X.. 	:e59999++H55N9I#2#9N!
 
 
 	
s$   B B  D;	D>DDD	image_urlc                "  K   ~|                      ||||           d {V }|j        s|                     |          s|S t                              d| j        ||j                   |r| d| n|}|                     |||           d {V S )Nr5  r=  r  r  z3[%s] Falling back to text send for image URL %s: %sra  r  )r  r  rC  r   r   r   r   r  )r   r5  r  r  r  r  r  fallback_texts           r"   
send_imagezWeComAdapter.send_image  s       .."	 / 
 
 
 
 
 
 
 
 > 	!5!5i!@!@ 	MLdiYbdjdpqqq5<K711i111)YYwPXYYYYYYYYYYr=   
image_pathc                F   K   ~|                      ||||           d {V S Nr  r  )r   r5  r  r  r  kwargss         r"   send_image_filezWeComAdapter.send_image_file  R       ,,#	 - 
 
 
 
 
 
 
 
 	
r=   	file_pathc                H   K   ~|                      |||||           d {V S )N)r5  r=  r  rD  r  r  )r   r5  r  r  rD  r  r  s          r"   send_documentzWeComAdapter.send_document  sU       ,," - 
 
 
 
 
 
 
 
 	
r=   
audio_pathc                F   K   ~|                      ||||           d {V S r  r  )r   r5  r  r  r  r  s         r"   
send_voicezWeComAdapter.send_voice  r  r=   
video_pathc                F   K   ~|                      ||||           d {V S r  r  )r   r5  r  r  r  r  s         r"   
send_videozWeComAdapter.send_video  r  r=   c                   K   ~~dS )z8WeCom does not expose typing indicators in this adapter.Nr<   )r   r5  r  s      r"   send_typingzWeComAdapter.send_typing  s      XXXr=   c                f   K   ||r)|                                                     d          rdnddS )zReturn minimal chat info.r3  r4  )r   r   )r]   r  )r   r5  s     r"   get_chat_infozWeComAdapter.get_chat_info  sB       &X7==??+E+Eg+N+NXGGTX
 
 	
r=   )rd   r   )r   r8   r7   r8   r7   r   )r   rJ   r7   r   )r   r   r7   r   )r   r   r7   r   )r   rJ   r   r   r   r  r7   r   )
r   rJ   r   r   r   rJ   r   r  r7   r   )r%  rJ   r7   rJ   )r   r   r7   rJ   )rQ   r   r7   r)  )rX  r   r7   rJ   )rX  r   r7   r   )re  rJ   r7   r   )r   r   r7   ro  )r   r   r7   r  )r  rJ   r  r   r7   r  )r   rJ   r7   r  )r   r  r7   rJ   )r  )r  rJ   r  rJ   r7   rJ   )r  rJ   r  rJ   r  rJ   r7   rJ   )r  rJ   r  r  r  rJ   r7   rJ   )r   r   r;  rJ   rA  r@   r7   r   r7   r8   )rT  rJ   r7   r8   )r5  rJ   rT  rJ   r7   r8   )r5  rJ   r7   r   )r?  rJ   r   rJ   r7   r   )r5  rJ   r   rJ   r7   r   )r  r  r7   r  )r  rJ   r7   rJ   )r  rJ   r  rJ   r7   rJ   )r  rJ   r7   rJ   rz  )r  r  r  rJ   r  r  r7   r   )r   r   r7   r  )r   r   r  rJ   r7   r   )r  r  r  rJ   r7   r  )r  rJ   r  r  r7   r)  )r=  rJ   r7   r8   )r=  rJ   rD  r  r7   rE  )r=  rJ   rD  r  r7   r   )r   r  rW  rJ   r  rJ   r7   r   )r5  rJ   rW  rJ   r`  rJ   r7   r   )r   rJ   rt  rJ   r7   r   )r   rJ   rW  rJ   r`  rJ   r7   r   )r5  rJ   rt  rJ   r  r  r7   r}  )NNN)r5  rJ   r=  rJ   r  r  rD  r  r  r  r7   r   )NN)
r5  rJ   rt  rJ   r  r  r  r)  r7   r   )r5  rJ   r  rJ   r  r  r  r  r  r)  r7   r   )
r5  rJ   r  rJ   r  r  r  r  r7   r   )r5  rJ   r  rJ   r  r  rD  r  r  r  r7   r   )
r5  rJ   r  rJ   r  r  r  r  r7   r   )
r5  rJ   r  rJ   r  r  r  r  r7   r   )r5  rJ   r7   r   )M__name__
__module____qualname____doc__ry  SUPPORTS_MESSAGE_EDITINGrl  r|   r   r   r   r   r   r   r  r   r
  r   r   REQUEST_TIMEOUT_SECONDSr  APP_CMD_RESPONSEr$  staticmethodr   r   r   r  r_  rP  rd  rJ  rK  r  r  r  r  r  r  rL  propertyr  r  r  rH  rG  r  rF  rI  r  r  r  r  r  r  classmethodr  r  r  rC  rR  rV  rs  rv  rz  r|  r  r  r  r  r  r  r  r  r  r  __classcell__)r   s   @r"   rc   rc      s$       KK+$ +5 +5 +5 +5 +5 +5b 5: , , , , , ,\4 4 4 4>   @ @ @ @:S S S S0Z Z Z Z6= = = =   (W W W W&6 6 6 6* * * * Tk 6 6 6 6 6& $0A A A A A4 . . . \.    \ > > > \>K- K- K- K-b
 
 
 

 
 
 
<%> %> %> %>N 0R 0R 0R \0Rd-( -( -( -(^0F 0F 0F 0Fd ) ) ) \) 	 	 	 \	 > > > > \>    \ 
 
 
 \
       \     XV V V V
         
> 
> 
> 
>E E E EM M M M"3 3 3 3 * * * \*    \    \ =
 =
 =
 =
 \=
~ 4 4 4 \4 ? ? ? [?
 $ $ $ \$:0& 0& 0& 0&d 2 2 2 \2 $(!1 !1 !1 !1 !1L $(
 
 
 
 
 8
 8
 8
 8
t
 
 
 
	 	 	 	   ( #'	    " "&#'"&Q
 Q
 Q
 Q
 Q
n #'-1,
 ,
 ,
 ,
 ,
d "&"&-1Z Z Z Z Z6 "&"&
 
 
 
 
( "&#'"&
 
 
 
 
, "&"&
 
 
 
 
( "&"&
 
 
 
 
     
 
 
 
 
 
 
 
r=   rc   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_secondsr  r  Optional[Dict[str, str]]c                `   	 ddl }ddl}n+# t          $ r t                              d           Y dS w xY wt
           d}t          ddd           	 |j                            |d	d
i          }|j        	                    |d          5 }t          j        |                                                    d                    }ddd           n# 1 swxY w Y   nE# t          $ r8}t                              d|           t          d|            Y d}~dS d}~ww xY w|                    d          pi }t!          |                    d          pd                                          }t!          |                    d          pd                                          }	|r|	s,t                              d|           t          d           dS t          d           t                       d}
	 ddl}|                                }|                    |	           |                    d           |                    d           d}
n# t          $ r Y nt          $ r Y nw xY wt.           |j                            |           }|
rt          d|            n"t          d| d           t          d           t                       t          ddd           t5          j                    | z   }t8           d|j                            |           }d}t5          j                    |k     rN	 |j                            |d	d
i          }|j        	                    |d           5 }t          j        |                                                    d                    }ddd           n# 1 swxY w Y   nK# t          $ r>}t                              d!|           t5          j        t>                     Y d}~d}~ww xY w|d"z  }t          d#dd           |                    d          pi }t!          |                    d$          pd                                           }|d%k    rt                       |                    d&          pi }t!          |                    d'          p|                    d(          pd                                          }t!          |                    d)          pd                                          }|r|r||d*S t          !                    d+|           t          d,           dS t5          j        t>                     t5          j                    |k     Nt                       t          d-| d.z   d/           dS )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...rS   T)endflushr/  r.  r1     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:

  ra  zH  Tip: pip install qrcode  to display a scannable QR code here next timez#  Fetching configuration results...z?scode=r2   zWeCom QR poll error: %sr   r  statusr  bot_infobotidrf   rh   )rf   rh   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 (r4   z minutes). Please try again.)"urllib.requesturllib.parser!  r   r   _QR_GENERATE_URLprintrequestRequesturlopenr+  r,  readrk  r   r   rJ   rC   qrcodeQRCodeadd_datamakeprint_ascii_QR_CODE_PAGEparserx  r   	monotonic_QR_QUERY_URLr   r  _QR_POLL_INTERVALr]   r   )r  urllibgenerate_urlreqresprQ   r   r   r  r  qr_rendered_qrcodeqrpage_urlr   	query_url
poll_countr  result_datar  r  rf   rh   s                          r"   qr_scan_for_bot_infor  ,  sw   "   ;<<<tt '666L 

$"D9999n$$\LJ[;\$]]^##C#44 	:*TYY[[//8899C	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	:   <cBBB#   ttttt
 776?? bD!!'R((..00E488J''-2..4466H  ?EEE3444t	(OOO 
GGGK
    ^^
H
D
d###        <!3!3E!:!:<<H ZU8UUVVVVJhJJJKKKXYYY	GGG	
/RtDDDD ~/1H DD););E)B)BDDIJ
.

X
%
%	.((\K\<](^^C''R'88 ADDIIKK$6$6w$?$?@@A A A A A A A A A A A A A A A 	 	 	LL2C888J()))HHHH	
 	a
cr&&&&jj((.B[__X..4"55;;==YGGG"z228bHg..N(,,x2H2HNBOOUUWWFh//5266<<>>F <& <"(F;;;NNX   =  
 4
$%%%G .

X
%
%J 
GGG	
U/R"7
U
U
UVVV4s    $33:C :CC CC CC 
D!)-DD!7AI 
I+	I+*I+=:O	 7:N=1O	 =OO	 OO	 	
P4PP)	thread_idmedia_filesforce_documentc                 K   t                      sddiS 	 t          |           }|                                 d{V }|sddt          |dd          pd iS 	 |                    ||           d{V }|j        s&dd|j         i|                                 d{V  S dd	||j        d
|                                 d{V  S # |                                 d{V  w xY w# t          $ r}	dd|	 icY d}	~	S d}	~	ww xY 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)r  platformr5  r?  )
r>   rc   r   rk  r  r  r   r   r?  r   )
pconfigr5  r   r  r  r  adapter	connectedr  es
             r"   _standalone_sendr    s       $%% \Z[[4w''!//++++++++	 	}77Lacg;h;h;{l{}}~~	'"<<99999999F> G!Ev|!E!EF $$&&&&&&&&&&  #"$/	  $$&&&&&&&&&&'$$&&&&&&&&&& 4 4 42q2233333334sB   AC( .C	 C( #C	 .C( 	C%%C( (
D2C>8D>Dr   c                    ddl m} 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dS  |dddgd          }d}d}|dk    r	 t                      }n># t          $ r  |	d           Y dS t           $ r} |	d|            d}Y d}~nd}~ww xY w|r7|                    dd          }|                    dd          } |d           |r|s |d           d}d}|r|sd |d            |d            |d            |d            |dd
          }|s |	d           dS  |dd          }|s |	d           dS  |d|            |d|            |d             |d!            |d"d
          }|r, |d#|                    d$d                      |d%           n |d&g d'd(          }|dk    r$ |d)d*            |d+d,            |	d-           nR|d(k    r# |d)d.            |d/            |d0           n)|d1k    r |d)d2            |	d3           n |d4            |d5d
                                          }|r |d6|            |d7|            n |d6          r |d8            |d9           dS ):u   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remove_env_valuesave_env_value)prompt_choice)promptprompt_yes_noprint_header
print_infoprint_successprint_warningprint_errorWeCom (Enterprise WeChat)rg   ri   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: rf   rS   rh   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)rr    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   rn   r  r  r  u0   Open access enabled — anyone can use your bot!ro   u@   DM pairing mode — users will receive a code to request access.z6Approve with: hermes pairing approve <platform> <code>r0   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 zHome channel cleared.u   💬 WeCom configured!)hermes_cli.configr  r  r  hermes_cli.setupr  hermes_cli.cli_outputr  r  r  r  r  r  r  r  KeyboardInterruptr   r   replacerC   )r  r  r  r  r  r  r  r  r  r  r  existing_bot_idexisting_secret
method_idxrf   rh   credentialsr   allowed
access_idxhomes                        r"   interactive_setupr    s    RQQQQQQQQQ......                  L,---#mN33O#mN33O ? 4555}1599 	F-R7	
 	
 J FFQ	.00KK  	 	 	M2333FF 	 	 	M2S22333KKKKKK	  	Q __Xr22F __Xr22FMOPPP 	V 	JPQQQFF  
deee
'(((
RSSS
TUUU5111 	MJKKKF4000 	MJKKKFN>6***N>6***JFGGGJGHHHfCeTTTG R,gooc2.F.FGGGMNNNN"]7   	
 	

 ??N,f555N4f===MLMMMM1__N,i888M\]]]JOPPPP1__N,j999M56666JPQQQ6CeTTTZZ\\D 0+T2223T334444011 	0J.///M*+++++s   ;B
 
C"	C+C  Cc                l    t          | di           pi }t          |                    d                    S )zWeCom (Smart Robot) is connected when a bot_id is configured. Mirrors the
    legacy _PLATFORM_CONNECTED_CHECKERS[Platform.WECOM] entry.r~   rf   rk  r8   r   rd   r~   s     r"   _is_connectedr  K  s4     FGR((.BE		(##$$$r=   c                    t          | di           pi }t          |                    d          p|                    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.r~   corp_idappsr  r  s     r"   _callback_is_connectedr  R  sE     FGR((.BE		)$$9		&(9(9:::r=   c                     t          |           S )zCFactory wrapper that constructs WeComAdapter from a PlatformConfig.)rc   )rd   s    r"   _build_adapterr  Z  s    r=   c                $    ddl m}  ||           S )zKFactory wrapper that constructs WecomCallbackAdapter from a PlatformConfig.r   )WecomCallbackAdapter)(plugins.platforms.wecom.callback_adapterr  )rd   r  s     r"   _build_callback_adapterr  _  s%    MMMMMM'''r=   c                    |                      ddt          t          t          t          ddgdt          dddt
          d	d
d           ddlm} |                      ddt          |t          t          ddgdddd
d           dS )u6   Plugin entry point — registers both WeCom platforms.r  r   rg   ri   z,Run `hermes setup` to install WeCom support.rr   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  r>   r  r  r  r  r0  r  r  )ctxr0  s     r"   registerr8  e  s    )&)"%$n5C"/-1-!!    & [ZZZZZ0/2+..0LMC86!      r=   rz  r  )r?   r   r7   r@   )rQ   rJ   r7   rJ   )rY   r@   rZ   rJ   r7   r8   )r  r  r7   r  r  )cr  
__future__r   r   r  re  r+  loggingr  r   rU   r   r   r   r   pathlibr   typingr   r   r	   r
   r   r  r   r   r   r:   r!  httpxr;   gateway.configr   r   gateway.platforms.helpersr   r   r   r   r   r   r   r   utilsr   agent.secret_scoper   r   r   r   r#   	getLoggerr  r   r   r   APP_CMD_CALLBACKAPP_CMD_LEGACY_CALLBACKr  ru  r  r   rd  ri  rl  r  r  ry  r   r  r   r  r   r  r  r  FILE_MAX_BYTESr  rb  rc  r  r>   rP   rX   ra   rc   r  r  r  r  _QR_POLL_TIMEOUTr  r  r  r  r  r  r  r8  r<   r=   r"   <module>rG     sf   : # " " " " "          				 				   ' ' ' ' ' ' ' '       3 3 3 3 3 3 3 3 3 3 3 3 3 3 * * * * * * * *NNN   GGGLLLOO   OEEE 4 3 3 3 3 3 3 3 9 9 9 9 9 9                      J J J J J J ? ? ? ? ? ?/ / / /( 
	8	$	$2% ' * / & 5 7 9 %'>? )-C,DD    ! &&& ""!!#   $ 1 1 1 1
> > > >      w
 w
 w
 w
 w
& w
 w
 w
|+ ? ?K  
 ,t t t t t tT $4 $4 $4 $4 $4Np, p, p, p,f% % % %; ; ; ;     
( ( (# # # # # #s$   A 	A)(A)-A4 4	B ?B 