+
    jHàjª¥  ã                  óò  € R t ^ RIH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 ^ RIHt ^ RIHt ^ RIHtHt ]! 0 R)m4      t]! R	R
04      t]! 0 R*m4      t]P4                  ! 4       t]P8                  ! ]4      t]P>                  R/t ^ RI!H"t# ^ RI!H$t% ^ RI!H&t' R R lt(R t)R+R R llt*R t+R R lt,R R lt-R R lt.Rt/R,t0R t1R R lt2R-R R  llt3R! R" lt4R# R$ lt5R% R& lt6 ! R' R(4      t7R# ).aO  User-authorization mixin for ``GatewayRunner``: may this user/chat talk to the agent,
the per-adapter DM policy, the unauthorized-DM behavior, and the bot loop guard.

``gateway.run`` is never imported at module import time (cycle). The unauthorized-DM method still
logs through ``gateway.run``'s logger so its records keep that name.
)ÚannotationsN)ÚPath)ÚOptional)ÚBotLoopGuard)ÚPlatform)ÚSessionSource)Úexpand_whatsapp_aliasesÚnormalize_whatsapp_identifierÚgroupÚforumÚTELEGRAM_GROUP_ALLOWED_USERS)Údecode_json_list_literal)Úextra_or_secret)Úplatform_gate_envc               ó    € V ^8„  d   QhRRRR/# ©é   ÚnameÚstrÚreturnÚbool© )Úformats   "Ú8/home/ubuntu/.hermes/hermes-agent/gateway/authz_mixin.pyÚ__annotate__r   ,   s   € ÷ .ñ .�cð .˜dñ .ó    c                óB   € \        V 4      P                  4       \        9   # ©N)Ú	_auth_envÚlowerÚ_TRUTHY)r   s   &r   Ú_env_truthyr!   ,   s   € Ü�T‹?× Ñ Ó"¤gÑ-Ð-r   c                ó    € ^ RI Hp V! WV4      # )z`Late-bound :func:`gateway.platform_env.authz_field` (entry field, else the bundled declaration).)Úauthz_field)Úgateway.platform_envr#   )r   ÚfieldÚentryÚ_fields   &&& r   r#   r#   0   s   € å:Ù�$˜uÓ%Ð%r   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   6   s   € ÷ lñ l¨Sñ lr   c                óv   € V f   R# \        V P                  RT;'       g    \        V 4      4      ;'       g    R# )z`The platform's group-chat allowlist env name (``PlatformEntry.group_allowed_chats_env``), or "".Ú Úgroup_allowed_chats_env)r#   ÚvalueÚ_registry_entry)Úplatformr&   s   &&r   Ú_group_chat_envr/   6   s6   € àÒÙÜ�x—~‘~Ð'@À%×BdÐBdÌ?Ð[cÓKdÓe×kÐkÐikÐkr   c                óh  € V f   R# \         P                  ! \        4      ;_uu_ 4        ^ RIHp V! 4        RRR4       \         P                  ! \        4      ;_uu_ 4        ^ RIHp VP                  V P                  4      uuRRR4       #   + '       g   i     L^; i  + '       g   i     R# ; i)z»Platform-registry entry for a (plugin) platform, or None. Discovers first (idempotent): the
allowlist env names live only on the entry, so an undiscovered process must not read "no list".N©Údiscover_plugins)Úplatform_registry)	Ú
contextlibÚsuppressÚ	ExceptionÚhermes_cli.pluginsr2   Úgateway.platform_registryr3   Úgetr,   )r.   r2   r3   s   &  r   r-   r-   =   ss   € ð ÒÙÜ	×	Ò	œY×	'Õ	'Ý7ÙÔ÷ 
(ô 
×	Ò	œY×	'Õ	'Ý?à ×$Ñ$ X§^¡^Ó4÷ 
(Ò	'÷ 
(×	'ú÷ 
(Ö	'ñ ús   ©BÁ!!B ÂB	Â B1	c               ó   € V ^8„  d   QhRR/# ©r   r   úset[str]r   )r   s   "r   r   r   L   s   € ÷ Jñ J˜hñ Jr   c                ó¦  € V f   \        4       # \        V 4      p \        V \        4      '       dK   V  Uu0 uF=  p\	        V4      P                  4       '       g   K$  \	        V4      P                  4       kK?  	  up# \	        V 4      P                  R4       Uu0 uF*  qP                  4       '       g   K  VP                  4       kK,  	  up# u upi u upi )zjParse an allowlist (YAML list, JSON list literal string, or comma-separated scalar) into a set of strings.Ú,)ÚsetÚ_decode_json_list_literalÚ
isinstanceÚlistr   ÚstripÚsplit)ÚrawÚparts   & r   Ú_coerce_allow_setrG   L   s�   € à
‚{Ü‹uˆÜ
# CÓ
(€CÜ�#”t×ÒÙ.1ÓG©c d´S¸³Y·_±_×5FÔ!”�D“	—‘Ö!©cÑGÐGÜ%(¨£X§^¡^°CÔ%8ÓIÑ%8˜T¿J¹J¿LŒLˆD�J‰JŽLÑ%8ÑIÐIùò HùÚIs   ´!C	ÁC	ÂCÂ2Cc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úallowedr<   Ú	candidateúOptional[str]r   r   r   )r   s   "r   r   r   V   s!   € ÷ 2ñ 2�Xð 2¨-ð 2¸Dñ 2r   c                ó$   € R V 9   ;'       g    W9   # )Ú*r   )rI   rJ   s   &&r   Ú_allowsrN   V   s   € Ø�'‰>×1Ð1˜YÑ1Ð1r   c               ó   € V ^8„  d   QhRR/# ©r   r   Údictr   )r   s   "r   r   r   Z   s   € ÷ Jñ J dñ Jr   c                óF   € \        \        V R R4      RR4      ;'       g    / # )ÚconfigNÚextra©Úgetattr)Úadapters   &r   Ú_adapter_config_extrarX   Z   s"   € Ü”7˜7 H¨dÓ3°W¸dÓC×IÐIÀrÐIr   Ú qpzry9x8gf2tvdw0s3jn54khce6mua7lc                óÌ   € ^pV  F[  pV^,	          pVR,          ^,          V,          p\        \        4       F%  w  rEYV,	          ^,          '       d   TM^ ,          pK'  	  K]  	  V# )é   iÿÿÿ)Ú	enumerateÚ_BECH32_GENERATOR)ÚvaluesÚchkr,   ÚtopÚiÚgens   &     r   Ú_bech32_polymodrc   g   s\   € Ø
€CÛˆØ�R�iˆØ�Y� 1Õ$ uÕ,ˆÜÔ 1Ö2‰FˆAØ 1�H¨Ÿ>œ>‘3°Õ1ŠCó 3ñ ð
 €Jr   c               ó   € V ^8„  d   QhRR/# )r   Úhrpr   r   )r   s   "r   r   r   q   s   € ÷ Iñ I˜Cñ Ir   c                ó¸   € V  Uu. uF  p\        V4      ^,	          NK  	  up^ .,           V  Uu. uF  p\        V4      ^,          NK  	  up,           # u upi u upi )é   )Úord)re   Úcs   & r   Ú_bech32_hrp_expandrj   q   sL   € Ù!$Ó%¡˜AŒC�‹F�a�KˆK¡Ñ%¨¨Õ+ÁCÓ.HÁC¸q¬s°1«v¸¯{¨{ÁCÑ.HÕHÐHùÒ%ùÒ.Hs
   …A®Ac               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚfrombitsÚintÚtobitsÚpadr   r   )r   s   "r   r   r   u   s!   € ÷ ñ  ð ¨cð ¸ñ r   c                óÄ  € ^ p^ p. p^V,          ^,
          pV  F`  pV^ 8  g   W�,	          '       d    R# WA,          V,          pWQ,          pWR8¼  g   K:  WR,          pVP                  WE,	          V,          4       K0  	  V'       d0   V'       d(   VP                  WBV,
          ,          V,          4       V# V'       g%   WQ8¼  g   WBV,
          ,          V,          '       d   R# V# )é    N)Úappend)	Údatarl   rn   ro   ÚaccÚbitsÚretÚmaxvr,   s	   &&&&     r   Ú_convertbitsrx   u   s·   € Ø
€CØ€DØ
€CØ��K˜1Õ€DÛˆØ�1Œ9˜×*Ô*ÚØ� %Õ'ˆØÕˆØŽnØ�NˆDØ�J‰J˜� tÕ+Ö,ñ ÷ �tØ�
‰
�C T�MÕ*¨dÕ2Ô3ð €J÷ �dÔ&¨C¸TµMÕ,BÀd×+JÔ+JÙØ€Jr   c               ó    € V ^8„  d   QhRRRR/# )r   Únpubr   r   rK   r   )r   s   "r   r   r   ‰   s   € ÷  ñ  �sð  ˜}ñ  r   c                óÆ  € V P                  4       P                  4       p V P                  R4      '       g   R#  V \        R4      R  Uu. uF  p\        P                  V4      NK  	  pp\        \        R4      T,           4      ^8w  d   R# \        TRR ^^RR7      pTe   \        T4      ^ 8w  d   R# \        T4      P                  4       # u upi   \         d     R# i ; i)uH   Decode an ``npub1â€¦`` bech32 string to a 64-char hex pubkey, else None.Únpub1Nrz   F)ro   iúÿÿÿ)rC   r   Ú
startswithÚlenÚ_BECH32_CHARSETÚindexÚ
ValueErrorrc   rj   rx   ÚbytesÚhex)rz   ri   rs   Údecodeds   &   r   Ú_npub_to_hexr…   ‰   sÊ   € à�:‰:‹<×ÑÓ€DØ�?‰?˜7×#Ò#ÙðØ26´s¸7³|°}Ñ2EÓFÑ2E¨Q”×%Ñ% aÖ(Ñ2EˆÐFô Ô)¨&Ó1°DÕ8Ó9¸QÔ>ÙÜ˜4  ˜9 a¨°Ô6€GØ‚œ#˜g›,¨"Ô,ÙÜ�‹>×ÑÓÐùò GøÜô Úðús#   ¹C Á	CÁ'C ÃC ÃC ÃC c               ó    € V ^8„  d   QhRRRR/# )r   Úentriesr?   r   r   )r   s   "r   r   r   š   s   € ÷ kñ k¨Cð k°Cñ kr   c                óÆ   € \        V 4      V  Uu0 uF@  qP                  4       P                  R4      '       g   K)  \        V4      ;p'       g   K>  VkKB  	  up,          # u upi )uH  Add the hex form of every valid ``npub1â€¦`` entry; invalid entries are kept as-is.

Hex entries pass through unchanged; each valid ``npub1â€¦`` entry is decoded and its 64-char hex form
added, so either form authorizes the same identity (#78428). Invalid entries are kept as-is (they simply
never match an inbound hex pubkey).
r|   )r?   r   r}   r…   )r‡   ÚeÚhs   &  r   Ú_normalize_nostr_allow_entriesr‹   š   sL   € ô ˆw‹<¡gÓj¡g ·±³×1EÑ1EÀg×1Nœ1ÔYeÐfgÓYhÐThÐTUÓThŸ1˜1¡gÑjÕjÐjùÒjs   �&AºAÁAc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úuser_idr   Úallowed_idsr?   r   r   r   )r   s   "r   r   r   ¤   s!   € ÷ )ñ )°#ð )ÀCð )ÈDñ )r   c                ó@  € V0pV P                   \        P                  \        P                  09   dh   \	        4       P
                  ! R V 4       !  ;'       g    TpVP                  \        V4      4       \        V4      pV'       d   VP                  V4       V P                   e   V P                   P                  MRpVR8X  dI   \        V4      pVP                  R4      '       d   \        V4      MRpV'       d   VP                  V4       \        W2,          4      # )zJWhether *user_id* (under any platform-specific alias) is in *allowed_ids*.c              3  ó8   "  € T F  p\        V4      x € K  	  R # 5ir   )Ú_expand_whatsapp_auth_aliases)Ú.0Úas   & r   Ú	<genexpr>Ú/_principal_matches_allowlist.<locals>.<genexpr>ª   s   é € Ð#ZÉkÈÔ$AÀ!×$DÐ$DËkùs   ‚NÚbuzzrz   )r.   r   ÚWHATSAPPÚWHATSAPP_CLOUDr?   ÚunionÚupdater‘   Ú_normalize_whatsapp_identifierÚaddr,   r‹   r}   r…   r   )Úsourcer�   rŽ   Ú	check_idsÚnormalized_user_idÚplatform_valueÚhex_users   &&&    r   Ú_principal_matches_allowlistr¢   ¤   sß   € à�	€Ið ‡�œ8×,Ñ,¬h×.EÑ.EÐFÔFÜ“e—k’kÑ#ZÉkÓ#ZÑ[×jÐjÐ_jˆØ×ÑÔ6°wÓ?Ô@Ü;¸GÓDÐßØ�M‰MÐ,Ô-à.4¯o©oÒ.I�V—_‘_×*Ò*Èt€Nð
 ˜Ôô
 5°[ÓAˆØ,3×,>Ñ,>¸v×,FÒ,F”< Ô(ÈDˆßØ�M‰M˜(Ô#Ü�	Õ'Ó(Ð(r   c                  ó˜  € ] tR t^ÁtRtR R ltR R ltRBR R lltR	 R
 ltR R lt	R R lt
R R l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/R# R$ lltR% R& ltR' R( ltR) R* ltR+ R, ltR- R. ltR/ R0 ltR1 R2 ltR3 R4 ltR5 R6 ltR7 R8 ltR9R:/R; R< llt R= R> lt!R"R/R? R@ llt"RAt#R# )CÚGatewayAuthorizationMixinz6User/chat authorization methods for ``GatewayRunner``.c               ó   € V ^8„  d   QhRR/# rP   r   )r   s   "r   r   Ú&GatewayAuthorizationMixin.__annotate__Ç   s   € ÷ 5ñ 5 4ñ 5r   c                	ó0   € \        V R R4      ;'       g    / # )ÚadaptersNrU   ©Úselfs   &r   Ú_primary_adaptersÚ+GatewayAuthorizationMixin._primary_adaptersÇ   s   € Ü�t˜Z¨Ó.×4Ð4°"Ð4r   c               ó   € V ^8„  d   QhRR/# rP   r   )r   s   "r   r   r¦   Ê   s   € ÷ >ñ > tñ >r   c                	ó0   € \        V R R4      ;'       g    / # )Ú_profile_adaptersNrU   r©   s   &r   Ú_profile_adapters_mapÚ/GatewayAuthorizationMixin._profile_adapters_mapÊ   s   € Ü�tÐ0°$Ó7×=Ð=¸2Ð=r   Nc               ó    € V ^8„  d   QhRRRR/# )r   r.   úOptional[Platform]ÚprofilerK   r   )r   s   "r   r   r¦   Í   s   € ÷ Añ AÐ/Að AÈMñ Ar   c                óV   € V'       g   R# V P                  V4      P                  V4      # )z Live adapter whose intake policy gates authorization (``_adapters_for_profile`` for the
profile rule). ``None`` when the profile has no adapter for *platform*.
N)Ú_adapters_for_profiler9   )rª   r.   r´   s   &&&r   Ú_authorization_adapterÚ0GatewayAuthorizationMixin._authorization_adapterÍ   s&   € ÷ ÙØ×)Ñ)¨'Ó2×6Ñ6°xÓ@Ð@r   c               ó    € V ^8„  d   QhRRRR/# )r   r´   rK   r   rQ   r   )r   s   "r   r   r¦   Õ   s   € ÷ Sñ S¨]ð S¸tñ Sr   c                ó  € T;'       g    RP                  4       ;'       g    RpV'       d   VR8X  d   V P                  4       # V P                  4       pW#9   d:   W2,          pV'       g   V P                  V4      '       g   V# V P                  4       # \	        V RR4      pV'       g<   \
        P                  ! \        4      ;_uu_ 4        V P                  4       pRRR4       W%8X  d   V P                  4       # / #   + '       g   i     L(; i)uP  The live adapter map *profile* may deliver through: ``_profile_adapters[p]`` for a
secondary, ``self.adapters`` only for the primary/default. ``_profile_adapters`` is consulted
BEFORE the active profile name: multiplex turns override ``HERMES_HOME`` so
``_active_profile_name()`` reports the secondary profile mid-turn, and treating it as primary
would hand it the default bot. A named profile with no map gets ``{}`` â€” fail closed: a
secondary whose adapter failed to connect must NOT fall back to the default profile's adapter
(replies, tool sends, marker-file notices out the wrong bot).r*   NÚdefaultÚ_primary_profile_name)	rC   r«   r°   Ú_is_shared_bot_satelliterV   r4   r5   r6   Ú_active_profile_name)rª   r´   Úprofile_nameÚprofile_adaptersr¨   Úprimary_profiles   &&    r   r¶   Ú/GatewayAuthorizationMixin._adapters_for_profileÕ   sÖ   € ð  Ÿ˜ 2×,Ñ,Ó.×6Ð6°$ˆß˜|¨yÔ8Ø×)Ñ)Ó+Ð+Ø×5Ñ5Ó7ÐØÔ+Ø'Õ5ˆHß˜t×<Ñ<¸\×JÒJØ�Ø×)Ñ)Ó+Ð+ä! $Ð(?ÀÓFˆßÜ×$Ò$¤Y×/Õ/Ø"&×";Ñ";Ó"=�÷ 0à+7Ô+Jˆt×%Ñ%Ó'ÐRÐPRÐR÷ 0×/ús   Ã
C;Ã;D	c               ó    € V ^8„  d   QhRRRR/# )r   r¿   r   r   r   r   )r   s   "r   r   r¦   í   s   € ÷ ñ °Sð ¸Tñ r   c                óÒ  a€ \        V RR4      p\        VRR4      '       g   R# \        V RR4      ;'       g    / P                  S4      '       d   R# \        VRR4      ;'       g    . p\        ;QJ d    V3R lV 4       F  '       g   K   RM	  RM! V3R lV 4       4      '       g   R# ^ R	IHp  SV! V4       UUu0 uF  w  rVVkK	  	  upp9   # u uppi   \
         d     R# i ; i)
a6  A served profile with NO adapter of its own that a ``profile_routes`` entry targets through the
default profile's bot: it drains through the primary's adapters (gateway/AGENTS.md). Its
``_profile_adapters`` entry is the ``{}`` startup placeholder; a secondary connected on ANY
platform is its own credential boundary and never borrows the primary. Restored/cached sources
carry no transport ref, so this is what keeps heartbeats, completions and goal notices for such a
profile deliverable after a restart (the same rule ``kanban_watchers_notifier`` and cron apply).rS   NÚmultiplex_profilesFÚ_profile_failed_platformsÚprofile_routesc              3  ó”   <"  € T F=  qP                   ;'       d&    VP                  S8H  ;'       d    VP                  R J x € K?  	  R # 5ir   )Úenabledr´   Úbot_profile)r’   Úrr¿   s   & €r   r”   ÚEGatewayAuthorizationMixin._is_shared_bot_satellite.<locals>.<genexpr>û   s<   øé € ÐeÑ^dÐYZ—9‘9×TÐT §¡¨lÑ!:×TÐT¸q¿}¹}ÐPTÐ?TÔTÓ^dùs   ƒAšA±AT)Ú_multiplex_profile_homes)rV   r9   ÚanyÚgateway.runrÍ   r6   )rª   r¿   rS   ÚroutesrÍ   r   Ú_homes   &f     r   r½   Ú2GatewayAuthorizationMixin._is_shared_bot_satelliteí   sË   ø€ ô ˜˜x¨Ó.ˆÜ�vÐ3°U×;Ò;Ùä�DÐ5°tÓ<×BÐBÀ×GÑGÈ×UÒUÙÜ˜Ð!1°4Ó8×>Ð>¸Bˆß‹sÔeÑ^dÓe�s�sŠsÔeÑ^dÓe×eÒeÙÝ8ð	ØÑ<TÐU[Ô<\Ô#]Ñ<\©[¨T£DÑ<\Ò#]Ñ]Ð]ùÓ#]øÜô 	Úð	ús$   Â2C Â>CÃC ÃC ÃC&Ã%C&c               ó   € V ^8„  d   QhRR/# ©r   r�   úOptional[SessionSource]r   )r   s   "r   r   r¦     s   € ÷ "ñ "Ð*Añ "r   c                óp  € Vf   R# V P                  V4      pVe
   V^ ,          # \        VRR4      RJ d.   V P                  4       P                  \        P
                  4      # \        VRR4      p^ RIHp V! V4      pVen   VP                  '       d\   V\        P                  8X  d   VP                  '       g5   V'       d+   V P                  VP                  4      P                  V4      # R# \        \        V RR4      RR4      '       g*   V'       d    V P                  4       P                  V4      # R# R# )	u÷  The adapter that RECEIVED *source*'s event â€” the only one whose intake policy (ignored
channels, relay fronting, re-dispatch of a still-live event) may act on it.

Live provenance only: the registered transport that built the source, the process-level
RelayAdapter for relay-delivered events, or the receiving bot named by a pinned
``RoutingIdentity`` (``transport_profile``) when its adapter reconnected. A source with none
of these (restored row, hand-built) fails closed under multiplexing â€” nothing can say which
bot admitted it, and guessing the runtime profile's bot is exactly the shared-bot /
route-override confusion the identity exists to end. A standalone gateway owns one adapter
per platform, so that adapter is the receiver by construction.
NÚdelivered_via_upstream_relayFTr.   ©Úidentity_ofrS   rÅ   )Ú_transport_ownerrV   r«   r9   r   ÚRELAYÚgateway.session_identityrÙ   ÚmultiplexedÚLOCALÚtransport_inferredr¶   Útransport_profile)rª   r�   Úownerr.   rÙ   Úidentitys   &&    r   Ú_intake_adapter_forÚ-GatewayAuthorizationMixin._intake_adapter_for  s	  € ð Š>ÙØ×%Ñ% fÓ-ˆØÒØ˜•8ˆOô �6Ð9¸5ÓAÀTÓIØ×)Ñ)Ó+×/Ñ/´·±Ó?Ð?Ü˜6 :¨tÓ4ˆÝ8Ù˜vÓ&ˆð
 Ò  X×%9×%9Ð%9Ø!¤X§^¡^Ô3¸×8S×8SÐ8Sß[c�4×-Ñ-¨h×.HÑ.HÓI×MÑMÈhÓWÐmÐimÐmÜ”w˜t X¨tÓ4Ð6JÈE×RÒRß=E�4×)Ñ)Ó+×/Ñ/°Ó9ÐOÈ4ÐOÙr   c               ó   € V ^8„  d   QhRR/# rÔ   r   )r   s   "r   r   r¦   '  s   € ÷ pñ pÐ,Cñ pr   c                óü   € Vf   R# V P                  V4      pVe   V# ^ RIHp V! V4      pVe'   VP                  '       d   VP                  '       g   R# V P                  \        VRR4      \        VRR4      4      # )uÖ  The adapter that ANSWERS *source*: sends, edits, typing, progress, pickers, pending slots.

The receiving bot whenever it is known (:meth:`_intake_adapter_for` â€” a routed event replies
through the bot that received it, whether the runtime is a shared-bot satellite or a profile
with a bot of its own). With no live provenance the unique owner of ``(platform,
runtime_profile)`` delivers â€” ``_adapters_for_profile`` holds one adapter per platform per
profile, a shared-bot satellite drains through the primary, and a disconnected secondary
fails closed to ``{}`` rather than borrowing the default bot. Restored sessions, cron,
kanban and completion notices all rely on this row. Matrix: gateway/AGENTS.md Â§ Profile scope.
NrØ   r.   r´   )rã   rÜ   rÙ   rÝ   rß   r·   rV   )rª   r�   rW   rÙ   râ   s   &&   r   Ú_delivery_adapter_forÚ/GatewayAuthorizationMixin._delivery_adapter_for'  s   € ð Š>ÙØ×*Ñ*¨6Ó2ˆØÒØˆNõ 	9Ù˜vÓ&ˆØÒ H×$8×$8Ð$8À×A\×A\ÐA\Ùð ×*Ñ*¬7°6¸:ÀtÓ+LÌgÐV\Ð^gÐimÓNnÓoÐor   c                óÐ   € WP                  4       P                  V4      J d   R# V P                  4       P                  4        F  w  r4WP                  V4      J g   K  RV3u # 	  R# )zMReturn (registered, profile) for a live adapter: profile is None for primary.T)TN)FN)r«   r9   r°   Úitems)rª   rW   r.   r´   rÀ   s   &&&  r   Ú_owning_profileÚ)GatewayAuthorizationMixin._owning_profileE  se   € à×,Ñ,Ó.×2Ñ2°8Ó<Ó<ØÐØ)-×)CÑ)CÓ)E×)KÑ)KÖ)MÑ%ˆGØ×.Ñ.¨xÓ8Õ8Ø˜W�}Ò$ñ *Nð Ðr   c               ó   € V ^8„  d   QhRR/# ©r   r�   r   r   )r   s   "r   r   r¦   N  s   € ÷ :ñ : }ñ :r   c                ó¾   € \        VRR4      p\        V4      '       d   V! 4       MRp\        VRR4      pVe   Vf   R# V P                  W44      w  rVV'       d   W63# R# )ax  ``(adapter, profile)`` of the registered adapter that created *source*, if retained; else None.

``source.profile`` may differ from the adapter profile when one shared credential serves
several routed runtimes; ``build_source`` keeps the receiving adapter as provenance so replies
stay on that transport. Restored/hand-built sources fall back (fail-closed) to profile lookup.
Ú_transport_adapter_refNr.   )rV   Úcallablerë   )rª   r�   Úadapter_refrW   r.   Ú
registeredr´   s   &&     r   rÚ   Ú*GatewayAuthorizationMixin._transport_ownerN  sd   € ô ˜fÐ&>ÀÓEˆÜ#+¨K×#8Ò#8‘+”-¸dˆÜ˜6 :¨tÓ4ˆØŠ?˜hÒ.ÙØ"×2Ñ2°7ÓEÑˆ
ß%/�Ð!Ð9°TÐ9r   c               ó   € V ^8„  d   QhRR/# rî   r   )r   s   "r   r   r¦   ]  s   € ÷ (ñ (°]ñ (r   c                óˆ  € ^ RI Hp V! V4      pVe!   VP                  '       d   VP                  # R# \	        VRR4      pVe   \        V4      # \	        \	        V RR4      RR4      '       g   R# V P                  V4      pVf   R# V P                  V\	        VRR4      4      w  rgVf   ^ RIH	p V! 4       # ^ R	I
Hp	 V	! V4      # )
u5  HERMES_HOME whose allowlist admits *source*: the identity's transport home (or the
ingress-stamped one), else the home of the profile owning the adapter that delivers it.
``None`` = authorize in the ambient scope (multiplex off, or no live adapter â€” the check then
fails closed on its own).

Inside a routed satellite's turn the ambient scope is the satellite's, whose ``.env`` has no
token/allowlist; every authorization decision made mid-turn (``/topic``, sibling ``/stop``, plugin
injection, voice, auto-resume) must read the admitting bot's allowlist instead.rØ   NÚ_authorization_profile_homerS   rÅ   Fr.   )Úget_process_hermes_home)Úget_profile_dir)rÜ   rÙ   rÝ   Úauthorization_homerV   r   rç   rë   Úhermes_constantsrø   Úhermes_cli.profilesrù   )
rª   r�   rÙ   râ   ÚstampedrW   Ú_registeredr´   rø   rù   s
   &&        r   Ú_authorization_home_for_sourceÚ8GatewayAuthorizationMixin._authorization_home_for_source]  sÆ   € õ 	9Ù˜vÓ&ˆØÒØ2:×2F×2FÐ2F�8×.Ñ.ÐPÈDÐPÜ˜&Ð"?ÀÓFˆØÒÜ˜“=Ð Ü”w˜t X¨tÓ4Ð6JÈE×RÒRÙð ×,Ñ,¨VÓ4ˆØŠ?ÙØ#×3Ñ3°G¼WÀVÈZÐY]Ó=^Ó_ÑˆØŠ?Ý@Ù*Ó,Ð,Ý7Ù˜wÓ'Ð'r   c               ó    € V ^8„  d   QhRRRR/# )r   r�   r   r   rK   r   )r   s   "r   r   r¦   {  s   € ÷ 	0ñ 	0°-ð 	0ÀMñ 	0r   c                óÞ   € V P                  V4      pVe
   V^,          # ^ RIHp V! V4      pVe2   VP                  '       d    VP                  R8X  d   R# VP                  # \        VRR4      # )z@Resolve the transport-owning profile for adapter policy lookups.NrØ   r»   r´   )rÚ   rÜ   rÙ   rÝ   rà   rV   )rª   r�   rá   rÙ   râ   s   &&   r   Ú_adapter_profile_for_sourceÚ5GatewayAuthorizationMixin._adapter_profile_for_source{  sl   € à×%Ñ% fÓ-ˆØÒØ˜•8ˆOÝ8Ù˜vÓ&ˆØÒ H×$8×$8Ð$8Ø#×5Ñ5¸ÔB�4ÐbÈ×HbÑHbÐbÜ�v˜y¨$Ó/Ð/r   c               ó   € V ^8„  d   QhRR/# )r   r   rÕ   r   )r   s   "r   r   r¦   †  s   € ÷ ñ Ð)@ñ r   c           	     ób   € \        VRR4      pVf   R# ^ RIHp V! W \        VRR4      R7       V# )u¹  ``entry.origin`` with its identity re-pinned from the routing entry's persisted
``transport_profile`` (no live adapter: the restored row of the transport matrix). Every path
that revives a session from durable state â€” auto-resume, heartbeat restore, plugin injection,
background-process events â€” reads the origin through here, so the receiving bot decides
delivery and authorization after a restart, not the runtime profile's heuristics.ÚoriginN)Úrestore_identityrà   )Úrunnerrà   )rV   rÜ   r  )rª   r&   r�   r  s   &&  r   Ú_restored_sourceÚ*GatewayAuthorizationMixin._restored_source†  s7   € ô ˜ ¨$Ó/ˆØŠ>ÙÝ=Ù˜ÄÈÐObÐdhÓ@iÕjØˆr   c               ó    € V ^8„  d   QhRRRR/# r   r   )r   s   "r   r   r¦   “  s   € ÷ Kñ K¨Cð K¸Tñ Kr   c                óz   € V'       g   R# V P                  W4      pVRJ;'       d    \        \        WBR4      4      # )u€  Adapter-declared boolean, False when unknown. ``authorization_is_upstream`` (relay: a trusted
authenticated upstream decides) is honored directly; ``enforces_own_access_policy`` (WeCom, Weixin,
Yuanbao, QQBot, WhatsApp gate at intake) is NOT "already authorized" â€” those adapters default to
``open``, so ``_is_user_authorized`` only trusts them under an actual ``allowlist`` policy.FN)r·   r   rV   )rª   r.   r   r´   rW   s   &&&& r   Ú_adapter_flagÚ'GatewayAuthorizationMixin._adapter_flag“  s9   € ÷
 ÙØ×-Ñ-¨hÓ@ˆØ˜dÐ"×JÐJ¤t¬G°GÀ5Ó,IÓ'JÐJr   c               ó   € V ^8„  d   QhRR/# rP   r   )r   s   "r   r   r¦   �  s   € ÷ 8ñ 8¨ñ 8r   c                óª   € \        \        V RR4      RR4      pVe   \        VP                  V4      RR4      MRp\        V\        4      '       d   V# / # )z@``config.platforms[platform].extra`` as a dict ({} when absent).rS   NÚ	platformsrT   )rV   r9   rA   rQ   )rª   r.   r  rT   s   &&  r   Ú_config_extraÚ'GatewayAuthorizationMixin._config_extra�  sO   € äœG D¨(°DÓ9¸;ÈÓMˆ	ØCLÒCX”˜	Ÿ™ hÓ/°¸$Ô?Ð^bˆÜ" 5¬$×/Ò/ˆuÐ7°RÐ7r   c               ó    € V ^8„  d   QhRRRR/# )r   Úattrr   Ú	extra_keyr   )r   s   "r   r   r¦   £  s   € ÷ ñ ¨sð ¸sñ r   c                ó”   € V P                  W4      pVe   \        WRR4      MRpVf!   V P                  V4      P                  V4      pV# )z‘Live adapter's resolved ``attr`` (folds in the ``<PLATFORM>_*`` env override),
else ``config.extra[extra_key]`` for bare runners with no adapter.N)r·   rV   r  r9   )rª   r.   r  r  r´   rW   r,   s   &&&&&  r   Ú_adapter_settingÚ*GatewayAuthorizationMixin._adapter_setting£  sL   € ð ×-Ñ-¨hÓ@ˆØ07Ò0C”˜ tÔ,ÈˆØŠ=Ø×&Ñ& xÓ0×4Ñ4°YÓ?ˆEØˆr   c               ó    € V ^8„  d   QhRRRR/# )r   Úkindr   r   r   )r   s   "r   r   r¦   ¬  s   € ÷ xñ x¨cð x¸sñ xr   c                óª   € V'       g   R# \        V P                  VRV R2V R2V4      ;'       g    R4      P                  4       P                  4       # )z¤Lowercased effective ``dm_policy`` (open/allowlist/disabled/pairing) or ``group_policy``
(open/allowlist/disabled) for *kind* in {"dm", "group"}; ``""`` if unknown.r*   Ú_Ú_policy)r   r  rC   r   )rª   r.   r  r´   s   &&&&r   Ú_adapter_policyÚ)GatewayAuthorizationMixin._adapter_policy¬  sR   € ÷ ÙÜ�4×(Ñ(¨°Q°t°f¸GÐ3DÈÈÈgÐFVÐX_Ó`×fÐfÐdfÓg×mÑmÓo×uÑuÓwÐwr   r´   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r.   r³   Úchat_idrK   r´   r   r   r   )r   s   "r   r   r¦   ³  s2   € ÷ pñ pØ*ðpØ5BðpØP]ðpà	ñpr   c               óú  a€ V'       d	   V'       g   R# V P                  VRRV4      p\        V\        4      '       g   R# \        V4      pVP	                  V4      p\        V\        4      '       gD   VP                  4       o\        V3R lVP                  4        4       VP	                  R4      4      p\        V\        4      '       g   R# VP	                  R4      ;'       g    VP	                  R4      p\        V\        4      '       d   \        VP                  4       4      # \        V\        \        \        34      ;'       d5    \        ;QJ d    R V 4       F  '       g   K   R	# 	  R# ! R V 4       4      # )
záWhether a per-group sender allowlist (WeCom ``groups.<id>.allow_from``) gated this message:
a group may be open at the chat level while restricting senders, so reaching the gateway
means the adapter already checked that list.FÚ_groupsÚgroupsc              3  óº   <"  € T FP  w  r\        V\        4      '       g   K  VP                  4       S8X  g   K4  \        V\        4      '       g   KL  Vx € KR  	  R # 5ir   )rA   r   r   rQ   )r’   ÚkÚvÚlowereds   &  €r   r”   ÚPGatewayAuthorizationMixin._adapter_group_has_sender_allowlist.<locals>.<genexpr>Ã  s<   øé € Ðu™~‘t�q´¸A¼s×1C”ÈÏÉË	ÐU\ÑH\”ÔakÐlmÔos×at—’›~ùs   ƒA¢A¹AÁ
ArM   Ú
allow_fromÚ	allowFromc              3  óT   "  € T F  p\        V4      P                  4       x € K   	  R # 5ir   )r   rC   )r’   Úitems   & r   r”   r+  Ë  s!   é € ÐCoÑbnÐZ^ÄCÈÃIÇOÁO×DUÐDUÓbnùs   ‚&(T)r  rA   rQ   r   r9   r   Únextrê   r   rC   rB   Útupler?   rÎ   )	rª   r.   r#  r´   r&  Úchat_id_strÚ	group_cfgÚsender_allowr*  s	   &&&$    @r   Ú#_adapter_group_has_sender_allowlistÚ=GatewayAuthorizationMixin._adapter_group_has_sender_allowlist³  s  ø€ ÷ ŸwÙØ×&Ñ& x°¸HÀgÓNˆÜ˜&¤$×'Ò'ÙÜ˜'“lˆØ—J‘J˜{Ó+ˆ	Ü˜)¤T×*Ò*Ø!×'Ñ'Ó)ˆGÜÜu˜vŸ|™|œ~ÓuØ—
‘
˜3“óˆIô ˜)¤T×*Ò*ÙØ —}‘} \Ó2×PÐP°i·m±mÀKÓ6PˆÜ�l¤C×(Ò(Ü˜×*Ñ*Ó,Ó-Ð-Ü˜,¬¬u´cÐ(:Ó;×oÐoÇÃÑCoÑbnÓCoÇÄÐoÂÐoÀÑCoÑbnÓCoÓ@oÐor   c               ó   € V ^8„  d   QhRR/# rî   r   )r   s   "r   r   r¦   Í  s   € ÷ tñ t¨ñ tr   c                ó�   € \        V RR4      ;'       g    / p\        VRR4      pV'       d   W29   d	   W#,          # \        V RR4      # )zNPer-profile PairingStore for a source, else the global ``self.pairing_store``.Úpairing_storesNr´   Úpairing_storerU   )rª   r�   Úper_profiler´   s   &&  r   Ú_pairing_store_forÚ,GatewayAuthorizationMixin._pairing_store_forÍ  sH   € ä˜dÐ$4°dÓ;×AÐA¸rˆÜ˜& )¨TÓ2ˆß'.°7Ô3Iˆ{Õ#ÐsÌwÐW[Ð]lÐnrÓOsÐsr   c               ó   € V ^8„  d   QhRR/# rP   r   )r   s   "r   r   r¦   Ó  s   € ÷ Iñ I°4ñ Ir   c                	ó6   € \        V P                  V4      4      # r   )rX   rç   )rª   r�   s   &&r   Ú_adapter_extra_for_sourceÚ3GatewayAuthorizationMixin._adapter_extra_for_sourceÓ  s   € Ü$ T×%?Ñ%?ÀÓ%GÓHÐHr   c               ó   € V ^8„  d   QhRR/# )r   r   zOptional[bool]r   )r   s   "r   r   r¦   Ö  s   € ÷ ñ ÐTbñ r   c                ó†  € V'       d1   V P                  VP                  VP                  VR7      '       d   R# T P                  VP                  V'       d   RMRV4      R8w  d   R# V'       gS   V P	                  VP                  VR7      pVe   \        VRR4      MRp\        V4      '       d   \        V! V4      4      # R# )u�  Own-policy adapter verdict when no env allowlist exists; None = no verdict.

Trusted only when the effective policy for THIS chat type is ``allowlist``: ``open`` forwards
EVERY sender (the fail-open SECURITY.md Â§2.6 forbids), ``disabled`` never forwards, ``pairing``
forwards unpaired DMs for the handshake (already denied by the pairing-store check).
Anything else â†’ default-deny.
)r´   Tr
   ÚdmÚ	allowlistNÚ_is_dm_allowed)r5  r.   r#  r   r·   rV   rñ   r   )rª   r�   r�   Úis_groupÚadapter_profilerW   Údm_checks   &&&&&  r   Ú_own_policy_authorizesÚ0GatewayAuthorizationMixin._own_policy_authorizesÖ  s¤   € ÷ ˜×@Ñ@ÀÇÁÐRX×R`ÑR`ÐjyÐ@×zÒzÙØ×Ñ §¡¿H±È$ÐP_Ó`ÐdoÔoÙ÷ Ø×1Ñ1°&·/±/È?Ð1Ó[ˆGØCJÒCV”w˜wÐ(8¸$Ô?Ð\`ˆHÜ˜×!Ò!Ü™H WÓ-Ó.Ð.Ùr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r¦   ì  s   € ÷ )ñ )ÐPTñ )r   c                óì  € V P                  V4      pVf   R# \        V4      pTP                  V'       d   RMR4      pV'       gN   \        VP                  4      p\        VP                  P                  RV4      '       d   VP                  R4      pV'       g   R# \        V4      p\        VRR4      p	\        V	4      '       d!   V Uu0 uF  qy! V4      ;'       g    TkK  	  pp\        W‚4      # u upi )z}Adapters (e.g. Telegram) that gate via config.extra.allow_from / group_allow_from
without setting enforces_own_access_policy.NFÚgroup_allow_fromr,  Úallowed_users_envÚallowed_usersÚnormalize_user_id)rç   rX   r9   r-   r.   r#   r,   rG   rV   rñ   rN   )
rª   r�   r�   rG  rW   rT   Úadapter_allowr&   rI   Ú	normalizes
   &&&&      r   Ú#_adapter_extra_allowlist_authorizesÚ=GatewayAuthorizationMixin._adapter_extra_allowlist_authorizesì  sÕ   € ð ×,Ñ,¨VÓ4ˆØŠ?ÙÜ% gÓ.ˆØŸ	™	¿Ñ"4ÀlÓSˆßô $ F§O¡OÓ4ˆEÜ˜6Ÿ?™?×0Ñ0Ð2EÀu×MÒMð !&§	¡	¨/Ó :�ßÙÜ# MÓ2ˆÜ˜GÐ%8¸$Ó?ˆ	Ü�I×Òá>EÓF¹g°U�y Ó'×0Ð0¨5Ò0¹gˆGÐFÜ�wÓ(Ð(ùò Gs   Ã
