+
    i$                        R t ^ RIt^ RIt^ RIHt ^ RIHtHtHtHt ^ RI	H
t
 ^ RIHt ]P                  ! ]4      t]
! 4       R,          tR R ltR	 R
 ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR# )a6  
Channel directory -- cached map of reachable channels/contacts per platform.

Built on gateway startup, refreshed periodically (every 5 min), and saved to
~/.hermes/channel_directory.json.  The send_message tool reads this file for
action="list" and for resolving human-friendly channel names to numeric IDs.
N)datetime)AnyDictListOptional)get_hermes_home)atomic_json_writezchannel_directory.jsonc                0    V ^8  d   QhR\         R\         /# )   valuereturnstr)formats   "6/home/ubuntu/hermes-agent/gateway/channel_directory.py__annotate__r      s     - -C -C -    c                 \    V P                  R 4      P                  4       P                  4       # )#)lstripstriplower)r   s   &r   _normalize_channel_queryr      s#    <<""$**,,r   c                ^    V ^8  d   QhR\         R\        \         \        3,          R\         /# )r
   platform_namechannelr   )r   r   r   )r   s   "r   r   r      s)       d38n  r   c                    VR,          pV R8X  d   VP                  R4      '       d   RV 2# V R8w  d'   VP                  R4      '       d   V RVR,           R2# V# )zHReturn the human-facing target label shown to users for a channel entry.namediscordguildr   typez ())get)r   r   r   s   && r   _channel_target_namer#      s`    6?D	!gkk'&:&:4&z	!gkk&&9&9r'&/*!,,Kr   c                h    V ^8  d   QhR\         \        \        3,          R\        \        ,          /# r
   originr   )r   r   r   r   )r   s   "r   r   r   $   s&      d38n # r   c                     V P                  R 4      pV'       g   R# V P                  R4      pV'       d   V RV 2# \        V4      # )chat_idN	thread_id:r"   r   )r&   r(   r)   s   &  r   _session_entry_idr,   $   sB    jj#G

;'I!I;''w<r   c                R    V ^8  d   QhR\         \        \        3,          R\        /# r%   r   r   r   )r   s   "r   r   r   .   s"     * *S#X *3 *r   c                    V P                  R 4      ;'       g4    V P                  R4      ;'       g    \        V P                  R4      4      pV P                  R4      pV'       g   V# V P                  R4      ;'       g    RV 2pV RV 2# )	chat_name	user_namer(   r)   
chat_topicztopic z / r+   )r&   	base_namer)   topic_labels   &   r   _session_entry_namer5   .   s}    

;'``6::k+B``c&**U^J_F`I

;'I**\*BByk.BK[K=))r   c                t    V ^8  d   QhR\         \        \        3,          R\         \        \        3,          /# )r
   adaptersr   )r   r   r   )r   s   "r   r   r   <   s*     " "d38n "c3h "r   c                H   ^ RI Hp / pV P                  4        FF  w  r4 W1P                  8X  d   \	        V4      VR&   K&  W1P
                  8X  d   \        V4      VR&   KF  KH  	  R	 F  pWb9  g   K  \        V4      W&&   K  	  R\        P                  ! 4       P                  4       RV/p \        \         V4       V#   \         d-   p\        P                  RTP                  T4        Rp?K  Rp?ii ; i  \         d"   p\        P                  RT4        Rp?T# Rp?ii ; i)
z
Build a channel directory from connected platform adapters and session data.

Returns the directory dict and writes it to DIRECTORY_PATH.
)Platformr   slackz)Channel directory: failed to build %s: %sN
updated_at	platformsz&Channel directory: failed to write: %s)telegramwhatsappsignalemailsms)gateway.configr9   itemsDISCORD_build_discordSLACK_build_slack	Exceptionloggerwarningr   _build_from_sessionsr   now	isoformatr   DIRECTORY_PATH)r7   r9   r<   platformadaptere	plat_name	directorys   &       r   build_channel_directoryrT   <   s    (13I%^^-	[+++'5g'>	)$^^+%1'%:	'" ,	 . H	%#7	#BI  H
 	hlln..0YI
D.)4 %  	[NNFXYZZ	[  D?CCDs4   B;B;)C5 ;C2!C--C25D! DD!c                \    V ^8  d   QhR\         \        \        \        3,          ,          /# r
   r   r   r   r   )r   s   "r   r   r   a   s       tDcN3 r   c                h   . p\        V RR4      pV'       g   V#  ^ RIpTP                   FV  pTP                   FC  pTP                  R\        TP                  4      RTP                  RTP                  RR/4       KE  	  KX  	  TP                  \        R4      4       T#   \         d    Tu # i ; i)	z4Enumerate all text channels the Discord bot can see._clientNidr   r   r    r   r   )getattrr   ImportErrorguildstext_channelsappendr   rZ   r   extendrK   )rP   channelsclient_discordr   chs   &     r   rE   rE   a   s    HWi.F" %%BOOc"%%j		  &  OO(34O!  s   B! !B10B1c                \    V ^8  d   QhR\         \        \        \        3,          ,          /# rV   rW   )r   s   "r   r   r   }   s      ) )T$sCx.1 )r   c                    . p\        V RR4      ;'       g    \        V RR4      pV'       g   \        R4      #  ^ RIHp \        R4      #   \         d     Li ; i)z'List Slack channels the bot has joined._appNrY   r:   )_send_slack)r[   rK   tools.send_message_toolrh   rH   )rP   ra   rb   rh   s   &   r   rG   rG   }   s]    HWfd+PPww	4/PF#G,,7  ((	  s   A
 
