
    .cj                    L   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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mZ ddlmZmZmZmZ  ej        e          Zd	Zd
ddddZddiZdZ dZ!dZ"d3dZ#d4dZ$d5dZ% G d de          Z&ddddd6d(Z'd7d*Z(d8d/Z)d4d0Z*d1 Z+d7d2Z,dS )9u  Mattermost gateway adapter.

Connects to a self-hosted (or cloud) Mattermost instance via its REST API
(v4) and WebSocket for real-time events.  No external Mattermost library
required — uses aiohttp which is already a Hermes dependency.

Environment variables:
    MATTERMOST_URL              Server URL (e.g. https://mm.example.com)
    MATTERMOST_TOKEN            Bot token or personal-access token
    MATTERMOST_ALLOWED_USERS    Comma-separated user IDs
    MATTERMOST_HOME_CHANNEL     Channel ID for cron/notification delivery
    )annotationsN)Path)AnyDictListOptionalTuple)PlatformPlatformConfig)MessageDeduplicator)BasePlatformAdapterMessageEventMessageType
SendResulti  dmgroupchannel)DGPOdisable_mentionsTg       @g      N@g?payloadDict[str, Any]returnc                    |                      d          }t          |t                    ri |t          | d<   nt          t                    | d<   | S )zDReturn a post payload that prevents Mattermost from firing mentions.props)get
isinstancedict"_MATTERMOST_DISABLE_MENTIONS_PROPS)r   r   s     F/home/ice/.hermes/hermes-agent/plugins/platforms/mattermost/adapter.py_with_mentions_disabledr#   7   sV    KK  E% DJeJ'IJ BCCN    boolc                 f    	 ddl } dS # t          $ r t                              d           Y dS w xY w)zFReturn True if the Mattermost adapter runtime dependency is available.r   NTz!Mattermost: aiohttp not installedF)aiohttpImportErrorloggerwarning)r'   s    r"   check_mattermost_requirementsr+   A   sJ    t   :;;;uus    $00configr   c                   t          | di           pi }t          | dd          pt          j        dd                                          }|                    dd          pt          j        dd                                          }|st
                              d           d	S |st
                              d
           d	S dS )z9Return True when Mattermost has enough config to connect.extratokenNMATTERMOST_TOKEN urlMATTERMOST_URLz$Mattermost: MATTERMOST_TOKEN not setFz"Mattermost: MATTERMOST_URL not setT)getattrosgetenvstripr   r)   debugr*   )r,   r.   r/   r2   s       r"   validate_mattermost_configr9   K   s    FGR((.BEVWd++Pry9KR/P/PWWYYE99UBB29-=r#B#B
I
I
K
KC ;<<<u ;<<<u4r$   c                  Z    e Zd ZdZdZdN fdZdOdZdPdZdQdZdRdZ	dSdZ
dTdZdQdZ	 dUdVdZd d!dWd#ZdXd%ZdYd'Z	 	 dZd[d+Zd\d,Z	 d]d^d-Zd d.d_d1Z	 	 	 d`dad4Z	 	 	 d`dbd6Z	 	 	 	 dcddd9Z	 	 	 d`ded;Z	 	 	 d`dfd=Zdgd>Z	 	 dhdidBZ	 	 dZdjdCZ	 	 dkdl fdIZdXdJZdXdKZdmdMZ  xZ!S )nMattermostAdapterz6Gateway adapter for Mattermost (self-hosted or cloud).Tr,   r   c                d   t                                          |t          j                   |j                            dd          pt          j        dd                              d          | _	        |j
        pt          j        dd          | _        d| _        d| _        d | _        d | _        d | _        d | _        d| _        |j                            dd          pt          j        dd	                                          | _        d | _        d| _        t/                      | _        d S )
Nr2   r1   r3   /r0   F
reply_modeMATTERMOST_REPLY_MODEoff)super__init__r
   
MATTERMOSTr.   r   r5   r6   rstrip	_base_urlr/   _token_bot_user_id_bot_username_session_ws_ws_task_reconnect_task_closinglower_reply_mode_last_post_status_last_post_errorr   _dedup)selfr,   	__class__s     r"   rB   zMattermostAdapter.__init__^   s   !4555 LUB'' /y)2..
&++ 	 "<L295G+L+L!#"$ "047; L\2.. 9y0%88
%'' 	
 15%' *++r$   r   Dict[str, str]c                    d| j          ddS )NBearer application/jsonAuthorizationzContent-Type)rF   rS   s    r"   _headerszMattermostAdapter._headers   s!    4t{44.
 
 	
