
    .cj                    ~    d Z ddlmZ ddlZddlmZ ddlmZ ddlm	Z	 ddl
mZmZ dddZddZ G d d          ZdS )a  User-authorization methods for ``GatewayRunner``.

Extracted from ``gateway/run.py`` as part of the god-file decomposition campaign
(``~/.hermes/plans/god-file-decomposition.md``, Phase 3 mechanical mixin lifts).
This mixin holds the inbound-message authorization cluster: whether a user/chat
is allowed to talk to the agent, the per-adapter DM policy, and the
unauthorized-DM behavior.

Behavior-neutral: every method is lifted verbatim from ``GatewayRunner``.
``self.*`` calls resolve unchanged via the MRO. Neutral dependencies import at
module top; the module-level ``logger`` is imported lazily inside the one method
that uses it (``from gateway.run import logger`` resolves at call time, when
``gateway.run`` is fully loaded) so this module never imports ``gateway.run`` at
import time -> no import cycle. The lazy import preserves the exact logger name
(``"gateway.run"``) so log records are unchanged.
    )annotationsN)Optional)Platform)SessionSource)expand_whatsapp_aliasesnormalize_whatsapp_identifier namestrdefaultreturnc                (   | s|S 	 ddl m}  ||           }|Bt          |                                          r!t          |                                          S n# t          $ r Y nw xY wt          j        |           p|                                S )zERead allowlist/auth env; prefer profile secret_scope under multiplex.r   )
get_secret)agent.secret_scoper   r   strip	Exceptionosgetenv)r
   r   r   vals       5/home/ice/.hermes/hermes-agent/gateway/authz_mixin.py	_auth_envr      s     111111j?s3xx~~//?s88>>###   IdOO&w--///s   AA 
A)(A)set[str]c                    | t                      S t          | t                    rd | D             S d t          |                               d          D             S )aG  Parse allowlist values from config or env var into a set of strings.

    Handles both list inputs (YAML sequences) and comma-separated string
    inputs (env vars or scalar YAML values).  A scalar string is split on
    commas so ``allow_from: "123,456"`` yields ``{"123", "456"}``, not
    ``{"1", "2", "3", ",", ...}``.
    Nc                    h | ]D}t          |                                          #t          |                                          ES  r   r   .0parts     r   	<setcomp>z$_coerce_allow_set.<locals>.<setcomp>9   s=    GGGdSYY__5F5FGD		!!GGG    c                ^    h | ]*}|                                 |                                 +S r   r   r   s     r   r    z$_coerce_allow_set.<locals>.<setcomp>:   s-    IIITDJJLLIDJJLLIIIr!   ,)set
isinstancelistr   split)raws    r   _coerce_allow_setr*   .   s_     {uu#t HGGcGGGGIISXX^^C%8%8IIIIr!   c                      e Zd ZdZ	 dddZdd
ZddZd dZddd!dZddd!dZ	ddd"dZ
ddd"dZddd#dZd$dZd%dZddd"dZdS )&GatewayAuthorizationMixinz6User/chat authorization methods for ``GatewayRunner``.NplatformOptional[Platform]profileOptional[str]c                   |sdS |pd                                 pd}|r:|dk    r4t          | dd          pi }||v r||                             |          S dS t          | dd          pi }|                    |          S )a  Resolve the live adapter whose intake policy should gate authorization.

        In multiplex mode, secondary-profile adapters live in
        ``_profile_adapters[profile]`` while the default/active profile uses
        ``self.adapters``. ``SessionSource.profile`` selects which map to consult.
        When a stamped profile has its own adapter registry entry, the default
        profile's same-platform adapter must not be consulted as a fallback.
        Nr	   r   _profile_adaptersadapters)r   getattrget)selfr-   r/   profile_nameprofile_adaptersr3   s         r   _authorization_adapterz0GatewayAuthorizationMixin._authorization_adapter@   s      	42,,..6$ 	LI55&t-@$GGM2///'599(CCC 44T228b||H%%%r!   sourceOptional[SessionSource]c                .   |dS |                      |          }||S t          |dd          du r2t          | dd          pi }|                    t          j                  S |                     t          |dd          t          |dd                    S )z:Resolve the live adapter for an inbound ``SessionSource``.Ndelivered_via_upstream_relayFTr3   r-   r/   )_registered_transport_adapterr4   r5   r   RELAYr9   )r6   r:   transport_adapterr3   s       r   _adapter_for_sourcez-GatewayAuthorizationMixin._adapter_for_source[   s    >4 >>vFF($$ 695AATII
 tZ66<"H<</// **FJ--FIt,,
 
 	
