
    (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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,  ej-        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ZJdGd'ZKdHd+ZLdId.ZMdJd1ZN G d2 d3e%          ZOd4ZPd5ZQd6ZRd7ZSd8ZTeTd9dKd=ZUddd	d>d?ZVdLdAZWdGdBZXdGdCZYdD ZZdE Z[dLdFZ\dS )Ma8  
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/amrreturnboolc                     t           ot          S )z2Check if WeCom runtime dependencies are available.)AIOHTTP_AVAILABLEHTTPX_AVAILABLE     E/home/rurouni/.hermes/hermes-agent/plugins/platforms/wecom/adapter.pycheck_wecom_requirementsr3   m   s    00r1   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 r0   )strip.0items     r2   
<listcomp>z _coerce_list.<locals>.<listcomp>w   s-    JJJTZZ\\J

JJJr1   ,c                    g | ]D}t          |                                          #t          |                                          ES r0   )strr8   r9   s     r2   r<   z _coerce_list.<locals>.<listcomp>y   s=    IIIds4yy7H7HID		!!IIIr1   )
isinstancer?   splitlisttuplesetr8   )r4   s    r2   _coerce_listrE   r   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=r1   rawr?   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):)r?   r8   resub
IGNORECASE)rF   r4   s     r2   _normalize_entryrM   }   sZ    HHNNEF:r5>>>EF$b%r}EEEE;;==r1   entriestargetc                    t          |                                                                          }| D ]2}t          |                                          }|dk    s||k    r dS 3dS )z4Case-insensitive allowlist match with ``*`` support.*TF)r?   r8   lowerrM   )rN   rO   normalized_targetentry
normalizeds        r2   _entry_matchesrV      sv    F))++1133  %e,,2244

.? ? ?44 !@5r1   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          j        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 | _         tC          dd          | _"        tC          dd          | _#        i | _$        i | _%        tM          j'                    j(        | _)        i | _*        d S )Nbot_idWECOM_BOT_IDrH   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extrar?   getosgetenvr8   _bot_id_secretDEFAULT_WS_URL_ws_urlrR   
_dm_policyrE   _allow_from_group_policy_group_allow_fromr@   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)selfrY   rs   	__class__s      r2   rq   zWeComAdapter.__init__   s   000"599X..O")NB2O2OPPVVXX599X..O")NB2O2OPPVVXX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r1   )is_reconnectr   r,   r+   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	} t          j        dd |                      | _        |                                  d{V  |                                  t          j        |                                           | _        t          j        |                                           | _        t                              d| j        | j                   dS # t.          $ 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_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/   rw   rx   %gateway.platforms._http_client_limitsr   httpxAsyncClientr   _open_connection_mark_connectedasynciocreate_task_listen_loopr   _heartbeat_loopr   inforz   	Exceptionerror_cleanup_wsaclose)r   r   messager   excs        r2   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 % 1t<Q<Q<S<S! ! !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F- -
I	7B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    r2   
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    r2   r   zWeComAdapter._cleanup_ws  s      8 	#DHO 	#(.."""""""""= 	(!5 	(-%%'''''''''r1   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_envr$   )	heartbeatr   	subscribereq_id)r[   r]   	device_idcmdheadersbodyerrcoder      Nr   errmsgzauthentication failedz
 (errcode=))r   aiohttpClientSessionr   