r$   pathstrr   c           	       K   ddl }d|v rt                              d|           i S | j         d|                    d           }	 | j                            ||                                 |                    d          	          4 d{V }|j	        d
k    rX|
                                 d{V }t                              d||j	        |dd                    i cddd          d{V  S |                                 d{V cddd          d{V  S # 1 d{V swxY w Y   dS # |j        $ r(}t                              d||           i cY d}~S d}~ww xY w)zGET /api/v4/{path}.r   N..!MM API path traversal blocked: %s/api/v4/r=      totalheaderstimeout  u   MM API GET %s → %s: %s   zMM API GET %s network error: %s)r'   r)   errorrE   lstriprI   r   r\   ClientTimeoutstatustextjsonClientError)rS   r]   r'   r2   respbodyexcs          r"   _api_getzMattermostAdapter._api_get   sQ     4<<LL<dCCCI;;S)9)9;;		}((dmmoowOdOdkmOdOnOn(oo ) ) ) ) ) ) )sw;#%%!%,,,,,,DLL!;T4;PTUYVYUYPZ[[[	) ) ) ) ) ) ) ) ) ) ) ) ) )
 "YY[[(((((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) " 	 	 	LL:D#FFFIIIIII	sP   A	D4 AD!"D4 5D!D4 !
D++D4 .D+/D4 4
E&>E!E&!E&r   c           	     D  K   ddl }d|v rt                              d|           i S | j         d|                    d           }d| _        d| _        	 | j                            || 	                                ||
                    d	          
          4 d{V 	 }|j        | _        |j        dk    ra|                                 d{V }|pd| _        t                              d||j        |dd                    i cddd          d{V  S |                                 d{V cddd          d{V  S # 1 d{V swxY w Y   dS # |j        $ r<}t          |          | _        t                              d||           i cY d}~S d}~ww xY w)z#POST /api/v4/{path} with JSON body.r   Nr`   ra   rb   r=   r1   rc   rd   )rg   rp   rh   ri   u   MM API POST %s → %s: %srj   z MM API POST %s network error: %s)r'   r)   rk   rE   rl   rP   rQ   rI   postr\   rm   rn   ro   rp   rq   r^   rS   r]   r   r'   r2   rr   rs   rt   s           r"   	_api_postzMattermostAdapter._api_post   s      	4<<LL<dCCCI;;S)9)9;;!% "	}))T]]__7--B-77 *   
) 
) 
) 
) 
) 
) 
) 
) )-&;#%%!%,,,,,,D,0JBD)LL!<dDKQUVZWZVZQ[\\\
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) "YY[[((((((
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) 
) " 	 	 	$'HHD!LL;T3GGGIIIIII	sP   A
E !A&EE E3E 
EE EE 
F#1FFFreply_toOptional[str]metadataOptional[Dict[str, Any]]c                   K   | j         dk    rdS |}|s?t          |t                    r*|                    d          p|                    d          }|sdS |                     t          |                     d{V S )z9Resolve the Mattermost root_id from reply_to or metadata.threadN	thread_idroot_id)rO   r   r    r   _resolve_root_idr^   )rS   rz   r|   	candidates       r"   _thread_root_for_sendz'MattermostAdapter._thread_root_for_send   s       x''4	 	MZ$77 	M [11LX\\)5L5LI 	4**3y>>:::::::::r$   r%   c                    | j         dvrdS | j        pd                                sdS t          fddD                       }t          fddD                       }|o|S )zCReturn True only for clear invalid/missing Mattermost thread roots.>     ri   Fr1   c              3      K   | ]}|v V  	d S N .0markerrs   s     r"   	<genexpr>zMMattermostAdapter._last_post_failure_is_broken_thread_root.<locals>.<genexpr>   s'      fffnffffffr$   )r   rootidzroot idr   rw   c              3      K   | ]}|v V  	d S r   r   r   s     r"   r   zMMattermostAdapter._last_post_failure_is_broken_thread_root.<locals>.<genexpr>   s'      hhVt^hhhhhhr$   )invalidz	not foundzdoes not existmissing)rP   rQ   rN   any)rS   rootishbrokenrs   s      @r"   (_last_post_failure_is_broken_thread_rootz:MattermostAdapter._last_post_failure_is_broken_thread_root   s    !335%+2244 	5ffff3efffffhhhh2ghhhhh!6!r$   chat_idc                  K   |                      d|           d{V }|sd|vr|S t          |t                    r|                    d          s|S |                                 s|S t          |          }|                    dd           t          |                    d          pd          }d|z                                   |d<   t          	                    d|           |                      d|           d{V S )	zAPost once, optionally falling back flat for final notify content.postsNr   notifymessager1   uK   ⚠️ Mattermost thread delivery failed; posting final reply in channel.

zNMattermost: falling back to flat channel delivery for notify-worthy post in %s)
ry   r   r    r   r   popr^   r7   r)   r*   )rS   r   r   r|   dataflat_payloadoriginals          r"   _post_preserving_threadz)MattermostAdapter._post_preserving_thread   s,      ^^GW55555555 	9G++K8T** 	x||H/E/E 	K<<>> 	KG}}D)))|''	228b99[
%'' 	Y 	\	
 	
 	
 ^^G\:::::::::r$   c           	       K   ddl }d|v rt                              d|           i S | j         d|                    d           }	 | j                            ||                                 |          4 d{V 	 }|j        dk    rX|	                                 d{V }t                              d	||j        |dd
                    i cddd          d{V  S |
                                 d{V cddd          d{V  S # 1 d{V swxY w Y   dS # |j        $ r(}t                              d||           i cY d}~S d}~ww xY w)z"PUT /api/v4/{path} with JSON body.r   Nr`   ra   rb   r=   rg   rp   ri   u   MM API PUT %s → %s: %srj   zMM API PUT %s network error: %s)r'   r)   rk   rE   rl   rI   putr\   rn   ro   rp   rq   rx   s           r"   _api_putzMattermostAdapter._api_put   sM      	4<<LL<dCCCI;;S)9)9;;	}((T]]__7 )   ) ) ) ) ) ) ) );#%%!%,,,,,,DLL!;T4;PTUYVYUYPZ[[[) ) ) ) ) ) ) ) ) ) ) ) ) ) "YY[[(((((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) " 	 	 	LL:D#FFFIIIIII	sO   5D! >ADD! "D;D! 
DD! DD! !
E+EEEapplication/octet-stream
channel_id	file_databytesfilenamecontent_typec                  K   ddl }| j         d}|                                }|                    d|           |                    d|||           dd| j         i}| j                            ||||                    d	
                    4 d{V }	|	j        dk    rW|		                                 d{V }
t                              d|	j        |
dd                    	 ddd          d{V  dS |	                                 d{V }|                    dg           }|r|d         d         ndcddd          d{V  S # 1 d{V swxY w Y   dS )z9Upload a file and return its file ID, or None on failure.r   N/api/v4/filesr   files)r   r   rZ   rW   <   rd   )rg   r   rh   ri   u   MM file upload → %s: %srj   
file_infosid)r'   rE   FormData	add_fieldrF   rI   rw   rm   rn   ro   r)   rk   rp   r   )rS   r   r   r   r   r'   r2   formrg   rr   rs   r   infoss                r"   _upload_filezMattermostAdapter._upload_file  sO      	...!!|Z000%	 	 	
 	
 	
 #$;dk$;$;<=%%c7wOdOdkmOdOnOn%oo 	5 	5 	5 	5 	5 	5 	5sw{c!!!YY[[((((((8$+tDSDzRRR		5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5
 $$$$$$DHH\2..E%*458D>>	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5 	5s   AE;AE
