+
    jHàj7W  ã                   ó:  a € R> t:0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHtHtHtHt ^ RIHt ^ RIHt ]P*                  ! ]4      tRt] ^ k Rt] ^k Rt/ t] ^k ]! 0 R?m4      t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 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t0R, R- lt1R. R/ lt2R0 R1 lt3R2 R3 lt4R4 R5 lt5R6 R7 lt6R8 R9 lt7R: R; lt8RAR< R= llt9R# )Ba  Channel directory -- cached map of reachable channels/contacts per platform.

Built on gateway startup, refreshed every 5 min, saved to ``<HERMES_HOME>/channel_directory.json``.
send_message reads it for action="list" and to resolve friendly channel names to IDs.
N)Údatetime©ÚPath)ÚAnyÚDictÚIterableÚListÚOptionalÚTuple)Úget_hermes_home)Úatomic_json_writei  c                ó$   € V ^8„  d   QhR\         /# ©é   Úreturnr   )Úformats   "Ú>/home/ubuntu/.hermes/hermes-agent/gateway/channel_directory.pyÚ__annotate__r   (   s   € ÷ Jñ Jœñ Jó    c                  ó@   € \         ;'       g    \        4       R ,          # )zchannel_directory.json)ÚDIRECTORY_PATHr   © r   r   Ú_directory_pathr   (   s   € Ü×IÐIœ_Ó.Ð1IÕIÐIr   c                ó$   € V ^8„  d   QhR\         /# r   r   )r   s   "r   r   r   ,   s   € ÷ Nñ N”tñ Nr   c                  ó@   € \         ;'       g    \        4       R ,          # )zchannel_aliases.json)ÚCHANNEL_ALIASES_PATHr   r   r   r   Ú_aliases_pathr   ,   s   € Ü×MÐM¤?Ó#4Ð7MÕ#MÐMr   c                ó0   € V ^8„  d   QhR\         R\        /# ©r   Úpathr   )r   r   )r   s   "r   r   r   0   s   € ÷ ñ ”Tð œcñ r   c                 ó–   € \        V R R7      ;_uu_ 4       p\        P                  ! V4      uuRRR4       #   + '       g   i     R# ; i)z	utf-8-sig)ÚencodingN)ÚopenÚjsonÚload)r   Úfs   & r   Ú
_read_jsonr&   0   s+   € ä	ˆd˜[×	)Õ	)¨QÜ�yŠy˜‹|÷ 
*×	)×	)Ó	)ús	   –7·A	c                óR   € V ^8„  d   QhR\         R\        \        \        3,          /# r   )r   ÚdictÚstrr   )r   s   "r   r   r   6   s"   € ÷ ñ œ$ð ¤4¬¬S¨¥>ñ r   c                ó¤   € V P                  4       '       g   / #  \        V 4      p\        V\        4      '       d   V# / #   \         d    / u # i ; i)zKRead a JSON object from *path*; {} when missing, unreadable, or not a dict.)Úexistsr&   Ú
isinstancer(   Ú	Exception)r   Údatas   & r   Ú_load_json_dictr/   6   sL   € à�;‰;�=Š=Øˆ	ðÜ˜$ÓˆÜ! $¬×-Ò-ˆtÐ5°2Ð5øÜô ØŠ	ðús   š"? ½? ¿AÁAc                óJ   € V ^8„  d   QhR\         \        \        3,          RR/# )r   Ú	platformsr   N©r(   r)   r   )r   s   "r   r   r   A   s&   € ÷ Yñ Y¤d¬3´¨8¥nð Y¸ñ Yr   c                óÂ  € \        \        4       4      P                  4        EF3  w  r\        V\        4      '       g   K  V P                  V. 4      p\        V\        4      '       g   KH  VP                  4        F×  w  rE\        V\        4      '       d   VP                  4       '       g   K3  \        V4      VP                  4       rTV Uu. uF5  p\        V\        4      '       g   K  VP                  R4      V8X  g   K3  VNK7  	  ppV F  pWVR&   K	  	  V'       d   K¥  TP                  RTRTRRRVP                  R4      '       d   RMR/4       KÙ  	  EK6  	  R# u upi )	zËOverlay friendly names onto directory entries by chat_id, in place.