r!   r   c                j   t          |dd          }t          |          r
 |            nd}t          |dd          }||dS |t          | dd          pi                     |          u r|S t          | dd          pi }|                                D ]}||                    |          u r|c S dS )a  Return the registered adapter that created *source*, if retained.

        ``source.profile`` is the runtime/session namespace. A chat-based
        profile route can therefore differ from the adapter profile when one
        shared credential serves several routed runtimes. ``build_source``
        keeps the receiving adapter as in-process provenance so replies and
        intake-policy checks stay on that transport without weakening the
        fail-closed fallback for restored or hand-built sources.
        _transport_adapter_refNr-   r3   r2   )r4   callabler5   values)r6   r:   adapter_refadapterr-   profile_mapsr8   s          r   r>   z7GatewayAuthorizationMixin._registered_transport_adapterv   s     f&>EE#+K#8#8B++---d6:t44?h.4wtZ66<"AA(KKKKNt%8$??E2 , 3 3 5 5 	 	*..x8888 9tr!   r   c                T   |                      |          }t          |dd          }|p|t          | dd          pi                     |          u rdS t          | dd          pi                                 D ] \  }}||                    |          u r|c S !t          |dd          S )z@Resolve the transport-owning profile for adapter policy lookups.r-   Nr3   r2   r/   )r>   r4   r5   items)r6   r:   rG   r-   r/   r8   s         r   _adapter_profile_for_sourcez5GatewayAuthorizationMixin._adapter_profile_for_source   s    44V<<6:t4474T::@bEEhOOOOt1488>Begg# #)) .228<<<<"NNN =vy$///r!   r/   boolc               z    |sdS |                      ||          }|dS t          t          |dd                    S )a  Whether the adapter for *platform* delegates authz to a trusted upstream.

        Mirrors ``BasePlatformAdapter.authorization_is_upstream``. The relay
        adapter sets this True: the Team Gateway connector authenticates the
        gateway's WS and resolves owner-only author bindings before delivering,
        so an inbound relay event is already authorized as this instance's bound
        user. Unlike ``_adapter_enforces_own_access_policy`` (a LOCAL config
        policy the gateway mirrors only when it's an allowlist), this is an
        UPSTREAM decision the gateway honors directly. Defaults to ``False`` when
        the adapter is unknown or doesn't expose the flag.
        FNauthorization_is_upstreamr9   rM   r4   r6   r-   r/   rG   s       r   "_adapter_authorization_is_upstreamz<GatewayAuthorizationMixin._adapter_authorization_is_upstream   sK    "  	5--h@@?5GG%@%HHIIIr!   c               z    |sdS |                      ||          }|dS t          t          |dd                    S )a  Whether the adapter for *platform* gates access at intake itself.

        Mirrors ``BasePlatformAdapter.enforces_own_access_policy``. Adapters
        such as WeCom, Weixin, Yuanbao, QQBot, and WhatsApp evaluate their
        documented ``dm_policy`` / ``group_policy`` / ``allow_from`` config before a
        message is dispatched to the gateway. The flag alone is NOT "already
        authorized": these adapters default to ``open``, which forwards every
        sender, so ``_is_user_authorized`` only trusts the adapter when its
        effective policy for the chat type is an actual ``allowlist`` restriction
        (see that method). Defaults to ``False`` when the adapter is unknown or
        doesn't expose the flag.
        FNenforces_own_access_policyrP   rQ   s       r   #_adapter_enforces_own_access_policyz=GatewayAuthorizationMixin._adapter_enforces_own_access_policy   sM    $  	5 --h@@?5GG%A5IIJJJr!   r   c                  |sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |pd                                          	                                S )u  Best-effort read of an own-policy adapter's effective DM policy.

        Returns the lowercased ``dm_policy`` (``"open"`` / ``"allowlist"`` /
        ``"disabled"`` / ``"pairing"``) for *platform*, or ``""`` when unknown.
        Prefers the live adapter's resolved ``_dm_policy`` — which already folds
        in both ``config.extra`` and the ``<PLATFORM>_DM_POLICY`` env var (the
        env var is not always bridged back into ``config.extra``) — and falls
        back to ``config.extra`` for bare runners built without a live adapter.

        Used by ``_is_user_authorized`` to decide whether an own-policy adapter
        actually restricted DM senders to a configured allowlist (trustworthy)
        or merely forwarded everyone under ``dm_policy: open`` / for a pairing
        handshake (not authorization). "Reached the gateway" only carries an
        authorization signal in the ``allowlist`` case.
        r	   N