C1ÃC1c               ó   € V ^8„  d   QhRR/# r;   r   )r   s   "r   r   r¦     s   € ÷ `ñ `¸ñ `r   c                ó|  € R;r#\         P                  ! \        4      ;_uu_ 4        V P                  V4      pRRR4       \	        VRR4      p\        V4      '       d3   \         P                  ! \        4      ;_uu_ 4        V! 4       pRRR4       \        V\        \        \        \        34      '       g   \        4       # V Uu0 uFF  p\        V\        \        34      '       g   K!  \        V4      P                  4       ;p'       g   KD  VkKH  	  up#   + '       g   i     Lã; i  + '       g   i     L¦; iu upi )añ  IDs an adapter resolved from username-shaped allowlist entries at connect time (Discord).

The per-turn .env hot-reload restores RAW usernames, so from the second turn on the env
allowlist holds usernames while user_id is numeric. Never a widening: the empty-allowlist
branch already returned and adapters only resolve operator-written entries. Only called with
a non-empty platform allowlist so group/global-only configs never consult adapter memory;
type-checked so mocks cannot auto-truthy in.
NÚresolved_allowlist_user_ids)r4   r5   r6   rç   rV   rñ   rA   r?   Ú	frozensetrB   r1  r   rm   rC   )rª   r�   rW   Úresolved_idsÚresolverr‰   Úss   &&     r   Ú_adapter_resolved_allowlist_idsÚ9GatewayAuthorizationMixin._adapter_resolved_allowlist_ids  sØ   € ð "&Ð%ˆÜ× Ò ¤×+Õ+Ø×0Ñ0°Ó8ˆG÷ ,ä˜7Ð$AÀ4ÓHˆÜ�H×ÒÜ×$Ò$¤Y×/Õ/Ù'›z�÷ 0ä˜,¬¬i¼¼uÐ(E×FÒFÜ“5ˆLÙ'Ó_™<�a¬:°a¼#¼s¸×+D”ÌsÐSTËvÏ|É|Ë~ÐJ]È!ÓJ]—�™<Ñ_Ð_÷ ,×+ú÷ 0×/üò `s)   ¦DÂ D&ÃD9Ã'D9Ä
D9ÄD#	Ä&D6	c               ó$   € V ^8„  d   QhRRRRRR/# )r   rG  r   Úallow_adapter_delegationr   r   )r   s   "r   r   r¦     s#   € ÷ ,ñ ,ÀDð ,Ðdhð ,Ðmqñ ,r   c                óÞ  € V'       d6   VP                   RJ g$   V P                  VP                  RV4      '       d   R# V'       dã   VP                  '       dÑ   \	        VP                  4      pV'       d1   \        \        \        V4      4      VP                  4      '       d   R# \        P                  ! \        4      ;_uu_ 4        V P                  V4      P                  R4      pV'       d1   \        \        V4      VP                  4      '       d    RRR4       R# RRR4       \        VRR4      '       dù   \        P                  ! \        4      ;_uu_ 4        ^ RIHp V! 4        RRR4       \!        VP                  P"                  R\%        VP                  4      4      ;'       g    R	pV'       d{   / p	\        P                  ! \        4      ;_uu_ 4        V P                  V4      p	RRR4       \'        \)        V	R
VR4      4      P+                  4       P-                  4       p
V
R9   d   R# R#   + '       g   i     EL; i  + '       g   i     Læ; i  + '       g   i     Lu; i)zFGrants that need no ``user_id`` (checked before the no-user-id guard).TÚauthorization_is_upstreamÚgroup_allowed_chatsNÚis_botFr1   Úallow_bots_envr*   Ú
allow_botsÚnone>   ÚallÚmentions)r×   r  r.   r#  r/   rN   rG   r   r4   r5   r6   r@  r9   rV   r7   r2   r#   r,   r-   r   Ú_extra_or_secretr   rC   )rª   r�   rH  rG  r`  Úchat_allowlist_envÚadapter_group_allowedr2   Úallow_bots_varrT   Úmodes   &&&&&      r   Ú_chat_scoped_grantÚ,GatewayAuthorizationMixin._chat_scoped_grant  s¶  € ÷ $Ø×/Ñ/°4Ó7Ø×!Ñ! &§/¡/Ð3NÐP_×`Ò`á÷ ˜ŸŸ˜Ü!0°·±Ó!AÐß!¤gÔ.?Ä	ÐJ\Ó@]Ó.^Ð`f×`nÑ`n×&oÒ&oÙô ×$Ò$¤Y×/Õ/Ø(,×(FÑ(FÀvÓ(N×(RÑ(RÐShÓ(iÐ%ß(¬WÔ5FÐG\Ó5]Ð_e×_mÑ_m×-nÒ-nØ÷ 0×/Ñ/ô �6˜8 U×+Ò+ô ×$Ò$¤Y×/Õ/Ý?Ù Ô"÷ 0ô )¨¯©×)>Ñ)>Ð@PÔRaÐbh×bqÑbqÓRrÓs×yÐyÐwyˆNßØ�Ü×(Ò(¬×3Õ3Ø ×:Ñ:¸6ÓB�E÷ 4äÔ+¨E°<ÀÐQWÓXÓY×_Ñ_Óa×gÑgÓi�ØÐ.Ô.ÙÙ÷/ 0×/Ð/ú÷ 0×/ú÷ 4×3ús%   ÃAH5ÅI	ÇIÈ5I	É	I	ÉI,	c               ó    € V ^8„  d   QhRRRR/# )r   Úgroup_user_allowlistr   r   r   r   )r   s   "r   r   r¦   I  s   € ÷ 1ñ 1Èð 1ÐPTñ 1r   c                óz  € ^ RI Hp VP                  R4       Uu0 uF9  qDP                  4       P	                  R4      '       g   K)  VP                  4       kK;  	  ppV'       g   R# \        V RR4      '       g2   VP                  RRP                  \        V4      4      4       RV n	        VP                  V9   # u upi )zƒTELEGRAM_GROUP_ALLOWED_USERS was once (mis)used as a chat-ID allowlist; "-"-prefixed
values are chat IDs, honor them and warn once.)Úloggerr>   Ú-FÚ#_warned_telegram_group_users_legacyuÔ   TELEGRAM_GROUP_ALLOWED_USERS contains chat-ID-shaped values (%s). Treating them as chat IDs for backward compatibility. Move chat IDs to TELEGRAM_GROUP_ALLOWED_CHATS â€” the _USERS var is now for sender user IDs.T)rÏ   rt  rD   rC   r}   rV   ÚwarningÚjoinÚsortedrv  r#  )rª   r�   rr  rt  r)  Úlegacy_chat_idss   &&&   r   Ú_legacy_telegram_chat_grantÚ5GatewayAuthorizationMixin._legacy_telegram_chat_grantI  sž   € õ 	'Ø.B×.HÑ.HÈÔ.MÓkÑ.M¨×QXÑQXÓQZ×QeÑQeÐfi×Qjœ9˜1Ÿ7™7ž9Ñ.MˆÐkßÙÜ�tÐBÀE×JÒJØ�N‰Nð.ð —‘œ Ó0Ó1ôð 8<ˆDÔ4Ø�~‰~ Ñ0Ð0ùò ls   š&B8ÁB8c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r¦   [  s   € ÷ ñ ¨,ñ r   c                	óÄ   € \        V R R4      pVf;   \        ;_uu_ 4        \        V R R4      pVf   \        4       ;qn        RRR4       V# V#   + '       g   i     T# ; i)Ú_bot_loop_guardN)rV   Ú_BOT_LOOP_GUARD_INIT_LOCKr   r  )rª   Úguards   & r   Ú_bot_loop_guard_instanceÚ2GatewayAuthorizationMixin._bot_loop_guard_instance[  s]   € Ü˜Ð/°Ó6ˆØŠ=ß*Ö*Ü Ð&7¸Ó>�Ø’=Ü3?³>ÐA�EÔ0÷ +ð ˆˆuˆ÷	 +Ö*ð ˆús    "AÁA	c               ó    € V ^8„  d   QhRRRR/# )r   r�   r   r   r1  r   )r   s   "r   r   r¦   d  s   € ÷ eñ e°=ð eÀUñ er   c                	óÌ   € VP                   '       d   VP                   P                  MR pV P                  V4      ;'       g    R T\        VP                  ;'       g    R 4      3# )r*   )r.   r,   r  r   r#  )rª   r�   r.   s   && r   Ú_bot_loop_guard_conversationÚ6GatewayAuthorizationMixin._bot_loop_guard_conversationd  sM   € à,2¯O¯O¨O�6—?‘?×(Ò(ÀˆØ×0Ñ0°Ó8×>Ð>¸BÀÌ#ÈfÏnÉn×NbÐNbÐ`bÓJcÐdÐdr   c               ó    € V ^8„  d   QhRRRR/# )r   r�   r   r   r   r   )r   s   "r   r   r¦   i  s   € ÷ ñ ¨ð ¸4ñ r   c                óF  € \        VRR4      '       g   R# V P                  4       P                  V P                  V4      4      w  r#VR8X  dU   \        P                  RVP                  '       d   VP                  P                  MRVP                  VP                  4       V# )zåCount one authorized bot-authored inbound message. False when it trips the budget or the chat is cooling down.
The inbound handler calls this once per message; ``_is_user_authorized`` only peeks because it is asked several times.rd  FTÚtrippedz�Bot loop guard is dropping bot messages in %s chat %s: bot %s sent one message too many for the window, cooling down (gateway.bot_loop_guard in config.yaml).r*   )
rV   r‚  Úadmitr†  rt  rw  r.   r,   r#  r�   )rª   r�   rI   Ústates   &&  r   Ú_admit_bot_messageÚ,GatewayAuthorizationMixin._admit_bot_messagei  s„   € ô �v˜x¨×/Ò/ÙØ×6Ñ6Ó8×>Ñ>¸t×?`Ñ?`ÐagÓ?hÓi‰ˆØ�IÔÜ�N‰NðXà)/¯¯¨�—‘×%Ò%¸bÀ&Ç.Á.ÐRX×R`ÑR`ôð
 ˆr   r`  Tc               ó$   € V ^8„  d   QhRRRRRR/# ©r   r�   r   r`  r   r   r   )r   s   "r   r   r¦   w  s(   € ÷ fñ f¨-ð fÐVZð fÐfjñ fr   c               óÆ   € V P                  WR7      '       g   R# \        VRR4      '       g   R# V P                  4       P                  V P	                  V4      4      '       * # )ah  Whether a user may use the bot.