An aliased id not yet discovered gets a placeholder entry so a freshly-created
group is addressable by name before its first message.
ÚidÚnameÚ	thread_idNÚtypez@g.usÚgroupÚdm)r/   r   Úitemsr,   r(   Ú
setdefaultÚlistr)   ÚstripÚgetÚappendÚendswith)r1   Ú	plat_nameÚid_mapÚentriesÚchat_idÚfriendlyÚeÚmatchess   &       r   Ú_apply_channel_aliasesrH   A   s  € ô -¬]«_Ó=×CÑC×EÑˆ	Ü˜&¤$×'Ò'ÙØ×&Ñ& y°"Ó5ˆÜ˜'¤4×(Ò(ÙØ!'§¡¦ÑˆGÜ˜h¬×,Ò,°H·N±N×4DÒ4DÙÜ # G£¨h¯n©nÓ.>�XÙ")Ó\¡'˜Q¬Z¸¼4×-@”qÀQÇUÁUÈ4Ã[ÐT[ÑE[—q�q¡'ˆGÐ\Û�Ø$�&“	ñ ç‘7Ø—‘  g¨v°xÀÈdØ &°7×3CÑ3CÀG×3LÒ3L©ÐRVð Xö Yô "0ó Fùò ]s   ÃEÃ(EÄ Ec                ó0   € V ^8„  d   QhR\         R\         /# )r   Úvaluer   ©r)   )r   s   "r   r   r   Y   s   € ÷ -ñ -¤Cð -¬Cñ -r   c                 ó\   € V P                  R 4      P                  4       P                  4       # )Ú#)Úlstripr=   Úlower)rJ   s   &r   Ú_normalize_channel_queryrP   Y   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.   € ÷ Jñ J¬ð J´d¼3Ä¸8µnð JÌñ Jr   c                ó®   € VR,          pV R8X  d   VP                  R4      '       d   RV 2# T# VP                  R4      '       d   V RVR,           R2# T# )z.Human-facing target label for a channel entry.r5   ÚdiscordÚguildrM   r7   z (Ú)©r>   )rR   rS   r5   s   && r   Ú_channel_target_namerY   ]   s]   € à�6�?€DØ˜	Ô!Ø$Ÿ[™[¨×1Ò1��4�&ˆzÐ;°tÐ;Ø,3¯K©K¸×,?Ò,?ˆdˆV�2�g˜f•oÐ& aÐ(ÐIÀTÐIr   c                óh   € V ^8„  d   QhR\         \        \        3,          R\        \        ,          /# ©r   Úoriginr   )r(   r)   r   r	   )r   s   "r   r   r   e   s*   € ÷ `ñ `œd¤3¬ 8�nð `´¼#µñ `r   c                 óŽ   € V P                  R 4      pV'       g   R# V P                  R4      ;p'       d   V RV 2# \        V4      # )rD   Nr6   Ú:©r>   r)   )r\   rD   r6   s   &  r   Ú_session_entry_idr`   e   sF   € Ø�j‰j˜Ó#€GßÙØ5;·Z±ZÀÓ5LÐ(L¨	Ö(LˆgˆY�a˜	�{Ð#Ð_ÔSVÐW^ÓS_Ð_r   c                óR   € V ^8„  d   QhR\         \        \        3,          R\        /# r[   r2   )r   s   "r   r   r   l   s&   € ÷ Oñ O¤¤S¬# X¥ð O´3ñ Or   c                 ó  € V P                  R 4      ;'       g4    V P                  R4      ;'       g    \        V P                  R4      4      pV P                  R4      ;p'       g   V# T RV P                  R4      ;'       g    RV 2 2# )Ú	chat_nameÚ	user_namerD   r6   z / Ú
chat_topicztopic r_   )r\   Ú	base_namer6   s   &  r   Ú_session_entry_namerg   l   s|   € Ø—
‘
˜;Ó'×`Ð`¨6¯:©:°kÓ+B×`Ð`ÄcÈ&Ï*É*ÐU^ÓJ_ÓF`€IØŸ™ KÓ0Ð0ˆIÖ0ØÐØˆ[˜˜FŸJ™J |Ó4×LÐL¸&ÀÀÐ8LÐMÐNÐNr   c                óV   € V ^8„  d   QhR\         R\        \         ,          R\         RR/# )r   Úteam_idÚ
error_codeÚdetailr   N©r)   r	   )r   s   "r   r   r   s   s3   € ÷ {ñ {¤3ð {´H¼SµMð {Ì3ð {ÐSWñ {r   c                ó\  € VR8X  d   \         P                  RV 4       R# \        V 4      \        V4      3p\        P                  ! 4       p\
        P                  V4      pVe   WE,
          \        8¼  d"   V\
        V&   \         P                  RW4       R# \         P                  RW4       R# )z\missing_scope is expected (session-history fallback); anything else warns once per interval.Úmissing_scopezPChannel directory: Slack team %s lacks channels:read; using session history onlyNz@Channel directory: failed to list Slack channels for team %s: %szQChannel directory: suppressed repeated Slack channel list failure for team %s: %s)	ÚloggerÚdebugr)   ÚtimeÚ	monotonicÚ_slack_directory_warning_lastr>   Ú)_SLACK_DIRECTORY_WARNING_INTERVAL_SECONDSÚwarning)ri   rj   rk   ÚkeyÚnowÚlasts   &&&   r   Ú_report_slack_failurery   s   s…   € à�_Ô$Ü�‰ÐgÐipÔqÙÜˆw‹<œ˜V›Ð
