§
    øžyja°  ã                  óˆ   — 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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       ú9/home/ragecks/.hermes/hermes-agent/gateway/authz_mixin.pyÚ	_auth_envr      s¤   € àð ØˆðØ1Ð1Ð1Ð1Ð1Ð1àˆj˜ÑÔˆØˆ?�s 3™xœxŸ~š~Ñ/Ô/ˆ?Ý�s‘8”8—>’>Ñ#Ô#Ð#øøÝð ð ð ØˆðøøøåŒI�d‰OŒOÐ&˜w×-Ò-Ñ/Ô/Ð/s   †AA Á
A)Á(A)c                ó.  — | s|S 	 ddl m}m}  |¦   «         }|�D |¦   «         r:|                     | ¦  «        }|€|S t	          |¦  «                             ¦   «         S n# t          $ r Y nw xY wt          j        | ¦  «        p|                     ¦   «         S )uÓ  Read a platform allow/deny gate env var with per-profile isolation.

    Like ``_auth_env`` but authoritative under multiplex: when a profile
    secret scope is installed AND multiplexing is active, a key absent from
    the scope returns ``default`` instead of falling through to
    ``os.environ``. Under multiplex the process env may hold ANOTHER
    profile's first-writer-bridged value (the YAMLâ†’env bridges in the
    Discord/Telegram adapters' ``_apply_yaml_config`` are first-writer-wins),
    so falling through would leak profile A's allowlist into profile B
    (issue #72348). Single-profile deployments â€” no scope installed, or
    multiplex off â€” behave exactly like the legacy ``os.getenv`` read.
    r   )Úcurrent_secret_scopeÚis_multiplex_active)	r   r   r   Úgetr   r   r   r   r   )r
   r   r   r   Úscoper   s         r   Ú_platform_gate_envr   .   sÀ   € ð ð Øˆð
