
    PL
jp                        d Z ddlZddlZddlZddlZddlZddlZddlmZmZ ddl	m
Z
mZmZmZ ddlmZmZ ddlmZmZmZmZmZmZmZ  ej        e          ZdZdZd	Zd
ZdZ dZ!dZ"de#dee#         fdZ$de%de#fdZ&de#de'fdZ(de#de'fdZ) G d de          Z*de'fdZ+de'fdZ,de'fdZ-de.dz  fdZ/ddddde#de#d ee#         d!eee#                  d"e'dee#e
f         fd#Z0d&d$Z1d&d%Z2dS )'u  SimpleX Chat platform adapter (Hermes plugin).

Connects to a simplex-chat daemon running in WebSocket mode.
Inbound messages arrive via a persistent WebSocket connection.
Outbound messages use the same WebSocket with JSON commands.

This adapter ships as a Hermes platform plugin under
``plugins/platforms/simplex/``. The Hermes plugin loader scans the
directory at startup, calls ``register(ctx)``, and the platform
becomes available to ``gateway/run.py`` and ``tools/send_message_tool``
through the registry — no edits to core files are required.

SimpleX chat daemon setup:
    simplex-chat -p 5225          # start daemon on port 5225
    # or via Docker:
    # docker run -p 5225:5225 simplexchat/simplex-chat-cli -p 5225

Required environment variables:
    SIMPLEX_WS_URL             WebSocket URL of the daemon
                               (default: ws://127.0.0.1:5225)

Optional environment variables:
    SIMPLEX_ALLOWED_USERS      Comma-separated contact IDs (allowlist)
    SIMPLEX_ALLOW_ALL_USERS    Set 'true' to allow all contacts
    SIMPLEX_HOME_CHANNEL       Default contact/group ID for cron delivery
    SIMPLEX_HOME_CHANNEL_NAME  Human label for the home channel

The ``websockets`` Python package is imported lazily — the plugin is
discoverable and `hermes setup` can describe it even when websockets is
not installed. ``check_requirements()`` returns False until the package
is present, so the gateway will not attempt to instantiate the adapter.
    N)datetimetimezone)AnyDictListOptional)PlatformPlatformConfig)BasePlatformAdapterMessageEventMessageType
SendResultcache_image_from_bytescache_audio_from_bytescache_document_from_bytesi>  g      $@g       @g      N@g      >@g      ^@zhermes-valuereturnc                 @    d |                      d          D             S )z4Split a comma-separated string into a stripped list.c                 ^    g | ]*}|                                 |                                 +S  )strip).0vs     E/home/kuhnn/.hermes/hermes-agent/plugins/platforms/simplex/adapter.py
<listcomp>z%_parse_comma_list.<locals>.<listcomp>O   s-    ===!17799=AGGII===    ,)split)r   s    r   _parse_comma_listr   M   s"    ==u{{3//====r   datac                    | dd         dk    rdS | dd         dk    rdS | dd         dk    rd	S t          |           d
k    r| dd         dk    r| dd
         dk    rdS | dd         dk    rdS t          |           dk    r| dd         dk    rdS | dd         dk    rdS t          |           dk    r| d         dk    r| d         dz  dk    rdS dS )z&Guess file extension from magic bytes.N   s   PNG.png   s   .jpgs   GIF8.gif   s   RIFF   s   WEBP.webps   %PDFz.pdfs   ftypz.mp4s   OggS.oggr            .mp3z.bin)len)r    s    r   _guess_extensionr0   R   s   BQBx:vBQBx;vBQBx7v
4yyB48w..4":3H3HwBQBx7v
4yyA~~$qs)w..vBQBx7v
4yyA~~$q'T//tAw~$.F.Fv6r   extc                 .    |                                  dv S )N>   .jpegr&   r%   r#   r)   lowerr1   s    r   _is_image_extr7   g   s    99;;DDDr   c                 .    |                                  dv S )N>   .aac.m4a.wavr.   r*   r4   r6   s    r   _is_audio_extr<   k   s    99;;BBBr   c                   p    e Zd ZdZdef fdZdefdZddZddZ	dd	Z
d
eddfdZdeddfdZdededee         fdZdefdZdeddfdZ	 	 ddededee         deeeef                  def
dZd deddfdZ	 	 	 d!dededee         dee         deeeef                  defdZdedefdZ xZS )"SimplexAdapterzSimpleX Chat adapter using the simplex-chat daemon WebSocket API.

    Instantiated by the ``adapter_factory`` passed to
    ``ctx.register_platform()`` in :func:`register`.
    configc                    t          d          }t                                          ||           t          |di           pi }|                    dd                              d          | _        d | _        d | _        d | _	        i | _
        d| _        d| _        t                      | _        d	| _        t                               d
