+
    jHàj”!  ã                  ó   € R t ^ RIHt ^ RIt^ RIHt ^ RIHtHtH	t	H
t
 ]P                  ! ]4      tRR0tRtR R ltR	 R
 lt ! R R]4      t]! RR7       ! R R4      4       tR R ltR R ltR R ltRR R lltR# )aÎ  Profile-based routing: route senders/locations to different profiles.

Matching is conjunctive; the highest additive specificity wins (``gateway.profile_routes``
in config.yaml): user_id 16, thread_id 8, chat_id 4, guild_id 2, else the default profile.
For Discord threads/forum posts ``parent_chat_id`` carries the direct parent, so a
channel route also matches any thread/post under it.

A route applies only to messages received by the bot of its ``bot_profile`` (default: the
default profile's shared bot). Telegram DM ``chat_id == user_id`` for EVERY bot, so without
this a ``chat_id`` route meant for the shared bot would re-home the same user's DM with a
dedicated secondary bot into another profile (#104933).
)ÚannotationsN)Ú	dataclass)ÚAnyÚDictÚListÚOptionalÚwhatsappÚwhatsapp_cloudc               ó    € V ^8„  d   QhRRRR/# )é   Úchat_idúOptional[str]ÚreturnÚbool© )Úformats   "Ú</home/ubuntu/.hermes/hermes-agent/gateway/profile_routing.pyÚ__annotate__r      s   € ÷ `ñ `¨ð `¸$ñ `ó    c                óœ   € \        V 4      ;'       d;    \        V 4      P                  4       P                  4       P	                  \
        4      # )uG   True for group / broadcast / newsletter JIDs â€” not a sender identity.)r   ÚstrÚstripÚlowerÚendswithÚ_WHATSAPP_NON_USER_SUFFIXES)r   s   &r   Ú_is_whatsapp_non_user_chatr      s6   € ä�‹=×_Ð_œS ›\×/Ñ/Ó1×7Ñ7Ó9×BÑBÔC^Ó_Ð_r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úplatformr   Úleftr   Úrightr   r   r   )r   s   "r   r   r       s/   € ÷ Uñ U¨Cð U°}ð UÈ]ð UÐ_cñ Ur   c                óN  € T ;'       g    RP                  4       P                  4       \        9  g3   V'       d+   V'       d#   \        V4      '       g   \        V4      '       d   R# ^ RIHp V! \        V4      4      p\        T;'       d    WC! \        V4      4      ,          4      # )u  True when two WhatsApp *user* chat_ids refer to the same person.

Uses ``expand_whatsapp_aliases`` (same helper as session keys and adapter allowlists) so a bare
number, JID and LID collapse to one identity. Group/broadcast JIDs are chats, not senders;
non-WhatsApp platforms â†’ False.
Ú F)Úexpand_whatsapp_aliases)r   r   Ú_WHATSAPP_IDENTITY_PLATFORMSr   Úgateway.whatsapp_identityr"   r   r   )r   r   r   r"   Úleft_aliasess   &&&  r   Ú_whatsapp_user_chat_ids_matchr&       sv   € ð 
�ˆ�R×ÑÓ ×&Ñ&Ó(Ô0LÔLßŸ5Ü% d×+Ò+Ô/IÈ%×/PÒ/PáÝAá*¬3¨t«9Ó5€LÜ�×SÐS Ð0GÌÈEË
Ó0SÕ!SÓTÐTr   c                  ó0   € ] tR t^3tRt]R 4       tR tRtR# )ÚProfileRouteRejectedzZAn explicit route matched a profile this gateway does not serve (``profile``, when known).c                	ó`   € \        V P                  4      ^8”  d   V P                  ^,          # R# )é   N)ÚlenÚargs©Úselfs   &r   ÚprofileÚProfileRouteRejected.profile6   s$   € ä" 4§9¡9›~°Ô1ˆt�y‰y˜�|Ð;°tÐ;r   c                	ób   € V P                   '       d   \        V P                   ^ ,          4      # R# )é    r!   )r,   r   r-   s   &r   Ú__str__ÚProfileRouteRejected.__str__:   s"   € Ø$(§I§I IŒs�4—9‘9˜Q•<Ó Ð5°2Ð5r   r   N)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Úpropertyr/   r3   Ú__static_attributes__r   r   r   r(   r(   3   s   † Ùdàñ<ó ð<ö6r   r(   T)Úfrozenc                  ó´   € ] tR t^>t$ RtR]R&   R]R&   R]R&   RtR]R&   RtR]R	&   RtR]R
&   Rt	R]R&   Rt
R]R&   RtR]R&   ]R R l4       tRR R lltRtR# )ÚProfileRoutez>A single routing rule that maps a platform scope to a profile.r   Únamer   r/   Nr   Úguild_idr   Ú	thread_idTr   ÚenabledÚbot_profileÚuser_idc               ó   € V ^8„  d   QhRR/# )r   r   Úintr   )r   s   "r   r   ÚProfileRoute.__annotate__M   s   € ÷ uñ u˜Sñ ur   c                ó   € ^\        V P                  4      ,          ^\        V P                  4      ,          ,           ^\        V P                  4      ,          ,           ^\        V P                  4      ,          ,           # )z#Higher value = more specific match.)r   r@   r   rA   rD   r-   s   &r   ÚspecificityÚProfileRoute.specificityL   sV   € ð ”4˜Ÿ™Ó&Õ&¨¬T°$·,±,Ó-?Õ)?Õ?À!ÄdÈ4Ï>É>ÓFZÕBZÕZÐ]_ÔbfÐgk×gsÑgsÓbtÕ]tÕtÐtr   c               ó8   € V ^8„  d   QhRRRRRRRRRRRRR	RR
R/# )r   r   r   r@   r   r   rA   Úparent_chat_idÚadapter_profilerD   r   r   r   )r   s   "r   r   rG   Q   s]   € ÷ Añ AØðAØ'4ðAØFSðAà ðAà9FðAð 'ðAð 9FðAð 
ñ	Ar   c                ón  € V P                   '       d   V P                  V8w  d   R# \        V P                  4      \        V4      8w  d   R# V P                  e4   V P                  P                  4       '       d   V P                  V8w  d   R# V P                  '       d   V P                  V8w  d   R# V P                  '       dM   V P                  W539  d;   \        WP                  V4      '       g   \        WP                  V4      '       g   R# V P                  ;'       d    V P                  V8g  '       * # )a  True if every discriminator the route declares holds (AND).