ws_connectrz   HEARTBEAT_INTERVAL_SECONDSCONNECT_TIMEOUT_SECONDSr   _new_req_id
_send_jsonAPP_CMD_SUBSCRIBErw   rx   r   _wait_for_handshakert   r   )r   r   auth_payloadr   r   s        r2   r   zWeComAdapter._open_connection  s              -===11L014+ 2 
 
 
 
 
 
 
 
 !!+..oo($f-"l"l!%  

 

 
	
 
	
 
	
 
	
 
	
 
	
 
	
 "55f========""9a00)##!%%h0GHHF&>>G>>>??? $#r1   r   r?   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datart   APP_CMD_PING_payload_req_idr   debugr   CLOSEDCLOSEERROR)r   r   deadline	remainingmsgpayloads         r2   r   z WeComAdapter._wait_for_handshake7  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r1   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        r2   r   zWeComAdapter._listen_loopO  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      r2   r   zWeComAdapter._read_eventsj  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 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	= 	=r1   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     r2   r   zWeComAdapter._heartbeat_loopx  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   rH   Nz#[%s] Ignoring websocket payload: %s)r   r?   rt   r   NON_RESPONSE_COMMANDSdone
set_resultCALLBACK_COMMANDS_on_messager   APP_CMD_EVENT_CALLBACKr   r   r   )r   r   r   r   futures        r2   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Vr1   r   r   c                    t          | j                                                  D ]I\  }}|                                s|                    |           | j                            |d           JdS )z%Fail all outstanding request futures.N)rB   r   itemsr  set_exceptionpop)r   r   r   r  s       r2   r   z$WeComAdapter._fail_pending_responses  sv    "4#:#@#@#B#BCC 	6 	6NFF;;== *$$S)))#''5555	6 	6r1   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     r2   r   zWeComAdapter._send_json  sZ      x 	C48? 	CABBBh  )))))))))))r1   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  responses          r2   _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  rH   zreply_req_id is requiredr   r   Nr   )r   r   r   r?   r8   
ValueErrorr   r   r  r   r   r   r  )r   r  r   r   r   normalized_req_idr  r  s           r2   _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    r2   r   zWeComAdapter._new_req_id  s    --4:<<+---r1   c                    |                      d          }t          |t                    r$t          |                     d          pd          S dS )Nr   r   rH   )rt   r@   r   r?   )r   r   s     r2   r   zWeComAdapter._payload_req_id  sI    ++i((gt$$ 	4w{{8,,2333rr1   rF   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   r@   r   )rF   r   s     r2   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useridrH   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   )$rt   r@   r   r?   r   r   r   r   r   is_duplicater   r   r   _remember_reply_req_idr8   rR   _is_group_allowed_is_dm_intake_allowed_remember_chat_req_id_extract_textrJ   rK   _extract_media_derive_message_typer,   build_sourcer   r   nowr   utcr   r   r   _enqueue_text_eventhandle_message)r   r   r   msg_idsender	sender_idr)  is_groupr/  
reply_textr4  r5  r0  has_reply_contextr1  events                   r2   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,,,,,,,,,,,r1   rL  r   c                    ddl m}  ||j        | j        j                            dd          | j        j                            dd                    S )z-Session-scoped key for text message batching.r   )build_session_keygroup_sessions_per_userTthread_sessions_per_userF)rO  rP  )gateway.sessionrN  r1  rY   rs   rt   )r   rL  rN  s      r2   _text_batch_keyzWeComAdapter._text_batch_key>  sg    555555  L$(K$5$9$9:SUY$Z$Z%)[%6%:%:;UW\%]%]
 
 
 	
r1   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.
        rH   N
)rR  r   rt   r   r/  _last_chunk_lenr4  extendr5  r   r  r   r   r   _flush_text_batch)r   rL  keyexisting	chunk_len
prior_tasks         r2   rD  z WeComAdapter._enqueue_text_eventG  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+++r1   rX  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.
        rU  r   Nz)[WeCom] Flushing text batch %s (%d chars)rH   )r   current_taskr   rt   getattr_SPLIT_THRESHOLDr   r   r   r   r  r   r   r   r/  rE  )r   rX  r]  pendinglast_lenr   rL  s          r2   rW  zWeComAdapter._flush_text_batche  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msgtyperH   mixedmsg_itemr/  contentvoiceappmsgtitlequoterT  c              3     K   | ]}||V  	d S Nr0   )r:   parts     r2   	<genexpr>z-WeComAdapter._extract_text.<locals>.<genexpr>  s'      ==$=======r1   )	r?   rt   rR   r@   r   rB   r8   appendjoin)r   