| j                   d S )Nsimplex)r?   platformextraws_urlws://127.0.0.1:5225/Fg           z#SimpleX adapter initialized: url=%s)r	   super__init__getattrgetrstriprD   _ws_ws_task_health_task_typing_tasks_running_last_ws_activityset_pending_corr_ids_max_pending_corrloggerinfo)selfr?   kwargsrB   rC   	__class__s        r   rI   zSimplexAdapter.__init__z   s    I&&:::,,2ii*?@@GGLL 044868!$ '*ee!$94;GGGGGr   r   c                   K   	 ddl }n+# t          $ r t                              d           Y dS w xY w| j        st                              d           dS 	 ddl }|                    | j        d          4 d{V  	 ddd          d{V  n# 1 d{V swxY w Y   n9# t          $ r,}t                              d| j        |           Y d}~dS d}~ww xY wd	| _        t          j                    | _	        t          j        |                                           | _        t          j        |                                           | _        t                              d
| j                   d	S )zDConnect to the simplex-chat daemon and start the WebSocket listener.r   NzHSimpleX: 'websockets' package not installed. Run: pip install websocketsFz#SimpleX: SIMPLEX_WS_URL is required
   )open_timeoutz&SimpleX: cannot reach daemon at %s: %sTzSimpleX: connected to %s)