``chat_id`` matches the channel directly or as the parent of a thread/forum post; WhatsApp
``chat_id`` also matches across number/JID/LID after the exact check (groups/broadcasts stay exact-only).
``adapter_profile`` is the profile owning the receiving bot (``None`` = default); it must equal
the route's ``bot_profile``.
F)
rB   r   Ú_bot_profile_keyrC   rD   r   rA   r   r&   r@   )r.   r   r@   r   rA   rL   rM   rD   s   &&&&&&&&r   ÚmatchesÚProfileRoute.matchesQ   sÔ   € ð �|�|ˆ|˜tŸ}™}°Ô8ÙÜ˜D×,Ñ,Ó-Ô1AÀ/Ó1RÔRÙØ�<‰<Ò#¨T¯\©\×-?Ñ-?×-AÒ-AÀTÇ\Á\ÐU\ÔE\ÙØ�>�>ˆ>˜dŸn™n°	Ô9Ùà�L�LˆLØ—‘ WÐ$=Ô=Ü1°(¿L¹LÈ'×RÒRÜ1°(¿L¹LÈ.×YÒYáØ—M‘M×?Ð? d§m¡m°xÑ&?Ô@Ð@r   r   ©NNNNNN)r5   r6   r7   r8   r9   Ú__annotations__r@   r   rA   rB   rC   rD   r:   rI   rP   r;   r   r   r   r>   r>   >   sp   ‡ áHà
ƒIØƒMØƒLØ"€HˆmÓ"Ø!€Gˆ]Ó!Ø#€Iˆ}Ó#Ø€GˆTÓØ!%€K�Ó%Ø!€Gˆ]Ó!àôuó ðu÷Aó Ar   r>   c               ó    € V ^8„  d   QhRRRR/# )r   r?   r   r   r   )r   s   "r   r   r   o   s   € ÷ ;ñ ;˜=ð ;¨]ñ ;r   c                ó\   € T ;'       g    RP                  4       p V '       d   V R8X  d   R# T # )zX``None`` for the default profile, else the profile name (mirrors ``set_owner_profile``).r!   ÚdefaultN)r   )r?   s   &r   rO   rO   o   s*   € à�JˆJ�B×ÑÓ€Dß˜t yÔ0ˆ4Ð:°dÐ:r   c               ó    € V ^8„  d   QhRRRR/# )r   Úvaluer   r   r   r   )r   s   "r   r   r   u   s   € ÷ ñ ˜Cð  Mñ r   c                ó  € V e   \        V \        4      '       d   V # \        V \        4      '       d"   \        V \        4      '       g   \        V 4      # \        P                  RV \        V 4      P                  V 4       \        V 4      # )u`  Normalize a route discriminator to str for strict equality matching.

YAML loads unquoted numeric IDs as ``int`` while ``SessionSource`` fields are ``str``. Only
``int`` (not ``bool``) is coerced; floats stringify to something (``"123.0"``) that can never
match, so they get a load-time warning instead.

``bool`` is an ``int`` subclass but never a valid id; floats and other types stringify to something
(``"123.0"``) that can never equal an inbound id â€” recreating the silent no-match this exists to fix â€”
so they are passed through with a load-time warning instead of being silently "fixed" (#86470).
ux   Profile route discriminator %r (type %s) can never match an inbound id â€” quote it in config.yaml (e.g. chat_id: "%s").)Ú
isinstancer   rF   r   ÚloggerÚwarningÚtyper5   )rX   s   &r   Ú_coerce_route_idr^   u   sl   € ð ‚}œ
 5¬#×.Ò.ØˆÜ�%œ×Ò¤j°¼×&=Ò&=Ü�5‹zÐÜ