ØPÐPÐPÐPÐPÐPÐPÐPà$Ð$Ñ&Ô&ˆØÐÐ!4Ð!4Ñ!6Ô!6ÐØ—)’)˜D‘/”/ˆCØˆ{Ø�Ý�s‘8”8—>’>Ñ#Ô#Ð#øøÝð ð ð ØˆðøøøåŒI�d‰OŒOÐ&˜w×-Ò-Ñ/Ô/Ð/s   †6A ½ 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>V   s=   € ÐGÐGÐG dµS¸±Y´Y·_²_Ñ5FÔ5FÐG•�D‘	”	—’Ñ!Ô!ÐGÐGÐGó    c                ó^   — h | ]*}|                      ¦   «         ¯|                      ¦   «         ’Œ+S r!   ©r   r#   s     r   r&   z$_coerce_allow_set.<locals>.<setcomp>W   s-   € ÐIÐIÐI˜T¸D¿JºJ¹L¼LÐIˆD�JŠJ‰LŒLÐIÐIÐIr'   Ú,)ÚsetÚ
isinstanceÚlistr   Úsplit)Úraws    r   Ú_coerce_allow_setr0   K   s_   € ð €{Ý‰uŒuˆÝ�#•tÑÔð HØGÐG¨cÐGÑGÔGÐGØIÐI¥S¨¡X¤X§^¢^°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    r¢d}t          | dd¦  «        }t          |¦  «        r	  |¦   «         }n# t          $ r d}Y nw xY w||k    r(t          | dd¦  «        pi }|                     |¦  «        S t          | 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   Ú_active_profile_nameÚadaptersÚ_profile_adapters)r   ÚgetattrÚcallabler   r   )Úselfr3   r5   Úprofile_nameÚactive_profileÚactive_profile_fnr9   Úprofile_adapterss           r   Ú_authorization_adapterz0GatewayAuthorizationMixin._authorization_adapter]   s>  € ð ð 	Ø�4Ø˜ 2×,Ò,Ñ.Ô.Ð6°$ˆØð 	˜L¨IÒ5Ð5Ø!ˆNÝ '¨Ð.DÀdÑ KÔ KÐÝÐ)Ñ*Ô*ð *ð*Ø%6Ð%6Ñ%8Ô%8�N�NøÝ ð *ð *ð *Ø%)�N�N�Nð*øøøà˜~Ò-Ð-Ý" 4¨°TÑ:Ô:Ð@¸b�Ø—|’| HÑ-Ô-Ð-Ý& tÐ-@À$ÑGÔGÐMÈ2ÐØÐ/Ð/Ð/Ø'¨Ô5×9Ò9¸(ÑCÔCÐCð �4Ý˜4 ¨TÑ2Ô2Ð8°bˆØ�|Š|˜HÑ%Ô%Ð%s   Á
A ÁA"Á!A"Ú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_relayFTr9   r3   r5   )Ú_registered_transport_adapterr;   r   r   ÚRELAYrB   )r=   rC   Útransport_adapterr9   s       r   Ú_adapter_for_sourcez-GatewayAuthorizationMixin._adapter_for_source‚   sª   € àˆ>Ø�4Ø ×>Ò>¸vÑFÔFÐØÐ(Ø$Ð$õ �6Ð9¸5ÑAÔAÀTÐIÐIõ
 ˜t Z°Ñ6Ô6Ð<¸"ˆHØ—<’<¥¤Ñ/Ô/Ð/ð ×*Ò*Ý�F˜J¨Ñ-Ô-Ý�F˜I tÑ,Ô,ñ
ô 
ð 	
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_refNr3   r9   r:   )r;   r<   r   Úvalues)r=   rC   Úadapter_refÚadapterr3   Úprofile_mapsrA   s          r   rG   z7GatewayAuthorizationMixin._registered_transport_adapter�   sá   € õ ˜fÐ&>ÀÑEÔEˆÝ#+¨KÑ#8Ô#8ÐB�+�+‘-”-�-¸dˆÝ˜6 :¨tÑ4Ô4ˆØˆ?˜hÐ.Ø�4Ø•w˜t Z°Ñ6Ô6Ð<¸"×AÒAÀ(ÑKÔKÐKÐKØˆNÝ˜tÐ%8¸$Ñ?Ô?ÐEÀ2ˆØ ,× 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.r3   Nr9   r:   r5   )rG   r;   r   Úitems)r=   rC   rO   r3   r5   rA   s         r   Ú_adapter_profile_for_sourcez5GatewayAuthorizationMixin._adapter_profile_for_source´   sÇ   € à×4Ò4°VÑ<Ô<ˆÝ˜6 :¨tÑ4Ô4ˆØÐØ�7 4¨°TÑ:Ô:Ð@¸b×EÒEÀhÑOÔOÐOÐOØ�tå˜Ð1°4Ñ8Ô8Ð>¸BßŠe‰gŒgð#ð #Ñ)�Ð)ð Ð.×2Ò2°8Ñ<Ô<Ð<Ð<Ø"�N�N�Nð =å�v˜y¨$Ñ/Ô/Ð/r'   ©r5   Ú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©rB   rU   r;   ©r=   r3   r5   rO   s       r   Ú"_adapter_authorization_is_upstreamz<GatewayAuthorizationMixin._adapter_authorization_is_upstreamÂ   sK   € ð" ð 	Ø�5Ø×-Ò-¨h¸Ñ@Ô@ˆØˆ?Ø�5Ý•G˜GÐ%@À%Ñ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_policyrX   rY   s       r   Ú#_adapter_enforces_own_access_policyz=GatewayAuthorizationMixin._adapter_enforces_own_access_policyÚ   sM   € ð$ ð 	Ø�5ð ×-Ò-¨h¸Ñ@Ô@ˆØˆ?Ø�5Ý•G˜GÐ%AÀ5Ñ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©
rB   r;   Úhasattrra   r   r,   Údictr   r   Úlower©r=   r3   r5   rO   Úpolicyr`   Úplatform_cfgrb   s           r   Ú_adapter_dm_policyz,GatewayAuthorizationMixin._adapter_dm_policyö   sû   € ð* ð 	Ø�2Ø×-Ò-¨h¸Ñ@Ô@ˆØ9@Ð9L•˜ ,°Ñ5Ô5Ð5ÐRVˆØˆ>Ý˜T 8¨TÑ2Ô2ˆFð Ð%­'°&¸+Ñ*FÔ*FÐ%ð Ô ×$Ò$ XÑ.Ô.Ð.àð ð
 =IÐR•G˜L¨'°4Ñ8Ô8Ð8ÈdˆEÝ˜%¥Ñ&Ô&ð 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_policyr`   ra   rb   Úgroup_policyrd   rh   s           r   Ú_adapter_group_policyz/GatewayAuthorizationMixin._adapter_group_policy  sû   € ð& ð 	Ø�2Ø×-Ò-¨h¸Ñ@Ô@ˆØ<CÐ<O•˜ /°4Ñ8Ô8Ð8ÐUYˆØˆ>Ý˜T 8¨TÑ2Ô2ˆFð Ð%­'°&¸+Ñ*FÔ*FÐ%ð Ô ×$Ò$ XÑ.Ô.Ð.àð ð
 =IÐR•G˜L¨'°4Ñ8Ô8Ð8ÈdˆEÝ˜%¥Ñ&Ô&ð 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Ú_groupsr`   ra   rb   ÚgroupsÚ*Ú