websocketsImportErrorrV   errorrD   connect	ExceptionrQ   timerR   asynciocreate_task_ws_listenerrN   _health_monitorrO   rW   )rX   r^   	_wsclientes       r   ra   zSimplexAdapter.connect   s     	 	 	 	LL.   55	 { 	LL>???5	**** ((2(FF                                   	 	 	LLA4;PQRRR55555	 !%+D,=,=,?,?@@#/0D0D0F0FGG.<<<tsH   	 $11&B$ >B B$ 
BB$ B B$ $
C.!CCNc                 l  K   d| _         | j        r=| j                                         	 | j         d{V  n# t          j        $ r Y nw xY w| j        r=| j                                         	 | j         d{V  n# t          j        $ r Y nw xY w| j                                        D ]}|                                 | j                                         | j	        r8	 | j	        
                                 d{V  n# t          $ r Y nw xY wd| _	        t                              d           dS )z%Stop WebSocket listener and clean up.FNzSimpleX: disconnected)rQ   rN   cancelrd   CancelledErrorrO   rP   valuesclearrM   closerb   rV   rW   )rX   tasks     r   
disconnectzSimplexAdapter.disconnect   s     = 	M  """m########)     	$$&&&''''''''')    &--// 	 	DKKMMMM  """8 	hnn&&&&&&&&&&   DH+,,,,,s3   9 A
A/A= =BB#D 
DDc                   K   ddl }ddl }t          }| j        rV	 t                              d| j                   |                    | j        dd          4 d{V 	 }|| _        t          }t          j                    | _	        t          
                    d           |2 3 d{V }| j        s nt          j                    | _	        	 t          j        |          }|                     |           d{V  Y# t          j        $ r t                              d|           Y t          $ r t                              d           Y w xY w6 	 ddd          d{V  n# 1 d{V swxY w Y   n# t"          j        $ r Y d| _        dS |j        $ r-}| j        rt                              d	||           Y d}~n=d}~wt          $ r-}| j        rt                              d
||           Y d}~nd}~ww xY wd| _        n# d| _        w xY w| j        rN|dz  t+          j                    z  }t#          j        ||z              d{V  t/          |dz  t0                    }| j        TdS dS )z9Maintain a persistent WebSocket connection to the daemon.r   NzSimpleX WS: connecting to %s   )ping_intervalping_timeoutzSimpleX WS: connectedz SimpleX WS: invalid JSON: %.100sz SimpleX WS: error handling eventz-SimpleX WS: error: %s (reconnecting in %.0fs)z8SimpleX WS: unexpected error: %s (reconnecting in %.0fs)g?r$   )r^   WS_RETRY_DELAY_INITIALrQ   rV   debugrD   ra   rM   rc   rR   rW   jsonloads_handle_eventJSONDecodeErrorrb   	exceptionrd   rl   WebSocketExceptionwarningrandomsleepminWS_RETRY_DELAY_MAX)	rX   rh   _wsexcbackoffwsrawmsgri   jitters	            r   rf   zSimplexAdapter._ws_listener   s     &&&&####(m ,	?& ;T[III$,,K"$!# -   Q Q Q Q Q Q Q Q !DH4G-1Y[[D*KK 7888%' 
Q 
Q 
Q 
Q 
Q 
Q 
Qc#} "!E15.Q"&*S//C"&"4"4S"9"999999999#3 R R R"LL)KSQQQQQ( Q Q Q",,-OPPPPPQ &(RQ Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q, )      ,   = NNGG      = NNR7    4} ? 38mGf$4555555555gk+=>>Y m ,	? ,	? ,	? ,	? ,	?s   AE6 AE$"E("E$/C;:E$;*E%E$'#E
E$EE$E6 $
E..E6 1E.2E6 5H 6G:H 	G:#G ;H  G:#G50H 5G::H 	Hc                   K   | j         rt          j        t                     d{V  | j         sdS t	          j                    | j        z
  }|t          k    rkt                              d|           t	          j                    | _        | j	        r1	 | j	        
                                 d{V  n# t          $ r Y nw xY w| j         dS dS )z8Force reconnect if the WebSocket has been idle too long.Nz-SimpleX: WS idle for %.0fs, forcing reconnect)rQ   rd   r   HEALTH_CHECK_INTERVALrc   rR   HEALTH_CHECK_STALE_THRESHOLDrV   r~   rM   ro   rb   )rX   elapseds     r   rg   zSimplexAdapter._health_monitor  s     m 	- 5666666666= ikkD$::G555CW   *.&8 "hnn..........$    m 	 	 	 	 	s   B3 3
C ?C eventc                   K   |                     d          p)|                     di                                dd          }|                     dd          }|r6|                    t                    r| j                            |           dS |dk    r|                     |           d{V  dS |dk    r7|                     d          pg }|D ]}|                     |           d{V  dS dS )	z3Dispatch a daemon event to the appropriate handler.typeresp corrIdNnewChatItemnewChatItems	chatItems)rK   
startswith_CORR_PREFIXrT   discard_handle_new_chat_item)rX   r   	resp_typecorr_iditemsitem_wrappers         r   rz   zSimplexAdapter._handle_event&  s*     IIf%%N62)>)>)B)B62)N)N	 ))Hb)) 	w)),77 	"**7333F%%,,U33333333333.((IIk**0bE % ? ?00>>>>>>>>>>	 )(? ?r   wrapperc           	      
  K   |                     d          p|                     d          pi }|                     d          p|                     d          pi }|                     d          pi }|                     d          pi }|sdS |                     d          pi }|                     d	          pi                      d
d          }|dv rdS |                     d
d          }|dv }	|	r|                     d          p|                     d          pi }
t          |
                     d          p|
                     d          pd          }|
                     d          p)|
                     di                                dd          }|rd| nd}|}n|                     d          pi }t          |                     d          p|                     d          pd          }|                     d          p|                     d          p|}|}|}|st                              d           dS |                     d          pi }|	rh|rft          |                     d          p|                     d          p|          }|                     d          p|                     d          p|}n|}|}|                     d          pd}g }g }|                     d          pi }|r@|                     d          dvr(|                     d          }|                     d d          }|r	 |                     ||           d{V }|r|                    d!d"          d#         }t          d!|z             r-|                    d$|                    d%d&          z              n@t          d!|z             r|                    d'|z              n|                    d(           |                    |           n+# t          $ r t                              d)|           Y nw xY w|                     d*          p|                     d+          pd}	 t          j        |                    d,d-                    }n6# t          t          f$ r" t          j        t"          j        .          }Y nw xY w|                     |||	rdnd/||0          }t(          j        } |rKt-          d1 |D                       rt(          j        } n%t-          d2 |D                       rt(          j        } t3          ||| ||||3          }!|                     |!           d{V  dS )4z7Process a single newChatItem event into a MessageEvent.chatInfochatchatItemitemcontent
