
    Jj                    4   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ZdZ dZ!d,dZ" G d de          Z#ddddd-d!Z$d.d#Z%d/d(Z&d,d)Z'd* Z(d.d+Z)dS )0u  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Og       @g      N@g?returnboolc                 2   t          j        dd          } t          j        dd          }| st                              d           dS |st                              d           dS 	 ddl}d	S # t          $ r t                              d
           Y dS w xY w)z2Return True if the Mattermost adapter can be used.MATTERMOST_TOKEN MATTERMOST_URLz$Mattermost: MATTERMOST_TOKEN not setFz"Mattermost: MATTERMOST_URL not setr   NTz!Mattermost: aiohttp not installed)osgetenvloggerdebugwarningaiohttpImportError)tokenurlr#   s      J/home/rurouni/.hermes/hermes-agent/plugins/platforms/mattermost/adapter.pycheck_mattermost_requirementsr(   5   s    I("--E
)$b
)
)C ;<<<u ;<<<ut   :;;;uus   (A. .$BB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).Tconfigr   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 )
Nr&   r   r   /r   F
reply_modeMATTERMOST_REPLY_MODEoff)super__init__r
   
MATTERMOSTextragetr   r   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'   r2   zMattermostAdapter.__init__L   s   !4555 LUB'' /y)2..
&++ 	 "<L295G+L+L!#"$ "047; L\2.. 9y0%88
%'' 	
 15%' *++    r   Dict[str, str]c                    d| j          ddS )NBearer application/jsonAuthorizationzContent-Type)r8   rE   s    r'   _headerszMattermostAdapter._headerso   s!    4t{44.
 
 	
rG   pathstrDict[str, Any]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    errorr7   lstripr;   r5   rO   ClientTimeoutstatustextjsonClientError)rE   rP   r#   r&   respbodyexcs          r'   _api_getzMattermostAdapter._api_getu   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&payloadc           	     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   NrT   rU   rV   r-   r   rW   rX   )r[   rd   r\   r]   u   MM API POST %s → %s: %sr^   z MM API POST %s network error: %s)r#   r    r_   r7   r`   rB   rC   r;   postrO   ra   rb   rc   rd   re   rQ   rE   rP   rj   r#   r&   rf   rg   rh   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)rA   
isinstancedictr5   _resolve_root_idrQ   )rE   ro   rq   	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>>:::::::::rG   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.>     r]   Fr   c              3      K   | ]}|v V  	d S N .0markerrg   s     r'   	<genexpr>zMMattermostAdapter._last_post_failure_is_broken_thread_root.<locals>.<genexpr>   s'      fffnffffffrG   )rv   rootidzroot idrt   rl   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hrG   )invalidz	not foundzdoes not existmissing)rB   rC   r@   any)rE   rootishbrokenrg   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!rG   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.postsNrv   notifymessager   uK   ⚠️ Mattermost thread delivery failed; posting final reply in channel.

zNMattermost: falling back to flat channel delivery for notify-worthy post in %s)
rn   rw   rx   r5   r   poprQ   stripr    r"   )rE   r   rj   rq   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\:::::::::rG   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   NrT   rU   rV   r-   r[   rd   r]   u   MM API PUT %s → %s: %sr^   zMM API PUT %s network error: %s)r#   r    r_   r7   r`   r;   putrO   rb   rc   rd   re   rm   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   rM   rJ   <   rX   )r[   r   r\   r]   u   MM file upload → %s: %sr^   
file_infosid)r#   r7   FormData	add_fieldr8   r;   rl   ra   rb   rc   r    r_   rd   r5   )rE   r   r   r   r   r#   r&   formr[   rf   rg   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 configuredFrW   rX   r\   zusers/mer   uP   Mattermost: failed to authenticate — check MATTERMOST_TOKEN and MATTERMOST_URLusernamer   z+Mattermost: authenticated as @%s (%s) on %sT)r#   r7   r8   r    r_   ClientSessionra   r;   r?   ri   closer9   r5   r:   infoasynciocreate_task_ws_loopr=   _mark_connected)rE   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<<trG   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)r?   r=   donecancelr   CancelledError	Exceptionr>   r<   r   r;   closedr    r   rN   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/Nrv   )ri   r5   )rE   r   r   s      r'   ry   z"MattermostAdapter._resolve_root_idF  sk        	N]]#5G#5#566666666 	#DHHY'' 	#	?"rG   Ncontentr   c                  K   |st          d          S |                     |          }|                     |t                    }d}|D ]h}||d}	|                     ||           d{V }
|
r|
|	d<   |                     ||	|           d{V }|rd|vrt          dd	          c S |d         }it          d|
          S )z1Send a message (or multiple chunks) to a channel.T)successNr   r   rv   r   FzFailed to create postr   r_   r   