EEF)is_reconnectr   c                 K   ddl }| j        r| j        st                              d           dS |                    |                    d                    | _        d| _        | 	                    d           d{V }|rd	|vr;t                              d
           | j        
                                 d{V  dS |d	         | _        |                    dd          | _        t                              d| j        | j        | j                   t          j        |                                           | _        |                                  dS )z7Connect to Mattermost and start the WebSocket listener.r   Nz'Mattermost: URL or token not configuredFrc   rd   rh   zusers/mer   uP   Mattermost: failed to authenticate — check MATTERMOST_TOKEN and MATTERMOST_URLusernamer1   z+Mattermost: authenticated as @%s (%s) on %sT)r'   rE   rF   r)   rk   ClientSessionrm   rI   rM   ru   closerG   r   rH   infoasynciocreate_task_ws_looprK   _mark_connected)rS   r   r'   mes       r"   connectzMattermostAdapter.connect  se     ~ 	T[ 	LLBCCC5--)))33 . 
 
  ==,,,,,,,, 	T^^LLklll-%%'''''''''5tHVVJ339N		
 	
 	
  +DMMOO<<tr$   Nonec                D  K   d| _         | j        r]| j                                        sD| j                                         	 | j         d{V  n# t          j        t          f$ r Y nw xY w| j        r2| j                                        s| j                                         | j        r&| j        	                                 d{V  d| _        | j
        r+| j
        j        s| j
        	                                 d{V  t                              d           dS )zDisconnect from Mattermost.TNzMattermost: disconnected)rM   rK   donecancelr   CancelledError	ExceptionrL   rJ   r   rI   closedr)   r   r[   s    r"   
disconnectzMattermostAdapter.disconnect@  sL     = 	!3!3!5!5 	M  """m########*I6     	*(<(A(A(C(C 	* '')))8 	(.."""""""""DH= 	(!5 	(-%%'''''''''./////s   A A+*A+post_idc                   K   |s|S |                      d|            d{V }|r|                    d          r|d         S |S )a=  Resolve a post_id to the thread root_id for Mattermost.

        Mattermost requires root_id to be the *root* post of a thread.
        If the post is a reply (has its own root_id), we must use that
        root_id instead.  Using a reply's own ID as root_id causes
        "Invalid RootId parameter" errors.
        posts/Nr   )ru   r   )rS   r   r   s      r"   r   z"MattermostAdapter._resolve_root_idX  sk        	N]]#5G#5#566666666 	#DHHY'' 	#	?"r$   Ncontentr   c                  K   |st          d          S |                     |          }|                     |t                    }d}|D ]u}t	          ||d          }	|                     ||           d{V }
|
r|
|	d<   |                     ||	|           d{V }|rd|vrt          dd	          c S |d         }vt          d|
          S )z1Send a message (or multiple chunks) to a channel.T)successNr   r   r   r   FzFailed to create postr   rk   r   
message_id)r   format_messagetruncate_messageMAX_POST_LENGTHr#   r   r   )rS   r   r   rz   r|   	formattedchunkslast_idchunkr   resolved_rootr   s               r"   sendzMattermostAdapter.sendh  s+       	,d++++''00	&&y/BB 	! 	!E&=% ? ? ' 'G
 #'"<"<Xx"P"PPPPPPPM 3%2	"55gwQQQQQQQQD P4t++!%7NOOOOOO4jGG$7;;;;r$   c                  K   |                      d|            d{V }|s|ddS t                              |                    dd          d          }|                    d          p|                    d          p|}||dS )	zReturn channel name and type.z	channels/Nr   )nametyper   r   display_namer   )ru   _CHANNEL_TYPE_MAPr   )rS   r   r   ch_typer   s        r"   get_chat_infozMattermostAdapter.get_chat_info  s      ]]#8w#8#899999999 	8#Y777#''(=(=yIIxx//N488F3C3CNw$g666r$   c                X   K   |                      d| j         dd|i           d{V  dS )zSend a typing indicator.zusers/z/typingr   N)ry   rG   )rS   r   r|   s      r"   send_typingzMattermostAdapter.send_typing  s_       nn/T&///7#
 
 	
 	
 	
 	
 	
 	
 	
 	
 	
r$   )finalizer   r   c                  K   |                      |          }|                     d| dt          d|i                     d{V }|rd|vrt          dd          S t          d	|d         
          S )zEdit an existing post.r   z/patchr   Nr   FzFailed to edit postr   Tr   )r   r   r#   r   )rS   r   r   r   r   r   r   s          r"   edit_messagezMattermostAdapter.edit_message  s       ''00	]]'Z'''#Y	$:;;
 
 
 
 
 
 
 
  	Jt4''e3HIIII$4:>>>>r$   	image_urlcaptionc                F   K   |                      ||||d|           d{V S )z5Download an image and upload it as a file attachment.imageN)_send_url_as_file)rS   r   r   r   rz   r|   s         r"   
send_imagezMattermostAdapter.send_image  sK       ++Y7H
 
 
 
 
 
 
 
 	
r$   
image_pathc                F   K   |                      |||||           d{V S )zUpload a local image file.r|   N_send_local_file)rS   r   r   r   rz   r|   s         r"   send_image_filez!MattermostAdapter.send_image_file  N       **Z(X + 
 
 
 
 
 
 
 
 	
r$   	file_path	file_namec                F   K   |                      ||||||           d{V S )z"Upload a local file as a document.Nr   )rS   r   r   r   r   rz   r|   s          r"   send_documentzMattermostAdapter.send_document  sK       **Y9h
 
 
 
 
 
 
 
 	
r$   
audio_pathc                F   K   |                      |||||           d{V S )zUpload an audio file.r   Nr   )rS   r   r   r   rz   r|   s         r"   
send_voicezMattermostAdapter.send_voice  r   r$   
video_pathc                F   K   |                      |||||           d{V S )zUpload a video file.r   Nr   )rS   r   r   r   rz   r|   s         r"   
send_videozMattermostAdapter.send_video  r   r$   c                2    t          j        dd|          }|S )u   Mattermost uses standard Markdown — mostly pass through.

        Strip image markdown into plain links (files are uploaded separately).
        z!\[([^\]]*)\]\(([^)]+)\)z\2)resub)rS   r   s     r"   r   z MattermostAdapter.format_message  s     &4eWEEr$   filer2   kindc           	       K   ddl m}  ||          sRt                              d           |                     ||pd d|                                 ||           d{V S ddl}d}	d}