msgContentNmeta
itemStatusr   r   >   sndNewsndSentsndSentDirectsndSentViaProxy>   group	groupInfor   r   groupIdiddisplayNamegroupProfilegroup:contact	contactIdlocalDisplayNamez'SimpleX: ignoring event with no chat_idchatItemMembermemberIdtextfile
fileStatus>   r`   	cancelledfileIdfileName.r,   image/jpgjpegaudio/zapplication/octet-streamz SimpleX: failed to fetch file %sitemTs	createdAtZz+00:00)tzdm)chat_id	chat_name	chat_typeuser_id	user_namec              3   @   K   | ]}|                     d           V  dS )r   Nr   r   mts     r   	<genexpr>z7SimplexAdapter._handle_new_chat_item.<locals>.<genexpr>  s.      AAr2==**AAAAAAr   c              3   @   K   | ]}|                     d           V  dS )r   Nr   r   s     r   r   z7SimplexAdapter._handle_new_chat_item.<locals>.<genexpr>  s.      CCR]]8,,CCCCCCr   )sourcer   message_type
media_urlsmedia_types	timestampraw_message)rK   strrV   rw   _fetch_filersplitr7   appendreplacer<   rb   r|   r   fromisoformat
ValueErrorAttributeErrornowr   utcbuild_sourcer   TEXTanyVOICEPHOTOr   handle_message)"rX   r   	chat_info	chat_itemitem_contentmsg_contentr   	directionchat_type_rawis_group
group_infogroup_id
group_namer   r   contact_info
contact_idcontact_namemember	sender_idsender_namer   r   r   	file_infofile_id	file_namecachedr1   ts_strr   r   msg_type	event_objs"                                     r   r   z$SimplexAdapter._handle_new_chat_item9  s      KK
++Hw{{6/B/BHb	KK
++Hw{{6/B/BHb	 !}}Y//52"&&|44: 	F }}V$$*XXl++1r66vrBB	QQQF "fb11 $:: 	%"{33Sy}}W7M7MSQSJ:>>)44R
t8L8LRPRSSH#66s*..Y[:\:\:`:`anpr:s:sJ-5=)x)))2G"II$==339rL\--k::Zl>N>Nt>T>TZXZ[[J  // ##$677 
 !G$I 	LLBCCCF /006B 		$ 		$FJJz22Qfjj6F6FQ'RRI

=)) ::011 K  I#K v&&," !#
!#MM&))/R	 	R|44<RRRmmH--G!j&99I RR#'#3#3GY#G#GGGGGGGF 2$mmC33B7(s33 K'..x#++eV:T:T/TUUUU*3955 K'..x#~>>>>'../IJJJ"))&111  R R R$$%GQQQQQR (##Btxx'<'<B	6 .v~~c8/L/LMMIIN+ 	6 	6 	6 555III	6 ""!)3ggt! # 
 
 # 	-AA[AAAAA -&,CC{CCCCC -&, !!#
 
 
	 !!),,,,,,,,,,,s%   ?CP %P65P6&(R 0SSr   r   c                   K   |                                  }|d| d}|                     |           d{V  t          j        d           d{V  t          j                            d          t          j                            d          dfD ]}t          j                            ||          }t          j                            |          rt          |d          5 }|
                                }ddd           n# 1 swxY w Y   t          |          }	t          |	          rt          ||	          c S t          |	          rt          ||	          c S t!          ||          c S dS )	z7Ask the daemon to receive and return a file attachment.z
/freceive r   cmdNr$   z~/Downloadsz~/.simplex/filesz/tmp/simplex_filesrb)_make_corr_id_send_wsrd   r   ospath
expanduserjoinexistsopenreadr0   r7   r   r<   r   r   )
rX   r   r   r   r  
search_dir	candidatefr    r1   s
             r   r   zSimplexAdapter._fetch_file  s      $$&&)))
 
 mmC          mA
 G}--G122 
 	F 	FJ
 Z;;Iw~~i(( 	F)T** $a6688D$ $ $ $ $ $ $ $ $ $ $ $ $ $ $&t,, %% F1$<<<<<"3'' F1$<<<<<4T9EEEEE	F ts   $DD		D		c                 z   t            t          t          j                    dz             dt          j        dd           }| j                            |           t          | j                  | j        k    rAt          | j                  d| j        dz           }| xj        t          |          z  c_        |S )z/Generate a unique correlation ID for a request.  -r   i'  Nr$   )r   intrc   r   randintrT   addr/   rU   listrS   )rX   r   	to_removes      r   r  zSimplexAdapter._make_corr_id  s    !V3ty{{T'9#:#:VVV^At=T=TVV""7+++t%&&)???T3445Qd6LPQ6Q5QRI""c)nn4""r   payloadc                 j  K   ddl }| j        }|st                              d           dS 	 |                    t          j        |                     d{V  dS # |j        $ r t                              d           Y dS t          $ r&}t                              d|           Y d}~dS d}~ww xY w)zESend a JSON payload over the WebSocket, queuing if not yet connected.r   Nz4SimpleX: WS not connected, dropping outbound commandz SimpleX: WS closed while sendingzSimpleX: WS send error: %s)
r^   rM   rV   rw   sendrx   dumpsConnectionClosedr~   rb   )rX   r  r   r   ri   s        r   r  zSimplexAdapter._send_ws  s      ####X 	LLOPPPF	<''$*W--...........& 	? 	? 	?NN=>>>>>> 	< 	< 	<NN7;;;;;;;;;	<s   -A $B2	B2B--B2r   r   reply_tometadatac                    K   |                                  }|                    d          r|dd         }d| d| }nd| d| }||d}|                     |           d{V  t          d	          S )
z*Send a text message to a contact or group.r      N#[] @[r  T)success)r  r   r  r   )	rX   r   r   r  r  r   r   cmd_strr  s	            r   r  zSimplexAdapter.send  s       $$&&h'' 	0qrr{H0800w00GG/7//g//G 
 

 mmG$$$$$$$$$$''''r   c                 
   K   dS )u9   SimpleX does not expose a typing indicator API — no-op.Nr   )rX   r   r  s      r   send_typingzSimplexAdapter.send_typing	  s      r   	image_urlcaptionc                 ~   K   |r| d|                                  n|}|                     ||||           d{V S )uG  Send an image (URL) as a message with optional caption.

        SimpleX has no native ``send_image`` over the WebSocket API — file
        attachments require the daemon's filesystem-backed flow which is
        not driven from this adapter. Fall back to a plain text message
        containing the URL and caption.
        
)r  r  N)r   r  )rX   r   r)  r*  r  r  r   s          r   
send_imagezSimplexAdapter.send_image  s]       5<J'((Y((..000YYwx(YSSSSSSSSSr   c                 X   K   |                     d          r|d|dd         dS |d|dS )zReturn basic chat info.r   r   r!  N)r   r   namer   r   )rX   r   s     r   get_chat_infozSimplexAdapter.get_chat_info  sE      h'' 	N&MMM"D'BBBr   r   N)NNN)NNN)__name__
__module____qualname____doc__r
   rI   boolra   rq   rf   rg   dictrz   r   r   r   r   r   r  r  r   r   r  r(  r-  r0  __classcell__)rZ   s   @r   r>   r>   s   s        H~ H H H H H H2t    @- - - -F3? 3? 3? 3?r   .? ?$ ? ? ? ?&t-4 t-D t- t- t- t-l$ $ $# $ $ $ $Ts    <d <t < < < <$ #'-1( (( ( 3-	(
 4S>*( 
( ( ( (0       "&"&-1T TT T #	T
 3-T 4S>*T 
T T T T$C3 C4 C C C C C C C Cr   r>   c                  `    t          j        d          sdS 	 ddl} n# t          $ r Y dS w xY wdS )a  Plugin gate: require SIMPLEX_WS_URL AND the websockets package.

    Returning False keeps the platform out of ``get_connected_platforms()``
    so the gateway never instantiates the adapter when the dependency is
    missing or no daemon URL is configured.
    SIMPLEX_WS_URLFr   NT)r  getenvr^   r_   )r^   s    r   check_requirementsr=  *  sW     9%&& u   uu4s    