text_partsrJ  rd  
_raw_mixedre  
_raw_itemsr	  r;   	_raw_text
text_blockrg  voice_block
voice_textri  rj  rk  
quote_type
quote_textquote_voices                       r2   r>  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Qr1   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.rd  rH   re  rf  imagefileri  rk  N)r?   rt   rR   r@   r   rB   rp  _cache_media)r   r   media_pathsr5  refsrd  rs  re  rt  r	  r;   	item_typeri  rk  ry  kindrefcachedpathcontent_types                       r2   r?  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''r1   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urlrH   	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)rt   _decode_base64r   r   r   r   _detect_image_extr   _mime_for_extr  r   r?   r   	mimetypes
guess_typer8   _download_remote_bytesABSOLUTE_MAX_BYTES_decrypt_file_bytesrA   _guess_extension_guess_filename)r   r  r  rF   r   extr  r  r   aes_keyr  s              r2   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 )Nr=   r   )rA   r8   r  	b64decode)r   r   s     r2   r  zWeComAdapter._decode_base64   s7    **S!$$R(..00(((r1   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    r2   r  zWeComAdapter._detect_image_ext%  s    ??/00 	6???++ 	6??122 	6??7## 	QrT
g(=(=7vr1   r  r  r  c                f    t           j                            |                                 |          S rm  )r  	types_maprt   rR   )r  r  s     r2   r  zWeComAdapter._mime_for_ext1  s#    "&&syy{{H===r1   r  r  c                    |rt          j        |          nd }|r|S t          t          |           j                  j        }|r|S |S rm  )r  guess_extensionr   r   r  suffix)r  r  r  r  path_exts        r2   r  zWeComAdapter._guess_extension5  sT    9EOi'5554 	J*++2 	Or1   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)	rJ   searchr'  r   r   r  r   r  r  )r  r  r  matchr   r  s         r2   r  zWeComAdapter._guess_filename?  s     	&I68KLLE &{{1~~%HSMM&'',:
d??+L99CVC>C>>Dr1   r/  r5  r5   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  r:   mtypes     r2   ro  z4WeComAdapter._derive_message_type.<locals>.<genexpr>O  s1      TTuu 9::TTTTTTr1   c              3  @   K   | ]}|                     d           V  dS )image/Nr  r  s     r2   ro  z4WeComAdapter._derive_message_type.<locals>.<genexpr>Q  s0      CCeu))CCCCCCr1   rd  rH   rh  )	anyr   DOCUMENTr   PHOTOr?   rt   rR   VOICE)r   r/  r5  s      r2   r@  z!WeComAdapter._derive_message_typeL  s     TTTTTTT 	(''CC{CCCCC 	C'+B;##1BBtxx	""(b))//11W<<$$r1   c                    dS )zAWeCom gates DM/group access at intake via dm_policy/group_policy.Tr0   r   s    r2   enforces_own_access_policyz'WeComAdapter.enforces_own_access_policy[  s	     tr1   c                    t          j        dd                                          dv rdS t          j        dd                                          dv S )NGATEWAY_ALLOW_ALL_USERSrH   >   1yestrueTWECOM_ALLOW_ALL_USERS)ru   rv   rR   r   s    r2   _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Ur1   rH  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{   rV   r|   r  )r   rH  s     r2   _is_dm_allowedzWeComAdapter._is_dm_allowede  sX    ?j((5?k))!$"2I>>>?f$$))+++ur1   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 )NrH   Fr  r  rd   Tr  )r?   r8   r{   rV   r|   r  )r   rH  	principals      r2   r<  z"WeComAdapter._is_dm_intake_allowedn  s    	R((..00	 	5?j((5?k))!$"2I>>>?i''4?f$$))+++ur1   r)  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  Frd   r  re   rf   T)r}   rV   r~   _resolve_group_cfgrE   rt   )r   r)  rH  	group_cfgsender_allows        r2   r;  zWeComAdapter._is_group_allowed|  s    ++5**5,,^DDZ\c5d5d,5++G44	#IMM,$?$?$]9==Q\C]C]^^ 	;!,	:::tr1   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 )NrQ   )r@   r   r   rR   r	  r?   rt   )r   r)  loweredrX  r4   wildcards         r2   r  zWeComAdapter._resolve_group_cfg  s    $,-- 	Idl""z$,w2G'N'N"<((--//,,,.. 	 	JC#s## 		w(>(>:eUYCZCZ(><##C((%h55=xx2=r1   r3  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 )NrH   )r?   r8   r   r   r   r  nextiter)r   r3  r   normalized_message_idr  s        r2   r:  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7r1   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.
        rH   N)r?   r8   r   r   r   r  r  r  )r   r)  r   normalized_chat_idr  s        r2   r=  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 $)**^;;;;;;r1   reply_toc                    t          |pd                                          }|r|                    d          rd S | j                            |          S )NrH   r-  )r?   r8   r  r   rt   )r   r  rU   s      r2   _reply_req_id_for_messagez&WeComAdapter._reply_req_id_for_message  sX    R((..00
 	Z228<< 	4"&&z222r1   r  c                    t          j        |           d         }|r|S t          |           j                                        dk    rdS dS )Nr   z.amrr*   r  )r  r  r   r  rR   )r  	mime_types     r2   _guess_mime_typezWeComAdapter._guess_mime_type  sP    (2215	 	>> &&((F22;))r1   c                    t          | pd                              dd          d                                                                         }t                              |          }|s|S |dv r|S |S )NrH   r  r   r   >   
text/plainr  )r?   rA   r8   rR   rX   r  )r  r  rU   guesseds       r2   _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r1   c                   t          | pd                                                                          }|                    d          rdS |                    d          rdS |                    d          s|dk    rdS d	S )
NrH   r  r~  zvideo/videozaudio/zapplication/oggrh  r  )r?   r8   rR   r  )r  r  s     r2   _detect_wecom_media_typez%WeComAdapter._detect_wecom_media_type  s    *++113399;;	)) 	7)) 	7)) 	Y:K-K-K7vr1   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  rH   Tu   文件大小 z.2fuq   MB 超过了企业微信允许的最大限制 20MB，无法发送。请尝试压缩文件或减小文件大小。F)
final_typerejectedreject_reason
downgradeddowngrade_noter~  u   图片大小 u3   MB 超过 10MB 限制，已转为文件格式发送r  u   视频大小 rh  u   语音格式 uE    不支持，企微仅支持 AMR 格式，已转为文件格式发送u   语音大小 u2   MB 超过 2MB 限制，已转为文件格式发送)r?   rR   r8   r  IMAGE_MAX_BYTESVIDEO_MAX_BYTESVOICE_SUPPORTED_MIMESVOICE_MAX_BYTES)r  r  r  file_size_mbnormalized_typenormalized_content_types         r2   _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   *!"
 
 	
r1   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: )rt   r?   )r  r   r   s      r2   _response_errorzWeComAdapter._response_error  sY    ,,y!,,i4X\\(++>??3336333r1   	operationc                \    |                      |          }|rt          | d|           d S )N	 failed: )r  r   )clsr  r  r   s       r2   _raise_for_wecom_errorz#WeComAdapter._raise_for_wecom_error   sC    ##H-- 	?)==e==>>>	? 	?r1   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 rm  r0   )r:   bytepad_lens     r2   ro  z3WeComAdapter._decrypt_file_bytes.<locals>.<genexpr>?  s'      @@4tw@@@@@@r1   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  rX  r  r  r  r   cipherr  	decryptedr  s             @r2   r  z WeComAdapter._decrypt_file_bytes&  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           	     `  K   ddl m}  ||          st          d|d d                    t          st	          d          | j        pt          j        dd          }|| 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   )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   c                >    i | ]\  }}|                                 |S r0   )rR   )r:   rX  r4   s      r2   
<dictcomp>z7WeComAdapter._download_remote_bytes.<locals>.<dictcomp>\  s&    YYY*#u399;;YYYr1   zcontent-lengthz"Remote media exceeds WeCom limit: z	 bytes > z bytesz4Remote media exceeds WeCom limit while downloading: )tools.url_safetyr  r  r/   r   r   r   r   streamraise_for_statusr   r	  rt   isdigitr  	bytearrayaiter_bytesrV  r   r  r   )r   r  r  r  clientcreated_clientr  r   content_lengthr   chunks              r2   r  z#WeComAdapter._download_remote_bytesD  so     
 	100000{3 	RPc#2#hPPQQQ 	MKLLL"\e&7W[&\&\&\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 H-media_sourcec                P    t          t          | pd                    }|j        dv S )NrH   >   httphttps)r   r?   scheme)r1  parseds     r2   _looks_like_urlzWeComAdapter._looks_like_urlp  s*    #l0b1122} 111r1   	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 )NrH   zmedia source is requiredz	<[^>\n]+>z:Media placeholder was not replaced with a real file path: >   r3  r4  r  r  r  r  zMedia file not found: )r?   r8   r  rJ   	fullmatchr   r5  r  r  rt   r  r  r   r   r  
expanduseris_absolutecwdresolveexistsis_fileFileNotFoundError
read_bytesr   )r   r1  r8  r1  r6  r   r   r  resolved_namer  
local_paths              r2   _load_outbound_mediaz!WeComAdapter._load_outbound_mediau  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0r1   c                   K   |                      ||           d {V \  }}}|                     |          }|                     t          |          ||          }||||d|S )Nr8  )r   r  r8  r  )rF  r  r  r   )r   r1  r8  r   r  rD  r  
size_checks           r2   _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
(&*	
 

 
 	
r1   
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_idrH   z8media upload init failed: missing upload_id in response r   ascii)rP  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   rT  rU  )r  r   UPLOAD_CHUNK_SIZEMAX_UPLOAD_CHUNKSr  APP_CMD_UPLOAD_MEDIA_INIThashlibrO  	hexdigestr  r@   rt   r   r?   r8   r   	enumeraterangeAPP_CMD_UPLOAD_MEDIA_CHUNKr  	b64encodedecodeAPP_CMD_UPLOAD_MEDIA_FINISH)r   r   rK  r  rM  rN  init_response	init_bodyrP  rR  startr0  chunk_responsefinish_responsefinish_bodyrT  s                   r2   _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
 
 	
r1   rT  c           
        K   |                      t          d|d||d|ii           d {V }|                     |d           |S )Nr%  rd  rT  zsend media message)r  APP_CMD_SENDr  )r   r)  rK  rT  r  s        r2   _send_media_messagez WeComAdapter._send_media_message  ss      ++':Z2
 
 
 
 
 
 
 
 	##H.BCCCr1   rg  c                   K   |                      |dd|d | j                 id           d {V }|                     |d           |S )Nmarkdownrg  )rd  rl  zsend reply markdown)r  MAX_MESSAGE_LENGTHr  )r   r  rg  r  s       r2   _send_reply_markdownz!WeComAdapter._send_reply_markdown  s}      11%&0H1H0H(IJ 
 
 
 
 
 
 
 
 	##H.CDDDr1   c                z   K   |                      |d||d|ii           d {V }|                     |d           |S )Nrd  rT  zsend reply media message)r  r  )r   r  rK  rT  r  s        r2   _send_reply_media_messagez&WeComAdapter._send_reply_media_message  sp       11:Z2
 
 
 
 
 
 
 
 	##H.HIIIr1   Optional[SendResult]c                   K   |sd S |                      |||           d {V }|j        s&t                              d| j        |j                   |S )Nr)  rg  r  z'[%s] Follow-up markdown send failed: %s)sendsuccessr   r   r   r   )r   r)  rg  r  results        r2   _send_followup_markdownz$WeComAdapter._send_followup_markdown	  sk        	4yy'HyUUUUUUUU~ 	_NNDdiQWQ]^^^r1   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ru  r   rH  z,[%s] Failed to prepare outbound media %s: %sr  u   ⚠️ r  )r  r   r  r8  rT  zTimeout sending media to WeComz [%s] Failed to send media %s: %sr  r  u   ℹ️ Tr  )uploadr  rx  caption_error	downgradedowngrade_errorru  r3  raw_response)r   rJ  rB  r?   r   r   r   r   rw  r  r   rg  rp  rj  r   r   r   r   r   r   r  ru  )r   r)  r1  rx  r8  r  preparedr   r  upload_resultmedia_responsecaption_resultdowngrade_results                r2   _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``.Frz  r{  Nrl  rg  )r%  rd  rl  z Timeout sending message to WeComz[%s] Send failed: %sTr  r  )r   r  r   rn  r  ri  rm  r   r   r   r   r   r   r?   r  r   r   r   r   )	r   r)  rg  r  r  r  r  r   r   s	            r2   rt  zWeComAdapter.sendi  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r)  r1  rx  r  z3[%s] Falling back to text send for image URL %s: %srT  rs  )r  ru  r7  r   r   r   r   rt  )r   r)  r  rx  r  r  rv  fallback_texts           r2   
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Yr1   
image_pathc                F   K   ~|                      ||||           d {V S Nr  r  )r   r)  r  rx  r  kwargss         r2   send_image_filezWeComAdapter.send_image_file  R       ,,#	 - 
 
 
 
 
 
 
 
 	
r1   	file_pathc                H   K   ~|                      |||||           d {V S )N)r)  r1  rx  r8  r  r  )r   r)  r  rx  r8  r  r  s          r2   send_documentzWeComAdapter.send_document  sU       ,," - 
 
 
 
 
 
 
 
 	
r1   
audio_pathc                F   K   ~|                      ||||           d {V S r  r  )r   r)  r  rx  r  r  s         r2   
send_voicezWeComAdapter.send_voice  r  r1   
video_pathc                F   K   ~|                      ||||           d {V S r  r  )r   r)  r  rx  r  r  s         r2   
send_videozWeComAdapter.send_video  r  r1   c                   K   ~~dS )z8WeCom does not expose typing indicators in this adapter.Nr0   )r   r)  r  s      r2   send_typingzWeComAdapter.send_typing  s      XXXr1   c                f   K   ||r)|                                                     d          rdnddS )zReturn minimal chat info.r'  r(  )r   r   )rR   r  )r   r)  s     r2   get_chat_infozWeComAdapter.get_chat_info  sB       &X7==??+E+Eg+N+NXGGTX
 
 	
r1   )rY   r   )r   r,   r+   r,   r+   r   )r   r?   r+   r   )r   r   r+   r   )r   r   r+   r   )r   r?   r   r   r   r  r+   r   )
r  r?   r   r   r   r?   r   r  r+   r   )r  r?   r+   r?   )r   r   r+   r?   )rF   r   r+   r  )rL  r   r+   r?   )rL  r   r+   r   )rX  r?   r+   r   )r   r   r+   rb  )r   r   r+   r|  )r  r?   r  r   r+   r  )r   r?   r+   r  )r   r  r+   r?   )r  )r  r?   r  r?   r+   r?   )r  r?   r  r?   r  r?   r+   r?   )r  r?   r  r  r  r?   r+   r?   )r   r   r/  r?   r5  r5   r+   r   r+   r,   )rH  r?   r+   r,   )r)  r?   rH  r?   r+   r,   )r)  r?   r+   r   )r3  r?   r   r?   r+   r   )r)  r?   r   r?   r+   r   )r  r  r+   r  )r  r?   r+   r?   )r  r?   r  r?   r+   r?   )r  r?   r+   r?   rm  )r  r  r  r?   r  r  r+   r   )r  r   r+   r  )r  r   r  r?   r+   r   )r  r  r  r?   r+   r  )r  r?   r  r  r+   r  )r1  r?   r+   r,   )r1  r?   r8  r  r+   r9  )r1  r?   r8  r  r+   r   )r   r  rK  r?   r  r?   r+   r   )r)  r?   rK  r?   rT  r?   r+   r   )r  r?   rg  r?   r+   r   )r  r?   rK  r?   rT  r?   r+   r   )r)  r?   rg  r?   r  r  r+   rq  )NNN)r)  r?   r1  r?   rx  r  r8  r  r  r  r+   r   )NN)
r)  r?   rg  r?   r  r  r  r  r+   r   )r)  r?   r  r?   rx  r  r  r  r  r  r+   r   )
r)  r?   r  r?   rx  r  r  r  r+   r   )r)  r?   r  r?   rx  r  r8  r  r  r  r+   r   )
r)  r?   r  r?   rx  r  r  r  r+   r   )
r)  r?   r  r?   rx  r  r  r  r+   r   )r)  r?   r+   r   )M__name__
__module____qualname____doc__rm  SUPPORTS_MESSAGE_EDITINGr_  rq   r   r   r   r   r   r   r   r   r   r   r   REQUEST_TIMEOUT_SECONDSr  APP_CMD_RESPONSEr  staticmethodr   r   r   r  rR  rD  rW  r>  r?  r  r  r  r  r  r  r@  propertyr  r  r  r<  r;  r  r:  r=  r  r  r  r  r  r  classmethodr  r  r  r7  rF  rJ  rg  rj  rn  rp  rw  r  rt  r  r  r  r  r  r  r  __classcell__)r   s   @r2   rX   rX      s$       KK+$ +5 +5 +5 +5 +5 +5b 5: & & & & & &P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 ? ? ? [?
 $ $ $ \$:*& *& *& *&X 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 "&"&
 
 
 
 
( "&#'"&
 
 
 
 
, "&"&
 
 
 
 
( "&"&
 
 
 
 
     
 
 
 
 
 
 
 
r1   rX   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...rH   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:

  rT  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  statusru  bot_infobotidr[   r]   )r[   r]   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   readr_  r   rt   r?   r8   qrcodeQRCodeadd_datamakeprint_ascii_QR_CODE_PAGEparserk  r   	monotonic_QR_QUERY_URLr   r   _QR_POLL_INTERVALrR   r   )r  urllibgenerate_urlreqresprF   r   r   r  r  qr_rendered_qrcodeqrpage_urlr   	query_url
poll_countrv  result_datar  r  r[   r]   s                          r2   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)ru  platformr)  r3  )
r3   rX   r   r^  rt  ru  r   r   r3  r   )
pconfigr)  r   r  r  r  adapter	connectedrv  es
             r2   _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                 H   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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|             |d8           dS )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)r\   r^   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: r[   rH   r]   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)rg    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   rc   r  r  r  u0   Open access enabled — anyone can use your bot!rd   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   rt   replace)r  r  r  r  r  r  r  r  r  r  existing_bot_idexisting_secret