|                    d	d
          d                             d          d         p| d}t          d          D ]}	 | j
                            ||                    d                    4 d{V }|j        dk    s|j        dk    rf|dk     r`t                              d|d
z   |dd         |j                   t          j        d|d
z   z             d{V  	 ddd          d{V  |j        dk    rL|                     ||pd d|                                 ||           d{V cddd          d{V  c S |                                 d{V }	|j        pd}
	 ddd          d{V   n# 1 d{V swxY w Y   X# |j        t          j        f$ r}|dk     r&t          j        d|d
z   z             d{V  Y d}~t                              d||d
z   |           |                     ||pd d|                                 ||           d{V cY d}~c S d}~ww xY w|	St                              d|           |                     ||pd d|                                 ||           d{V S |                     ||	||
           d{V }|s8|                     ||pd d|                                 ||           d{V S t-          ||pd|gd          }|                     ||           d{V }|r||d<   |                     |||           d{V }|rd|vrt3          dd           S t3          d!|d         "          S )#z2Download a URL and upload it as a file attachment.r   is_safe_urlz0Mattermost: blocked unsafe URL (SSRF protection)r1   
r   Nr   r=      ?.png   rc   rd   r   i  i     z1Mattermost download retry %d/2 for %s (status %d)P   g      ?ri   z7Mattermost: failed to download %s after %d attempts: %sz,Mattermost: download returned no data for %sr   r   file_idsr   r   FFailed to post with filer   Tr   )tools.url_safetyr  r)   r*   r   r7   r'   rsplitsplitrangerI   r   rm   rn   r8   r   sleepreadr   rq   TimeoutErrorr   r#   r   r   r   )rS   r   r2   r   rz   r  r|   r  r'   r   ctfnameattemptrr   rt   file_idr   r   r   s                      r"   r   z#MattermostAdapter._send_url_as_file  s      	100000{3 	lNNMNNN7w}",E,E,E,E,K,K,M,Mxbjkkkkkkkkk	'

3""2&,,S11!4E4Qxx 	p 	pGp=,,S':O:OVX:O:Y:Y,ZZ       ^b{c))T[C-?-?"Q;;"LL)\)01c#2#hM M M")-w{0C"D"DDDDDDDD$             {c))%)YYw7=b8Q8QC8Q8Q8W8W8Y8Y[cnvY%w%wwwwwww                '+iikk 1 1 1 1 1 1I*H.HB                             ')=> p p pQ;;!-w{(;<<<<<<<<<HHHHXZ]_fij_jloppp!YYw7=b0I0IC0I0I0O0O0Q0QS[fnYoooooooooooooooop NNI3OOO7w}",E,E,E,E,K,K,M,Mxbjkkkkkkkkk))'9eRHHHHHHHH 	l7w}",E,E,E,E,K,K,M,Mxbjkkkkkkkkk"9!}" 	;
 ;
 # #
 #888LLLLLLLL 	/!.GI11'7HMMMMMMMM 	Ot4''e3MNNNN$4:>>>>sc   ;6H1A+G<H.AG<0H$G<)H<
H	H	H	
HJ5%&J0AJ0(J50J5c                  K   ddl }t          |          }|                                s,t                              d|           t          dd          S |p|j        }	|                    |	          d         pd}
|                                }| 	                    |||	|
           d{V }|st          dd	          S t          ||pd
|gd          }|                     ||           d{V }|r||d<   |                     |||           d{V }|rd|vrt          dd	          S t          d|d                   S )z,Upload a local file and attach it to a post.r   Nz.Mattermost: local file not found, skipping: %sTr   r   FzFile upload failedr   r1   r  r   r   r  )	mimetypesr   existsr)   r*   r   r   
guess_type
read_bytesr   r#   r   r   )rS   r   r   r   rz   r   r|   r  pr  r  r   r  r   r   r   s                   r"   r   z"MattermostAdapter._send_local_file<  s      	OOxxzz 	=NN@)   dt<<<<#QV!!%((+I/ILLNN	))'9eRHHHHHHHH 	Ie3GHHHH"9!}" 	;
 ;
 # #
 #888LLLLLLLL 	/!.GI11'7HMMMMMMMM 	Ot4''e3MNNNN$4:>>>>r$           imagesList[Tuple[str, str]]human_delayfloatc           
       K   sdS ddl }ddl}ddlm} dfdt	          dt                              D             }t          |          D ]\  }	}
|dk    r |	dk    rt          j        |           d{V  g }g }	 |
D ]k\  }}|r|	                    |           |
                    d          r ||dd                   }t          |          }|                                st                              d|           |j        }|                    |          d         pd	}|                                }nxdd
lm}  ||          st                              d           	 | j                            ||                    d                    4 d{V 	 }|j        dk    r=t                              d|j        |dd                    	 ddd          d{V  k|                                 d{V }|j        pd	}ddd          d{V  n# 1 d{V swxY w Y   n<# t4          $ r/}t                              d|dd         |           Y d}~d}~ww xY w|                    dd          d                             d          d         pdt          |           d}|                     ||||           d{V }|r|	                    |           m|st=          |d                    |          |d          }|                      d|           d{V }|r||d<   t          !                    dt          |          |	dz   t          |                     | "                    |||           d{V }|rd|vrEt                              d           tG                      $                    ||
||           d{V  # t4          $ re}t                              d |	dz   t          |          |d!"           tG                      $                    ||
||           d{V  Y d}~d}~ww xY wdS )#a  Send a batch of images as a single Mattermost post with multiple attachments.

        Mattermost supports up to 5 ``file_ids`` per post. Each image is
        uploaded individually (Mattermost's file API is one-at-a-time),
        then a single post is created referencing all uploaded file_ids
        at once. Batches larger than 5 are chunked. Falls back to the
        base per-image loop on total failure.
        Nr   )unquote   c                *    g | ]}||z            S r   r   )r   iCHUNKr%  s     r"   
<listcomp>z:MattermostAdapter.send_multiple_images.<locals>.<listcomp>|  s&    LLL!&1u9%LLLr$   zfile://   z%Mattermost: skipping missing image %sz	image/pngr  z-Mattermost: blocked unsafe image URL in batchrc   rd   r   ri   z2Mattermost: failed to download image (HTTP %d): %sr  z&Mattermost: download failed for %s: %sr=   r	  r
  r  image_r  r  r  r   z<Mattermost: sending %d image(s) as single post (chunk %d/%d)r   z1Mattermost: multi-image post failed, falling back)r'  zCMattermost: multi-image send failed (chunk %d/%d), falling back: %sT)exc_info)%r  r'   urllib.parser*  r  len	enumerater   r  append
startswithr   r   r)   r*   r   r!  r"  r  r  rI   r   rm   rn   r  r   r   r  r  r   r#   joinr   r   r   rA   send_multiple_images)rS   r   r%  r|   r'  r  r'   _unquoter   	chunk_idxr   r  caption_partsr   alt_text
local_pathr#  r  r  r   r  rr   dl_errfidr   r   r   er.  rT   s     `                         @r"   r9  z&MattermostAdapter.send_multiple_imagese  sP       	F444444LLLLLuQFU/K/KLLL )& 1 1 F	f F	fIuQ9q==mK000000000"$H')M@f+0 %- %-'Ix 7%,,X666 ++I66 k%-Xim%<%<
 ,, xxzz %"NN+RT^___$ !&11%88;J{$%LLNN		@@@@@@*{955 %"NN+Z[[[$%'+}'8'8 )73H3Hr3H3R3R (9 ( ( 
F 
F 
F 
F 
F 
F 
F 
F!%#';##5#5$*NN(\(,Yss^%& %& %& %-
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 37))++,=,=,=,=,=,=	%)%6%E+
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F 
F  ) % % %"NN+SU^_b`b_bUceklll$HHHH% !* 0 0a 8 8 < B B3 G G J jNjWZ[cWdWdNjNjNj $ 1 1'9eR P PPPPPPPC - ,,, *A")#yy77 (C C + +
 '+&@&@x&P&P P P P P P P  7)6GI&RMM9q=#f++   "99'7HUUUUUUUU jt4//NN#VWWW''66wx]h6iiiiiiiii f f fYM3v;;D     gg227E8Yd2eeeeeeeeeeeeeefCF	f F	fs   C,O%26H9)6H'H90O%2#H'H9'