++c                     t          | di           pi }t          j        d          p|                    dd          }t	          |          S )z=Validate that the platform config has enough info to connect.rC   r;  rD   r   rJ   r  r<  rK   r7  r?   rC   rD   s      r   validate_configrA  :  G    FGR((.BEY'((CEIIh,C,CF<<r   c                     t          | di           pi }t          j        d          p|                    dd          }t	          |          S )z9Check whether SimpleX is configured (env or config.yaml).rC   r;  rD   r   r?  r@  s      r   is_connectedrD  A  rB  r   c                     t          j        dd                                          } | sdS d| i}t          j        dd                                          }|r/|t          j        dd                                          p|d|d<   |S )	uA  Seed ``PlatformConfig.extra`` from env vars during gateway config load.

    Called by the platform registry's env-enablement hook BEFORE adapter
    construction, so ``gateway status`` and ``get_connected_platforms()``
    reflect env-only configuration without instantiating the WebSocket
    client. Returns ``None`` when SimpleX isn't minimally configured.

    The special ``home_channel`` key in the returned dict is handled by
    the core hook — it becomes a proper ``HomeChannel`` dataclass on the
    ``PlatformConfig`` rather than being merged into ``extra``.
    r;  r   NrD   SIMPLEX_HOME_CHANNELSIMPLEX_HOME_CHANNEL_NAME)r   r/  home_channel)r  r<  r   )rD   seedhomes      r   _env_enablementrK  H  s     Y',,2244F tF#D9+R006688D 