allow_fromÚ	allowFromc              3  óX   K  — | ]%}t          |¦  «                             ¦   «         V — Œ&d S ©Nr"   )r$   Úitems     r   ú	<genexpr>zPGatewayAuthorizationMixin._adapter_group_has_sender_allowlist.<locals>.<genexpr>p  s2   è è € ÐBÐB¨T•s˜4‘y”y—’Ñ(Ô(ÐBÐBÐBÐBÐBÐBr'   )rB   r;   re   ra   r   r,   rf   r   rg   rR   rU   r   r-   Útupler+   Úany)r=   r3   rp   r5   rO   rs   r`   rj   rb   Ú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Ð2ÈtˆØˆ>Ý˜T 8¨TÑ2Ô2ˆFð Ð%­'°&¸+Ñ*FÔ*FÐ%ð Ô ×$Ò$ XÑ.Ô.Ð.àð ð
 =IÐR•G˜L¨'°4Ñ8Ô8Ð8ÈdˆEÝ˜%¥Ñ&Ô&ð -ØŸš 8Ñ,Ô,�Ý˜&¥$Ñ'Ô'ð 	Ø�5å˜'‘l”lˆØ—J’J˜{Ñ+Ô+ˆ	Ý˜)¥TÑ*Ô*ð 	Ø!×'Ò'Ñ)Ô)ˆGØ$Ÿlšl™nœnð ð ‘
��UÝ˜c¥3Ñ'Ô'ð ¨C¯IªI©K¬K¸7Ò,BÐ,BÅzÐRWÕY]ÑG^ÔG^Ð,BØ %�IØ�EøÝ˜)¥TÑ*Ô*ð 	(ØŸ
š
 3™œˆIÝ˜)¥TÑ*Ô*ð 	Ø�5à —}’} \Ñ2Ô2ÐP°i·m²mÀKÑ6PÔ6PˆÝ�l¥CÑ(Ô(ð 	.Ý˜×*Ò*Ñ,Ô,Ñ-Ô-Ð-Ý�l¥T­5µ#Ð$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_storesNr5   Úpairing_store)r;   )r=   rC   Úper_profiler5   s       r   Ú_pairing_store_forz,GatewayAuthorizationMixin._pairing_store_fors  s[   € õ ˜dÐ$4°dÑ;Ô;ÐA¸rˆÝ˜& )¨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¦  «        }|r?t          |¦  «        }|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¦  «        rT|                     |j        ¦  «        }|r8t          |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%tY          |¦  «                             ¦   «         d>v rdS t%          |d?d¦  «        du rdS |j        r|j        j)        nd}|  -                    |¦  «        }|�| .                    ||¦  «        rdS tY          |                     |j        d¦  «        ¦  «        }d}d}|j
        d@v rPtY          |                     |j        d¦  «        ¦  «        }tY          |                     |j        d¦  «        ¦  «        }tY          dA¦  «        }|�sŸ|�sœ|�s™|�s–|  /                    |j        |¬¦  «        rÏ|j
        dv rA|  0                    |j        |¬¦  «        }|  1                    |j        |j        |¬¦  «        rdS n|  2                    |j        |¬¦  «        }|dBk    rc|j
        dvrX|  3                    |j        |¬¦  «        }|�t%          |dCd¦  «        nd}ti          |¦  «        rtk           ||¦  «        ¦  «        S dS |                      |¦  «        }|�qt%          t%          |dd¦  «        dd¦  «        pi }	|j
        dv r|	                     dD¦  «        }n|	                     dE¦  «        }|rt'          |¦  «        }||v sd|v rdS tY          dF¦  «                             ¦   «         d>v S |r>|j
        d@v r5|j        r.dG„ |                     d