method_idxr[   r]   credentialsr   allowed
access_idxhomes                       r2   interactive_setupr    s    @???????......                  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TD 5+T2223T33444M*+++++s   9B C 	C)B>>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.rs   r[   r^  r,   rt   rY   rs   s     r2   _is_connectedr  #  s4     FGR((.BE		(##$$$r1   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.rs   corp_idappsr  r  s     r2   _callback_is_connectedr  *  sE     FGR((.BE		)$$9		&(9(9:::r1   c                     t          |           S )zCFactory wrapper that constructs WeComAdapter from a PlatformConfig.)rX   )rY   s    r2   _build_adapterr  2  s    r1   c                $    ddl m}  ||           S )zKFactory wrapper that constructs WecomCallbackAdapter from a PlatformConfig.r   )WecomCallbackAdapter)(plugins.platforms.wecom.callback_adapterr  )rY   r  s     r2   _build_callback_adapterr  7  s%    MMMMMM'''r1   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  r\   r^   z!pip install 'hermes-agent[wecom]'rg   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  r3   r  r  r  r  r#  r  r  )ctxr#  s     r2   registerr+  =  s    )&)"%$n58"/-1-!!    & [ZZZZZ0/2+..0LM886!      r1   r  )r4   r   r+   r5   )rF   r?   r+   r?   )rN   r5   rO   r?   r+   r,   )r  r  r+   r  r  )]r  
__future__r   r   r  rY  r  loggingr  ru   rJ   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   ry   r   APP_CMD_CALLBACKAPP_CMD_LEGACY_CALLBACKr  ri  r  r   rX  r]  r`  r  r  rm  r   r  r   r   r   r  r  r  FILE_MAX_BYTESr  rV  rW  r  r3   rE   rM   rV   rX   r  r  r  r  _QR_POLL_TIMEOUTr  r  r  r  r  r  r  r+  r0   r1   r2   <module>r9     s,   : # " " " " "          				 				   ' ' ' ' ' ' ' '       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                     		8	$	$2% ' * / & 5 7 9 %'>? )-C,DD    ! &&& ""!!#   $ 1 1 1 1
> > > >      j
 j
 j
 j
 j
& j
 j
 j
b+ ? ?K  
 ,t t t t t tT $4 $4 $4 $4 $4Nm, m, m, m,`% % % %; ; ; ;     
( ( (# # # # # #s$   A 	A)(A)-A4 4	B ?B 