Ë
    âß²jA  ã                  ó¸   — d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZ ddlmZ  ej                  e«      Z eddh«      Zdd	„Zdd
„Z G d„ d«      Zy)ao  Persistent registry of delivery targets confirmed unreachable. Re-sending to a permanently gone chat
(deleted group, bot kicked/blocked, deactivated user) every cron tick wastes flood-control budget; delivery
short-circuits targets proven dead and any later successful send clears the flag. Only *whole-chat* deaths
(``forbidden``, chat-level ``not_found``) are recorded: adapters self-heal thread/topic-level ``not_found``
by retrying without ``reply_to``. Storage is a per-profile JSON file; reads/writes are best-effort (a
corrupt or unwritable file degrades to in-memory-only rather than raising on the delivery path).
é    )ÚannotationsN)ÚPath)ÚDictÚOptional)Úget_hermes_homeÚ	forbiddenÚ	not_foundc                óˆ   — t        | «      j                  «       j                  «       › dt        |«      j                  «       › �S )z-Canonical key for a (platform, chat_id) pair.ú:)ÚstrÚstripÚlower)ÚplatformÚchat_ids     ú9/home/ubuntu/.hermes/hermes-agent/gateway/dead_targets.pyÚ
_normalizer      s8   € ä�(‹m×!Ñ!Ó#×)Ñ)Ó+Ð,¨A¬c°'«l×.@Ñ.@Ó.BÐ-CÐDÐDó    c                óŒ   — | sy	 ddl m}m}  |d| ¬«      }|t        v xr |dk7  xs	  || ¬«      }|r|S dS # t        $ r Y yw xY w)uœ  Best-effort dead-target error_kind from a raised error's text, else None. ``_deliver_to_platform``
    raises on hard failure (no SendResult), so ``deliver()`` only has the exception string. ``not_found``
    collapses chat-level and thread/topic/message-level failures: only a whole-chat not_found means the
    target is dead â€” a deleted forum topic or edited-away message must not mark the entire chat dead.Né   )Úclassify_send_errorÚis_chat_level_not_found)Ú
error_textr	   )Úplatforms.baser   r   Ú	ExceptionÚ_DEAD_ERROR_KINDS)r   r   r   ÚkindÚdeads        r   Úclassify_dead_errorr      sb   € ñ
 ØðßPñ ˜t°
Ô;€DØÔ$Ð$Òp¨$°+Ñ*=Ò*oÑAXÐdnÔAo€DÙˆ4Ð!˜TÐ!øô	 ò Ùðús   …7 ·	AÁAc                  ó<   — e Zd ZdZdd	d„Zd
d„Zdd„Zddd„Zdd„Zy)ÚDeadTargetRegistryzËThread-safe, persistent set of confirmed-dead targets keyed ``platform:chat_id``. Each entry stores
    reason + timestamp for observability; :meth:`clear` (called on a successful send) removes the flag.Nc                ó8  — t        j                  «       | _        i | _        |�|nt	        «       dz  dz  | _        	 | j
                  j                  «       r/t        j                  | j
                  j                  d¬«      «      ni }t        |t        «      r;|j                  «       D ��ci c]  \  }}t        |t        «      sŒ||“Œ c}}| _        y i | _        y c c}}w # t        t        f$ r+}t        j!                  d| j
                  |«       Y d }~y d }~ww xY w)NÚgatewayzdead_targets.jsonúutf-8©Úencodingu7   dead_targets: could not load %s (%s) â€” starting empty)Ú	threadingÚRLockÚ_lockÚ_deadr   Ú_pathÚexistsÚjsonÚloadsÚ	read_textÚ