¦  «        D ¦   «         }d|v s	|j        |v rdS |j        t          j        k    rŒ|rŠ|j
        d@v r�|j        rzdH„ |                     d
¦  «        D ¦   «         }|rYt%          | dId¦  «        s=| 6                    dJd
 7                    tq          |¦  «        ¦  «        ¦  «         d| _9        |j        |v rdS tu          ¦   «         }|r2| ;                    dK„ |                     d
¦  «        D ¦   «         ¦  «         |r2| ;                    dL„ |                     d
¦  «        D ¦   «         ¦  «         |r2| ;                    dM„ |                     d
¦  «        D ¦   «         ¦  «         d|v rdS |h} dN|v r.|  <                    |                     dN¦  «        d         ¦  «         |j        t          j        t          j        hv r�tu          ¦   «         }!|D ]$}"|! ;                    t{          |"¦  «        ¦  «         Œ%|!r|!}|  ;                    t{          |¦  «        ¦  «         t}          |¦  «        }#|#r|  <                    |#¦  «         |j        �1|j        j)        dOk    r!|j?        r|  <                    |j?        ¦  «         tk          | |z  ¦  «        S )Pao  
        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   )ÚloggerTrT   >   Ú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>   € ð )ð )ð )àØŸ9š9™;œ;ð)ØŸ	š	™œð)ð )ð )r'   r*   rt   Nr`   rb   Ú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Ú_is_dm_allowedÚgroup_allow_fromru   ÚGATEWAY_ALLOW_ALL_USERSc                ó^   — h | ]*}|                      ¦   «         ¯|                      ¦   «         ’Œ+S r!   r)   )r$   rp   s     r   r&   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>¹  sB   € ð !ð !ð !Ø$+ÐRY×R_ÒR_ÑRaÔRað!Ø—’‘”ð!ð !ð !r'   c                ó„   — h | ]=}|                      ¦   «                              d ¦  «        ¯)|                      ¦   «         ’Œ>S )Ú-)r   Ú