Order: trusted-upstream delegation, chat-scoped group allowlists, ``{PLATFORM}_ALLOW_BOTS``,
per-platform allow-all, adapter role auth, pairing store, env/config allowlists,
``GATEWAY_ALLOW_ALL_USERS``, default deny. A bot-authored message that any of these admits
is still refused while its chat's loop guard is cooling down.
)r`  Frd  T)Ú_principal_authorizedrV   r‚  Úblockedr†  )rª   r�   r`  s   &&$r   Ú_is_user_authorizedÚ-GatewayAuthorizationMixin._is_user_authorizedw  sV   € ð ×)Ñ)¨&Ð)×dÒdÙÜ�v˜x¨×/Ò/Ùà×0Ñ0Ó2×:Ñ:¸4×;\Ñ;\Ð]cÓ;dÓeÔeÐer   c               ó$   € V ^8„  d   QhRRRRRR/# r�  r   )r   s   "r   r   r¦   †  s-   € ÷ D`ñ D`¨Mð D`ÐX\ð D`Ðaeñ D`r   c               ó@  € ^ RI Hp V! W4      pVe   V# VP                  \        P                  8X  g%   \        \        VP                  4      RR4      RJ d   R# V P                  V4      pVP                  \        9   pVP                  \        9   pV P                  WWb4      '       d   R# VP                  pV'       g   R# \        VP                  4      p	\        VP                  P                  RV	4      ;'       g    Rp
\        VP                  P                  RV	4      ;'       g    RpV'       d   \        V4      '       d   R# V'       d   \        VRR4      RJ d   R# V P!                  V4      pVeC   TP#                  VP                  '       d   VP                  P                  MRV4      '       d   R# \%        V
4      pV'       d*   \%        \&        P)                  VP                  R4      4      MRpV'       d    \%        \+        VP                  V	4      4      MRp\%        R	4      pV'       g�   V'       gy   V'       gq   V'       gi   V'       d<   V P-                  VP                  R
V4      '       d   V P/                  WWe4      pVe   V# V P1                  WV4      '       d   R# \        R4      # V'       d‚   VP2                  '       dp   V'       d(   \5        \7        V4      VP2                  4      '       d   R# VP                  \        P8                  8X  d"   V'       d   V P;                  W4      '       d   R# \7        V4      \7        V4      ,          \7        V4      ,          pV'       d   VV P=                  V4      ,          pRV9   ;'       g    \?        WV4      # )z7The allowlist verdict alone, before the bot loop guard.)Úauthorize_local_sourceÚtrusted_inboundFTrO  r*   Úallow_all_envÚrole_authorizedÚGATEWAY_ALLOWED_USERSÚenforces_own_access_policyÚGATEWAY_ALLOW_ALL_USERSrM   ) Úgateway.session_localr˜  r.   r   ÚWEBHOOKrV   r-   r  Ú	chat_typeÚ_GROUP_CHAT_TYPESÚ_GROUP_FORUM_TYPESro  r�   r#   r,   r!   r<  Úis_approvedr   Ú_GROUP_USER_ENVr9   r/   r  rJ  rT  r#  rN   rG   ÚTELEGRAMr{  r]  r¢   )rª   r�   r`  r˜  Úlocal_verdictrH  rG  Úis_group_or_forumr�   r&   Úplatform_allow_envÚplatform_allow_all_varr:  Úplatform_allowlistrr  Úgroup_chat_allowlistÚglobal_allowlistÚverdictrŽ   s   &&$                r   r’  Ú/GatewayAuthorizationMixin._principal_authorized†  sÝ  € å@Ù.¨tÓ<ˆØÒ$Ø Ð ð �?‰?œh×.Ñ.Ô.´'¼/È&Ï/É/Ó:ZÐ\mÐotÓ2uÐy}Ó2}Ùà×:Ñ:¸6ÓBˆØ×#Ñ#Ô'8Ñ8ˆØ"×,Ñ,Ô0BÑBÐØ×"Ñ" 6¸H×_Ò_ÙØ—.‘.ˆßÙä §¡Ó0ˆÜ(¨¯©×)>Ñ)>Ð@SÐUZÓ[×aÐaÐ_aÐÜ!,¨V¯_©_×-BÑ-BÀOÐUZÓ![×!aÐ!aÐ_aÐß!¤kÐ2H×&IÒ&IÙç#¬°Ð8IÈ5Ó(QÐUYÓ(YÙð ×/Ñ/°Ó7ˆØÒ$¨×)BÑ)BÐ\b×\k×\kÐ\kÀ6Ç?Á?×CXÒCXÐqsÐu|×)}Ò)}Ùä&Ð'9Ó:ÐßVgœy¬×)<Ñ)<¸V¿_¹_ÈbÓ)QÔRÐmoÐßUfœy¬¸¿¹È%Ó)PÔQÐlnÐÜ$Ð%<Ó=Ðç"×&:×>R×Vfç'¨D×,>Ñ,>¸v¿¹ÐPlÐn}×,~Ò,~Ø×5Ñ5°fÀxÓa�ØÒ&Ø"�NØ×7Ñ7¸È×RÒRÙÜÐ8Ó9Ð9ç §§ ç#¬Ô0AÐBVÓ0WÐY_×YgÑYg×(hÒ(hÙà—‘¤8×#4Ñ#4Ô4×9MØ×4Ñ4°V×RÒRáô Ð0Ó1ÜÐ 4Ó5õ6äÐ 0Ó1õ2ð 	÷
 Ø˜4×?Ñ?ÀÓGÕGˆKØ�kÑ!×_Ð_Ô%AÀ&ÐS^Ó%_Ð_r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r.   r³   r´   rK   r   r   r   )r   s   "r   r   r¦   Ì  s$   € ÷ *ñ *Ð6Hð *ÐVcð *Ðorñ *r   c               ó¨  € \        V RR4      pV'       dB   \        VR4      '       d0   V'       d(   RV P                  V4      9   d   VP                  V4      # V\        P
                  8X  d   R# V'       d0   \        VR4      '       d   VP                  R8w  d   VP                  # R.pV'       dç   V P                  VRV4      pV'       gP   \        V P                  V4      P                  R	4      ;'       g    R