%€CÜ
�.Š.Ó
€CÜ(×,Ñ,¨SÓ1€DØ‚|�s•zÔ%NÔNØ-0Ô% cÑ*Ü�‰ÐYÐ[bÖkä�‰ÐhÐjqÖzr   c                ót   € V ^8„  d   QhR\         \        \        3,          R\         \        \        3,          /# )r   Úadaptersr   )r(   r   r)   )r   s   "r   r   r   „   s*   € ÷ (ñ (¬D´´c°­Nð (¼tÄCÌÀH½~ñ (r   c           
   ƒ  óø  aa"  € ^ RI Hp / oV P                  4        F±  w  r# \        VRR4      p\	        V4      '       d.   V! 4       G Rj  x€L
 pVe   \        V4      SVP                  &   KQ  W!P                  8X  d)   \        P                  ! \        V4      G Rj  x€L
 SR&   K‰  W!P                  8X  d   \        V4      G Rj  x€L
 SR&   K±  K³  	  V  Uu0 uF  p\        VR\!        V4      4      kK  	  upoR VV3R	 llpV F  p	V! V	P                  4      G Rj  x€L
  K  	  \"        P$                  ! \        4      ;_uu_ 4        ^ R
IHp
 V
P+                  4        F  pV! VP,                  4      G Rj  x€L
  K  	  RRR4       \/        S4       R\0        P2                  ! 4       P5                  4       RS/p \        P                  ! \6        \9        4       V4      G Rj  x€L
  V#  EL� ELO EL*  \         d.   p\        P                  RTP                  T4        Rp?EK  Rp?ii ; iu upi  EL L½  + '       g   i     L¿; i Lg  \         d"   p\        P                  RT4        Rp?T# Rp?ii ; i5i)zJBuild the directory from connected adapters + session data and persist it.)ÚPlatformÚlist_channelsNrU   Úslackz)Channel directory: failed to build %s: %srJ   c                ó(   € V ^8„  d   QhR\         RR/# )r   rA   r   NrK   )r   s   "r   r   Ú-build_channel_directory.<locals>.__annotate__š   s   € ÷ Xñ X¤3ð X¨4ñ Xr   c              “   ó–   <"  € V \         9   g   V S9   g   V S9  d   R # \        P                  ! \        V 4      G R j  x€L
 SV &   R #  L	5i©N)Ú_SKIP_SESSION_DISCOVERYÚasyncioÚ	to_threadÚ_build_from_sessions)rA   Úadapter_platform_namesr1   s   &€€r   Ú	_discoverÚ*build_channel_directory.<locals>._discoverš   s?   øé € ØÔ/Ô/°9À	Ô3IÈYÐ^tÔMtÙÜ%,×%6Ò%6Ô7KÈYÓ%W×Wˆ	�)ÓÑWùs   ƒ:A	½A¾
A	)Úplatform_registryÚ
updated_atr1   z&Channel directory: failed to write: %s)Úgateway.configr}   r:   ÚgetattrÚcallableÚ_normalize_adapter_channelsrJ   ÚDISCORDr…   r†   Ú_build_discordÚSLACKÚ_build_slackr-   ro   ru   r)   Ú
contextlibÚsuppressÚgateway.platform_registryr‹   Úloaded_entriesr5   rH   r   rw   Ú	isoformatr   r   )r{   r}   ÚplatformÚadapterr~   Úplatform_channelsrF   Úpr‰   Úplatr‹   ÚentryÚ	directoryrˆ   r1   s   &            @@r   Úbuild_channel_directoryr¡   „   s  ùé € å'Ø13€IØ%Ÿ^™^Ö-Ñˆð	[Ü# G¨_¸dÓCˆMÜ˜×&Ò&Ù*7«/×$9Ð!Ø$Ò0Ü0KÐL]Ó0^�I˜hŸn™nÑ-ÙØ×+Ñ+Ô+Ü-4×->Ò->¼~ÈwÓ-W×'W�	˜)Ó$ØŸ^™^Ô+Ü+7¸Ó+@×%@�	˜'Ó"ñ ,ñ .ñ" DLÓLÁ8¸aœg a¨´#°a³&Ö9Á8ÑLÐ÷Xñ Xó ˆÙ˜Ÿ
™
Ó#×#Ò#ñ ô 
×	Ò	œY×	'Õ	'Ý?Ø&×5Ñ5Ö7ˆEÙ˜EŸJ™JÓ'×'Ò'ñ 8÷ 
(ô ˜9Ô%ØœxŸ|š|›~×7Ñ7Ó9¸;È	ÐR€IðDÜ×ÒÔ 1´?Ó3DÀiÓP×PÐPð ÐòA %:ò
 (Xâ%@øÜô 	[Ü�N‰NÐFÈÏÉÐXY×ZÓZûð	[üò
 Mò 	$ñ (÷ 
(×	'úñ 	QøÜô DÜ�‰Ð?À×CÐCØÐûðDüsú   „I:£(G1ÁG(Á G1Á,I:Á..G1ÂG+ÂG1Â$I:Â&G1ÃG.ÃG1Ã
I:ÃH,Ã5'I:ÄH1Ä*I:Å/H6Å6H4
Å7H6Å?:I:Æ:'I Ç!I	Ç"I Ç&I:Ç(G1Ç+G1Ç.G1Ç1H)Ç<!H$ÈI:È$H)È)	I:È4H6È6I	ÉI:É	I ÉI7ÉI2É,I:É2I7É7I:c                ó\   € V ^8„  d   QhR\         \        \        \        3,          ,          /# r   )r<   r(   r)   )r   s   "r   r   r   ¯   s    € ÷ ñ œt¤D¬¬c¨¥NÕ3ñ r   c                óä  € . p\        V RR4      pV'       g   V#  ^ RIp^ RIHp TP
                   FŽ  p\        TRR4      ;'       g    . pTP                  R3TR33 F^  w  rxT FS  p	T! T	4      '       d   K  TP                  R\        T	P                  4      RT	P                  R	TP                  R
T/4       KU  	  K`  	  K�  	  TP                  \        R4      4       T#   \         d    Tu # i ; i)zJEnumerate text + forum channels the Discord bot can see, plus session DMs.Ú_clientN)Úis_discord_channel_obfuscatedÚforum_channelsrS   Úforumr4   r5   rV   r7   rU   )rŽ   rU   ÚImportErrorÚgateway.platforms.helpersr¥   ÚguildsÚtext_channelsr?   r)   r4   r5   Úextendr‡   )
r›   ÚchannelsÚclientÚ_discordr¥   rV   ÚforumsÚchsÚch_typeÚchs
   &         r   r’   r’   ¯   sè   € à€HÜ�W˜i¨Ó.€FßØˆðÛ"õ Hà—”ˆä˜Ð 0°$Ó7×=Ð=¸2ˆØ#×1Ñ1°9Ð=ÀÈÐ?PÓQ‰LˆCÛ�á0°×4Ò4ÙØ—‘ ¤s¨2¯5©5£z°6¸2¿7¹7ÀGÈUÏZÉZÐY_ÐahÐ iÖjó	 ó Rñ ð ‡O�OÔ(¨Ó3Ô4Ø€Oøô ô ØŠðús   ›C ÃC/Ã.C/c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   Úerrorr   )r-   r	   r)   )r   s   "r   r   r   É   s   € ÷ ñ ¤ð ¬x¼­}ñ r   c                óä   € \         P                  ! \        4      ;_uu_ 4        V P                  P	                  R4      pV'       d   \        V4      MRuuRRR4       #   + '       g   i     R# ; i)z<Slack Web API error code from SlackApiError-like exceptions.rµ   N)r•   r–   r-   Úresponser>   r)   )rµ   rJ   s   & r   Ú_slack_api_error_coder¸   É   sF   € ä	×	Ò	œY×	'Õ	'Ø—‘×"Ñ" 7Ó+ˆß"Œs�5Œz¨÷ 
(×	'×	'Ñ	'ñ ús   £0AÁA/	c                óh   € V ^8„  d   QhR\         R\        \        \        \         3,          ,          /# )r   Úraw_channelsr   )r   r<   r(   r)   )r   s   "r   r   r   Ñ   s'   € ÷ ñ ¬cð ´d¼4ÄÄSÀ½>Õ6Jñ r   c                óÀ  € . p\        4       p\        V \        4      '       d   T MR EF-  p\        V\        4      '       g   K  \	        VP                  R4      ;'       g    R4      P                  4       p\	        VP                  R4      ;'       g    T4      P                  4       pV'       d   V'       d   WB9   d   K˜  RTRTR\	        VP                  R4      ;'       g    R4      /pTP                  R Uu/ uF-  qsP                  V4      '       g   K  V\	        W7,          4      bK/  	  up4       VP                  V4       VP                  V4       EK0  	  V# u upi )zNValidate and dedupe entries returned by an adapter's ``list_channels()`` hook.r4   Ú r5   r7   r9   r   )r6   rV   )
Úsetr,   r<   r(   r)   r>   r=   Úupdater?   Úadd)rº   r­   Úseen_idsÚrawÚ
channel_idr5   rŸ   rv   s   &       r   r�   r�   Ñ   s  € à%'€HÜ‹u€HÜ)¨,¼×=Ò=‰|À2ÓEˆÜ˜#œt×$Ò$ÙÜ˜Ÿ™ ›×,Ð,¨"Ó-×3Ñ3Ó5ˆ
Ü�3—7‘7˜6“?×0Ð0 jÓ1×7Ñ7Ó9ˆß§¨Ô)?ÙØ!% z°6¸4ÀÌÈSÏWÉWÐU[Ë_×MdÐMdÐ`dÓIeÐ fˆØ�‰Ñ4JÓ[Ñ4J¨SÏgÉgÐVYÏlÔ(�cœ3˜s�x›=Ò(Ñ4JÑ[Ô\Ø�‰˜ÔØ�‰�Z× ñ Fð €Oùò \s   Ã8E
ÄE
c                ó0   € V ^8„  d   QhR\         R\         /# )r   Úentry_idr   rK   )r   s   "r   r   r   ã   s   € ÷ %ñ %œSð %¤Sñ %r   c                ó4   € V P                  R^4      ^ ,          # )zQThread-qualified IDs (``C0xxx:ts``) are internal routing keys, not Slack API IDs.r^   )Úsplit)rÄ   s   &r   Ú_slack_base_idrÇ   ã   s   € à�>‰>˜#˜qÓ! !Õ$Ð$r   c                óR   € V ^8„  d   QhR\         \        \        3,          R\        /# )r   rŸ   r   )r(   r)   r   Úbool)r   s   "r   r   r   è   s&   € ÷ Dñ Dœt¤C¬ H�~ð D´$ñ Dr   c                 óL   € V P                  R R4      P                  \        4      # )r5   r¼   )r>   Ú
startswithÚ_SLACK_RAW_ID_PREFIXES)rŸ   s   &r   Ú_slack_has_raw_namerÍ   è   s   € Ø�9‰9�V˜RÓ ×+Ñ+Ô,BÓCÐCr   c          	      ót   € V ^8„  d   QhR\         R\        R\        \        \         \        3,          ,          /# )r   ri   rÀ   r   )r)   r½   r<   r(   r   )r   s   "r   r   r   ì   s1   € ÷ ñ ¬ð ¼sð ÄtÌDÔQTÔVYÐQYÍNÕG[ñ r   c              ƒ  óø  "  € . p Rp\        ^4       EF$  pVP                  RR^ÈVR7      G Rj  x€L
 pVP                  R4      '       g%   VP                  RR4      p\        WRV 24        V# VP                  R	. 4       F}  pVP                  R
4      VP                  R4      r©V	'       d   V
'       d   W’9   d   K<  VP	                  V	4       TP                  R
T	RT
RVP                  R4      '       d   RMR/4       K  	  VP                  R4      ;'       g    / P                  R4      pV'       d   EK$   V# 	   V#  EL  \         d+   p\        T \        T4      \        T4      4        Rp?T# Rp?ii ; i5i)zX``users.conversations`` for one workspace (public + private member channels), paginated.Nzpublic_channel,private_channelT)ÚtypesÚexclude_archivedÚlimitÚcursorÚokrµ   Úunknownzusers.conversations not ok: r­   r4   r5   r7   Ú
is_privateÚprivaterS   Úresponse_metadataÚnext_cursor)	ÚrangeÚusers_conversationsr>   ry   r¿   r?   r-   r¸   r)   )ri   r®   rÀ   r­   rÓ   Ú_pager·   rj   r³   Úcidr5   rF   s   &&&         r   Ú_slack_team_channelsrÞ   ì   sh  é € à%'€HðIØ $ˆÜ˜2—YˆEØ#×7Ñ7Ø6ÈÐUXÐagð 8ó ÷ ˆHð —<‘< ×%Ò%Ø%Ÿ\™\¨'°9Ó=�
Ü% gÐ=YÐZdÐYeÐ;fÔgØð €Oð —l‘l :¨rÖ2�ØŸF™F 4›L¨"¯&©&°«.�Tß§$¨#¬/ÙØ—‘˜SÔ!Ø—‘  s¨F°D¸&ÈrÏvÉvÐVb×OcÒOcÁ)ÐirÐ sÖtñ 3ð —l‘lÐ#6Ó7×=Ð=¸2×BÑBÀ=ÓQˆFß’6Øð €Oò' ð& €Oò%øô  ô IÜ˜gÔ'<¸QÓ'?ÄÀQÃ×HÐHØ€OûðIüsu   ‚E:†)E ¯D?°E Á"E Á-E:Á/AE Â5?E Ã5$E ÄE Ä7E Ä8E:Ä:E Ä<E:Ä?E ÅE7ÅE2Å,E:Å2E7Å7E:c                ó`   € V ^8„  d   QhR\         \        \        \        3,          ,          RR/# )r   r­   r   N©r<   r(   r)   r   )r   s   "r   r   r     s+   € ÷ bñ b´T¼$¼sÄC¸x½.Õ5Ið bÈdñ br   c           
   ƒ  ól  a "  € / pV FG  p\        V4      '       g   K  VP                  \        VR,          4      . 4      P                  V4       KI  	  V'       g   R# R V 3R llp\        P
                  ! VP                  4        UUu. uF  w  rVV! WV4      NK  	  upp!  G Rj  x€L
  R# u uppi  L5i)z�Name remaining raw-ID entries (DMs, channels outside bot scope) via
conversations.info + users.info once per base conversation, concurrently.r4   Nc                ó4   € V ^8„  d   QhR\         R\        RR/# )r   Úbase_idrC   r   N)r)   r<   )r   s   "r   r   Ú._slack_resolve_raw_names.<locals>.__annotate__  s&   € ÷ Tñ T¤Sð T´4ð T¸Dñ Tr   c              “   ó4  <"  €  S
P                  V R 7      G Rj  x€L
 pVP                  R4      '       g   R# VP                  R/ 4      pR;rEVP                  R4      '       g,   VP                  R4      ;'       g    VP                  R4      pM®VP                  RR4      '       d—   S
P                  VR,          R	7      G Rj  x€L
 pVP                  R4      '       d_   VR,          pVP                  R
/ 4      P                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      pRpV'       d   TMR F  pWHR&   V'       g   K  WXR&   K  	  R#  ELJ L£  \         d"   p	\        P                  RY	4        Rp	?	R# Rp	?	ii ; i5i))rS   NrÔ   rS   Úis_imr5   Úname_normalizedÚuserr¼   )rè   ÚprofileÚdisplay_nameÚ	real_namer9   r7   z+Channel directory: failed to resolve %s: %sr   )Úconversations_infor>   Ú
users_infor-   ro   rp   )rã   rC   ÚrespÚch_infoÚresolved_nameÚresolved_typeÚ	user_respÚurŸ   rF   r®   s   &&        €r   Ú_resolve_baseÚ/_slack_resolve_raw_names.<locals>._resolve_base  s\  øé € ð	TØ×2Ñ2¸7Ð2ÓC×CˆDØ—8‘8˜D—>’>ÙØ—h‘h˜y¨"Ó-ˆGØ,0Ð0ˆMØ—;‘;˜w×'Ò'Ø '§¡¨FÓ 3× UÐ U°w·{±{ÐCTÓ7U‘Ø—‘˜V R×(Ò(Ø"(×"3Ñ"3¸À½Ð"3Ó"I×I�	Ø—=‘= ×&Ò&Ø! &Õ)�AØ$%§E¡E¨)°RÓ$8×$<Ñ$<¸^Ó$L×$sÐ$sÐPQ×PUÑPUÐVaÓPb×$sÐ$sÐfg×fkÑfkÐlrÓfs�MØ$(�Mß$1™°rÒ9�Ø -�f‘ß ‘=Ø$1˜&“Mó :ò Dñ Jøô ô 	TÜ�L‰LÐFÈ×SÒSûð	Tüs…   ƒF…E) šE$›E) ¶F¸AE) Á<*E) Â'E) ÃE'ÃE) Ã0E) ÄE) Ä)E) ÅE) ÅE) Å"FÅ$E) Å'E) Å)FÅ4FÆ
FÆFÆF)rÍ   r;   rÇ   r?   r…   Úgatherr:   )r®   r­   Úunresolved_by_baserŸ   rô   ÚbidÚentss   f&     r   Ú_slack_resolve_raw_namesrú     s˜   øé € ð +-ÐÛˆÜ˜u×%Ô%Ø×)Ñ)¬.¸¸t½Ó*EÀrÓJ×QÑQÐRWÖXñ ÷ Ù÷Tð Tô* �.Š.ÐEW×E]ÑE]ÔE_Ô`ÑE_¹	¸™=¨Ö3ÑE_Ò`Ñ
a×aÒaùÓ`Ñaùs(   ƒB4ž<B4Á.B4Â	B,
Â	B4Â%B2Â&B4c                ó\   € V ^8„  d   QhR\         \        \        \        3,          ,          /# r   rà   )r   s   "r   r   r   (  s    € ÷ ñ ¤4¬¬S´#¨X­Õ#7ñ r   c              ƒ  óH  "  € \        V RR4      ;'       g    / pV'       g$   \        P                  ! \        R4      G Rj  x€L
 # . p\	        4       pVP                  4        F(  w  rETP                  \        WEV4      G Rj  x€L
 4       K*  	  V Uu/ uF  qfR,          VR,          bK  	  pp\        P                  ! \        R4      G Rj  x€L
  FŠ  pVP                  R4      p	\        V	\        4      '       d   W“9   d   K2  \        V4      '       d&   \        V	4      V9   d   V\        V	4      ,          VR&   VP                  V4       VP                  V	4       KŒ  	  \        \!        \#        VP%                  4       4      4      V4      G Rj  x€L
  V#  ELS ELu upi  LÓ L5i)z›List Slack channels the bot has joined across all workspaces, merged with
session-history DMs. Missing channels:read falls back to session history quietly.Ú_team_clientsNr   r4   r5   )rŽ   r…   r†   r‡   r½   r:   r¬   rÞ   r>   r,   r)   rÍ   rÇ   r?   r¿   rú   ÚnextÚiterÚvalues)
r›   Úteam_clientsr­   rÀ   ri   r®   r³   Úapi_name_lookuprŸ   Úeids
   &         r   r”   r”   (  sW  é € ô ˜7 O°TÓ:×@Ð@¸b€LßÜ×&Ò&Ô';¸WÓE×EÐEØ%'€HÜ“E€HØ'×-Ñ-Ö/‰ˆØ�‰Ô2°7ÀHÓM×MÖNñ 0ñ 7?Ó?±h°˜$•x  F¥Ò+±h€OÐ?Ü×(Ò(Ô)=¸wÓG×GÒGˆØ�i‰i˜‹oˆÜ˜#œs×#Ò# s¤ÙÜ˜u×%Ò%¬.¸Ó*=ÀÔ*PØ+¬N¸3Ó,?Õ@ˆE�&‰MØ�‰˜ÔØ�‰�SÖñ Hô #¤4¬¨\×-@Ñ-@Ó-BÓ(CÓ#DÀhÓ
O×OÐOØ€Oò# Fò Nùò @ÙGñ PùsY   ‚F"¢F"Á FÁ?F"Â F
ÂF"ÂFÂ* F"Ã
FÃCF"ÆF ÆF"ÆF"ÆF"Æ F"c                óh   € V ^8„  d   QhR\         R\        \        \         \         3,          ,          /# ©r   rR   r   ©r)   r<   r(   )r   s   "r   r   r   A  s+   € ÷ ^ñ ^¬ð ^´´T¼#¼s¸(µ^Õ0Dñ ^r   c                ó>   € \        V 4      ;'       g    \        V 4      # )z¹Known channels/contacts from session origins: state.db first, sessions.json fallback (pre-migration).

state.db is the primary source (#9006): gateway session rows persist origin_json.
)Ú_build_from_sessions_dbÚ_build_from_sessions_json)rR   s   &r   r‡   r‡   A  s   € ô
 # =Ó1×]Ð]Ô5NÈ}Ó5]Ð]r   c          	      ót   € V ^8„  d   QhR\         R\         R\        \        \         \        3,          ,          /# )r   rR   Úsourcer   )r)   r<   r(   r   )r   s   "r   r   r   I  s1   € ÷ ñ ¬ð ´cð Ì$ÌtÔTWÔY\ÐT\Í~ÕJ^ñ r   c                óZ  € . p \        4       pV! 4        Fb  w  rV\        V4      pV'       d   Wt9   d   K   VP                  V4       VP                  RVR\	        V4      RVRVP                  R4      /4       Kd  	  V#   \         d#   p\        P                  RYT4        Rp?T# Rp?ii ; i)z{Deduped entries for the (origin, chat_type) pairs from ``origins_fn()``; a mid-iteration
failure keeps entries read so far.r4   r5   r7   r6   z Channel directory: %s for %s: %sN)	r½   r`   r¿   r?   rg   r>   r-   ro   rp   )	rR   r  Ú
origins_fnrC   rÀ   r\   Ú	chat_typerÄ   rF   s	   &&&      r   Ú_entries_from_originsr  I  s­   € ð %'€GðSÜ“5ˆÙ!+¦ÑˆFÜ(¨Ó0ˆHß˜xÔ3ÙØ�L‰L˜Ô"Ø�N‰NØ�h Ô(;¸FÓ(CØ˜	 ;°·
±
¸;Ó0Gðö ñ ".ð €Nøô ô SÜ�‰Ð7¸ÐPQ×RÐRØ€NûðSús   „A7A= Á=B*ÂB%Â%B*c                óh   € V ^8„  d   QhR\         R\        \        \         \         3,          ,          /# r  r  )r   s   "r   r   r   ]  s+   € ÷ Zñ Z¬3ð Z´4¼¼SÄ#¸X½Õ3Gñ Zr   c                ó0   a € R V 3R llp\        S RV4      # )z:Pull channels/contacts from state.db gateway session rows.c                ó~   € V ^8„  d   QhR\         \        \        \        \        3,          \        3,          ,          /# r   ©r   Útupler(   r)   r   )r   s   "r   r   Ú-_build_from_sessions_db.<locals>.__annotate___  s*   € ÷ 7ñ 7”hœu¤T¬#¬s¨(¥^´SÐ%8Õ9Õ:ñ 7r   c            	   3   óª  <"  € ^ RI Hp Hp V ! 4       p \        VRR4      p\	        V4      '       g    V! V4       R# V! SRR7      pV! V4       V FÜ  pRp\
        P                  ! \        \        4      ;_uu_ 4        VP                  R4      '       d   \        P                  ! VR,          4      MRpRRR4       \        V\        4      '       d	   V'       g6   RVP                  R4      RVP                  R4      R	VP                  R
4      /pYeP                  R4      ;'       g    R3x € KÞ  	  R#   T! T4       i ; i  + '       g   i     L”; i5i)é    )ÚacquireÚrelease_or_closeÚlist_gateway_sessionsNF)rš   Úactive_onlyÚorigin_jsonrD   r6   rc   rê   r  r9   )Úhermes_state_registryr  r  rŽ   r�   r•   r–   Ú	TypeErrorÚ
ValueErrorr>   r#   Úloadsr,   r(   )r  r  ÚdbÚlisterÚrowsÚrowr\   rR   s          €r   Ú_originsÚ)_build_from_sessions_db.<locals>._origins_  s  øé € ßCÙ‹Yˆð	!Ü˜RÐ!8¸$Ó?ˆFÜ˜F×#Ò#Øñ ˜RÖ ñ  =¸eÔDˆDá˜RÔ ÛˆCØˆFÜ×$Ò$¤Y´
×;Õ;Ø;>¿7¹7À=×;QÒ;QœŸš C¨Õ$6Ô7ÐW[�÷ <ä˜f¤d×+Ò+·6Ø# S§W¡W¨YÓ%7¸ÀcÇgÁgÈkÓFZÐ\gÐil×ipÑipÐqó  jAð  B�ØŸ'™' +Ó.×6Ð6°$Ð6Ô6ó øñ ˜RÕ ú÷ <×;üsK   ƒE”D3 ³
E½
D3 Á6EÁ=7E Â4%EÃAEÄ'EÄ3
D=Ä=EÅ EÅEzstate.db session read failed)r  )rR   r%  s   f r   r  r  ]  s    ø€ ÷7ð 7ô" ! Ð0NÐPXÓYÐYr   c                óh   € V ^8„  d   QhR\         R\        \        \         \         3,          ,          /# r  r  )r   s   "r   r   r   s  s+   € ÷ Uñ U¬Sð U´T¼$¼sÄC¸x½.Õ5Iñ Ur   c                ó”   a a€ \        4       R,          R,          oSP                  4       '       g   . # R V V3R llp\        S RV4      # )zGLegacy fallback: pull channels/contacts from sessions.json origin data.Úsessionszsessions.jsonc                ó~   € V ^8„  d   QhR\         \        \        \        \        3,          \        3,          ,          /# r   r  )r   s   "r   r   Ú/_build_from_sessions_json.<locals>.__annotate__x  s*   € ÷ =ñ =”hœu¤T¬#¬s¨(¥^´SÐ%8Õ9Õ:ñ =r   c               3   óT  <"  € \        S4      P                  4        F…  w  r\        V 4      P                  R 4      '       g   \	        V\
        4      '       g   K=  VP                  R4      ;'       g    / pVP                  R4      S8X  g   Kp  W!P                  RR4      3x € K‡  	  R# 5i)Ú_r\   rš   r  r9   N)r&   r:   r)   rË   r,   r(   r>   )Ú_keyÚsessionr\   rR   Úsessions_paths      €€r   r%  Ú+_build_from_sessions_json.<locals>._originsx  s   øé € Ü'¨Ó6×<Ñ<Ö>‰MˆDä�4‹y×#Ñ# C×(Ò(´
¸7ÄD×0IÒ0IÙØ—[‘[ Ó*×0Ð0¨bˆFØ�z‰z˜*Ó%¨Ö6ØŸk™k¨+°tÓ<Ð<Ô<ó ?ùs   ƒA.B(Á2B(ÂB(zfailed to read sessions)r   r+   r  )rR   r%  r0  s   f @r   r	  r	  s  sE   ù€ ä#Ó%¨
Õ2°_ÕD€MØ×Ñ×!Ò!Øˆ	÷=ñ =ô ! Ð0IÈ8ÓTÐTr   c                óF   € V ^8„  d   QhR\         \        \        3,          /# r   r2   )r   s   "r   r   r   …  s   € ÷ ñ œœS¤#˜X�ñ r   c                 óF  € \        4       p V P                  4       '       dV   \        P                  ! \        4      ;_uu_ 4        \        V 4      p\        VP                  R/ 4      4       VuuRRR4       # RRR/ /p\        VR,          4       V#   + '       g   i     L*; i)zELoad the cached directory from disk, with aliases re-applied on read.r1   NrŒ   )r   r+   r•   r–   r-   r&   rH   r;   )Údirectory_pathr.   Úbases      r   Úload_directoryr6  …  s}   € ä$Ó&€NØ×Ñ×ÒÜ× Ò ¤×+Õ+Ü˜nÓ-ˆDä" 4§?¡?°;ÀÓ#CÔDØ÷	 ,Ò+ð
 ˜$ ¨RÐ0€DÜ˜4 Õ,Ô-Ø€K÷ ,×+ús   Á(BÂB 	c                óR   € V ^8„  d   QhR\         R\         R\        \         ,          /# )r   rR   rD   r   rl   )r   s   "r   r   r   “  s*   € ÷ Vñ V¤sð V´Sð V¼XÄc½]ñ Vr   c                ó„   a€ \        4       P                  R/ 4      P                  V . 4      p\        V3R lV 4       R4      # )z\Channel ``type`` string (e.g. ``"channel"``, ``"forum"``) for *chat_id*, or None if unknown.r1   c              3   ót   <"  € T F-  qP                  R 4      S8X  g   K  VP                  R4      x € K/  	  R# 5i)r4   r7   NrX   )Ú.0r³   rD   s   & €r   Ú	<genexpr>Ú&lookup_channel_type.<locals>.<genexpr>–  s*   øé € ÐN©( B·f±f¸T³lÀgÑ6M”�—‘˜—�«(ùs   ƒ8Ÿ8N)r6  r>   rþ   )rR   rD   r­   s   &f r   Úlookup_channel_typer=  “  s9   ø€ äÓ×#Ñ# K°Ó4×8Ñ8¸ÈÓK€HÜÔN©(ÓNÐPTÓUÐUr   c                óR   € V ^8„  d   QhR\         R\         R\        \         ,          /# )r   rR   r5   r   rl   )r   s   "r   r   r   ™  s%   € ÷ ;ñ ;¬ð ;´3ð ;¼8ÄC½=ñ ;r   c           	     ó,  € \        4       P                  R/ 4      P                  V . 4      pV'       g   R# VP                  4       pV F$  pVP                  R4      V8X  g   K  VR,          u # 	  \        V4      pV F9  pV\        VR,          4      \        \	        W4      4      39   g   K0  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W†8X  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 friendly channel name (e.g. "bot-home", "#bot-home", "GuildName/bot-home",
Slack "#engineering") to an ID; case-insensitive, first match wins.r1   Nr4   r5   Ú/rV   r¼   )	r6  r>   r=   rP   rY   ÚrsplitrO   rË   Úlen)
rR   r5   r­   rÁ   r³   ÚqueryÚ
guild_partÚch_partrV   rG   s
   &&        r   Úresolve_channel_namerF  ™  s\  € ô Ó×#Ñ# K°Ó4×8Ñ8¸ÈÓK€HßÙð �*‰*‹,€CÛˆØ�6‰6�$‹<˜3ÖØ�d•8ŠOñ ô % TÓ*€EãˆØÔ-¨b°­jÓ9Ô;SÔThÐivÓT{Ó;|Ð}Ö}Ø�d•8ŠOñ ð ˆe„|Ø#Ÿl™l¨3°Ó2Ñˆ
ÛˆBØ—F‘F˜7 BÓ'×-Ñ-Ó/×5Ñ5Ó7ˆEØÖ"Ô'?ÀÀ6Å
Ó'KÈwÖ'VØ˜$•x’ñ ñ
 %Ó_™H�bÔ(@ÀÀFÅÓ(L×(WÑ(WÐX]×(^�rˆr™H€GÐ_Ü" 7›|¨qÔ0ˆ7�1�:�dÕÐ:°dÐ:ùò `s   Ä9)FÅ'Fc                óh   € V ^8„  d   QhR\         \        \        \        3,          ,          R\        /# )r   r1   r   )r	   r(   r)   r   )r   s   "r   r   r   ¶  s(   € ÷ %ñ %¬H´T¼#¼s¸(µ^Õ,Dð %ÔPSñ %r   c                ó  a€ V f   \        4       P                  R/ 4      p V '       g   R# R.p\        V P                  4       4       EF  w  opV'       g=   VP	                  SP                  4        R24       VP	                  RS RS R24       EM#SR8X  dà   / p. pV FD  pVP                  R	4      '       d   VP                  VR	,          . 4      MTP	                  V4       KF  	  \        VP                  4       4       UUu. uF  w  rgR
V R2\        VR R7      3NK  	  pppV'       d   VP	                  RV34       V F1  w  ršVP	                  V	4       VP                  V3R lV
 4       4       K3  	  M=VP	                  SP                  4        R24       VP                  V3R lV 4       4       VP	                  R4       EK‚  	  VP	                  R4       VP	                  R4       RP                  V4      # u uppi )a5  Format the channel directory as a human-readable list for the model.

``platforms`` overrides the on-disk directory (``hermes send --list`` merges in
configured-but-undiscovered platforms); an empty channel list renders a "(no channels
discovered yet)" hint because the platform is still a valid send target.
r1   z?No messaging platforms connected or no channels discovered yet.zAvailable messaging targets:
r^   u5     (no channels discovered yet â€” send directly with z:<chat_id>, or bare 'z' for the home channel)rU   rV   z	Discord (z):c                 ó   € V R ,          # )r5   r   )Úcs   &r   Ú<lambda>Ú.format_directory_for_display.<locals>.<lambda>Ï  s   € ÀAÀfÆIr   )rv   zDiscord (DMs):c              3   óB   <"  € T F  pR \        SV4       2x € K  	  R# 5i)z
  discord:N©rY   ©r:  r³   rA   s   & €r   r;  Ú/format_directory_for_display.<locals>.<genexpr>Ô  s$   øé € Ð`ÑZ_ÐTV˜zÔ*>¸yÈ"Ó*MÐ)NÕOÓZ_ùs   ƒc              3   óH   <"  € T F  pR S R\        SV4       2x € K  	  R# 5i)z  r^   NrN  rO  s   & €r   r;  rP  ×  s*   øé € ÐcÑZbÐTV˜2˜i˜[¨Ô*>¸yÈ"Ó*MÐ)NÕOÓZbùs   ƒ"r¼   z1Use these as the "target" parameter when sending.z;Bare platform name (e.g. "telegram") sends to home channel.Ú
)	r6  r>   Úsortedr:   r?   Útitler;   r¬   Újoin)r1   Úlinesr­   rª   Údmsr³   Úgr±   ÚgroupsÚheaderr8   rA   s   &          @r   Úformat_directory_for_displayr[  ¶  sÃ  ø€ ð ÒÜ"Ó$×(Ñ(¨°bÓ9ˆ	ßÙPØ-Ð.€EÜ% i§o¡oÓ&7×8Ñˆ	�8ßØ�L‰L˜IŸO™OÓ-Ð.¨aÐ0Ô1Ø�L‰LØGØ�+Ð2°9°+Ð=TðVöð ˜)Ô#à&(ˆFØˆCÛ�Ø79·v±v¸g·²�×"Ñ" 2 g¥;°Ô3ÈC×WÑWÐXZÖ[ñ ä^dÐek×eqÑeqÓesÔ^tÔuÑ^tÑTZÐTU˜ 1 # RÐ(¬&°Ñ:MÔ*NÓOÑ^tˆFÑußØ—‘Ð/°Ð5Ô6Û!'‘�Ø—‘˜VÔ$Ø—‘Ô`ÑZ_Ó`Ö`ò "(ð �L‰L˜IŸO™OÓ-Ð.¨aÐ0Ô1Ø�L‰LÔcÑZbÓcÔcØ�‰�R×ñ-  9ð. 
‡L�LÐDÔEØ	‡L�LÐNÔOØ�9‰9�UÓÐùó vs   ÄG=c                ó   € V ^8„  d   Qh/ ^ \         9   d   \        \        ,          ;R&   ^\         9   d   \        \        ,          ;R&   ^\         9   d,   \        \        \
        \
        3,          \        3,          ;R&   # )r   r   r   rs   )Ú__conditional_annotations__r	   r   r(   r  r)   Úfloat)r   s   "r   r   r      s_   € ÷ ñ ÷. &Ò %”œ•Ñ %ñ/÷6 ,Ò +”hœt•nÑ +ñ7÷@ AÒ @œt¤E¬#¬s¨(¥O´UÐ$:Õ;Ñ @òAr   >   ÚlocalÚwebhookÚ
api_server)ÚC0ÚD0ÚG0rƒ   );r]  Ú__doc__r…   r•   r#   Úloggingrq   r   Úpathlibr   Útypingr   r   r   r   r	   r
   Úhermes_cli.configr   Úutilsr   Ú	getLoggerÚ__name__ro   r   r   rt   rs   Ú	frozensetr„   rÌ   r   r   r&   r/   rH   rP   rY   r`   rg   ry   r¡   r’   r¸   r�   rÇ   rÍ   rÞ   rú   r”   r‡   r  r  r	  r6  r=  rF  r[  r   )r]  s   @r   Ú<module>rn     s  øðöó Û Û Û Û Ý Ý ß =× =å -Ý #à	×	Ò	˜8Ó	$€ð
 "&€Ó %ð (,Ð Ó +ð -1Ð )Ø>@Ð Ó @ñ $Ò$FÓGÐ Ø+Ð õJõNõõõYõ0-õJõ`õOõ{õ"(õVõ4õõ$%õ
Dõõ6bõBõ2^õõ(Zõ,Uõ$õVõ;÷:%ñ %r   