AAc                h    V ^8  d   QhR\         R\        \        \         \         3,          ,          /# )r
   r   r   )r   r   r   )r   s   "r   r   r      s'       T#s(^0D r   c                   \        4       R,          R,          pVP                  4       '       g   . # . p \        VRR7      ;_uu_ 4       p\        P                  ! V4      pRRR4       \        4       pXP                  4        F  w  rgVP                  R4      ;'       g    / pVP                  R4      V 8w  d   K8  \        V4      p	V	'       d   W9   d   KS  VP                  V	4       VP                  RV	R	\        V4      R
VP                  RR4      RVP                  R4      /4       K  	  V#   + '       g   i     L; i  \         d"   p
\        P                  RY
4        Rp
?
T# Rp
?
ii ; i)z<Pull known channels/contacts from sessions.json origin data.sessionszsessions.jsonutf-8encodingNr&   rO   rZ   r   r    	chat_typedmr)   z5Channel directory: failed to read sessions for %s: %s)r   existsopenjsonloadsetrC   r"   r,   addr_   r5   rH   rI   debug)r   sessions_pathentriesfdataseen_ids_keysessionr&   entry_idrQ   s   &          r   rK   rK      s3   #%
2_DM!!	G`-'22a99Q<D 3 5!ZZ\MD[[*00bFzz*%6(0Hx3LL"NNh+F3K6VZZ4	  *" N+ 32$  `Lm__N`s6   E  	D- >E  BE  -D=	8E   E,E''E,c                F    V ^8  d   QhR\         \        \        3,          /# rV   r.   )r   s   "r   r   r      s     5 5S#X 5r   c                    \         P                  4       '       g   RRR/ /#  \        \         RR7      ;_uu_ 4       p \        P                  ! V 4      uuRRR4       #   + '       g   i     R# ; i  \
         d
    RRR/ /u # i ; i)z,Load the cached channel directory from disk.r;   Nr<   rm   rn   )rN   rr   rs   rt   ru   rH   )r{   s    r   load_directoryr      sk      ""dK445.733q99Q< 4333 5dK445s.   A0 A
A0 A-	'A0 -A0 0BBc                R    V ^8  d   QhR\         R\         R\        \         ,          /# )r
   r   r   r   )r   r   )r   s   "r   r   r      s%     $ $ $3 $8C= $r   c                   \        4       pVP                  R/ 4      P                  V . 4      pV'       g   R# \        V4      pV FI  p\        VR,          4      V8X  d   VR,          u # \        \        W4      4      V8X  g   K@  VR,          u # 	  RV9   dv   VP	                  R^4      w  rgV F[  pVP                  RR4      P                  4       P                  4       pW8X  g   K9  \        VR,          4      V8X  g   KR  VR,          u # 	  V Uu. uF.  p\        VR,          4      P                  V4      '       g   K,  VNK0  	  p	p\        V	4      ^8X  d   V	^ ,          R,          # R# u upi )z
Resolve a human-friendly channel name to a numeric ID.

Matching strategy (case-insensitive, first match wins):
- Discord: "bot-home", "#bot-home", "GuildName/bot-home"
- Telegram: display name or group name
- Slack: "engineering", "#engineering"
r<   Nr   rZ   /r    )	r   r"   r   r#   rsplitr   r   
startswithlen)
r   r   rS   ra   queryrd   
guild_partch_partr   matchess
   &&        r   resolve_channel_namer      s8     I}}["-11-DH$T*E #BvJ/58d8O#$8$KLPUUd8O	  e|#ll32
BFF7B'--/557E"'?6
'Kw'V$x  %_Hb(@F(L(W(WX](^rrHG_
7|qqz$	 `s   )E)?E)c                $    V ^8  d   QhR\         /# rV   r   )r   s   "r   r   r      s     + +c +r   c                 8   \        4       p V P                  R/ 4      p\        VP                  4       4      '       g   R# R.p\	        VP                  4       4       EF  w  r4V'       g   K  VR8X  Ed   / p. pV FP  pVP                  R4      pV'       d$   VP                  V. 4      P                  V4       K?  VP                  V4       KR  	  \	        VP                  4       4       FL  w  rVP                  RV	 R24       \	        V
R R	7       F   pVP                  R
\        W74       24       K"  	  KN  	  V'       d8   VP                  R4       V F   pVP                  R
\        W74       24       K"  	  VP                  R4       EK.  VP                  VP                  4        R24       V F#  pVP                  RV R\        W74       24       K%  	  VP                  R4       EK  	  VP                  R4       VP                  R4       RP                  V4      # )zDFormat the channel directory as a human-readable list for the model.r<   z?No messaging platforms connected or no channels discovered yet.zAvailable messaging targets:
r   r   z	Discord (z):c                     V R ,          # )r    )cs   &r   <lambda>.format_directory_for_display.<locals>.<lambda>   s    qyr   )keyz
  discord:zDiscord (DMs):r   r*   z  z1Use these as the "target" parameter when sending.z;Bare platform name (e.g. "telegram") sends to home channel.
)r   r"   anyvaluessortedrC   
setdefaultr_   r#   titlejoin)rS   r<   linesrR   ra   r]   dmsrd   r   
guild_nameguild_channelss              r   format_directory_for_displayr      s    Ik2.Iy!""P-.E%ioo&78	 	!&(FCw%%eR077;JJrN  /5V\\^.D*
yB78 5HIBLL:.B9.Q-R!ST J /E -.BLL:.B9.Q-R!ST LLLLIOO-.a01r)A.B9.Q-RST LL9  9< 
LLDE	LLNO99Ur   )__doc__rt   loggingr   typingr   r   r   r   hermes_cli.configr   utilsr   	getLogger__name__rI   rN   r   r#   r,   r5   rT   rE   rG   rK   r   r   r   r   r   r   <module>r      sy       , , - #			8	$ "%==-*"J8)$H5$N+r   