H11H94H15H98O%9
I2$I-'O%-I22BO%8C+O%%
Q/AQQc                  K   t           }| j        su	 |                                  d{V  t           }n# t          j        $ r Y dS t
          $ r}| j        rY d}~dS ddl}t          |                                          }t          ||j
                  r/|j        dv r&t                              d|j                   Y d}~dS d|v sd|v sd|v r!t                              d|           Y d}~dS t                              d	||           Y d}~nd}~ww xY w| j        rdS ddl}|t           z  |                                z  }t          j        ||z              d{V  t%          |d
z  t&                    }| j        sdS dS )zHConnect to the WebSocket and listen for events, reconnecting on failure.Nr   >       u:   Mattermost WS auth failed (HTTP %d) — stopping reconnect401403unauthorizedu8   Mattermost WS permanent error: %s — stopping reconnectu1   Mattermost WS error: %s — reconnecting in %.0fsr  )_RECONNECT_BASE_DELAYrM   _ws_connect_and_listenr   r   r   r'   r^   rN   r   WSServerHandshakeErrorrn   r)   rk   r*   random_RECONNECT_JITTERr  min_RECONNECT_MAX_DELAY)rS   delayrt   r'   err_strrK  jitters          r"   r   zMattermostAdapter._ws_loop  s     %- 	9`11333333333-)    ` ` `= FFFFF c((..**c7#ABB szU_G_G_LL!]_b_ijjjFFFFFG##u'7'7>W;T;TLL![]`aaaFFFFFRTWY^________` }  MMM..@F-/////////	#788E; - 	9 	9 	9 	9 	9s.   !5 D	DDA#D'D3DDc                   K   t          j        dd| j                  dz   }t                              d|           | j                            |d           d{V | _        dd	d
| j        id}| j        	                    |           d{V  t                              d           | j        2 3 d{V }| j
        r dS |j        |j        j        |j        j        hv rS	 t          j        |j                  }n# t          j        t$          f$ r Y cw xY w|                     |           d{V  |j        |j        j        |j        j        |j        j        |j        j        hv r#t                              d|j                    dS 6 dS )z@Single WebSocket session: connect, authenticate, process events.z^httpwsz/api/v4/websocketzMattermost: connecting to %sg      >@)	heartbeatNr	  authentication_challenger/   )seqactionr   z1Mattermost: WebSocket connected and authenticatedz!Mattermost: WebSocket closed (%s))r  r  rE   r)   r   rI   