_dm_policyconfig	platformsextra	dm_policy
r9   r4   hasattrrY   r5   r&   dictr   r   lowerr6   r-   r/   rG   policyrX   platform_cfgrZ   s           r   _adapter_dm_policyz,GatewayAuthorizationMixin._adapter_dm_policy   s    *  	2--h@@9@9L,555RV>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& 0;//6<R  &&((..000r!   c                  |sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |pd                                          	                                S )a  Best-effort read of an own-policy adapter's effective group policy.

        Mirror of ``_adapter_dm_policy`` for group / forum / channel traffic:
        returns the lowercased ``group_policy`` (``"open"`` / ``"allowlist"`` /
        ``"disabled"``) for *platform*, or ``""`` when unknown. Prefers the live
        adapter's resolved ``_group_policy`` and falls back to ``config.extra``
        for bare runners built without a live adapter.

        Used by ``_is_user_authorized`` to decide whether an own-policy adapter
        restricted group senders to a configured allowlist (trustworthy) or
        forwarded the whole channel under ``group_policy: open`` (not
        authorization).
        r	   N_group_policyrX   rY   rZ   group_policyr\   r`   s           r   _adapter_group_policyz/GatewayAuthorizationMixin._adapter_group_policy   s    &  	2--h@@<C<O/4888UY>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& 3>226<R  &&((..000r!   chat_idc                  |r|sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |t                    sdS t          |          }	|                    |	          }
t          |
t                    st|	                                }|	                                D ]K\  }}t          |t                    r1|                                |k    rt          |t                    r|}
 nLt          |
t                    s|                    d          }
t          |
t                    sdS |
                    d	          p|
                    d
          }t          |t                    r!t          |                                          S t          |t          t          t          f          rt          d |D                       S dS )a  Whether a per-group sender allowlist gated this group message.

        WeCom supports ``groups.<group_id>.allow_from`` on top of the top-level
        ``group_policy``. A group may be open at the chat level while still
        restricting which senders inside that group can invoke Hermes. If such a
        message reached the gateway, the adapter already checked that sender
        allowlist, so it is a trustworthy intake decision rather than the
        fail-open ``group_policy: open`` case.
        FN_groupsrX   rY   rZ   groups*
allow_from	allowFromc              3  X   K   | ]%}t          |                                          V  &d S Nr   )r   items     r   	<genexpr>zPGatewayAuthorizationMixin._adapter_group_has_sender_allowlist.<locals>.<genexpr>I  s2      BBTs4yy((BBBBBBr!   )r9   r4   r]   rY   r5   r&   r^   r   r_   rJ   rM   r   r'   tupler%   any)r6   r-   rh   r/   rG   rk   rX   rb   rZ   chat_id_str	group_cfgloweredkeyvaluesender_allows                  r   #_adapter_group_has_sender_allowlistz=GatewayAuthorizationMixin._adapter_group_has_sender_allowlist  sJ      	w 	5--h@@6=6I)T222t>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& -8,,&$'' 	5'llJJ{++	)T** 	!''))G$llnn  
Uc3'' CIIKK7,B,BzRWY]G^G^,B %IE)T** 	(

3I)T** 	5 }}\22PimmK6P6PlC(( 	.**,,---lT5#$677 	CBB\BBBBBBur!   'SessionSource'c                    t          | dd          pi }t          |dd          }|r||v r||         S t          | dd          S )a  Pick the per-profile PairingStore for a source, falling back to global.

        In a multiplexing gateway, each profile owns its own pairing whitelist
        so isolation is preserved. When the source has no profile (single-
        profile gateway, or a path that hasn't stamped profile yet) or the
        profile isn't registered, fall back to ``self.pairing_store`` (the
        global default) so existing behavior is preserved.
        pairing_storesNr/   pairing_store)r4   )r6   r:   per_profiler/   s       r   _pairing_store_forz,GatewayAuthorizationMixin._pairing_store_forL  s[     d$4d;;Ar&)T22 	(w+--w''t_d333r!   c                ^   ddl m} |j        t          j        t          j        hv rdS |                     |          }|j        du s|                     |j        |          rdS |j	        }|j
        dv r|j        rt          j        dt          j        di                    |j        d          }|rWt          j        |d                                          }|r.d	 |                    d
          D             }d|v s	|j        |v rdS 	 |                     |          }|Wt)          t)          |dd          dd          pi }	|	                    d          }
|
rt+          |
          }d|v s	|j        |v rdS n# t,          $ r Y nw xY wt          j        dt          j        dt          j        dt          j        di}t)          |dd          rY|                    |j                  }|r=t          j        |d                                                                          dv rdS |sdS i t          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j         d t          j!        d!t          j"        d"t          j        d#t          j#        d$t          j$        d%t          j%        d&t          j&        d't          j        d(t          j'        d)i}t          j        d*i}t          j        dt          j        di}i t          j        d+t          j        d,t          j        d-t          j        d.t          j        d/t          j        d0t          j        d1t          j        d2t          j         d3t          j!        d4t          j"        d5t          j        d6t          j#        d7t          j$        d8t          j%        d9t          j&        d:t          j        d;t          j'        d<i}|j        |vre	 dd=l(m)} |                    |j        j*                  }|r,|j+        r|j+        ||j        <   |j,        r|j,        ||j        <   n# t,          $ r Y nw xY w|                    |j        d          }|r%t[          |                                          d>v rdS t)          |d?d          du rdS |j        r|j        j*        nd}| .                    |          }||/                    ||          rdS t[          |                    |j        d                    }d}d}|j
        d@v rPt[          |                    |j        d                    }t[          |                    |j        d                    }t[          dA          }|s>|s;|s8|s5| 0                    |j        |          rn|j
        dv rA| 1                    |j        |          }| 2                    |j        |j        |          rdS n| 3                    |j        |          }|dBk    rdS |                     |          }|qt)          t)          |dd          dd          pi }	|j
        dv r|	                    dC          }n|	                    dD          }|rt+          |          }||v sd|v rdS t[          dE                                          d>v S |r>|j
        d@v r5|j        r.dF |                    d
          D             }d|v s	|j        |v rdS |j        t          j        k    r|r|j
        d@v r|j        rzdG |                    d
          D             }|rYt)          | dHd          s=|4                    dId
5                    tm          |                               d| _7        |j        |v rdS tq                      }|r2|9                    dJ |                    d
          D                        |r2|9                    dK |                    d
          D                        |r2|9                    dL |                    d
          D                        d|v rdS |h}dM|v r.|:                    |                    dM          d                    |j        t          j        k    rtq                      } |D ]$}!| 9                    tw          |!                     %| r| }|9                    tw          |                     ty          |          }"|"r|:                    |"           |j        1|j        j*        dNk    r!|j=        r|:                    |j=                   t}          ||z            S )Oao  
        Check if a user is authorized to use the bot.
        
        Checks in order:
        1. Per-platform allow-all flag (e.g., DISCORD_ALLOW_ALL_USERS=true)
        2. Environment variable allowlists (TELEGRAM_ALLOWED_USERS, etc.)
        3. DM pairing approved list
        4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
        5. Default: deny
        r   )loggerTrL   >   forumgroupchannelTELEGRAM_GROUP_ALLOWED_CHATSQQ_GROUP_ALLOWED_USERSr	   c                ^    h | ]*}|                                 |                                 +S r   r#   )r   cids     r   r    z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>  s>     ) ) )99;;)		) ) )r!   r$   rl   NrX   rZ   group_allowed_chatsDISCORD_ALLOW_BOTSFEISHU_ALLOW_BOTSTELEGRAM_ALLOW_BOTSSLACK_ALLOW_BOTSis_botFnone>   allmentionsTELEGRAM_ALLOWED_USERSDISCORD_ALLOWED_USERSWHATSAPP_ALLOWED_USERSWHATSAPP_CLOUD_ALLOWED_USERSSLACK_ALLOWED_USERSSIGNAL_ALLOWED_USERSEMAIL_ALLOWED_USERSSMS_ALLOWED_USERSMATTERMOST_ALLOWED_USERSMATRIX_ALLOWED_USERSDINGTALK_ALLOWED_USERSFEISHU_ALLOWED_USERSWECOM_ALLOWED_USERSWECOM_CALLBACK_ALLOWED_USERSWEIXIN_ALLOWED_USERSBLUEBUBBLES_ALLOWED_USERSQQ_ALLOWED_USERSYUANBAO_ALLOWED_USERSTELEGRAM_GROUP_ALLOWED_USERSTELEGRAM_ALLOW_ALL_USERSDISCORD_ALLOW_ALL_USERSWHATSAPP_ALLOW_ALL_USERSWHATSAPP_CLOUD_ALLOW_ALL_USERSSLACK_ALLOW_ALL_USERSSIGNAL_ALLOW_ALL_USERSEMAIL_ALLOW_ALL_USERSSMS_ALLOW_ALL_USERSMATTERMOST_ALLOW_ALL_USERSMATRIX_ALLOW_ALL_USERSDINGTALK_ALLOW_ALL_USERSFEISHU_ALLOW_ALL_USERSWECOM_ALLOW_ALL_USERSWECOM_CALLBACK_ALLOW_ALL_USERSWEIXIN_ALLOW_ALL_USERSBLUEBUBBLES_ALLOW_ALL_USERSQQ_ALLOW_ALL_USERSYUANBAO_ALLOW_ALL_USERS)platform_registry>   1yestruerole_authorized>   r   r   GATEWAY_ALLOWED_USERS	allowlistgroup_allow_fromrm   GATEWAY_ALLOW_ALL_USERSc                ^    h | ]*}|                                 |                                 +S r   r#   )r   rh   s     r   r    z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>~  sB     ! ! !$+RYR_R_RaRa!! ! !r!   c                    h | ]=}|                                                     d           )|                                 >S )-)r   
startswith)r   vs     r   r    z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>  sL       7799'',,		  r!   #_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.c              3  f   K   | ],}|                                 |                                 V  -d S rp   r#   r   uids     r   rr   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      ccsWZW`W`WbWbcsyy{{ccccccr!   c              3  f   K   | ],}|                                 |                                 V  -d S rp   r#   r   s     r   rr   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      eesY\YbYbYdYdesyy{{eeeeeer!   c              3  f   K   | ],}|                                 |                                 V  -d S rp   r#   r   s     r   rr   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      aasUXU^U^U`U`asyy{{aaaaaar!   @simplex)?gateway.runr   r-   r   HOMEASSISTANTWEBHOOKrK   r=   rR   user_id	chat_typerh   TELEGRAMQQBOTr5   r   r   r   r(   rA   r4   r*   r   DISCORDFEISHUSLACKr_   WHATSAPPWHATSAPP_CLOUDSIGNALEMAILSMS
MATTERMOSTMATRIXDINGTALKWECOMWECOM_CALLBACKWEIXINBLUEBUBBLESYUANBAOgateway.platform_registryr   ry   allowed_users_envallow_all_envr   r   is_approvedrU   rg   r{   rc   warningjoinsortedr   r%   updateadd_expand_whatsapp_auth_aliases_normalize_whatsapp_identifier	user_namerM   )#r6   r:   r   adapter_profiler   chat_allowlist_envraw_chat_allowlistallowed_group_idsrG   rZ   adapter_group_allowedallowedplatform_allow_bots_mapallow_bots_varplatform_env_mapplatform_group_user_env_mapplatform_group_chat_env_mapplatform_allow_all_mapr   entryplatform_allow_all_varplatform_namer   platform_allowlistgroup_user_allowlistgroup_chat_allowlistglobal_allowlisteffective_policyadapter_allowlegacy_chat_idsallowed_ids	check_idsnormalized_allowed_ids
allowed_idnormalized_user_ids#                                      r   _is_user_authorizedz-GatewayAuthorizationMixin._is_user_authorized[  s    	'&&&&& ?x5x7GHHH4::6BB: .$66$:a:aO# ;b ;
 ;
6 4. <<<<!#A 8" c&/2&&  " 	$%'Y/A2%F%F%L%L%N%N"% $) )#5#;#;C#@#@) ) )%
 ///6>EV3V3V#t
226::&#GGXt$D$DgtTTZXZE,1II6K,L,L), ("34I"J"J'>>V^w-F-F#'4    2O04N.	#
 68U++ 	488IIN ")NF"C"C"I"I"K"K"Q"Q"S"SWj"j"jt 	5
7
5
 7
 #%C	

 N1
 O3
 N1
 L-
 !;
 O3
 7
 O3
 N1
 #%C
 O3
   "=!
" N.#
$ 5%
 
* ='
# =N4'
#"
9"
7"
 9"
 #%E	"

 N3"
 O5"
 N3"
 L/"
 !="
 O5"
 9"
 O5"
 N3"
 #%E"
 O5"
   "?!"
" N0#"
$ 7%"
 "
, ?"222	GGGGGG)--fo.CDD V. T<A<S(9* VBGBU.v?    "8!;!;FOR!P!P! 	i0F&G&G&M&M&O&OSg&g&g4 6,e44<<4  28H--b//77$)B)B=RY)Z)Z$4 ''7';';FOR'P'PQQ!!111#,-H-L-LV_^`-a-a#b#b #,-H-L-LV_^`-a-a#b#b $%<==! >	X*> >	XG[ >	Xdt >	X0 77' 8     #'DDD'+'A'A / (B ( ($ ?? / @   $
  $t$ (,'>'> / (? ( ($ ${224
 ..v66G"4 @ @'4PPVTV#'DDD$)II.@$A$AMM$)IIl$;$;M  $/>>G'))SG^^#t677==??CWWW
   	F$48J$J$Jv~$J! !/C/I/I#/N/N! ! ! '''6>=N+N+Nt Ox000$ 1 $666 7 -33C88  O
   t%JERR DNN6 !8!899   @DD<>_444 ee 	dcc6H6N6Ns6S6Scccccc 	fee6J6P6PQT6U6Ueeeeee 	baa6F6L6LS6Q6Qaaaaaa +4I	'>>MM'--,,Q/000 ?h///%(UU") Y Y
&--.KJ.W.WXXXX% 54:7CCDDD!?!H!H! 20111 O'%22  3 MM&*+++I+,,,s&   
A,E9 9
FF5AR	 	
RRc               @   t          | dd          }|rht          |d          rX|rVt          |d          r|j                            |          nd}|r(dt          |di           v r|                    |          S |t
          j        k    rdS |r"t          |d          r|j        dk    r|j        S |r|                     ||	          }|s|rt          |d          r|j                            |          }|rt          |dd          nd}t          |t                    rHt          |                    d
          pd                                                                          }|dk    rdS |dv rdS |ryi t
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        d}t
          j        dt
          j        d i}t=          j        |                    |d          d                                          rdS |                    |d!          D ],}	t=          j        |	d                                          r dS -t=          j        d"d                                          rdS dS )#u}  Return how unauthorized DMs should be handled for a platform.

        Resolution order:
        1. Explicit per-platform ``unauthorized_dm_behavior`` in config — always wins.
        2. Email defaults to ``"ignore"`` unless explicitly opted into
           pairing. Inboxes may contain arbitrary unread human messages, so
           replying with pairing codes is not a safe platform default.
        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. No allowlist and no explicit config → ``"pair"`` (open-gateway default).
        rX   Nget_unauthorized_dm_behaviorrY   unauthorized_dm_behaviorrZ   ignorepairrL   r[   r	   pairing>   disabledr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   )r   r   r   ) r4   r]   rY   r5   r  r   r   r  rc   r&   r^   r   r   r_   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )
r6   r-   r/   rX   rb   r[   rZ   r   platform_group_env_mapenv_keys
             r   _get_unauthorized_dm_behaviorz7GatewayAuthorizationMixin._get_unauthorized_dm_behavior  s   0 x..  	Egf&DEE 	E( 	E=DV[=Y=Yc6+//999_cL E :glT[]_>`>` ` `::8DDD x~%%8  	7gf&@AA 	7.&8866  
	 //'/JJI R RGFK,H,H R%/33H==@LVgt<<<RVeT** R #EIIk$:$:$@b A A G G I I O O Q QII%%v555x
  	$ !#;  #:  !#;  ')G	 
 #8  #9  #8  #6  #%?  #9  !#;  #9  #8  ')G  #9   $&A! " #5# ( ! $  ;&" y)--h;;R@@FFHH  x155hCC $ $9Wb))//11 $#88$ 9,b117799 	8vr!   rp   )r-   r.   r/   r0   )r:   r;   )r:   r   )r:   r   r   r0   )r-   r.   r/   r0   r   rM   )r-   r.   r/   r0   r   r   )r-   r.   rh   r0   r/   r0   r   rM   )r:   r|   )r:   r   r   rM   )__name__
__module____qualname____doc__r9   rA   r>   rK   rR   rU   rc   rg   r{   r   r  r  r   r!   r   r,   r,   =   s       @@
 "&& & & & &6
 
 
 
6   .0 0 0 0$ "&	J J J J J J8 "&	K K K K K K@ "&	#1 #1 #1 #1 #1 #1R "&	!1 !1 !1 !1 !1 !1P "&3 3 3 3 3 3j4 4 4 4x- x- x- x-| "&	g g g g g g g gr!   r,   )r	   )r
   r   r   r   r   r   )r   r   )r  
__future__r   r   typingr   gateway.configr   gateway.sessionr   gateway.whatsapp_identityr   r   r   r   r   r*   r,   r   r!   r   <module>r!     s    " # " " " " " 				       # # # # # # ) ) ) ) ) )       0 0 0 0 0J J J J         r!   