startswith)r$   Úvs     r   r&   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>Ë  sL   € ð ð ð àØ—7’7‘9”9×'Ò'¨Ñ,Ô,ðØ—’‘	”	ðð ð 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 rx   r)   ©r$   Úuids     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>ã  s<   è è € ÐcÐc¨sÐWZ×W`ÒW`ÑWbÔWbÐc˜sŸyšy™{œ{ÐcÐcÐcÐcÐcÐcr'   c              3  óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S rx   r)   rÒ   s     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>å  s<   è è € ÐeÐe¨sÐY\×YbÒYbÑYdÔYdÐe˜sŸyšy™{œ{ÐeÐeÐeÐeÐeÐer'   c              3  óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S rx   r)   rÒ   s     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>ç  s<   è è € ÐaÐa¨sÐUX×U^ÒU^ÑU`ÔU`Ða˜sŸyšy™{œ{ÐaÐaÐaÐaÐaÐar'   ú@Úsimplex)@Úgateway.runr‹   r3   r   ÚHOMEASSISTANTÚWEBHOOKrS   rF   rZ   Úuser_idÚ	chat_typerp   ÚTELEGRAMÚQQBOTr   r   r.   rJ   r;   r0   r   ÚDISCORDÚFEISHUÚSLACKrg   r   ÚWHATSAPPÚWHATSAPP_CLOUDÚSIGNALÚEMAILÚSMSÚ
MATTERMOSTÚMATRIXÚDINGTALKÚWECOMÚWECOM_CALLBACKÚWEIXINÚBLUEBUBBLESÚYUANBAOÚgateway.platform_registryrÁ   r�   Úallowed_users_envÚallow_all_envr   r‰   Úis_approvedr]   ro   rƒ   rk   rB   r<   rU   ÚwarningÚjoinÚsortedrÐ   r+   ÚupdateÚaddÚ_expand_whatsapp_auth_aliasesÚ_normalize_whatsapp_identifierÚ	user_name)$r=   rC   r‹   Úadapter_profilerÛ   Úchat_allowlist_envÚraw_chat_allowlistÚallowed_group_idsrO   rb   Ú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Údm_checkÚ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Ô5µxÔ7GÐHÐHÐHØ�4à×:Ò:¸6ÑBÔBˆð: Ô.°$Ð6Ð6¸$×:aÒ:aØŒOØ#ð ;bñ ;
ô ;
Ð6ð �4à”.ˆð ÔÐ<Ð<Ð<ÀÄÐ<åÔ!Ð#AÝ”Ð 8ð"÷ Šc�&”/ 2Ñ&Ô&ð ð "ð 	$Ý%7Ð8JÑ%KÔ%KÐ"Ø%ð $ð)ð )à#5×#;Ò#;¸CÑ#@Ô#@ð)ñ )ô )Ð%ð
 Ð/Ð/Ð/°6´>ÐEVÐ3VÐ3VØ#˜tð
Ø×2Ò2°6Ñ:Ô:�ØÐ&Ý#¥G¨G°X¸tÑ$DÔ$DÀgÈtÑTÔTÐZÐXZ�EØ,1¯IªIÐ6KÑ,LÔ,LÐ)Ø,ð (Ý"3Ð4IÑ"JÔ"J˜Ø '˜>˜>¨V¬^¸wÐ-FÐ-FØ#' 4øøÝð ð ð Ø�ðøøøõ ÔÐ2ÝŒOÐ0ÝÔÐ4ÝŒNÐ.ð	#
Ðõ �6˜8 UÑ+Ô+ð 	Ø4×8Ò8¸¼ÑIÔIˆNØð Õ"4°^ÀVÑ"LÔ"L×"RÒ"RÑ"TÔ"T×"ZÒ"ZÑ"\Ô"\Ð`sÐ"sÐ"sØ�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Ø)×-Ò-¨f¬oÔ.CÑDÔD�Øð VØÔ.ð TØ<AÔ<SÐ(¨¬Ñ9ØÔ*ð VØBGÔBUÐ.¨v¬Ñ?øøÝð ð ð Ø�ðøøøð "8×!;Ò!;¸F¼OÈRÑ!PÔ!PÐØ!ð 	¥iÐ0FÑ&GÔ&G×&MÒ&MÑ&OÔ&OÐSgÐ&gÐ&gØ�4õ �6Ð,¨eÑ4Ô4¸Ð<Ð<Ø�4ð  28´ÐH˜œÔ-Ð-ÀbˆØ×/Ò/°Ñ7Ô7ˆØÐ$¨×)BÒ)BÀ=ÐRYÑ)ZÔ)ZÐ$Ø�4õ 'Ð'7×';Ò';¸F¼OÈRÑ'PÔ'PÑQÔQÐØ!ÐØ!ÐØÔÐ1Ð1Ð1Ý#,Ð-H×-LÒ-LÈVÌ_Ð^`Ñ-aÔ-aÑ#bÔ#bÐ Ý#,Ð-H×-LÒ-LÈVÌ_Ð^`Ñ-aÔ-aÑ#bÔ#bÐ Ý$Ð%<Ñ=Ô=Ðà!ñ R	XÐ*>ñ R	XÐG[ñ R	XÐdtñ R	Xð0 ×7Ò7Ø”Ø'ð 8ñ ô ð ) ð Ô#Ð'DÐDÐDØ'+×'AÒ'AØœØ /ð (Bñ (ô (Ð$ð ×?Ò?ØœØœØ /ð @ñ ô ð $ð
  $˜tð$ð (,×'>Ò'>ØœØ /ð (?ñ (ô (Ð$ð $ {Ò2Ð2ð Ô'Ð/LÐLÐLØ"&×"=Ò"=Ø"œOØ$3ð #>ñ #ô #˜ð  'Ð2õ $ GÐ-=¸tÑDÔDÐDà!%ð !õ
 $ HÑ-Ô-ð ;Ý#'¨¨°Ñ(9Ô(9Ñ#:Ô#:Ð:Ø˜4ð
 ×.Ò.¨vÑ6Ô6ˆGØÐ"Ý¥¨°¸4Ñ @Ô @À'È4ÑPÔPÐVÐTV�ØÔ#Ð'DÐDÐDØ$)§I¢IÐ.@Ñ$AÔ$A�M�Mà$)§I¢I¨lÑ$;Ô$;�MØ ð $Ý/°Ñ>Ô>�GØ 'Ð)Ð)¨S°G¨^¨^Ø#˜tåÐ6Ñ7Ô7×=Ò=Ñ?Ô?ÐCWÐWÐWð
  ð 	 FÔ$4Ð8JÐ$JÐ$JÈvÌ~Ð$Jð!ð !Ø/C×/IÒ/IÈ#Ñ/NÔ/Nð!ñ !ô !Ðð Ð'Ð'Ð'¨6¬>Ð=NÐ+NÐ+NØ�tð ŒO�xÔ0Ò0Ð0Ø$ð 1àÔ Ð$6Ð6Ð6Ø”ð 7ðð à-×3Ò3°CÑ8Ô8ðñ ô ˆOð
 ð  Ý˜tÐ%JÈEÑRÔRð DØ—N’Nð6ð Ÿš¥¨Ñ!8Ô!8Ñ9Ô9ñô ð ð @D�DÔ<Ø”> _Ð4Ð4Ø˜4õ ‘e”eˆØð 	dØ×ÒÐcÐcÐ6H×6NÒ6NÈsÑ6SÔ6SÐcÑcÔcÑcÔcÐcØð 	fØ×ÒÐeÐeÐ6J×6PÒ6PÐQTÑ6UÔ6UÐeÑeÔeÑeÔeÐeØð 	bØ×ÒÐaÐaÐ6F×6LÒ6LÈSÑ6QÔ6QÐaÑaÔaÑaÔaÐað �+ÐÐØ�4à�Iˆ	Ø�'ˆ>ˆ>Ø�MŠM˜'Ÿ-š-¨Ñ,Ô,¨QÔ/Ñ0Ô0Ð0ð Œ?�xÔ0µ(Ô2IÐJÐJÐJÝ%(¡U¤UÐ"Ø)ð Yð Y�
Ø&×-Ò-Õ.KÈJÑ.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ð �MŠM˜&Ô*Ñ+Ô+Ð+å�I Ñ+Ñ,Ô,Ð,s&   Ã0A,E Å
E,Å+E,ÐAQ* Ñ*
Q7Ñ6Q7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ž|rœt          |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 |�rmi 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=          |                     |d¦  «        ¦  «                             ¦   «         rdS |                     |d!¦  «        D ]&}	t=          |	¦  «                             ¦   «         r dS Œ't=          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).
        r`   NÚget_unauthorized_dm_behaviorra   Úunauthorized_dm_behaviorrb   ÚignoreÚpairrT   rc   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Æ   )r;   re   ra   r   r  r   rå   r  rk   r,   rf   r   r   rg   rÝ   rß   râ   rã   rá   rä   ræ   rç   rè   ré   rà   rê   rë   rì   rí   rÞ   r   )
r=   r3   r5   r`   rj   rc   rb   r  Úplatform_group_env_mapÚenv_keys
             r   Ú_get_unauthorized_dm_behaviorz7GatewayAuthorizationMixin._get_unauthorized_dm_behavior  sœ  € õ0 ˜˜x¨Ñ.Ô.ˆð ð 	E•g˜fÐ&DÑEÔEð 	EÈ(ð 	EÝ=DÀVÈ[Ñ=YÔ=YÐc˜6Ô+×/Ò/°Ñ9Ô9Ð9Ð_cˆLØð EÐ :½gÀlÐT[Ð]_Ñ>`Ô>`Ð `Ð `à×:Ò:¸8ÑDÔDÐDð •x”~Ò%Ð%Ø�8ð ð 	7•g˜fÐ&@ÑAÔAð 	7ØÔ.°&Ò8Ð8ØÔ6Ð6ð ð 
	 Ø×/Ò/°À'Ð/ÑJÔJˆIØð R ð R­G°F¸KÑ,HÔ,Hð RØ%Ô/×3Ò3°HÑ=Ô=�Ø@LÐV� ¨g°tÑ<Ô<Ð<ÐRV�Ý˜e¥TÑ*Ô*ð RÝ # E§I¢I¨kÑ$:Ô$:Ð$@¸bÑ AÔ A× GÒ GÑ IÔ I× OÒ OÑ QÔ Q�IØ˜IÒ%Ð%Ø�vØÐ5Ð5Ð5Ø�xð
 ñ 	$ð ÝÔ!Ð#;ð åÔ Ð#:ð õ Ô!Ð#;ð õ Ô'Ð)Gð	 õ
 ”Ð#8ð õ ”Ð#9ð õ ”Ð#8ð õ ”Ð#6ð õ Ô#Ð%?ð õ ”Ð#9ð õ Ô!Ð#;ð õ ”Ð#9ð õ ”Ð#8ð õ Ô'Ð)Gð õ ”Ð#9ð õ  Ô$Ð&Að! õ" ”Ð#5ð# Ðõ( Ô!ð $õ ”Ð ;ð&Ð"õ "Ð"2×"6Ò"6°xÀÑ"DÔ"DÑEÔE×KÒKÑMÔMð  Ø�xØ1×5Ò5°hÀÑCÔCð $ð $�Ý% gÑ.Ô.×4Ò4Ñ6Ô6ð $Ø#˜8˜8ð$õ Ð5Ñ6Ô6×<Ò<Ñ>Ô>ð 	Ø�8àˆvr'   rx   )r3   r4   r5   r6   )rC   rD   )rC   r   )rC   r   r   r6   )r3   r4   r5   r6   r   rU   )r3   r4   r5   r6   r   r   )r3   r4   rp   r6   r5   r6   r   rU   )rC   r„   )rC   r   r   rU   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rB   rJ   rG   rS   rZ   r]   rk   ro   rƒ   r‰   r  r!  r!   r'   r   r2   r2   Z   sŒ  € € € € € Ø@Ð@ð
 "&ð#&ð #&ð #&ð #&ð #&ðJ
ð 
ð 
ð 
ð6ð ð ð ð.0ð 0ð 0ð 0ð$ "&ð	Jð Jð Jð Jð Jð Jð8 "&ð	Kð Kð Kð Kð Kð Kð@ "&ð	#1ð #1ð #1ð #1ð #1ð #1ðR "&ð	!1ð !1ð !1ð !1ð !1ð !1ðP "&ð3ð 3ð 3ð 3ð 3ð 3ðj4ð 4ð 4ð 4ðM-ð M-ð M-ð M-ðf "&ð	gð gð gð gð gð gð gð gr'   r2   )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   r0   r2   r!   r'   r   ú<module>r+     s  ððð ð" #Ð "Ð "Ð "Ð "Ð "à 	€	€	€	Ø Ð Ð Ð Ð Ð à #Ð #Ð #Ð #Ð #Ð #Ø )Ð )Ð )Ð )Ð )Ð )ðð ð ð ð ð ð ð ð0ð 0ð 0ð 0ð 0ð0ð 0ð 0ð 0ð 0ð:Jð Jð Jð Jð^ð ^ð ^ð ^ð ^ñ ^ô ^ð ^ð ^ð ^r'   