ws_connectrJ   rF   	send_jsonrM   r   TEXTBINARYrp   loadsr   JSONDecodeError	TypeError_handle_ws_eventERRORCLOSECLOSINGCLOSED)rS   ws_urlauth_msgraw_msgevents        r"   rI  z(MattermostAdapter._ws_connect_and_listen  s      $77:MM2F;;;11&D1IIIIIIII 0dk*
 

 h  *********GHHH!X 	 	 	 	 	 	 	'} |!#    Jw|44EE,i8   H++E2222222222""$#	"   ?NNN &XXs   1F"C<<DDrg  c           
     `  ,K   |                     d          }|dk    rdS |                     di           }|                     d          }|sdS 	 t          j        |          }n# t          j        t          f$ r Y dS w xY w|                     d          | j        k    rdS |                     d          rdS |                     dd	          }| j                            |          rdS |                     d
d	          }|                     dd          }t                               |d          }	|                     dd	          ,|dk    r| j	        j
        r| j	        j
                             d          nd}
|
t          j        dd	          }
t          |
t                    rd |
D             }n,d t          |
                              d          D             }|r!||vrt"                              d|           dS t          j        dd                                          dv}t          j        dd	          }d |                    d          D             }||v }d| j         d| j         g}t+          ,fd|D                       }|r!|s|st"                              d|           dS |rK|D ]H}t-          j        t-          j        |          d	,t,          j                                                  ,I|                     dd	          }|                     dd	                              d          p|}|                     d           pd}|s| j        d!k    r
|dk    r|r|}|                     d"          pg }t:          j        },dd#                                         r;,                                                     d$          r,                                ,,                     d$          rt:          j!        }g }g }|D ]9}	 | "                    d%| d&           d{V }|                     d'd(|           }tG          |          j$        pd	}|                     d)d*          }d+dl%}| j&         d,| } | j'                             | d-d.| j(         i|)                    d/0          1          4 d{V 	 }!|!j*        d2k     r|!+                                 d{V }"d+d3l,m-}#m.}$ |                     d4          r9 |#|"|pd5          }%|/                    |%           |/                    |           n|                     d6          r?d+d7l,m0}&  |&|"|pd8          }%|/                    |%           |/                    |           nX |$|"|          }%|/                    |%           |/                    |           n!t"          1                    d9||!j*                   ddd          d{V  n# 1 d{V swxY w Y   # td          $ r'}'t"          1                    d:||'           Y d}'~'3d}'~'ww xY w|rj|t:          j        k    rZt+          d; |D                       rt:          j3        }n4t+          d< |D                       rt:          j4        }n|rt:          j5        }| 6                    ||	||||=          }(d+d>l,m7})  |)| j	        j
        |d          }*tq          ,||(|||r|nd|r|nd|*?          }+| 9                    |+           d{V  dS )@z!Process a single WebSocket event.rg  postedNr   rw   user_idr   r   r1   r   channel_typer   r   r   r   allowed_channelsMATTERMOST_ALLOWED_CHANNELSc                    h | ]D}t          |                                          #t          |                                          ES r   )r^   r7   r   cs     r"   	<setcomp>z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>F  s9    #Z#Z#Zq3q66<<>>#ZCFFLLNN#Z#Z#Zr$   c                ^    h | ]*}|                                 |                                 +S r   r7   ro  s     r"   rq  z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>H  s=     $ $ $"#aggii$GGII$ $ $r$   ,z7Mattermost: ignoring message in non-allowed channel: %sMATTERMOST_REQUIRE_MENTIONtrue>   0nofalse!MATTERMOST_FREE_RESPONSE_CHANNELSc                ^    h | ]*}|                                 |                                 +S r   rs  )r   chs     r"   rq  z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>W  s2    ]]]BRTRZRZR\R\]RXXZZ]]]r$   @c              3  h   K   | ],}|                                                                  v V  -d S r   )rN   )r   patternmessage_texts     r"   r   z5MattermostAdapter._handle_ws_event.<locals>.<genexpr>^  sP         <#5#5#7#77     r$   zAMattermost: skipping non-DM message without @mention (channel=%s))flagssender_namer   r   r  r	  r=   zfiles/z/infor   file_	mime_typer   r   z/api/v4/files/rZ   rW   rc   rd   rf   ri   )cache_image_from_bytescache_document_from_bytesimage/r  audio/)cache_audio_from_bytesz.oggz/Mattermost: failed to download file %s: HTTP %sz)Mattermost: error downloading file %s: %sc              3  @   K   | ]}|                     d           V  dS )r  Nr7  r   ms     r"   r   z5MattermostAdapter._handle_ws_event.<locals>.<genexpr>  s.      ??a1<<))??????r$   c              3  @   K   | ]}|                     d           V  dS )r  Nr  r  s     r"   r   z5MattermostAdapter._handle_ws_event.<locals>.<genexpr>  s.      AAQ\\(++AAAAAAr$   )r   	chat_typerj  	user_namer   r   )resolve_channel_prompt)ro   message_typesourceraw_messager   
media_urlsmedia_typeschannel_prompt):r   rp   r\  r]  r^  rG   rR   is_duplicater   r,   r.   r5   r6   r   listr^   r  r)   r8   rN   rH   r   r  r  escape
IGNORECASEr7   rl   rO   r   rZ  isspacer7  COMMANDru   r   suffixr'   rE   rI   rF   rm   rn   r  gateway.platforms.baser  r  r6  r  r*   r   PHOTOVOICEDOCUMENTbuild_sourcer  r   handle_message)-rS   rg  
event_typer   raw_post_strrw   r   r   channel_type_rawr  allowed_rawrl  require_mentionfree_channels_rawfree_channelsis_free_channelmention_patternshas_mentionr  	sender_idr  r   r  msg_typer  r  r@  	file_infor  extmimer'   dl_urlrr   r   r  r  r>  r  rt   r  r  _channel_prompt	msg_eventr  s-                                               @r"   r_  z"MattermostAdapter._handle_ws_event  sa	     YYw''
!!Fyy$$xx'' 	F	:l++DD$i0 	 	 	FF	 88I$"333F 88F 	F((4$$ ;##G,, 	F XXlB//
88NC88%))*:IFF	 xx	2.. s"" HL{GXb$++//0BCCC^bK" i(ErJJ+t,, #Z#ZK#Z#Z#Z  $ $'*;'7'7'='=c'B'B$ $ $    J6F$F$FM    i,f egg12O !#	*Mr R R]]2C2I2I#2N2N]]]M(M9O )D&(('D%''      /    K
   { W     /  G#%6	'**BBM$ $ $egg !L
 HHY++	hh}b1188==J HHY''/4		  H,, C'' (  I 88J''-2###%% 	1,*=*=*?*?*J*J3*O*O 	1'..00L""3'' 	+"*H !#
!# !	V !	VC V"&--0C0C0C0C"D"DDDDDDD	!fmcmm<<5kk(.B }}[2LMM N??#??=,,,.E.E.EF#111;; -   l l l l l l l l {S((*.))++$5$5$5$5$5$5	llllllll??844 5)?)?	3=RX)Y)YJ&--j999'..t4444!__X66 5UUUUUU)?)?	3=RX)Y)YJ&--j999'..t4444)B)B9e)T)TJ&--j999'..t4444'XZ]_c_jkkk-l l l l l l l l l l l l l l l l l l l l l l l l l l l.  V V VJCQTUUUUUUUUV  	08{'777??;????? 0&,AA[AAAAA 0&, 0&/""! # 
 
 	BAAAAA00Kz4
 
 !!%/9zzT'2<*	
 	
 	
	 !!),,,,,,,,,,,sP   A& &B ?B B5XD(X	7X	
X	XX	X
Y&YY)r,   r   )r   rU   )r]   r^   r   r   )r]   r^   r   r   r   r   )rz   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^   )NN)
r   r^   r   r^   rz   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   )NNN)r   r^   r   r^   r   r{   rz   r{   r|   r}   r   r   )r   r^   r   r^   r   r{   rz   r{   r|   r}   r   r   )NNNN)r   r^   r   r^   r   r{   r   r{   rz   r{   r|   r}   r   r   )r   r^   r   r^   r   r{   rz   r{   r|   r}   r   r   )r   r^   r   r^   r   r{   rz   r{   r|   r}   r   r   )r   r^   r   r^   )r  N)r   r^   r2   r^   r   r{   rz   r{   r  r^   r|   r}   r   r   )r   r^   r   r^   r   r{   rz   r{   r   r{   r|   r}   r   r   )Nr$  )
r   r^   r%  r&  r|   r}   r'  r(  r   r   )rg  r   r   r   )"__name__
__module____qualname____doc__splits_long_messagesrB   r\   ru   ry   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r9  r   rI  r_  __classcell__)rT   s   @r"   r;   r;   Y   sL       @@, , , , , ,F
 
 
 
   $   8; ; ; ;	" 	" 	" 	"; ; ; ;8   . Uo5 5 5 5 5: 5:            D0 0 0 00   ( #'-1< < < < <@7 7 7 7 BF
 
 
 
 
 PU? ? ? ? ? ?" "&"&-1
 
 
 
 
" "&"&-1
 
 
 
 
" "&#'"&-1
 
 
 
 
$ "&"&-1
 
 
 
 
" "&"&-1
 
 
 
 
   ( -1>? >? >? >? >?L $(-1'? '? '? '? '?Z .2 _f _f _f _f _f _f _fJ 9  9  9  9D% % % %N- - - - - - - -r$   r;   F)r   media_filesforce_documentr   r^   r   r   r{   r  Optional[list]r  c          
     ~  K   	 ddl }n# t          $ r ddicY S w xY wt          | di           pi                     d          pt	          j        dd                              d	          }t          | d
d          pt	          j        dd                                          }|r|sddiS d| dd}	dd| i}
|pg }	 ddlm	}m
}  |d          } ||          \  }} |j        d/d|                    d          i|4 d{V 	 }g }|D ]}t          |t                    r|                    d          n|}|rt          j                            |          sQ|                                }|                    d|           t'          |d          5 }|                    d|                                t          j                            |                     ddd           n# 1 swxY w Y    |j        | df||
d|4 d{V 	 }|j        dvrW|                                 d{V }dd|j         d |dd!          icddd          d{V  c cddd          d{V  S |                                 d{V }|                    d"g           D ]2}|                    d#          r|                    |d#                    3	 ddd          d{V  n# 1 d{V swxY w Y   ||d$}|r||d%<   |r||d&<    |j        | d'f|	|d(|4 d{V 	 }|j        dvrU|                                 d{V }dd)|j         d |dd!          icddd          d{V  cddd          d{V  S |                                 d{V }ddd          d{V  n# 1 d{V swxY w Y   d*d+||                    d#          d,cddd          d{V  S # 1 d{V swxY w Y   dS # |j        $ r}dd-| icY d}~S d}~wt8          $ r}dd.| icY d}~S d}~ww xY w)0u  Send via the Mattermost v4 REST API without a live gateway adapter.

    Used by ``tools/send_message_tool._send_via_adapter`` when the gateway
    runner is not in this process (typical for cron jobs running out-of-process).
    Reads ``MATTERMOST_TOKEN`` from ``pconfig.token`` (set by the gateway
    config loader from env) and falls back to the ``MATTERMOST_TOKEN`` env
    var.  Server URL comes from ``pconfig.extra["url"]`` (set by the YAML
    bridge / env loader) or the ``MATTERMOST_URL`` env var.

    Thread replies (Mattermost CRT) are supported via the ``root_id`` field
    on the ``POST /posts`` payload — pass ``thread_id`` when threading is
    desired.  ``media_files`` are uploaded via ``POST /files``
    (multipart/form-data), then their returned ``file_id`` values are
    attached to the post.

    ``force_document`` is accepted for signature parity with other
    standalone senders but unused — Mattermost stores every uploaded file
    as a generic attachment regardless.
    r   Nrk   z/aiohttp not installed. Run: pip install aiohttpr.   r2   r3   r1   r=   r/   r0   zPMattermost standalone send: MATTERMOST_URL and MATTERMOST_TOKEN must both be setrW   rX   rY   rZ   )resolve_proxy_urlproxy_kwargs_for_aiohttpMATTERMOST_PROXY)platform_env_varrh   r   rd   r]   r   rbr   )r   r   )r   rg   >   rj      zMattermost file upload failed (z): ri   r   r   r   r   r  z/api/v4/postsr   zMattermost API error (T
mattermost)r   platformr   r   z"Mattermost send failed (network): zMattermost send failed: r   )r'   r(   r4   r   r5   r6   rD   r7   r  r  r  r   rm   r   r    r]   r   r   r   openr  basenamerw   rn   ro   rp   r6  rq   r   )pconfigr   r   r   r  r  r'   base_urlr/   rg   upload_headersr  r  _proxy_sess_kw_req_kwsessionr  mediar   r   fhupload_resprs   upload_datar   r   rr   r   rt   s                                 r"   _standalone_sendr    s     8L L L LJKKKKL 
'2	&	&	,"11%88 	+9%r**fSkk  Wgt,,Q	:Lb0Q0QXXZZE 
5 
4
 	
 +5*** G &'8'8'89N#KO; 	WVVVVVVV""4FGGG44V<<'(7( 
 
)))33

 
 D	 D	 D	 D	 D	 D	 D	 D	 "$H$ 8 81;E41H1HSEIIf---e	  y(A(A '')) |W555)T** bNN		!#!1!1)!<!< #                  (7<...*  	  8 8 8 8 8 8 8 8
 !");;%0%5%5%7%7777777#!H$/$6!H !H;?:!H !H 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	D )4(8(8(:(:":":":":":":K +b A A 8 888D>> 8$OODJ77788 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8* &"' 'G  /%.	" /&.
##w|***  	  ) ) ) ) ) ) ) )
 ;j00!%,,,,,,D,T[ , ,#DSDz, ,) ) ) ) ) ) ) ) ) ) ) ) )aD	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	| "YY[[(((((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )   (""hhtnn	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	 D	J  E E ECcCCDDDDDDD ; ; ;9C99:::::::;s   	 ?AP BO5AG/#O5/G33O56G37O5:K,O5#P 6A#K,O5,
K66O59K6:2O5-:N8'O59P N8&O58
O	O5O	O5"P 5
O??P O?P 
P<PP<P<+P71P<7P<r   c                    ddl m} m}m} ddlm}m}m}m}m	}  |d            | d          }|r |d            |dd          sd	S  |d
            |d            |d           t                        |d          }	|	r |d|	                    d                      |dd          }
|
sd	S  |d|
            |d           t                        |d            |d            |d           t                        |d          }|r, |d|                    dd                      |d           n |d           t                        |d            |d            |d            |d                                           }|r |d!|           n |d!          r |d"            |d#           d	S )$a  Guide the user through Mattermost bot setup.

    Mirrors Discord/Teams' ``interactive_setup`` shape: lazy-imports CLI
    helpers so the plugin's import surface stays small, prompts for the
    server URL + bot token, captures an allowlist, and offers to set a
    home channel.  Replaces the central
    ``hermes_cli/setup.py::_setup_mattermost`` function this migration
    removes.
    r   )get_env_valueremove_env_valuesave_env_value)promptprompt_yes_noprint_header
print_infoprint_success
Mattermostr0   zMattermost: already configuredzReconfigure Mattermost?FNz/Works with any self-hosted Mattermost instance.uF      1. In Mattermost: Integrations → Bot Accounts → Add Bot Accountz   2. Copy the bot tokenz3Mattermost server URL (e.g. https://mm.example.com)r3   r=   z	Bot tokenT)passwordzMattermost token savedu,   🔒 Security: Restrict who can use your botu6      To find your user ID: click your avatar → Profilez'   or use the API: GET /api/v4/users/mez?Allowed user IDs (comma-separated, leave empty for open access)MATTERMOST_ALLOWED_USERS r1   zMattermost allowlist configureduE   ⚠️  No allowlist set - anyone who can message the bot can use it!uL   📬 Home Channel: where Hermes delivers cron job results and notifications.uH      To get a channel ID: click channel name → View Info → copy the IDzK   You can also set this later by typing /set-home in a Mattermost channel.z9Home channel ID (leave empty to set later with /set-home)MATTERMOST_HOME_CHANNELzHome channel cleared.z2   Open config in your editor:  hermes config edit)hermes_cli.configr  r  r  hermes_cli.cli_outputr  r  r  r  r  printrD   replacer7   )r  r  r  r  r  r  r  r  existingmm_urlr/   allowed_usershome_channels                r"   interactive_setupr  i  s    RQQQQQQQQQ              L}/00H 
3444}6>> 	FJ@AAAJWXXXJ)***	GGGVIJJF ='s););<<<F;...E N%u---M*+++	GGGJ=>>>JGHHHJ8999	GGGF\]]M \1=3H3Hb3Q3QRRR78888
Z[[[	GGGJ]^^^JYZZZJ\]]]6UVV\\^^L 00,????566 	0J.///JCDDDDDr$   yaml_cfgr    mattermost_cfgdict | Nonec                   d|v rHt          j        d          s4t          |d                                                   t           j        d<   |                    d          }|dt          j        d          sPt          |t                    rd                    d |D                       }t          |          t           j        d<   |                    d          }|dt          j        d	          sPt          |t                    rd                    d
 |D                       }t          |          t           j        d	<   dS )u  Translate ``config.yaml`` ``mattermost:`` keys into env vars.

    Implements the ``apply_yaml_config_fn`` contract (#24836 / #25443).
    Mirrors the legacy ``mattermost_cfg`` block that used to live in
    ``gateway/config.py::load_gateway_config()`` before this migration.

    The MattermostAdapter reads its runtime configuration via
    ``os.getenv()`` for ``MATTERMOST_REQUIRE_MENTION``,
    ``MATTERMOST_FREE_RESPONSE_CHANNELS``, and
    ``MATTERMOST_ALLOWED_CHANNELS``.  Rather than rewrite those call sites
    to read from ``PlatformConfig.extra``, this hook keeps the env-driven
    model and merely owns the YAML→env translation here, next to the
    adapter that consumes it.

    Env vars take precedence over YAML — every assignment is guarded
    by ``not os.getenv(...)`` so an explicit env var survives a config.yaml
    update.  Returns ``None`` because no extras are seeded into
    ``PlatformConfig.extra`` directly (everything flows through env).
    r  ru  free_response_channelsNrz  rt  c              3  4   K   | ]}t          |          V  d S r   r^   r   vs     r"   r   z%_apply_yaml_config.<locals>.<genexpr>  s(      //a3q66//////r$   rl  rm  c              3  4   K   | ]}t          |          V  d S r   r  r  s     r"   r   z%_apply_yaml_config.<locals>.<genexpr>  s(      --Q#a&&------r$   )	r5   r6   r^   rN   environr   r   r  r8  )r  r  frcacs       r"   _apply_yaml_configr    s%   ( N**29=Y3Z3Z*36~FW7X3Y3Y3_3_3a3a
/0


5
6
6C
ry)LMMc4   	0((//3/////C:=c((
67			.	/	/B	~bi(EFF~b$ 	.--"-----B47GG
014r$   c                    ddl m} t          |                    d          pd                                o(|                    d          pd                                          S )ay  Mattermost is considered connected when BOTH MATTERMOST_TOKEN and
    MATTERMOST_URL are set.

    Looks up via ``hermes_cli.gateway.get_env_value`` at call time (not via
    the plugin's own bound import) so tests that patch
    ``gateway_mod.get_env_value`` can suppress ambient env vars.  Matches
    what the legacy connected-platforms check did before this migration.
    r   Nr0   r1   r3   )hermes_cli.gatewaygatewayr%   r  r7   )r,   gateway_mods     r"   _is_connectedr    st     -,,,,,		"	"#5	6	6	<"CCEE 	H&&'788>BEEGG  r$   c                     t          |           S )zHFactory wrapper that constructs MattermostAdapter from a PlatformConfig.)r;   )r,   s    r"   _build_adapterr    s    V$$$r$   c                    |                      ddt          t          t          t          ddgdt
          t          dddt          t          d	d
           dS )u:   Plugin entry point — called by the Hermes plugin system.r  r  r3   r0   zpip install aiohttpr  MATTERMOST_ALLOW_ALL_USERSr  u   💬T)r   labeladapter_factorycheck_fnvalidate_configis_connectedrequired_envinstall_hintsetup_fnapply_yaml_config_fnallowed_users_envallow_all_envcron_deliver_env_varstandalone_sender_fnmax_message_lengthemojiallow_update_commandN)	register_platformr  r+   r9   r  r  r  r  r   )ctxs    r"   registerr    sk    &.2"&(:;* # 0426
 . +!G  $ $ $ $ $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  
__future__r   r   rp   loggingr5   r  pathlibr   typingr   r   r   r   r	   gateway.configr
   r   gateway.platforms.helpersr   r  r   r   r   r   	getLoggerr  r)   r   r   r!   rH  rN  rL  r#   r+   r9   r;   r  r  r  r  r  r  r   r$   r"   <module>r!     sX    # " " " " "    				 				       3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 9 9 9 9 9 9            
	8	$	$  
				   '9$%? "             y- y- y- y- y-+ y- y- y-P  $"& E; E; E; E; E; E;Z=E =E =E =EJ! ! ! !R   *% % %
& & & & & &r$   