isinstanceÚdictÚitemsÚOSErrorÚ
ValueErrorÚloggerÚdebug)ÚselfÚpathÚrawÚkÚvÚexcs         r   Ú__init__zDeadTargetRegistry.__init__3   sÜ   € Ü—_‘_Ó&ˆŒ
Ø35ˆŒ
Ø!Ð-‘T´?Ó3DÀyÑ3PÐSfÑ3fˆŒ
ð	eØHLÏ
É
×HYÑHYÔH[”$—*‘*˜TŸZ™Z×1Ñ1¸7Ð1ÓCÔDÐacˆCÜR\Ð]`ÔbfÔRg¨3¯9©9«;×N¡4 1 a¼*ÀQÌÕ:M˜!˜Q™$ÓNˆD�JÐmoˆD�JùÓNøÜœÐ$ò 	eÜ�L‰LÐRÐTX×T^ÑT^Ð`c×dÑdûð	eús6   »A.C Â)CÃCÃ	C ÃC ÃC ÃDÃ.!DÄDc                óÀ  — 	 | j                   j                  j                  dd¬«       | j                   j                  | j                   j                  dz   «      }|j                  t        j                  | j                  d¬«      d¬«       |j                  | j                   «       y # t        $ r+}t        j                  d| j                   |«       Y d }~y d }~ww xY w)	NT)ÚparentsÚexist_okz.tmpé   )Úindentr#   r$   z'dead_targets: could not persist %s (%s))r*   ÚparentÚmkdirÚwith_suffixÚsuffixÚ
write_textr,   Údumpsr)   Úreplacer2   r4   r5   )r6   Útmpr;   s      r   Ú_flush_lockedz DeadTargetRegistry._flush_locked=   s¢   € ð	UØ�J‰J×Ñ×#Ñ#¨D¸4Ð#Ô@Ø—*‘*×(Ñ(¨¯©×):Ñ):¸VÑ)CÓDˆCØ�N‰Nœ4Ÿ:™: d§j¡j¸Ô;ÀgˆNÔNØ�K‰K˜Ÿ
™
Õ#øÜò 	UÜ�L‰LÐBÀDÇJÁJÐPS×TÑTûð	Uús   ‚B&B) Â)	CÂ2!CÃCc                ó�   — | j                   5  t        |«      xr t        ||«      | j                  v cd d d «       S # 1 sw Y   y xY w©N)r(   Úboolr   r)   )r6   r   r   s      r   Úis_deadzDeadTargetRegistry.is_deadF   s>   € Ø�Z‰Zñ 	QÜ˜“=ÒP¤Z°¸'Ó%BÀdÇjÁjÐ%P÷	Q÷ 	Qò 	Qús	   �%<¼Ac                óž  — |syt        ||«      }| j                  5  || j                  v }t        |«      j	                  «       j                  «       t        |«      t        |«      dd t        j                  «       dœ| j                  |<   | j                  «        ddd«       st        j                  d||xs d«       | S # 1 sw Y   Œ)xY w)z?Record a target as confirmed-dead. Returns True if newly added.FNéÈ   )r   r   ÚreasonÚ	marked_atuv   dead_targets: marked %s as unreachable (%s) â€” future deliveries to this target will be skipped until a send succeedszno reason given)
r   r(   r)   r   r   r   ÚtimerJ   r4   Úinfo)r6   r   r   rQ   ÚkeyÚexisteds         r   Ú	mark_deadzDeadTargetRegistry.mark_deadJ   s¾   € áØÜ˜ 7Ó+ˆØ�Z‰Zñ 	!Ø˜TŸZ™ZÐ'ˆGÜ+.¨x«=×+>Ñ+>Ó+@×+FÑ+FÓ+HÔUXÐY`ÓUaÜ),¨V«°T°cÐ):ÌÏÉËñVˆD�J‰J�s‰Oà×ÑÔ ÷		!ñ
 Ü�K‰Kð OØPSÐU[ÒUpÐ_pôràˆ{Ð÷	!ð 	!ús   œA?CÃCc                óü   — |syt        ||«      }| j                  5  | j                  j                  |d«      €
	 ddd«       y| j	                  «        t
        j                  d|«       ddd«       y# 1 sw Y   yxY w)zGRemove a target's dead flag (self-healing). Returns True if it was set.FNz3dead_targets: cleared %s (delivery succeeded again)T)r   r(   r)   ÚpoprJ   r4   rT   )r6   r   r   rU   s       r   ÚclearzDeadTargetRegistry.clearY   sy   € áØÜ˜ 7Ó+ˆØ�Z‰Zñ 	TØ�z‰z�~‰~˜c 4Ó(Ð0Ø÷	Tð 	Tð ×ÑÔ Ü�K‰KÐMÈsÔS÷		Tð
 ÷	Tð
 ús   œA2Á&A2Á2A;rL   )r7   zOptional[Path]ÚreturnÚNone)r[   r\   )r   r   r   úOptional[str]r[   rM   )Ú )r   r   r   r]   rQ   r   r[   rM   )	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r<   rJ   rN   rW   rZ   © r   r   r    r    /   s$   „ ñkôeóUóQôô
r   r    )r   r   r   r   r[   r   )r   r]   r[   r]   )rb   Ú
__future__r   r,   Úloggingr&   rS   Úpathlibr   Útypingr   r   Úhermes_cli.configr   Ú	getLoggerr_   r4   Ú	frozensetr   r   r   r    rc   r   r   ú<module>rk      s]   ðñõ #ã Û Û Û Ý ß !å -à	ˆ×	Ñ	˜8Ó	$€ñ ˜{¨KÐ8Ó9Ð óEó
"÷ 4ò 4r   