4      P                  4       P                  4       pVR8X  d   R# VR9   d   R# \        V4      p\        VP                  RV4      '       dC   \        VP                  RV4      ;'       g    R
\        P                  V4      \!        W4      .VOp\"        ;QJ d    R V 4       F  '       g   K   RM	  RM! R V 4       4      '       d   R# R# )u  How unauthorized DMs are handled ("pair" / "ignore" / "decline") for a platform.

Order: explicit per-platform config; Email â†’ "ignore" (inboxes hold arbitrary mail); explicit
non-default global; adapter dm_policy (pairing â†’ "pair", allowlist/disabled â†’ "ignore"); any
configured allowlist â†’ "ignore" (spamming unknown contacts with codes is noisy and leaks); else "pair".

1. 2. Email defaults to ``"ignore"`` unless explicitly opted into pairing. 3. Explicit global
``unauthorized_dm_behavior`` in config â€” wins for chat-shaped platforms when no per-platform
override is set. 4. When an adapter-level DM policy opts into pairing or silent drop, honor it. 5.
When an allowlist (``PLATFORM_ALLOWED_USERS``, ``PLATFORM_GROUP_ALLOWED_USERS`` /
``PLATFORM_GROUP_ALLOWED_CHATS``, or ``GATEWAY_ALLOWED_USERS``) is configured, default to
``"ignore"`` â€” the allowlist signals that the owner has deliberately restricted access; spamming
unknown contacts with pairing codes is both noisy and a potential info-leak. (#9337) 6.
rS   NÚget_unauthorized_dm_behaviorÚunauthorized_dm_behaviorÚignoreÚpairrœ  rD  Ú	dm_policyr*   ÚpairingÚallowlist_suppresses_pairingrO  c              3  óf   "  € T F'  q;'       d    \        V4      P                  4       x € K)  	  R # 5ir   )r   rC   )r’   Úkeys   & r   r”   ÚJGatewayAuthorizationMixin._get_unauthorized_dm_behavior.<locals>.<genexpr>ô  s&   é € ÐH¹°#×-Ð-”y “~×+Ñ+Ó-Ô-»ùs   ‚1�"1TF>   ÚdisabledrE  )rV   Úhasattrr  r²  r   ÚEMAILr³  r   r   r9   rC   r   r-   r#   r,   r¥  r/   rÎ   )rª   r.   r´   rS   Úallowlist_keysr¶  r&   s   &&$    r   Ú_get_unauthorized_dm_behaviorÚ7GatewayAuthorizationMixin._get_unauthorized_dm_behaviorÌ  s|  € ô ˜˜x¨Ó.ˆç”w˜vÐ'E×FÒFÏ8Ø*¨d×.@Ñ.@ÀÓ.JÔJà×6Ñ6°xÓ@Ð@Ø”x—~‘~Ô%Ùß”g˜fÐ&@×AÒAÀf×FeÑFeÐioÔFoØ×2Ñ2Ð2à1Ð2ˆßØ×,Ñ,¨X°t¸WÓEˆIßÜ × 2Ñ 2°8Ó <× @Ñ @ÀÓ M× SÐ SÐQSÓT×ZÑZÓ\×bÑbÓd�	Ø˜IÔ%ÙØÐ5Ô5ÙÜ# HÓ-ˆEä˜8Ÿ>™>Ð+IÈ5×QÒQÜ"-¨h¯n©nÐ>QÐSXÓ"Y×"_Ð"_Ð]_Ü"1×"5Ñ"5°hÓ"?ÄÐQYÓAað"tØdrð"t�ç‹3ÑH¹ÓH�3�3Š3ÑH¹ÓH×HÒHÙÙr   )r  rv  r   )$Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r«   r°   r·   r¶   r½   rã   rç   rë   rÚ   rÿ   r  r
  r  r  r  r   r5  r<  r@  rJ  rT  r]  ro  r{  r‚  r†  r�  r”  r’  rÀ  Ú__static_attributes__r   r   r   r¤   r¤   Á   sÊ   † Ù@õ
5õ>÷AõSõ0õ,"õHpò<õ:õ(õ<	0õõKõ8õõxðpØ`d÷põ4tõIõõ,)õ6`õ(,õ\1õ$õeõ
ðfÐ]a÷ fõD`ðL*Ðfj÷ *ó *r   r¤   >   r   r
   Úchannel>   Ú1ÚyesÚtruer   )i²Wj;imŽP&iú¡iÝ3B=i³b*)T)8rÆ  Ú
__future__r   r4   ÚloggingÚosÚ	threadingÚpathlibr   Útypingr   Úgateway.bot_loop_guardr   Úgateway.configr   Úgateway.sessionr   Úgateway.whatsapp_identityr   r‘   r	   r›   rY  r¢  r£  r    ÚLockr€  Ú	getLoggerrÂ  rt  r¦  r¥  Úgateway.platforms._sharedr   r@   r   rj  r   r   r!   r#   r/   r-   rG   rN   rX   r   r]   rc   rj   rx   r…   r‹   r¢   r¤   r   r   r   Ú<module>rÙ     só   ðñõ #ã Û Û 	Û Ý Ý å /Ý #Ý )÷ñ
 Ò;Ó<Ð Ù ¨Ð1Ó2Ð Ù
Ò(Ó
)€Ø%ŸNšNÓ,Ð Ø	×	Ò	˜8Ó	$€ð ×$Ñ$Ð&DÐE€õ \Ý IÝ Dõ.ò&÷lòõJõ2õJð 5€ØPÐ òõI÷õ( õ"kõ)÷:uó ur   