I92>>DDFFN$ 
  
^ Kr   F)	thread_idmedia_filesforce_documentr   messagerL  rM  rN  c                  K   	 ddl }n# t          $ r ddicY S w xY wt          | di           pi }t          j        d          p|                    dd          }|sdd	iS 	 |                    d
          r|dd         }	d|	 d| }
nd| d| }
dt          t          j                    dz             |
d}|	                    |dd          4 d{V }|
                    t          j        |                     d{V  t          j        d           d{V  ddd          d{V  n# 1 d{V swxY w Y   dd|dS # t          $ r}dd| icY d}~S d}~ww xY w)u  Open an ephemeral WebSocket to the daemon, send, and close.

    Used by ``tools/send_message_tool._send_via_adapter`` when the gateway
    runner is not in this process (e.g. ``hermes cron`` running as a
    separate process from ``hermes gateway``). Without this hook,
    ``deliver=simplex`` cron jobs fail with "No live adapter for platform".

    ``thread_id`` and ``force_document`` are accepted for signature parity
    with other plugins but are not meaningful here. ``media_files`` is
    accepted but only the text body is delivered — SimpleX requires the
    daemon's filesystem-backed file flow which an ephemeral connection
    cannot drive safely.
    r   Nr`   z5websockets not installed. Run: pip install websocketsrC   r;  rD   rE   z3SimpleX standalone send: SIMPLEX_WS_URL is requiredr   r!  r"  r#  r$  zhermes-snd-r  r  r\      )r]   close_timeoutg      ?TrA   )r%  rB   r   zSimpleX send failed: )r^   r_   rJ   r  r<  rK   r   r  rc   ra   r  rx   r  rd   r   rb   )pconfigr   rO  rL  rM  rN  rh   rC   rD   r   r&  r  r   ri   s                 r   _standalone_sendrT  a  s     ,R&&&&& R R RPQQQQR GWb))/REY'((VEIIh@U,V,VF PNOO6h'' 	0qrr{H0800w00GG/7//g//G >C	d(:$;$;==
 

 $$V"A$NN 	% 	% 	% 	% 	% 	% 	%RT''$*W--.........-$$$$$$$$$	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%
  Y7KKK 6 6 644455555556sP   	 !A7E	 AD2 E	 2
D<<E	 ?D< E	 	
E$EE$E$c            	         t                       t          d           t          d           t          d           t          d           t          d           t                       	 ddlmm n # t          $ r t          d           Y d	S w xY wd
ddt
          dt
          dt          dd	ffd}  | dd            | dd            | dd           t          d           d	S )u   Minimal stdin wizard for ``hermes setup gateway`` → SimpleX.

    Prompts for the WebSocket URL and the optional allowlist / home channel.
    Writes to ``~/.hermes/.env`` via ``hermes_cli.config``.
    zSimpleX Chat setupz------------------zRequirements:z?  1. simplex-chat daemon running (e.g. `simplex-chat -p 5225`).zF  2. Python package `websockets` installed (`pip install websockets`).r   )get_env_valuesave_env_valuezNhermes_cli.config not available; set SIMPLEX_* vars manually in ~/.hermes/.envNF)secretvarpromptrX  r   c                J   t                    r |           nd }|rdnd}	 |rdd l}|                    | | d          }n&t          | | d                                          }n&# t          t
          f$ r t                       Y d S w xY w|r | |           d S d S )Nz [keep current]r   r   z: )callablegetpassinputr   EOFErrorKeyboardInterruptprint)	rY  rZ  rX  existingsuffixr]  r   rV  rW  s	          r   _promptz"interactive_setup.<locals>._prompt  s    )1-)@)@J==%%%d&.6""B	 =6(=6(=(=(=>>333344::<<+, 	 	 	GGGFF	  	'N3&&&&&	' 	's   AA- -BBr;  z2Daemon WebSocket URL (default ws://127.0.0.1:5225)SIMPLEX_ALLOWED_USERSz1Allowed contact IDs (comma-separated; blank=skip)rF  z(Home channel contact/group ID (or empty)zODone. Make sure the simplex-chat daemon is running before starting the gateway.)ra  hermes_cli.configrV  rW  r_   r   r7  )rd  rV  rW  s    @@r   interactive_setuprg    sT    
GGG	
	
	/	
KLLL	
RSSS	GGGCCCCCCCCC   ^___ :? ' ' 'S '# '$ '4 ' ' ' ' ' ' ' GRSSSG#%XYYYG"$NOOO	
[\\\\\s   +A4 4BBc                     |                      ddd t          t          t          dgdt          t
          dt          ddt          d	d
d
d           dS )uE   Plugin entry point — called by the Hermes plugin system at startup.rA   zSimpleX Chatc                      t          |           S r2  )r>   )cfgs    r   <lambda>zregister.<locals>.<lambda>  s    N3$7$7 r   r;  zJpip install websockets   # SimpleX adapter requires the websockets packagerF  re  SIMPLEX_ALLOW_ALL_USERSu   🔒Ta&  You are chatting via SimpleX Chat, a private decentralised messenger. Contacts are identified by opaque internal IDs, not phone numbers or usernames. SimpleX supports standard markdown formatting. There is no typing indicator and no hard message length limit, but keep responses conversational.)r/  labeladapter_factorycheck_fnrA  rD  required_envinstall_hintsetup_fnenv_enablement_fncron_deliver_env_varstandalone_sender_fnallowed_users_envallow_all_envmax_message_lengthemojipii_safeallow_update_commandplatform_hintN)register_platformr=  rA  rD  rg  rK  rT  MAX_MESSAGE_LENGTH)ctxs    r   registerr    st    77#'!&'a" * 4 .1/- !LE  ( ( ( ( (r   r1  )3r6  rd   rx   loggingr  r   rc   r   r   typingr   r   r   r   gateway.configr	   r
   gateway.platforms.baser   r   r   r   r   r   r   	getLoggerr3  rV   r~  TYPING_INTERVALrv   r   r   r   r   r   r   bytesr0   r7  r7   r<   r>   r=  rA  rD  r8  rK  rT  rg  r  r   r   r   <module>r     s,   B    				   ' ' ' ' ' ' ' ' , , , , , , , , , , , ,
 4 3 3 3 3 3 3 3                  
	8	$	$
     $  >S >T#Y > > > >
5 S    *Es Et E E E ECs Ct C C C CpC pC pC pC pC( pC pC pCnD     t    D        <  $'+ 36 36 3636 36
 }36 $s)$36 36 
#s(^36 36 36 36l&] &] &] &]R* * * * * *r   