message_id)r   format_messagetruncate_messageMAX_POST_LENGTHr{   r   )rE   r   r   ro   rq   	formattedchunkslast_idchunkrj   resolved_rootr   s               r'   sendzMattermostAdapter.sendV  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;;;;rG   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   )ri   _CHANNEL_TYPE_MAPr5   )rE   r   r   ch_typer   s        r'   get_chat_infozMattermostAdapter.get_chat_infov  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6rG   c                X   K   |                      d| j         dd|i           d{V  dS )zSend a typing indicator.zusers/z/typingr   N)rn   r9   )rE   r   rq   s      r'   send_typingzMattermostAdapter.send_typing  s_       nn/T&///7#
 
 	
 	
 	
 	
 	
 	
 	
 	
 	
rG   )finalizer   r   c                  K   |                      |          }|                     d| d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   )rE   r   r   r   r   r   r   s          r'   edit_messagezMattermostAdapter.edit_message  s       ''00	]]'Z'''	"
 
 
 
 
 
 
 
  	Jt4''e3HIIII$4:>>>>rG   	image_urlcaptionc                F   K   |                      ||||d|           d{V S )z5Download an image and upload it as a file attachment.imageN)_send_url_as_file)rE   r   r   r   ro   rq   s         r'   
send_imagezMattermostAdapter.send_image  sK       ++Y7H
 
 
 
 
 
 
 
 	
rG   
image_pathc                F   K   |                      |||||           d{V S )zUpload a local image file.rq   N_send_local_file)rE   r   r   r   ro   rq   s         r'   send_image_filez!MattermostAdapter.send_image_file  N       **Z(X + 
 
 
 
 
 
 
 
 	
rG   	file_path	file_namec                F   K   |                      ||||||           d{V S )z"Upload a local file as a document.Nr   )rE   r   r   r   r   ro   rq   s          r'   send_documentzMattermostAdapter.send_document  sK       **Y9h
 
 
 
 
 
 
 
 	
rG   
audio_pathc                F   K   |                      |||||           d{V S )zUpload an audio file.r   Nr   )rE   r   r   r   ro   rq   s         r'   
send_voicezMattermostAdapter.send_voice  r   rG   
video_pathc                F   K   |                      |||||           d{V S )zUpload a video file.r   Nr   )rE   r   r   r   ro   rq   s         r'   
send_videozMattermostAdapter.send_video  r   rG   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)rE   r   s     r'   r   z MattermostAdapter.format_message  s     &4eWEErG   filer&   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 ||pd|gd}|                     ||           d{V }|r||d<   |                     |||           d{V }|rd|vrt1          dd           S t1          d!|d         "          S )#z2Download a URL and upload it as a file attachment.r   is_safe_urlz0Mattermost: blocked unsafe URL (SSRF protection)r   
r   Nr   r-      ?.png   rW   rX   r   i  i     z1Mattermost download retry %d/2 for %s (status %d)P   g      ?r]   z7Mattermost: failed to download %s after %d attempts: %sz,Mattermost: download returned no data for %sr   r   file_idsrv   r   FFailed to post with filer   Tr   )tools.url_safetyr   r    r"   r   r   r#   rsplitsplitranger;   r5   ra   rb   r!   r   sleepreadr   re   TimeoutErrorr   r{   r   r   )rE   r   r&   r   ro   r   rq   r   r#   r   ctfnameattemptrf   rh   file_idrj   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 "}" 	#
 #

 #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                p  K   ddl }t          |          }|                                s,t                              d|           t          dd          S |p|j        }	|                    |	          d         pd}
|                                }| 	                    |||	|
           d{V }|st          dd	          S ||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   r   r  rv   r   r
  )	mimetypesr   existsr    r"   r   r   
guess_type
read_bytesr   r{   r   )rE   r   r   r   ro   r   rq   r  pr  r  r   r  rj   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 "}" 	#
 #

 #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:>>>>rG           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|d                    |          |d}|                     d|           d{V }|r||d<   t                               dt          |          |	dz   t          |                     | !                    |||           d{V }|rd|vrEt                              d           tE                      #                    ||
||           d{V  # t4          $ re}t                              d |	dz   t          |          |d!"           tE                      #                    ||
||           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>j  s&    LLL!&1u9%LLLrG   zfile://   z%Mattermost: skipping missing image %sz	image/pngr   z-Mattermost: blocked unsafe image URL in batchrW   rX   r   r]   z2Mattermost: failed to download image (HTTP %d): %sr  z&Mattermost: download failed for %s: %sr-   r  r  r  image_r  r   r  rv   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   r;   r5   ra   rb   r  r   r   r  r  r   joinr{   r   r   r1   send_multiple_images)rE   r   r  rq   r  r  r#   _unquoter   	chunk_idxr   r	  caption_partsr   alt_text
local_pathr  r  r  r   r   rf   dl_errfidrj   r   r   er&  rF   s     `                         @r'   r1  z&MattermostAdapter.send_multiple_imagesS  sC       	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 - ,,,  #*#yy77 (+ +
 '+&@&@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_DELAYr?   _ws_connect_and_listenr   r   r   r#   rQ   r@   rw   WSServerHandshakeErrorrb   r    r_   r"   random_RECONNECT_JITTERr  min_RECONNECT_MAX_DELAY)rE   delayrh   r#   err_strrC  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   r7   r    r   r;   
ws_connectr<   r8   	send_jsonr?   r   TEXTBINARYrd   loadsr   JSONDecodeError	TypeError_handle_ws_eventERRORCLOSECLOSINGCLOSED)rE   ws_urlauth_msgraw_msgevents        r'   rA  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r_  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.r_  postedNr   rl   user_idr   r   r   r   channel_typer   r   r   r   allowed_channelsMATTERMOST_ALLOWED_CHANNELSc                    h | ]D}t          |                                          #t          |                                          ES r   )rQ   r   r   cs     r'   	<setcomp>z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>4  s9    #Z#Z#Zq3q66<<>>#ZCFFLLNN#Z#Z#ZrG   c                ^    h | ]*}|                                 |                                 +S r   r   rg  s     r'   ri  z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>6  s=     $ $ $"#aggii$GGII$ $ $rG   ,z7Mattermost: ignoring message in non-allowed channel: %sMATTERMOST_REQUIRE_MENTIONtrue>   0nofalse!MATTERMOST_FREE_RESPONSE_CHANNELSc                ^    h | ]*}|                                 |                                 +S r   rk  )r   chs     r'   ri  z5MattermostAdapter._handle_ws_event.<locals>.<setcomp>E  s2    ]]]BRTRZRZR\R\]RXXZZ]]]rG   @c              3  h   K   | ],}|                                                                  v V  -d S r   )r@   )r   patternmessage_texts     r'   r   z5MattermostAdapter._handle_ws_event.<locals>.<genexpr>L  sP         <#5#5#7#77     rG   zAMattermost: skipping non-DM message without @mention (channel=%s))flagssender_namerv   rt   r	  r  r-   zfiles/z/infor   file_	mime_typer   r   z/api/v4/files/rM   rJ   rW   rX   rZ   r]   )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r/  r   ms     r'   r   z5MattermostAdapter._handle_ws_event.<locals>.<genexpr>  s.      ??a1<<))??????rG   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ArG   )r   	chat_typerb  	user_nameru   r   )resolve_channel_prompt)rc   message_typesourceraw_messager   
media_urlsmedia_typeschannel_prompt):r5   rd   rT  rU  rV  r9   rD   is_duplicater   r+   r4   r   r   rw   listrQ   r  r    r!   r@   r:   r   r   r   escape
IGNORECASEr   r`   rA   r   rR  isspacer/  COMMANDri   r   suffixr#   r7   r;   r8   ra   rb   r  gateway.platforms.baser}  r~  r.  r  r"   r   PHOTOVOICEDOCUMENTbuild_sourcer  r   handle_message)-rE   r_  
event_typer   raw_post_strrl   r   r   channel_type_rawr  allowed_rawrd  require_mentionfree_channels_rawfree_channelsis_free_channelmention_patternshas_mentionrw  	sender_idrz  ru   r	  msg_typer  r  r8  	file_infor  extmimer#   dl_urlrf   r   r}  r~  r6  r  rh   r  r  _channel_prompt	msg_eventrx  s-                                               @r'   rW  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   rH   )rP   rQ   r   rR   )rP   rQ   rj   rR   r   rR   )ro   rp   rq   rr   r   rp   r   r   )r   rQ   rj   rR   rq   rr   r   rR   )r   )
r   rQ   r   r   r   rQ   r   rQ   r   rp   )r   r   r   r   r   r   )r   rQ   r   rQ   )NN)
r   rQ   r   rQ   ro   rp   rq   rr   r   r   )r   rQ   r   rR   r   )r   rQ   rq   rr   r   r   )
r   rQ   r   rQ   r   rQ   r   r   r   r   )NNN)r   rQ   r   rQ   r   rp   ro   rp   rq   rr   r   r   )r   rQ   r   rQ   r   rp   ro   rp   rq   rr   r   r   )NNNN)r   rQ   r   rQ   r   rp   r   rp   ro   rp   rq   rr   r   r   )r   rQ   r   rQ   r   rp   ro   rp   rq   rr   r   r   )r   rQ   r   rQ   r   rp   ro   rp   rq   rr   r   r   )r   rQ   r   rQ   )r   N)r   rQ   r&   rQ   r   rp   ro   rp   r   rQ   rq   rr   r   r   )r   rQ   r   rQ   r   rp   ro   rp   r   rp   rq   rr   r   r   )Nr  )
r   rQ   r  r  rq   rr   r  r   r   r   )r_  rR   r   r   )"__name__
__module____qualname____doc__splits_long_messagesr2   rO   ri   rn   r{   r   r   r   r   r   r   ry   r   r   r   r   r   r   r   r   r   r   r   r   r1  r   rA  rW  __classcell__)rF   s   @r'   r*   r*   G   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- - - - - - - -rG   r*   F)ru   media_filesforce_documentr   rQ   r   ru   rp   r  Optional[list]r  rR   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   Nr_   z/aiohttp not installed. Run: pip install aiohttpr4   r&   r   r   r-   r%   r   zPMattermost standalone send: MATTERMOST_URL and MATTERMOST_TOKEN must both be setrJ   rK   rL   rM   )resolve_proxy_urlproxy_kwargs_for_aiohttpMATTERMOST_PROXY)platform_env_varr\   r   rX   rP   r   rbr   )r   r   )r   r[   >   r^      zMattermost file upload failed (z): r]   r   r   r   rv   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$   getattrr5   r   r   r6   r   r  r  r  r   ra   rw   rx   rP   r  r   r   openr  basenamerl   rb   rc   rd   r.  re   r   )pconfigr   r   ru   r  r  r#   base_urlr%   r[   upload_headersr  r  _proxy_sess_kw_req_kwsessionr	  mediar   r   fhupload_resprg   upload_datar   rj   rf   r   rh   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                 B   ddl 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!|            |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save_env_value)promptprompt_yes_noprint_header
print_infoprint_success
Mattermostr   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)r   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 r   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_CHANNELz2   Open config in your editor:  hermes config edit)hermes_cli.configr  r  hermes_cli.cli_outputr  r  r  r  r  printr6   replace)r  r  r  r  r  r  r  existingmm_urlr%   allowed_usershome_channels               r'   interactive_setupr  W  s    @???????              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VL @0,???JCDDDDDrG   yaml_cfgrx   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  rm  free_response_channelsNrr  rl  c              3  4   K   | ]}t          |          V  d S r   rQ   r   vs     r'   r   z%_apply_yaml_config.<locals>.<genexpr>  s(      //a3q66//////rG   rd  re  c              3  4   K   | ]}t          |          V  d S r   r  r  s     r'   r   z%_apply_yaml_config.<locals>.<genexpr>  s(      --Q#a&&------rG   )	r   r   rQ   r@   environr5   rw   r  r0  )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4rG   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   Nr   r   r   )hermes_cli.gatewaygatewayr   r  r   )r+   gateway_mods     r'   _is_connectedr    st     -,,,,,		"	"#5	6	6	<"CCEE 	H&&'788>BEEGG  rG   c                     t          |           S )zHFactory wrapper that constructs MattermostAdapter from a PlatformConfig.)r*   )r+   s    r'   _build_adapterr    s    V$$$rG   c                    |                      dd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  r   r   zpip install aiohttpr  MATTERMOST_ALLOW_ALL_USERSr  u   💬T)r   labeladapter_factorycheck_fn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(   r  r  r  r  r   )ctxs    r'   registerr    sh    &."&(:;* # 0426
 . +!E  # # # # #rG   r  )r   rQ   r   rQ   ru   rp   r  r  r  r   r   rR   r  )r  rx   r  rx   r   r  )*r  
__future__r   r   rd   loggingr   r   pathlibr   typingr   r   r   r   r	   gateway.configr
   r   gateway.platforms.helpersr   r  r   r   r   r   	getLoggerr  r    r   r   r@  rF  rD  r(   r*   r  r  r  r  r  r  r   rG   r'   <module>r     s#    # " " " " "    				 				       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	$	$  
				         $y- y- y- y- y-+ y- y- y-P  $"& E; E; E; E; E; E;Z:E :E :E :ED! ! ! !R   *% % %
% % % % % %rG   