‡N�Nð	AàŒt�E‹{×#Ñ# Uôô
 ˆu‹:Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚrawzOptional[list[dict[str, Any]]]r   úlist[ProfileRoute]r   )r   s   "r   r   r   Œ   s   € ÷ &ñ &Ð<ð &ÐASñ &r   c                ó  € V '       g   . # . pV  EF“  p\        V\        4      '       g   K  VP                  RR4      pVP                  RR4      pVP                  RR4      pV'       d	   V'       g   \        P	                  RV4       Kz   ^ RIHpHp V! V4      pV! V4       RT9   TP                  R4      r˜T'       dI   T	e-   \        T	\        4      '       d/   T	P                  4       '       g   \        P	                  R	T4       Kø  TP                  \        Y4T\        TP                  R
4      4      \        TP                  R4      4      \        TP                  R4      4      \        T	4      TP                  RR4      \        TP                  R4      4      R7	      4       EK–  	  VP!                  R RR7       \        P#                  R\%        V4      4       V#   \        \        3 d    \        P	                  RY54        EKø  i ; i)zBParse profile_routes from config.yaml, sorted most-specific-first.r?   r!   r   r/   z6Skipping profile route %s: missing platform or profile)Únormalize_profile_nameÚvalidate_profile_namez2Skipping profile route %s: invalid profile name %rrD   z:Skipping profile route %s: user_id cannot be null or emptyr@   r   rA   rB   TrC   )	r?   r   r/   r@   r   rA   rD   rB   rC   c                ó   € V P                   # )N)rI   )Úrs   &r   Ú<lambda>Ú&parse_profile_routes.<locals>.<lambda>°   s   € ˜aŸmšmr   )ÚkeyÚreversez.Loaded %d profile routes (most-specific-first))rZ   ÚdictÚgetr[   r\   Úhermes_cli.profilesrc   rd   Ú
ValueErrorÚImportErrorr   r   Úappendr>   r^   rO   ÚsortÚdebugr+   )
r`   ÚroutesÚentryr?   r   r/   rc   rd   Úhas_user_idrD   s
   &         r   Úparse_profile_routesrv   Œ   s�  € çØˆ	Ø!#€FÜˆÜ˜%¤×&Ò&ÙØ�y‰y˜ Ó$ˆØ—9‘9˜Z¨Ó,ˆØ—)‘)˜I rÓ*ˆßŸwÜ�N‰NÐSÐUYÔZÙð	ßYá,¨WÓ5ˆGÙ! 'Ô*ð  )¨EÑ1°5·9±9¸YÓ3G�Wß˜GšO¬z¸'Ä3×/GÒ/GÐPW×P]ÑP]×P_ÒP_Ü�N‰NÐWÐY]Ô^ÙØ�‰”lØ°'Ü% e§i¡i°
Ó&;Ó<Ü$ U§Y¡Y¨yÓ%9Ó:Ü& u§y¡y°Ó'=Ó>Ü$ WÓ-Ø—I‘I˜i¨Ó.Ü(¨¯©°=Ó)AÓBô
÷ 	ñ- ð> ‡K�KÑ+°T€KÔ:Ü
‡L�LÐAÄ3ÀvÃ;ÔOØ€Møô% œKÐ(ô 	Ü�N‰NÐOÐQUÔ_Ûð	ús   Â
GÇ'HÈHc               ó<   € V ^8„  d   QhRRRRRRRRRRR	RR
RRRRR/	# )r   rs   ra   r   r   r@   r   r   rA   rL   rM   rD   r   zOptional[ProfileRoute]r   )r   s   "r   r   r   µ   s[   € ÷ ñ ØðØ*-ðØ9FðØXeðàðà5Bðð #ðð 5Bðð ñ	r   c                óV   € V  F"  pVP                  WW4WVVR7      '       g   K   Vu # 	  R# )z9Return the first (most specific) matching route, or None.)r@   r   rA   rL   rM   rD   N)rP   )	rs   r   r@   r   rA   rL   rM   rD   Úroutes	   &&&&&&&& r   Úmatch_profile_routerz   µ   s:   € ó ˆØ�=‰=˜¸gØ(6Ø!(ð ÷ *ô *ð ŠLñ	 ñ
 r   )z@g.usz
@broadcastz@newsletterrR   )r9   Ú
__future__r   ÚloggingÚdataclassesr   Útypingr   r   r   r   Ú	getLoggerr5   r[   r#   r   r   r&   ÚRuntimeErrorr(   r>   rO   r^   rv   rz   r   r   r   Ú<module>r�      s“   ðñõ #ã Ý !ß ,Ó ,à	×	Ò	˜8Ó	$€ð !+Ð,<Ð=Ð ØDÐ õ`õ
Uô&6˜<ô 6ñ �$Ô÷-Að -Aó ð-Aõ`;õõ.&÷Rñ r   