
    PL
jNB                       d Z ddlZddlZddlZddlZddlZddlZddlZddl	m
Z
mZmZmZ  ej        e          Z	 ddlmamamamama 	 ddlma n# e$ r daY nw xY wddlmamamamamam a  ddl!m"a"m#a# ddl$m%a% d	a&n8# e$ r0 d
a&eaeaeaeaeadaeaeaeaeaea%da da"da# G d d          Z'e'aY nw xY wddl(Z(ddl)m*Z+ e(j,        -                    d e. e+e/          0                                j1        d                              ddl2m3Z3m4Z4 ddl5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAmBZB ddlCmDZDmEZEmFZF ddlGmHZH h dZIddddddZJddddddZKdeLfdZM ejN        d           ZOd!e.de.fd"ZPd!e.de.fd#ZQ ejN        d$          ZRd%e.deLfd&ZSd%e.deTe.         fd'ZUd(eTe.         de.fd)ZVd!e.de.fd*ZW G d+ d,e6          ZXdS )-z
Telegram platform adapter.

Uses python-telegram-bot library for:
- Receiving messages from users/groups
- Sending responses back
- Handling media and commands
    N)DictListOptionalAny)UpdateBotMessageInlineKeyboardButtonInlineKeyboardMarkupLinkPreviewOptionsApplicationCommandHandlerCallbackQueryHandlerMessageHandlerContextTypesfilters	ParseModeChatTypeHTTPXRequestTFc                       e Zd ZeZdS )_MockContextTypesN)__name__
__module____qualname__r   DEFAULT_TYPE     >/home/kuhnn/.hermes/hermes-agent/gateway/platforms/telegram.pyr   r   9   s        r!   r   )Path   )PlatformPlatformConfig)BasePlatformAdapterMessageEventMessageTypeProcessingOutcome
SendResultcache_image_from_bytescache_audio_from_bytescache_video_from_bytescache_document_from_bytesresolve_proxy_urlSUPPORTED_VIDEO_TYPESSUPPORTED_DOCUMENT_TYPES	utf16_len)TelegramFallbackTransportdiscover_fallback_ipsparse_fallback_ip_env)atomic_replace>   .gif.jpg.png.jpeg.webpr:   r9   r<   r8   )	image/png
image/jpegz	image/jpg
image/webp	image/gifr=   r>   r?   r@   )r:   r9   r;   r<   r8   returnc                  ^   t           rdS 	 ddlm}   | dd           n# t          $ r Y dS w xY w	 ddlm}m}m} ddlm}m	} 	 dd	lm
} n# t          $ r d
}Y nw xY wddlm}m}m}	m}
m}m} ddlm}m} ddlm} n# t          $ r Y dS w xY w|a|a|a|a|a	|a
|a|a|	a|
a|a|a|a|a|ada dS )aF  Check if Telegram dependencies are available.

    If python-telegram-bot is missing, attempts to lazy-install it via
    ``tools.lazy_deps.ensure("platform.telegram")``. After a successful
    install, re-imports the SDK and flips ``TELEGRAM_AVAILABLE`` to True
    so the adapter's class-level type aliases get rebound.
    Tr   )ensurezplatform.telegramF)prompt)r   r   r	   )r
   r   r   Nr   r   r   )TELEGRAM_AVAILABLEtools.lazy_depsrC   	Exceptiontelegramr   r   r	   r
   r   r   ImportErrortelegram.extr   r   r   r   r   r   telegram.constantsr   r   telegram.requestr   TelegramMessageHandler)_lazy_ensure_Update_Bot_Message_IKB_IKM_LPO_App_CH_CQH_MH_CT_filters_PM_CtT_HRs                   r"   check_telegram_requirementsr^   i   s     t::::::(77777   uuPPPPPPPPPPWWWWWWWW	;;;;;;; 	 	 	DDD		
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	JIIIIIII8888888   uuF
CGKN LGIHL4sA    
--A< A 
A< AA< A!A< <
B
	B
z([_*\[\]()~`>#\+\-=|{}.!\\])textc                 8    t                               d|           S )zIEscape Telegram MarkdownV2 special characters with a preceding backslash.z\\\1)_MDV2_ESCAPE_REsubr_   s    r"   _escape_mdv2rd      s    w---r!   c                     t          j        dd|           }t          j        dd|          }t          j        dd|          }t          j        dd|          }t          j        dd|          }|S )zStrip MarkdownV2 escape backslashes to produce clean plain text.

    Also removes MarkdownV2 formatting markers so the fallback
    doesn't show stray syntax characters from format_message conversion.
    z\\([_*\[\]()~`>#\+\-=|{}.!\\])\1z\*([^*]+)\*z(?<!\w)_([^_]+)_(?!\w)z	~([^~]+)~z\|\|([^|]+)\|\|rerb   )r_   cleaneds     r"   _strip_mdv2rj      sm     f6tDDGf^UG44G f.w??Gf\5'22Gf'88GNr!   z0^\s*\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*){1,}\|?\s*$linec                 P    |                                  }t          |          od|v S )z:Return True if *line* could plausibly be a table data row.|)stripboolrk   strippeds     r"   _is_table_rowrr      s$    zz||H>>-cXo-r!   c                     |                                  }|                    d          r
|dd         }|                    d          r
|dd         }d |                    d          D             S )z7Split a simple GFM table row into stripped cell values.rm      Nc                 6    g | ]}|                                 S r    rn   .0cells     r"   
<listcomp>z-_split_markdown_table_row.<locals>.<listcomp>   s     999TDJJLL999r!   )rn   
startswithendswithsplitrp   s     r"   _split_markdown_table_rowr      su    zz||H3  ABB< !CRC=99X^^C%8%89999r!   table_blockc                 "   t          |           dk     rd                    |           S t          | d                   }t          |          dk     rd                    |           S t          |           dk    rt          | d                   ng }t          |          t          |          dz   k    }g }t          | dd         d          D ],\  }}t          |          }|r"|r|d         r|d         nd| }|dd         }	nt	          d	 |D             d|           }|}	t          |	          t          |          k     r7|	                    d
gt          |          t          |	          z
  z             n7t          |	          t          |          k    r|	dt          |                   }	|                    d| d           |                    d t          ||	          D                        .d                    |          S )z<Render a detected GFM table as Telegram-friendly row groups.   
r   r$   rt   N)startzRow c              3      K   | ]}||V  	d S Nr    rx   s     r"   	<genexpr>z3_render_table_block_for_telegram.<locals>.<genexpr>   s'      ;;Td;D;;;;;;r!    **c              3   ,   K   | ]\  }}d | d| V  dS )u   • z: Nr    )ry   headervalues      r"   r   z3_render_table_block_for_telegram.<locals>.<genexpr>   sH       
 
)6$6$$U$$
 
 
 
 
 
r!   

)lenjoinr   	enumeratenextextendappendzip)
r   headersfirst_data_rowhas_row_label_colrendered_rowsindexrowcellsheading
data_cellss
             r"    _render_table_block_for_telegramr      s/   
;!yy%%%'A77G
7||ayy%%% CFkBRBRUVBVBV.{1~>>>\^NN++s7||a/??!MABBq999 
 

s)#.. 	"'HE!HHeAhh...GqrrJJ ;;U;;;^E^^LLGJ z??S\\))rdc'llS__&DEFFFF__s7||++#Nc'llN3J-'---... 
 
:=gz:R:R
 
 
 	
 	
 	
 	
 ;;}%%%r!   c                    d| vsd| vr| S |                      d          }g }d}d}|t          |          k     r||         }|                                }|                    d          r| }|                    |           |dz  }c|r|                    |           |dz  }d|v r|dz   t          |          k     rt
                              ||dz                      r|||dz            g}|dz   }|t          |          k     r]t          ||                   rH|                    ||                    |dz  }|t          |          k     rt          ||                   H|                    t          |                     |}e|                    |           |dz  }|t          |          k     d	                    |          S )	ay  Rewrite GFM-style pipe tables into Telegram-friendly bullet groups.

    Detected by a row containing '|' immediately followed by a delimiter
    row matching :data:`_TABLE_SEPARATOR_RE`.  Subsequent pipe-containing
    non-blank lines are consumed as the table body and rewritten as
    per-row bullet groups. Tables inside existing fenced code blocks are left
    alone.
    rm   -r   Fr   ```rt   r$   )
r~   r   lstripr|   r   _TABLE_SEPARATOR_REmatchrr   r   r   )	r_   linesoutin_fenceirk   rq   r   js	            r"   _wrap_markdown_tablesr     s    $#T//JJtECH	A
c%jj..Qx;;== u%% 	#|HJJtFA 	JJtFA
 4KKAE

""#))%A,77 #  q1u.KAAc%jj..]58%<%<.""58,,,Q c%jj..]58%<%<. JJ7DDEEEA

4	QA c%jj..D 99S>>r!   c                       e Zd ZU dZdZdZdZdZdZe	e
d<   dZd	Zd
ZdZeddddededee         dee         def
d            Zed             Zdef fdZdeeeef                  deeef         fdZddddddedee         dee         dee         dee         de	fdZedeeeef                  dee         fd             Zedeeeef                  dee         fd!            Zedeeeef                  dee         fd"            Z e	 dd#ee         deeeef                  dee         fd$            Z!e	 	 ddedee         deeeef                  d%ee         deeef         f
d&            Z"edee         dee         fd'            Z#edee         dee         fd(            Z$ed)e%de	fd*            Z&ed)e%de	fd+            Z'ed)e%deeeef                  d%ee         de	fd,            Z(	 dd-ed.eeef         deeeef                  d%ee         d/ed0ee         defd1Z)de*e         fd2Z+ed)e%de	fd3            Z,ed)e%de	fd4            Z-dd6ede	de	fd7Z.deeef         fd8Z/dd9Z0d)e%ddfd:Z1dd;Z2d)e%ddfd<Z3	 	 ddeded=ee         d>ee         dee         f
d?Z4d@ededee         fdAZ5dedededdfdBZ6dedCededdfdDZ7ddEZ8de	fdFZ9ddGZ:d#ee         dHede	fdIZ;	 	 ddedJed#ee         deeeef                  de<f
dKZ=d5dLdedMedJedNe	de<f
dOZ>dedMedJedNe	de<f
dPZ?dedMede	fdQZ@	 	 ddee         deeeef                  de	fdRZA	 ddedSedJedeeeef                  de<f
dTZBdU ZC	 	 	 ddedWededXedeeeef                  de<fdYZD	 	 dded[edXed\edeeeef                  de<fd]ZE	 dded^ed_edXed`edeeeef                  de<fdaZF	 ddedbedcee*         ddedXedeeeef                  de<fdeZG	 ddedfe*dgedhedXedeeeef                  de<fdiZHdjZIdke*dledeJfdmZKdnededdfdoZL	 	 	 	 	 	 ddtZMduedvedefdwZN	 	 	 ddedxedyee         d#ee         deeeef                  de<f fdzZO	 	 dded|ePeJ         deeeef                  d}eddf
 fd~ZQ	 	 	 ddededyee         d#ee         deeeef                  de<f fdZR	 	 	 	 ddededyee         dee         d#ee         deeeef                  de<f fdZS	 	 	 ddededyee         d#ee         deeeef                  de<f fdZT	 	 	 ddededyee         d#ee         deeeef                  de<f fdZU	 	 	 ddededyee         d#ee         deeeef                  de<fdZVddedeeeef                  ddfdZWdedeeef         fdZXdJedefdZYde	fdZZde	fdZ[de\e         fdZ]de\e         fdZ^de\e         fdZ_dePe`ja                 fdZbd_ecde	fdZdd_ecde	fdZed_ecde	fdZfd_ecde	fdZgd_ecde	fdZhdee         dee         fdZid5dd_ecde	de	fdZjdpekdreljm        ddfdZndpekdreljm        ddfdZodpekdreljm        ddfdZpdeqdefdZrdeqddfdZsd6eddfdZtdeqdecdefdZudeddfdZvdedeqddfdZwdpekdreljm        ddfdZxdedeqddfdZydeddfdZzdecddddfdZ{ddZ|dedee         deeeef                  fdZ}dededCeddfdZ~	 dd_ecdedee         deqfdZde	fdZdedMedede	fdZdedMede	fdZdeqddfdZdeqdeddfdZ xZS )TelegramAdapterz
    Telegram bot adapter.

    Handles:
    - Receiving messages from users and groups
    - Sending responses with Telegram markdown
    - Forum topics (thread_id support)
    - Media messages
    i   i  g?1TREQUIRES_EDIT_FINALIZEi@  g
ףp=
?   gQ?N	min_value	max_valuenamedefaultr   r   rA   c                V   ddl }t          j        |           }	 |t          |          nt          |          }n&# t          t
          f$ r t          |          }Y nw xY w|                    |          st          |          }|t          ||          }|t          ||          }|S )zRead a float env var, reject non-finite values, and clamp to bounds.

        Guarantees the returned value is a finite number usable directly in
        ``asyncio.sleep()`` and similar APIs that reject NaN / Inf.
        r   N)	mathosgetenvfloat	TypeError
ValueErrorisfinitemaxmin)r   r   r   r   r   rawr   s          r"   _env_float_clampedz"TelegramAdapter._env_float_clampeda  s     	ioo	#"%/E#JJJuW~~EE:& 	# 	# 	#'NNEEE	#}}U## 	#'NNE y))E y))Es    ;  AAc                     t           S )z6Telegram measures message length in UTF-16 code units.)r3   selfs    r"   message_len_fnzTelegramAdapter.message_len_fn}  s
     r!   configc                    t                                          |t          j                   d | _        d | _        d| _        |                                 | _        t          |dd          pd| _
        |                     dd          | _        t          t          j        dd                    | _        i | _        i | _        i | _        i | _        |                     ddd	d
          | _        |                     dd| j        d          | _        i | _        i | _        d | _        d| _        d| _        d | _        i | _        | j        j         !                    dg           | _"        i | _#        i | _$        i | _%        i | _&        d| _'        d S )NFreply_to_modefirstdisable_link_previews)HERMES_TELEGRAM_MEDIA_BATCH_DELAY_SECONDSz0.8(HERMES_TELEGRAM_TEXT_BATCH_DELAY_SECONDSg333333?g{Gz?g       @r   .HERMES_TELEGRAM_TEXT_BATCH_SPLIT_DELAY_SECONDS      ?g      @r   	dm_topics	important)(super__init__r%   TELEGRAM_app_bot_webhook_mode_compile_mention_patterns_mention_patternsgetattr_reply_to_mode_coerce_bool_extra_disable_link_previewsr   r   r   _media_batch_delay_seconds_pending_photo_batches_pending_photo_batch_tasks_media_group_events_media_group_tasksr   _text_batch_delay_seconds_text_batch_split_delay_seconds_pending_text_batches_pending_text_batch_tasks_polling_error_task_polling_conflict_count_polling_network_error_count_polling_error_callback_ref
_dm_topicsr   extraget_dm_topics_config_model_picker_state_approval_state_slash_confirm_state_clarify_state_notifications_mode)r   r   	__class__s     r"   r   zTelegramAdapter.__init__  s   !2333+/	#'	#(!%!?!?!A!A#*6?G#L#L#WPW,0,C,CD[]b,c,c# +0	:egl0m0m*n*n'?A#CE'<> ;= *.)@)@6	 *A *
 *
& 04/F/F<4	 0G 0
 0
, ?A"BD&;? ,-$12)+/(*,7;{7H7L7L[Z\7]7]46 /1 57! /1 )4   r!   metadatac                 j    t          | dd          dk    ri S |pi                     d          ri S ddiS )a!  Return disable_notification kwargs when the adapter is in silent mode.

        In "important" mode, all message sends are silently delivered
        (disable_notification=True) unless the caller explicitly requests a
        notification by setting ``metadata["notify"] = True``.
        r   r   notifydisable_notificationT)r   r   )r   r   s     r"   _notification_kwargsz$TelegramAdapter._notification_kwargs  sK     4.<<KKIN)) 	I&--r!   chat_id	chat_type	thread_id	user_nameuser_idr   r   r   r   c          
      `   t          |pd                                          }|sdS t          t          | dd          dd          }t          |dd          }t          |          r	 ddlm}	 t          |pd	                                                                          pd	}
|
d
k    rd	}
n|
dk    r|dnd}
 |	t          j        t          |p|          |
||r!t          |                                          nd|t          |          nd          }t           ||                    S # t          $ r  t                              d|d           Y nw xY wt          j        dd                                          }|sdS d |                    d          D             }d|v p||v S )zIReturn whether a Telegram inline-button caller may perform gated actions.r   F_message_handlerN__self___is_user_authorizedr   )SessionSourcedmprivate
supergroupforumgroup)platformr   r   r   r   r   z=[Telegram] Falling back to env-only callback auth for user %sTexc_infoTELEGRAM_ALLOWED_USERSc                 ^    h | ]*}|                                 |                                 +S r    rw   )ry   uids     r"   	<setcomp>z?TelegramAdapter._is_callback_user_authorized.<locals>.<setcomp>  s-    TTTs		Tsyy{{TTTr!   ,*)strrn   r   callablegateway.sessionr  lowerr%   r   ro   rG   loggerdebugr   r   r~   )r   r   r   r   r   r   normalized_user_idrunnerauth_fnr  normalized_chat_typesourceallowed_csvallowed_idss                 r"   _is_callback_user_authorizedz,TelegramAdapter._is_callback_user_authorized  s    !B//5577! 	5'94@@*dSS&"7>>G 	999999'*9+<'='='C'C'E'E'K'K'M'M'UQU$'944+/(()\996?6K77QX(&%. =+=>>2.8AKc)nn22444t090Ec)nnn4   GGFOO,,,   S&!       i 8"==CCEE 	4TTk.?.?.D.DTTTk!F%7;%FFs   )CD1 1'EEc                     |sd S |                     d          p|                     d          }|t          |          nd S )Nr   message_thread_idr   r  )clsr   r   s      r"   _metadata_thread_idz#TelegramAdapter._metadata_thread_id  sG     	4LL--R>Q1R1R	!*!6s9~~~D@r!   c                     |sd S |                     d          p|                     d          }|t          |          nd S )Ndirect_messages_topic_id!telegram_direct_messages_topic_idr!  )r"  r   topic_ids      r"   "_metadata_direct_messages_topic_idz2TelegramAdapter._metadata_direct_messages_topic_id  sH     	4<< :;;px||Lo?p?p ( 4s8}}}$>r!   c                 Z    |sd S |                     d          }|t          |          nd S )Ntelegram_reply_to_message_id)r   int)r"  r   reply_tos      r"   _metadata_reply_to_message_idz-TelegramAdapter._metadata_reply_to_message_id  s6     	4<< >?? ( 4s8}}}$>r!   r,  c                     |rt          |          S |r*|                    d          r|                     |          S d S )N telegram_dm_topic_reply_fallback)r+  r   r-  )r"  r,  r   s      r"   _reply_to_message_id_for_sendz-TelegramAdapter._reply_to_message_id_for_send  sM      	!x==  	?%GHH 	?44X>>>tr!   reply_to_message_idc                    |rG|                     d          r2||                     |          }|i S d|                     |          iS |                     |          }|dt	          |          dS d|                     |          iS )a  Return Telegram send kwargs for forum and direct-message topic routing.

        Supergroup/forum topics use ``message_thread_id``. True Bot API Direct
        Messages topics can opt in with explicit ``direct_messages_topic_id``
        metadata. Hermes-created private-chat topic lanes are marked with
        ``telegram_dm_topic_reply_fallback`` and must send the private topic
        thread id together with a reply anchor. Live testing showed that either
        parameter alone can render outside the visible lane.
        r/  Nr   )r   r%  )r   r-  _message_thread_id_for_sendr(  r+  )r"  r   r   r   r1  direct_topic_ids         r"   _thread_kwargs_for_sendz'TelegramAdapter._thread_kwargs_for_send"  s    "  	U%GHH 	U"*&)&G&G&Q&Q#"*	')H)H)S)STT@@JJ&%),/,@,@   $S%D%DY%O%OPPr!   c                 X    |rt          |          | j        k    rd S t          |          S r   )r  _GENERAL_TOPIC_THREAD_IDr+  r"  r   s     r"   r3  z+TelegramAdapter._message_thread_id_for_sendA  s.     	C	NNc.JJJ49~~r!   c                 (    |sd S t          |          S r   )r+  r8  s     r"   _message_thread_id_for_typingz-TelegramAdapter._message_thread_id_for_typingG  s      	49~~r!   errorc                 H    dt          |                                           v S )Nzthread not found)r  r  )r;  s    r"   _is_thread_not_found_errorz*TelegramAdapter._is_thread_not_found_errorT  s    !SZZ%5%5%7%777r!   c                     | j         j                                        }|dk    s|                    d          rdS 	 ddlm} t          | |          S # t          $ r Y dS w xY w)N
badrequestTr   
BadRequestF)r   r   r  r}   telegram.errorrA  
isinstancerI   )r;  r   rA  s      r"   _is_bad_request_errorz%TelegramAdapter._is_bad_request_errorX  s    '--//<4==#>#>4	111111eZ000 	 	 	55	s   A 
A! A!c                     t          |o|                    d                    o;|d uo7|                     |          o"dt          |                                          v S )Nr/  message to be replied not found)ro   r   rD  r  r  )r"  r;  r   r1  s       r"   +_should_retry_without_dm_topic_reply_anchorz;TelegramAdapter._should_retry_without_dm_topic_reply_anchorc  sm     Nhll+MNNOO H#4/H))%00H 2SZZ5E5E5G5GG		
r!   send_fnsend_kwargsmedia_labelreset_mediac                 z  K   	  |di | d{V S # t           $ r}|                     |||          s t                              d| j        ||           |
 |             t          |          }d|d<   |                    dd           |                    dd            |di | d{V cY d}~S d}~ww xY w)zFRetry stale private-topic media replies once without the topic anchor.NzR[%s] Reply target deleted for Telegram %s, retrying without reply/topic anchor: %sr1  r   r%  r    )rG   rG  r  warningr   dictpop)	r   rH  rI  r   r1  rJ  rK  send_errretry_kwargss	            r"   &_send_with_dm_topic_reply_anchor_retryz6TelegramAdapter._send_with_dm_topic_reply_anchor_retryq  s.     	1 //;///////// 	1 	1 	1CC#  
 NN:	   &,,L26L./0$7777>>> 00<00000000000000)	1s    
B:BB5/B:5B:c                 &   t          | j        dd          r | j        j                            dg           ng }t	          |t
                    r|                    d          }t          |rd                    d |D                       nd          S )zNReturn validated fallback IPs from config (populated by _apply_env_overrides).r   Nfallback_ipsr  c              3   4   K   | ]}t          |          V  d S r   )r  )ry   vs     r"   r   z0TelegramAdapter._fallback_ips.<locals>.<genexpr>  s(      -I-Ic!ff-I-I-I-I-I-Ir!   )	r   r   r   r   rC  r  r~   r6   r   r   
configureds     r"   _fallback_ipszTelegramAdapter._fallback_ips  s    BI$+W^`dBeBemT[&**>2>>>km
j#&& 	/#))#..J$Z%aSXX-I-Ij-I-I-I%I%I%I]abbbr!   c                     t          |                                           }| j        j                                        dk    pd|v pd|v S )Nconflictz&terminated by other getupdates requestzanother bot instance is running)r  r  r   r   )r;  r_   s     r"   _looks_like_polling_conflictz,TelegramAdapter._looks_like_polling_conflict  sQ    5zz!!O$**,,
: 974?90D8	
r!   c                     | j         j                                        }|dv rdS 	 ddlm}m} t          | ||f          rdS n# t          $ r Y nw xY wt          | t                    S )zJReturn True for transient network errors that warrant a reconnect attempt.>   timedoutnetworkerrorconnectionerrorTr   NetworkErrorTimedOut)	r   r   r  rB  rb  rc  rC  rI   OSError)r;  r   rb  rc  s       r"   _looks_like_network_errorz)TelegramAdapter._looks_like_network_error  s     '--//BBB4	========%,!9:: t 	 	 	D	%)))s   A 
AAFkeyc                 (   t          | j        dd           r| j        j                            |          nd }||S t	          |t
                    r4|                                                                }|dv rdS |dv rdS |S t          |          S )Nr   >   r   onyestrueT>   0noofffalseF)	r   r   r   r   rC  r  rn   r  ro   )r   rf  r   r   lowereds        r"   r   z"TelegramAdapter._coerce_bool_extra  s    .5dk7D.Q.Q[!%%c***W[=NeS!! 	kkmm))++G444t555uNE{{r!   c                 b    t          | dd          si S t          dt          d          iS ddiS )Nr   Flink_preview_optionsT)is_disableddisable_web_page_preview)r   r   r   s    r"   _link_preview_kwargsz$TelegramAdapter._link_preview_kwargs  sD    t5u== 	I)*,>4,P,P,PQQ*D11r!   c                    K   | j         r| j         j        sdS 	 | j         j        j        d         }n# t          $ r Y dS w xY w	 |                                 d{V  n2# t          $ r% t
                              d| j        d           Y nw xY w	 |                                 d{V  t
                              d| j                   dS # t          $ r& t
                              d| j        d           Y dS w xY w)ur  Reset the httpx connection pool used for getUpdates polling.

        Network errors (especially through proxies like sing-box) can leave
        httpx connections in a half-closed state that still occupy pool slots.
        After enough reconnect cycles the pool fills up entirely, causing
        ``Pool timeout: All connections in the connection pool are occupied.``

        We reset ONLY ``_request[0]`` (the getUpdates request) — the general
        request (``_request[1]``) is left untouched so concurrent
        ``send_message`` / ``edit_message`` calls are never interrupted.

        Implementation note: accesses ``Bot._request[0]`` which is the
        get-updates ``BaseRequest`` in the PTB 22.x internal tuple
        ``(get_updates_request, general_request)``.  There is no public
        accessor for the polling request; review if upgrading to PTB 23+.
        Nr   z0[%s] Polling request shutdown failed (non-fatal)Tr	  z2[%s] Polling request pool drained before reconnectz5[%s] Polling request re-initialize failed (non-fatal))	r   bot_requestrG   shutdownr  r  r   
initialize)r   polling_reqs     r"   _drain_polling_connectionsz*TelegramAdapter._drain_polling_connections  su     " 	 	dim 	F	 )-03KK 	 	 	FF		&&(((((((((( 	 	 	LLB	D      	
		((*********LLDdi      	 	 	LLG	D       	s0   1 
??A ,BB:C ,C=<C=c                   K   | j         rdS d}d}d}| xj        dz  c_        | j        }||k    r[d|z  }t                              d| j        ||           |                     d|d	
           |                                  d{V  dS t          |d|dz
  z  z  |          }t                              d| j        ||||           t          j
        |           d{V  	 | j        rA| j        j        r5| j        j        j        r$| j        j                                         d{V  n# t          $ r Y nw xY w|                                  d{V  	 | j        j                            t$          j        d| j                   d{V  t                              d| j        |           d| _        | j         sat          j        |                                           }| j                            |           |                    | j        j                   dS dS # t          $ r}	t                              d| j        |	           | j         sft          j        |                     |	                    }
| j                            |
           |
                    | j        j                   Y d}	~	dS Y d}	~	dS d}	~	ww xY w)aK  Reconnect polling after a transient network interruption.

        Triggered by NetworkError/TimedOut in the polling error callback, which
        happen when the host loses connectivity (Mac sleep, WiFi switch, VPN
        reconnect, etc.).  The gateway process stays alive but the long-poll
        connection silently dies; without this handler the bot never recovers.

        Strategy: exponential back-off (5s, 10s, 20s, 40s, 60s cap) up to
        MAX_NETWORK_RETRIES attempts, then mark the adapter retryable-fatal so
        the supervisor restarts the gateway process.
        N
      <   rt   zXTelegram polling could not reconnect after %d network error retries. Restarting gateway.z[%s] %s Last error: %stelegram_network_errorT	retryabler$   zK[%s] Telegram network error (attempt %d/%d), reconnecting in %ds. Error: %sFallowed_updatesdrop_pending_updateserror_callbackz>[%s] Telegram polling resumed after network error (attempt %d)r   z*[%s] Telegram polling reconnect failed: %s)has_fatal_errorr   r  r;  r   _set_fatal_error_notify_fatal_errorr   rM  asynciosleepr   updaterrunningstoprG   r{  start_pollingr   	ALL_TYPESr   infoensure_future_verify_polling_after_reconnect_background_tasksaddadd_done_callbackdiscard_handle_polling_network_error)r   r;  MAX_NETWORK_RETRIES
BASE_DELAY	MAX_DELAYattemptmessagedelayprobe	retry_errtasks              r"   r  z-TelegramAdapter._handle_polling_network_error  s       	F 
	))Q.))3(((&(;<  LL149guMMM!!":Gt!TTT**,,,,,,,,,FJ!!"45yAAYIw 3UE	
 	
 	
 mE"""""""""	y /TY. /493D3L /i',,......... 	 	 	D	 --/////////	G)#11 & 0%*#? 2         
 KKP	7   12D- ' H-d.R.R.T.TUU&**5111''(>(FGGGGGH H  		G 		G 		GNNGT]^^^ ' G,66yAA  &**4000&&t'='EFFFFFFFFFG G G G G G			Gs-   "AD+ +
D87D8CH 
K)BJ==Kc                 P  K   d}d}t          j        |           d{V  | j        rdS | j        r| j        j        r| j        j        j        sKt                              d| j        |           | 	                    t          d                     d{V  dS 	 t          j        | j        j                                        |           d{V  dS # t          $ rH}t                              d| j        ||           | 	                    |           d{V  Y d}~dS d}~ww xY w)a(  Heartbeat probe scheduled after a successful reconnect.

        PTB's Updater can survive a botched stop()+start_polling() cycle
        with `running=True` but a wedged consumer task. No error callback
        fires, so the reconnect ladder doesn't advance on its own. This
        probe detects the wedge by:

        1. Sleeping HEARTBEAT_PROBE_DELAY so a healthy long-poll has time
           to complete at least one cycle.
        2. Verifying `Updater.running` is still True.
        3. Probing the bot endpoint with a tight asyncio timeout. A
           wedged httpx pool fails this probe; a healthy one returns
           well under the timeout.

        On any failure, re-enter the reconnect ladder so the existing
        MAX_NETWORK_RETRIES path can ultimately escalate to fatal-error.
        r  r}  NuC   [%s] Updater not running %ds after reconnect — treating as wedgedz-Updater not running after reconnect heartbeatz;[%s] Polling heartbeat probe failed %ds after reconnect: %s)r  r  r  r   r  r  r  rM  r   r  RuntimeErrorwait_forrv  get_merG   )r   HEARTBEAT_PROBE_DELAYPROBE_TIMEOUT	probe_errs       r"   r  z/TelegramAdapter._verify_polling_after_reconnect@  s     $ !#m1222222222 	F	 	di/ 	DI4E4M 	NNU	0   44LMM         F	@"49=#7#7#9#9=IIIIIIIIIII 	@ 	@ 	@NNM	0)   44Y???????????????	@s   7C 
D%=D  D%c                   K   | j         r| j        dk    rd S | xj        dz  c_        d}d}| j        |k    rWt                              d| j        | j        |||           	 | j        rA| j        j        r5| j        j        j        r$| j        j        	                                 d {V  n# t          $ r Y nw xY wt          j        |           d {V  |                                  d {V  	 | j        j                            t          j        d| j                   d {V  t                              d| j        | j                   d	| _        d S # t          $ r,}t                              d
| j        |           Y d }~d S d }~ww xY wd|z  }t                              d| j        ||           |                     d|d           	 | j        r0| j        j        r$| j        j        	                                 d {V  n:# t          $ r-}t                              d| j        |d           Y d }~nd }~ww xY w|                                  d {V  d S )Ntelegram_polling_conflictrt   r   r}  zD[%s] Telegram polling conflict (%d/%d), will retry in %ds. Error: %sFr  z5[%s] Telegram polling resumed after conflict retry %dr   z&[%s] Telegram polling retry failed: %su   Another process is already polling this Telegram bot token (possibly OpenClaw or another Hermes instance). Hermes stopped Telegram polling after %d retries. Only one poller can run per token — stop the other process and restart with 'hermes start'.z[%s] %s Original error: %sr  z8[%s] Failed stopping Telegram polling after conflict: %sTr	  )r  fatal_error_coder   r  rM  r   r   r  r  r  rG   r  r  r{  r  r   r  r   r  r;  r  r  )r   r;  MAX_CONFLICT_RETRIESRETRY_DELAYr  r  
stop_errors          r"   _handle_polling_conflictz(TelegramAdapter._handle_polling_conflictl  s      	D$9=X$X$XF 	$$)$$ '+???NNV	479MU  
9 3!2 3ty7H7P 3)+00222222222   -,,,,,,,,,11333333333i'55$*$4).#'#C 6         
 SUYU^`d`|}}}/0,   GT]^^^ 	/
 ## 	 	149guMMM97eTTT	}y /TY. /i',,......... 	} 	} 	}NNUW[W`blw{N||||||||	}&&(((((((((((sD   !AB* *
B76B7/A$E 
F!FF7H 
H=#H88H=
icon_coloricon_custom_emoji_idc                 ,  K   | j         sdS 	 ||d}|||d<   |r||d<    | j         j        di | d{V }|j        }t                              d| j        |||           |S # t          $ r}t          |                                          }	d|	v sd|	v r#t                              d| j        ||           nNd	|	v sd
|	v r#t          	                    d| j        ||           n#t          	                    d| j        |||           Y d}~dS d}~ww xY w)zCreate a forum topic in a private (DM) chat.

        Uses Bot API 9.4's createForumTopic which now works for 1-on-1 chats.
        Returns the message_thread_id on success, None on failure.
        N)r   r   r  r  z5[%s] Created DM topic '%s' in chat %s -> thread_id=%stopic_name_duplicatealreadyzT[%s] DM topic '%s' already exists in chat %s (will be mapped from incoming messages)znot a forumforums_disabledz[%s] Cannot create DM topic '%s' in chat %s: Topics mode is not enabled. The user must open the DM with this bot in Telegram, tap the bot name at the top, and enable 'Topics' in chat settings before topics can be created.z2[%s] Failed to create DM topic '%s' in chat %s: %sr    )
r   create_forum_topicr   r  r  r   rG   r  r  rM  )
r   r   r   r  r  kwargstopicr   e
error_texts
             r"   _create_dm_topicz TelegramAdapter._create_dm_topic  s      y 	4#	18$%G%GF%'1|$# F1E-.6$)6@@@@@@@@@@E/IKKG	4)    	 	 	QJ &33yJ7N7NjItW    *,,0AZ0O0Oe ItW	    HItWa   44444+	s   AA$ $
D.BDDparent_chat_idc                    K   	 t          |          }n# t          t          f$ r Y dS w xY w|                     ||           d{V }|rt	          |          ndS )a	  Create a forum topic for a session handoff.

        Works for DM topics (Bot API 9.4+, requires user to enable Topics
        in their chat with the bot) and forum supergroups. Returns the
        ``message_thread_id`` as a string, or ``None`` on failure.
        N)r   )r+  r   r   r  r  )r   r  r   chat_id_intr   s        r"   create_handoff_threadz%TelegramAdapter.create_handoff_thread  s      	n--KK:& 	 	 	44	//$/GGGGGGGG	!*4s9~~~4s    ))c                   K   | j         sdS 	 t          |          }n# t          t          f$ r |}Y nw xY w| j                             |t          |          |           d{V  t
                              d| j        |||           dS )z,Rename a forum topic in a private (DM) chat.N)r   r   r   z5[%s] Renamed DM topic in chat %s thread_id=%s -> '%s')r   r+  r   r   edit_forum_topicr  r  r   )r   r   r   r   chat_id_args        r"   rename_dm_topiczTelegramAdapter.rename_dm_topic  s       y 	F	"g,,KK:& 	" 	" 	"!KKK	"i((!)nn ) 
 
 	
 	
 	
 	
 	
 	
 	

 	CIw	4	
 	
 	
 	
 	
s    33
topic_namec                    	 ddl m}  |            dz  }|                                s#t                              d| j        |           dS ddl}t          |dd          5 }|                    |          pi }ddd           n# 1 swxY w Y   |	                    d	i           	                    d
i           	                    di           	                    dg           }	|	sdS d}
|	D ]}t          |	                    dd                    t          |          k    r7|	                    dg           D ]9}|	                    d          |k    r|	                    d          s	||d<   d}
 n:|
rt          j        t          |j                  dd          \  }}	 t          j        |dd          5 }|                    ||dd           |                                 t          j        |                                           ddd           n# 1 swxY w Y   t+          ||           n5# t,          $ r( 	 t          j        |           n# t0          $ r Y nw xY w w xY wt                              d| j        ||           dS dS # t4          $ r.}t                              d| j        |d           Y d}~dS d}~ww xY w)zTSave a newly created thread_id back into config.yaml so it persists across restarts.r   get_hermes_homeconfig.yamlz:[%s] Config file not found at %s, cannot persist thread_idNrutf-8encoding	platformsrH   r   r   Fr   topicsr   r   T.tmpz.config_)dirsuffixprefixw)default_flow_style	sort_keysz9[%s] Persisted thread_id=%s for topic '%s' in config.yamlz.[%s] Failed to persist thread_id to config: %sr	  )hermes_constantsr  existsr  rM  r   yamlopen	safe_loadr   r+  tempfilemkstempr  parentr   fdopendumpflushfsyncfilenor7   BaseExceptionunlinkrd  r  rG   )r   r   r  r   r  config_path_yamlfr   r   changed
chat_entrytfdtmp_pathr  s                   r"   _persist_dm_topic_thread_idz+TelegramAdapter._persist_dm_topic_thread_id  s   6	j888888)/++m;K%%'' []a]fhsttt    k3999 2Q++1r2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 

;++Z$$Wb!!["%%	   G'  
z~~i3344GDD#"55  AuuV}}
22155;M;M2)2+"& '/K.//!%     H
2sW=== -

61RW
XXX			,,,- - - - - - - - - - - - - - - #8[9999$   	(++++"    OIy*    % ,  	j 	j 	jNNKTYXYdhNiiiiiiiii	js   AJ J "B:J B

J B
AJ 'B;J #H6 :AHH6 HH6 !H"H6 5J 6
I(II(
I# I("I##I((%J 
K	#KK	c           	        K   | j         sdS | j         D ]}|                    d          }|                    dg           }|r|s3t                              d| j        t          |          |           |D ]}|                    d          }|s| d| }|                    d          }|r:t          |          | j        |<   t                              d| j        ||           s|                    d	          }|                    d
          }	|                     t          |          |||	           d{V }
|
r|
| j        |<   t                              d| j        ||
           | 	                    t          |          ||
           	 | j
                            t          |          |
d|            d{V  Q# t          $ r-}t                              d| j        ||           Y d}~d}~ww xY wdS )u  Load or create configured DM topics for specified chats.

        Reads config.extra['dm_topics'] — a list of dicts:
        [
            {
                "chat_id": 123456789,
                "topics": [
                    {"name": "General", "icon_color": 7322096, "thread_id": 100},
                    {"name": "Accessibility Auditor", "icon_color": 9367192, "skill": "accessibility-auditor"}
                ]
            }
        ]

        If a topic already has a thread_id in the config (persisted from a previous
        creation), it is loaded into the cache without calling createForumTopic.
        Only topics without a thread_id are created via the API, and their thread_id
        is then saved back to config.yaml for future restarts.
        Nr   r  z*[%s] Setting up %d DM topic(s) for chat %sr   :r   z4[%s] DM topic loaded from config: %s -> thread_id=%sr  r  )r   r   r  r  z([%s] DM topic cached: %s -> thread_id=%su   📌 )r   r   r_   z2[%s] Could not send seed message to topic '%s': %s)r   r   r  r  r   r   r+  r   r  r  r   send_messagerG   r  )r   r  r   r  
topic_confr  	cache_keyexisting_thread_idr  
icon_emojir   seed_errs               r"   _setup_dm_topicsz TelegramAdapter._setup_dm_topics=  s     & % 	F0 <	 <	J nnY//G^^Hb11F & KK<	3v;;  
 % 1 1
'^^F33
! &5555	 &0^^K%@%@"% 145G1H1HDOI.KKN	9.@    (^^L99
'^^,BCC
"&"7"7LL#))3	 #8 # #      	  1:DOI.KKB	9i  
 44S\\:yYYY
"i44$'LL.7!;z!;!; 5          
 %   P Iz8       #91<	 <	s   3G
G?"G::G?c           
          !K   t           s"t                              d j                   dS  j        j        s"t                              d j                   dS 	                      d j        j        d          sdS t          j                                         j        j                  } j        j	        
                    d          }|ri|                    |          }|                     j        j	        
                    d|                    }t                              d j        |           d	t          d
t          dt          fd}d	t          d
t           dt           fd} |dd           |dd           |dd           |dd           |dd          d}t#          j        dd                                                                          dv }                                 }|sHt-                       d{V }t                              d j        d                    |                     dg|}t1          d|           }	|rw|	su|sst                              d! j        d                    |                     t3          dOi |d"d#t5          |          ii}
t3          dOi |d"d#t5          |          ii}n~|	rBt                              d$ j        |	           t3          dOi |d%|	i}
t3          dOi |d%|	i}n:|r t                              d& j                   t3          dOi |}
t3          dOi |}|                    |
                              |          }|                                 _         j        j         _          j        !                    tE          tF          j$        tF          j%         z   j&                              j        !                    tE          tF          j%         j'                              j        !                    tE          tF          j(        tS          tF          d'tF          j(                  z   j*                              j        !                    tE          tF          j+        tF          j,        z  tF          j-        z  tF          j.        z  tF          j/        j0        z  tF          j1        j0        z   j2                              j        !                    tg           j4                             	 d(d)l5m6}m7} n# tp          $ r tr          x}}Y nw xY wd*}tu          |          D ]}	  j        ;                                 d{V   ny# ||tr          f$ ri}||d+z
  k     rUty          d,|z  d-          }t          =                    d. j        |d+z   |||           t}          j?        |           d{V  n Y d}~d}~ww xY w j        @                                 d{V  t#          j        d/d                                          }|rt          t#          j        d0d1                    }t#          j        d2d                                          }|st          d3          d(d4lBmC}  ||          jD        pd5} j        jE        F                    d6||||t          jH        d78           d{V  d7 _I        t                              d9 j        ||           ntS           j         d:d          }t          |          r |d;           d{V  t}          jK                    !d<t          ddf! fd=}| _M         j        jE        N                    t          jH        d7|>           d{V  	 d(d?lOmP  d(d@lQmR}  |dAB          \  }} j         S                     fdC|D                        d{V  |r/t                              dD j        t          |          |           n:# t          $ r-}t          =                    dE j        |d7F           Y d}~nd}~ww xY w U                                  jI        rdGndH}t                              dI j        |           	  V                                 d{V  n:# t          $ r-}t          =                    dJ j        |d7F           Y d}~nd}~ww xY wd7S # t          $ r_} W                                 dK| } X                    dL|d7M           t                              dN j        |d7F           Y d}~dS d}~ww xY w)Pa_  Connect to Telegram via polling or webhook.

        By default, uses long polling (outbound connection to Telegram).
        If ``TELEGRAM_WEBHOOK_URL`` is set, starts an HTTP webhook server
        instead.  Webhook mode is useful for cloud deployments (Fly.io,
        Railway) where inbound HTTP can wake a suspended machine.

        Env vars for webhook mode::

            TELEGRAM_WEBHOOK_URL    Public HTTPS URL (e.g. https://app.fly.dev/telegram)
            TELEGRAM_WEBHOOK_PORT   Local listen port (default 8443)
            TELEGRAM_WEBHOOK_SECRET Secret token for update verification
        zL[%s] python-telegram-bot not installed. Run: pip install python-telegram-botFz[%s] No bot token configuredztelegram-bot-tokenzTelegram bot tokenbase_urlbase_file_urlz'[%s] Using custom Telegram base_url: %sr   r   rA   c                     	 t          t          j        | t          |                              S # t          t
          f$ r |cY S w xY wr   )r+  r   r   r  r   r   r   r   s     r"   _env_intz)TelegramAdapter.connect.<locals>._env_int  sP    #rys7||<<===!:. # # #"NNN#   .1 AAc                     	 t          t          j        | t          |                              S # t          t
          f$ r |cY S w xY wr   )r   r   r   r  r   r   r  s     r"   
_env_floatz+TelegramAdapter.connect.<locals>._env_float  sP    # 4W!>!>???!:. # # #"NNN#r  HERMES_TELEGRAM_HTTP_POOL_SIZEi   !HERMES_TELEGRAM_HTTP_POOL_TIMEOUTg       @$HERMES_TELEGRAM_HTTP_CONNECT_TIMEOUTg      $@!HERMES_TELEGRAM_HTTP_READ_TIMEOUTg      4@"HERMES_TELEGRAM_HTTP_WRITE_TIMEOUT)connection_pool_sizepool_timeoutconnect_timeoutread_timeoutwrite_timeout$HERMES_TELEGRAM_DISABLE_FALLBACK_IPSr   >   r   rh  ri  rj  Nz.[%s] Auto-discovered Telegram fallback IPs: %s, zapi.telegram.orgTELEGRAM_PROXY)target_hostsz%[%s] Telegram fallback IPs active: %shttpx_kwargs	transportz;[%s] Proxy detected; passing explicitly to HTTPXRequest: %sproxyz4[%s] Telegram fallback-IP transport disabled via envVENUEr   ra     rt   r$      u9   [%s] Connect attempt %d/%d failed: %s — retrying in %dsTELEGRAM_WEBHOOK_URLTELEGRAM_WEBHOOK_PORT8443TELEGRAM_WEBHOOK_SECRETu  TELEGRAM_WEBHOOK_SECRET is required when TELEGRAM_WEBHOOK_URL is set. Without it, the webhook endpoint accepts forged updates from anyone who can reach it — see https://github.com/NousResearch/hermes-agent/security/advisories/GHSA-3vpc-7q5r-276h.

Generate a secret and set it in your .env:
  export TELEGRAM_WEBHOOK_SECRET="$(openssl rand -hex 32)"

Then register it with Telegram when setting the webhook via setWebhook's secret_token parameter.)urlparsez	/telegramz0.0.0.0T)listenporturl_pathwebhook_urlsecret_tokenr  r  z-[%s] Webhook server listening on 0.0.0.0:%d%sdelete_webhook)r  r;  c                    j         rj                                         sd S                     |           r/                                        |                     _         d S                     |           rPt                              dj        |                                	                    |                     _         d S t          
                    dj        | d           d S )Nz5[%s] Telegram network error, scheduling reconnect: %sz[%s] Telegram polling error: %sTr	  )r   doner\  create_taskr  re  r  rM  r   r  r;  )r;  loopr   s    r"   _polling_error_callbackz8TelegramAdapter.connect.<locals>._polling_error_callback_  s    / 8P8U8U8W8W 88?? i373C3CDDaDabgDhDh3i3i00077>> i'^`d`ikpqqq373C3CDDfDfglDmDm3n3n000%F	SXcghhhhhr!   r  )
BotCommand)telegram_menu_commandsd   )max_commandsc                 .    g | ]\  }} ||          S r    r    )ry   r   descr$  s      r"   r{   z+TelegramAdapter.connect.<locals>.<listcomp>}  s6     1 1 1/9tTJJtT**1 1 1r!   zd[%s] Telegram menu: %d commands registered, %d hidden (over 100 limit). Use /commands for full list.z1[%s] Could not register Telegram command menu: %sr	  webhookpollingz$[%s] Connected to Telegram (%s mode)z+[%s] DM topics setup failed (non-fatal): %szTelegram startup failed: telegram_connect_errorr  z&[%s] Failed to connect to Telegram: %sr    )YrE   r  r;  r   r   token_acquire_platform_lockr   builderr   r   r  r  r  r  r+  r   r   r   rn   r  rY  r5   r   r0   r   r4   requestget_updates_requestbuildr   rv  r   add_handlerrM   r   TEXTCOMMAND_handle_text_message_handle_commandLOCATIONr   _handle_location_messagePHOTOVIDEOAUDIOVOICEDocumentALLSticker_handle_media_messager   _handle_callback_queryrB  rb  rc  rI   rd  rangery  r   rM  r  r  r   r  urllib.parser  pathr  start_webhookr   r  r   r  get_running_looprG   r   r  rH   r$  hermes_cli.commandsr%  set_my_commandsr   _mark_connectedr  _release_platform_lockr  )"r   r/  custom_base_urlr  r  request_kwargsdisable_fallbackrT  proxy_targets	proxy_urlr0  r1  rb  rc  _max_connect_attemptinit_errwaitr  webhook_portwebhook_secretr  webhook_pathr  r#  r%  menu_commandshidden_countr  mode
topics_errr  r$  r"  s"   `                               @@r"   connectzTelegramAdapter.connect  s      " 	LL^	   5{  	LL7CCC5y	../CT[EVXlmm u ")++11$+2CDDG"k/33J??O !**?;;!//K%))/?KK  =I  #s #S #S # # # ## #u # # # # # )11QSV(W(W *
+NPS T T#-:.TVZ#[#[ *
+NPT U U!+,PRV!W!W N !#	*PRT U U [ [ ] ] c c e e  jD  !D--//L %:%<%<<<<<<<DIIIl++   0?,?M)*:WWWI EI E6F E;IIIl++   '  $ "-/H/V/V!W   '3 ' '$' '"-/H/V/V!W' ' '##  EY[_[dfoppp&IIIIyIII&2&U&U^&U&U9&U&U&U### cKK VX\Xabbb&8888&2&D&D^&D&D#oog..BBCVWWGDI	DI I!!"8//)# #    I!!"8$# #    I!!"8 77GW=M#N#NN-# #    I!!"8-=MPWP`Pddgngvgzz*# #   
 I!!"6t7R"S"STTT2AAAAAAAAA 2 2 2*11xxx2L!,//  )..000000000E$h8 	 	 	,"222"1="55W Ix!|\8T   &mD1111111111 21111	 )//######### )$:B??EEGGK H  #29-Df#M#MNN!#+Db!I!I!O!O!Q!Q% &	K   211111'x449H[i'55$%) +!/$*$4)- 6          &*"CI|\    ")4Dd!K!KN++ E(.eDDDDDDDDDD/11	i9 	i 	i 	i 	i 	i 	i 	i 	i 4K0i'55$*$4)-#: 6         //////FFFFFF /E.DRU.V.V.V+|i// 1 1 1 1=J1 1 1            KK~	3}#5#5|      GI!	            """ $ 2A99	DKK>	4PPP
++----------   AIzD          4 	 	 	'')))5!55G!!":Gt!TTTLLA49aZ^L___55555	s   !c+ R c+ "T+ *c+ +U>c+  Uc+ U97c+ 9W/AW*%c+ *W//F0c+  A8` c+ 
a##ac+ aAc+ b/ .c+ /
c&9#c!c+ !c&&c+ +
e5Aeec                 V  K   t          | j                                                  }|D ]}|                                 |rt	          j        |ddi d{V  | j                                         | j                                         | j        r	 | j        j	        r5| j        j	        j
        r$| j        j	                                         d{V  | j        j
        r| j                                         d{V  | j                                         d{V  n:# t          $ r-}t                              d| j        |d           Y d}~nd}~ww xY w|                                  | j                                        D ],}|r(|                                s|                                 -| j                                         | j                                         |                                  d| _        d| _        t                              d| j                   dS )zCStop polling/webhook, cancel pending album flushes, and disconnect.return_exceptionsTNz)[%s] Error during Telegram disconnect: %sr	  z[%s] Disconnected from Telegram)listr   valuescancelr  gatherclearr   r   r  r  r  rx  rG   r  rM  r   rK  r   r   r   _mark_disconnectedr   r  )r   pending_media_group_tasksr  r  s       r"   
disconnectzTelegramAdapter.disconnect  sY     $()@)G)G)I)I$J$J!- 	 	DKKMMMM$ 	U.";TtTTTTTTTTT%%''' &&(((9 		ii9$ 3):)B 3)+002222222229$ +)..*********i((********** i i iJDIWXcghhhhhhhhi##%%%3::<< 	 	D DIIKK '--///#))+++!!!		5tyAAAAAs   BD! !
E+#EEchunk_indexc                 D    |sdS | j         }|dk    rdS |dk    rdS |dk    S )a3  Determine if this message chunk should thread to the original message.

        Args:
            reply_to: The original message ID to reply to
            chunk_index: Index of this chunk (0 = first chunk)

        Returns:
            True if this chunk should be threaded to the original message
        Frm  allTr   )r   )r   r,  rg  rZ  s       r"   _should_thread_replyz$TelegramAdapter._should_thread_reply  s@      	5"5==5U]]4!##r!   contentc                 8  K   | j         st          dd          S |r|                                st          dd          S 	 |                     |          }|                     || j        t                    }t          |          dk    rd	 |D             }g }|                     |          }	 d
dl	m
}	 n# t          $ r
 t          }	Y nw xY w	 d
dl	m}
 n# t          $ r d}
Y nw xY w	 d
dl	m} n# t          t          f$ r d}Y nw xY wt!          |          D ]D\  }}|                     |          }|p)|r&|                    d          r|t'          |          nd}|r|                    d          r|du}n|                     ||          }|r|rt+          |          nd}|                     ||||          }|                    d          }d}t/          d          D ]N}	 	  | j         j        d*t+          |          |t2          j        |d||                                 |                     |           d{V }n# t:          $ r}dt'          |                                          v s#dt'          |                                          v rt>                               d| j!        |           tE          |          } | j         j        d*t+          |          |d|d||                                 |                     |           d{V }n Y d}~nd}~ww xY w n# |	$ r\}|
rtG          ||
          r| $                    |          r/|-t>                               d| j!        |           d}ddi}Y d}~t'          |                                          }d|v ru|st>                               d| j!        |           d}|r|                    d          ri }d}n.|                     ||||          }|                    d          }Y d}~> |rtG          ||          r |dk     rFd|z  }t>                               d| j!        |dz   ||           tK          j&        |           d{V  n Y d}~d}~wt:          $ r}tO          |dd          }|#dt'          |                                          v r_|dk     rY|tQ          |          nd}t>                               d| j!        |dz   ||           tK          j&        |           d{V  Y d}~G d}~ww xY w|)                    t'          |j*                             F	 | +                    ||           d{V  n# t:          $ r Y nw xY wt          d|r|d
         ndd |i!          S # t:          $ r}t>          ,                    d"| j!        |d#           t'          |                                          }d$|v sd%|v r6t>          -                    d&| j!                   t          dd$          cY d}~S t]                                          d'          }|rtG          ||          pd(|v }t          dt'          |          | )          cY d}~S d}~ww xY w)+z"Send a message to a Telegram chat.FNot connectedsuccessr;  TNro  
message_idlen_fnrt   c                 :    g | ]}t          j        d d|          S )z \((\d+)/(\d+)\)$z \\(\1/\2\\)rg   )ry   chunks     r"   r{   z(TelegramAdapter.send.<locals>.<listcomp>  s7        F/%HH  r!   r   )rb  r@  )rc  r/  r1  r   r   r   r_   
parse_moder1  parsemarkdownz<[%s] MarkdownV2 parse failed, falling back to plain text: %sz<[%s] Thread %s not found, retrying without message_thread_idrF  z8[%s] Reply target deleted, retrying without reply_to: %sr$   z>[%s] Network error on send (attempt %d/3), retrying in %ds: %sretry_afterretry afterr   zI[%s] Telegram flood control on send (attempt %d/3), retrying in %.1fs: %sr   message_ids)ro  rq  raw_responsez([%s] Failed to send Telegram message: %sr	  message_too_longtoo longzF[%s] send() content too long, falling back to new-message continuation	_TimedOutz	timed out)ro  r;  r  r    )/r   r+   rn   format_messagetruncate_messageMAX_MESSAGE_LENGTHr3   r   r#  rB  rb  rI   rd  rA  rc  AttributeErrorr   r-  r   r  rj  r+  r5  rC  r  r   MARKDOWN_V2rt  r   rG   r  r  rM  r   rj   rC  r=  r  r  r   r   r   rq  send_typingr;  r  locals) r   r   rk  r,  r   	formattedchunksr~  r   _NetErr_BadReqr  r   ru  metadata_reply_toreply_to_sourceshould_threadreply_to_idthread_kwargseffective_thread_idmsg_send_attemptmd_errorplain_chunkrP  	err_lowerrT  r{  r  err_str_to
is_timeouts                                    r"   sendzTelegramAdapter.send  s	      y 	De?CCCC  	=gmmoo 	=dt<<<<y	U++G44I**429 +  F 6{{Q !'  
 K00::I"BBBBBBB " " "!"@@@@@@@   !@@@@@@@0 ! ! ! 			! &f-- w8 w85$($F$Fx$P$P!"* #A$,LL1S$T$TAYjYv )*** }A    R-O P P R$34$?MM$($=$=oq$Q$QM6Caac/222]a $ < <(3	 != ! ! '4&7&78K&L&L#%*1XX b bMa&(>	(> )(+G%*+4+@4?	) )
 #0) #'";";"="=) #'";";H"E"E) ) # # # # # #CC  ) & & &&#h--*=*=*?*???:QTU]Q^Q^QdQdQfQfCfCf &/mosox  {C  !D  !D  !D.9%.@.@,BDI,B -",/LL)4/38C	-" -"
 '4-" '+&?&?&A&A-" '+&?&?&I&I-" -" '" '" '" '" '" '" !& !$&  " 5" 5" 5"
 # $"z(G'D'D $"#>>xHH 
)M`Ml !'$b$(I/B!" !" !" 7; 31Dd0K ((+H(;(;(=(=I@IMMR]Ri
 !'$^$(Ix!" !" !" /3#+ 
!a=_0`0` 
!a46M:>$7$7484P4P(/(1(0<G	 5Q 5& 5&M ;H:K:KL_:`:`$7 (! % "Hi)H)H "!(1,,#$#5D"NN+k+/9ma6GxY Y Y")-"5"555555555! 65555 %   &-ht&L&L&2ms8}}GZGZG\G\6\6\,q00=H=Tu['9'9'9Z] &$o$(I$1A$5$($,!" !" !" '.mD&9&9 9 9 9 9 9 9 9 ( ""3s~#6#67777&&w&BBBBBBBBBB    -8B;q>>d+[9     	U 	U 	ULLCTYPQ\`Laaa!ffllnnG "W,,
g0E0E\I   "%7IJJJJJJJJJ ((,,{++C4*Q"4"4O9OJe3q66^TTTTTTTTT	UsB  A-V, 1B8 7V, 8C	V, CV, C V, C&#V, %C&&V, *C1 0V, 1DV, DC"V, +AI	L.	
L(CL#L.#L((L.+V, .T34ARV, 
BRV, $A!RV, T3BT.'V, -T..T33,V,  U> =V, >
VV, 
V V, ,
Z6A<Z2Z8AZZZfinalizerq  r  c                x  K   | j         st          dd          S t          |          | j        k    r|                     ||||           d{V S 	 |sN| j                             t          |          t          |          |           d{V  t          d|          S |                     |          }	 | j                             t          |          t          |          |t          j	        	           d{V  n# t          $ r}d
t          |                                          v rt          d|          cY d}~S | j                             t          |          t          |          |           d{V  Y d}~nd}~ww xY wt          d|          S # t          $ r}t          |                                          }d
|v rt          d|          cY d}~S d|v sd|v rXt                              d| j        t          |          | j                   |                     ||||           d{V cY d}~S t!          |dd          }	|	d|v r|	r|	nd}
t                              d| j        |
           |
dk    rt          dd|
           cY d}~S t%          j        |
           d{V  	 | j                             t          |          t          |          |           d{V  t          d|          cY d}~S # t          $ rN}t                              d| j        |           t          dt          |                    cY d}~cY d}~S d}~ww xY wt                              d| j        ||d           t          dt          |                    cY d}~S d}~ww xY w)a"  Edit a previously sent Telegram message.

        Telegram caps single-message text at 4096 UTF-16 codeunits.  Streaming
        replies that grow past this limit must NOT be silently truncated and
        must NOT return failure (the consumer would re-send and create a
        duplicate).  Instead this method split-and-delivers: edit the
        existing message with the first chunk and send the rest as
        continuation messages, returning the final chunk's id so subsequent
        edits target the most recent visible message.
        Frm  rn  r  Nr   rq  r_   Trp  r   rq  r_   rx  not modifiedr  r  z6[%s] edit_message overflow (%d UTF-16 > %d), splittingr{  r|  r   z*[%s] Telegram flood control, waiting %.1fsg      @zflood_control:z+[%s] Edit retry failed after flood wait: %sz+[%s] Failed to edit Telegram message %s: %sr	  )r   r+   r3   r  _edit_overflow_splitedit_message_textr+  r  r   r  rG   r  r  r  r  r   r   rM  r  r  r;  )r   r   rq  rk  r  r  fmt_errr  r  r{  rT  r  s               r"   edit_messagezTelegramAdapter.edit_message  sI     $ y 	De?CCCC W 77722Wx 3         M	; Gi11LL":  2         
 "$:FFFF++G44Ii11LL":"(4	 2            	 	 	!S\\%7%7%9%999%dzJJJJJJJJJi11LL":  2              	 dzBBBB 1	; 1	; 1	;!ffllnnG((!$:FFFFFFFFF "W,,
g0E0ELIy1143J   "66Z8 7               "!]D99K&-7*B*B&1:{{s@It   #::%e;RD;R;RSSSSSSSSSmD)))))))))K)55 #G#&z??$ 6         
 &dzJJJJJJJJJ  K K KLLE	9   &e3y>>JJJJJJJJJJJJJJK LL=	     e3q66:::::::::c1	;s   AF  #F  9AD F  
F3F?F F  =FF  FF   N9+5N4 N9&AN4 N9AN4N9$N4?ALN9
M*>M%M*N4N9%M**AN4.N94N9c          
      2  K   |                      || j        t                    }t          |          dk    r|g}|d         }	 |r|                     |          }	 | j                            t          |          t          |          |t          j	                   d{V  n# t          $ rj}dt          |                                          vr=| j                            t          |          t          |          |           d{V  Y d}~nEd}~ww xY w| j                            t          |          t          |          |           d{V  n# t          $ rv}	t          |	                                          }
d|
v rnFt                              d| j        |	d	
           t!          dt          |	                    cY d}	~	S Y d}	~	nd}	~	ww xY wg }|}|dd         D ]}d}|rdndD ]H}	 |r|                     |          n|}| j                            t          |          ||rt          j	        nd|rt          |          nd           d{V } n# t          $ r}dt          |                                          v ru	 | j                            t          |          |           d{V }Y d}~ nx# t          $ r2}t                              d| j        |           d}Y d}~Y d}~ nAd}~ww xY w|rY d}~t                              d| j        |           d}Y d}~ nd}~ww xY w|At                              d| j        dt          |          z   t          |                      n9t          t'          |dd                    p|}|                    |           |}|r|d         n|}t                              d| j        dt          |          z   |           t!          d	|t-          |                    S )u  Split an oversized edit across the existing message + continuations.

        Edit the original ``message_id`` with chunk 1 (with the platform's
        usual ``(1/N)`` suffix preserved), then send the remaining chunks as
        new messages threaded as replies to the previous chunk so the user
        sees them grouped.  Returns ``SendResult(success=True,
        message_id=<last-chunk-id>, continuation_message_ids=(...))`` so the
        stream consumer can keep editing the most recent visible message
        and the gateway has full visibility into every message id we put on
        screen.

        Falls back to ``SendResult(success=False)`` only if even the first-
        chunk edit fails — that's a real adapter problem, not an overflow.
        rr  rt   r   r  Nr  r  z0[%s] Overflow split: first-chunk edit failed: %sTr	  Frn  )TFFrw  zreply message not found)r   r_   z4[%s] Overflow continuation no-reply retry failed: %sz*[%s] Overflow continuation send failed: %sz6[%s] Overflow split: stopped at %d/%d chunks deliveredrq  r   ru   z3[%s] Overflow split delivered %d chunks; last_id=%s)ro  rq  continuation_message_ids)r  r  r3   r   r  r   r  r+  r   r  rG   r  r  r  r;  r   r+   r  rM  r   r   r  tuple)r   r   rq  rk  r  r  first_chunkr  r  r  r  continuation_idsprev_idru  sent_msguse_markdownr_   rP  
_retry_errnew_idlast_ids                        r"   r  z$TelegramAdapter._edit_overflow_split  s`     , &&T,Y ' 
 
 v;;! YF Qi$	?  !//<<	)55 #G#&z??&#,#8	 6           !   %S\\-?-?-A-AAA"i99$'LL'*:!, :          i11LL":$ 2         
  	? 	? 	?!ffllnnG(( FIq4     "%s1vv>>>>>>>>> 	?& ')ABBZ 2	 2	EH19 Gx " "!9EP4..u5555D%)Y%;%; #G!<H#R9#8#8d<C,MCLLL	 &< & &            H E    0CMM4G4G4I4III"-1Y-C-C(+G%* .D . . ( ( ( ( ( (H "EEEEE( " " ""NN V $	:   (,H!EEEEEEEEE" $ ! NND	8    $HEEEEE12  LIq3'7#8#88#f++   <<<==HF##F+++GG*:J"2&&
AIq3/000'	
 	
 	
 %*+;%<%<
 
 
 	
s   E AB% $E %
D/A DE DA E 
G$A&G
GG:A)I%%
L=/#L8/K
L	#K?	5L8?L	L8#L88L=c                 
  K   | j         sdS 	 | j                             t          |          t          |                     d{V  dS # t          $ r-}t                              d| j        ||           Y d}~dS d}~ww xY w)u  Delete a previously sent Telegram message.

        Used by the stream consumer's fresh-final cleanup path (ported
        from openclaw/openclaw#72038) to remove long-lived preview
        messages after sending the completed reply as a fresh message.
        Telegram's Bot API ``deleteMessage`` works for bot-posted
        messages in the last 48 hours.  Failures are non-fatal — the
        caller leaves the preview in place and logs at debug level.
        F)r   rq  NTz-[%s] Failed to delete Telegram message %s: %s)r   delete_messager+  rG   r  r  r   r   r   rq  r  s       r"   r  zTelegramAdapter.delete_message  s       y 	5	)**Gz?? +          4 	 	 	LL?	:q   55555	s   <A 
B"A==Bc                 n    | j         rt          | j         d          sdS |pd                                dv S )a  Telegram supports sendMessageDraft for private chats only.

        Bot API 9.5 (March 2026) opened ``sendMessageDraft`` to all bots
        unconditionally for private (DM) chats.  Groups, supergroups, and
        channels still rely on the edit-based path.

        We additionally require ``self._bot`` to expose ``send_message_draft``
        (added to python-telegram-bot in 22.6); older PTB installs gracefully
        fall back to the edit path even on DMs.
        send_message_draftFr   >   r  r  )r   hasattrr  )r   r   r   s      r"   supports_draft_streamingz(TelegramAdapter.supports_draft_streaming  sC     y 		3G H H 	5R&&((,===r!   draft_idc                   K   | j         st          dd          S t          | j         d          st          dd          S t          |          | j        k    r|n'|                     || j        t                    d         }t          |          t          |          |d}|                     |          }|||d
<   	  | j         j	        di | d	{V }|rt          dd	          S t          dd          S # t          $ rK}	t                              d| j        |||	           t          dt          |	                    cY d	}	~	S d	}	~	ww xY w)u  Stream a partial message via Telegram's native sendMessageDraft.

        The Bot API animates the preview when the same ``draft_id`` is reused
        across consecutive calls in the same chat.  When the response
        finishes, the caller sends the final text via the normal ``send``
        path; the draft preview clears naturally on the client (Telegram has
        no Bot API to "promote" a draft to a real message — the final
        ``sendMessage`` is what the user receives in their history).
        Fnot_connectedrn  r  api_unavailablerr  r   )r   r  r_   Nr   Trp  draft_rejectedz6[%s] sendMessageDraft failed (chat=%s draft_id=%s): %sr    )r   r+   r  r   r  r  r3   r+  r#  r  rG   r  r  r   r  )
r   r   r  rk  r   r_   r  r   okr  s
             r"   
send_draftzTelegramAdapter.send_draft  s       y 	De?CCCCty"677 	Fe3DEEEE g,,$*AAAww!!'4+B9!UUVWX 	 7||H"
 "

 ,,X66	 *3F&'	;3ty3==f========B A "$4@@@@e3CDDDD 	; 	; 	; LLH	7Ha   e3q66:::::::::	;s%    *C< +C< <
EA EEEc                   K   | j         st          d          |                    d          }	  | j         j        di | d{V S # t          $ r}||                     |          rx|                     |          rct                              d| j	        |           t          |          }|                    dd            | j         j        di | d{V cY d}~S  d}~ww xY w)a  Send a Telegram message, retrying once without message_thread_id
        if Telegram returns 'Message thread not found'.

        Used for control-style sends (approval prompts, model picker,
        update prompts) that can carry a stale thread_id from a DM
        reply chain.  The streaming send loop has its own equivalent
        (PR #3390) at the body of ``send``; this helper applies the
        same retry pattern to the non-streaming control paths.
        rm  r   NzP[%s] Thread %s not found for control message, retrying without message_thread_idr    )r   r  r   r  rG   rD  r=  r  rM  r   rN  rO  )r   r  r   rP  rQ  s        r"   "_send_message_with_thread_fallbackz2TelegramAdapter._send_message_with_thread_fallback  s<      y 	0///"JJ':;;	//99&999999999 	 	 	!-..x88 .33H== . fI%  
  $F||  !4d;;;3TY3CClCCCCCCCCCCCCCC	s$   A 
C&B	C!C& C!!C&r   rD   session_keyc           
        K   | j         st          dd          S 	 |rd| dnd}|                     d| |           }t          t	          dd	
          t	          dd
          gg          }|                     |          }	|                     d|          }
 | j        dt          |          |t          j
        ||
d|                     ||	||
          |                                  d{V }t          dt          |j                            S # t          $ rI}t                               d| j        |           t          dt          |                    cY d}~S d}~ww xY w)zSend an inline-keyboard update prompt (Yes / No buttons).

        Used by the gateway ``/update`` watcher when ``hermes update --gateway``
        needs user input (stash restore, config migration).
        Frm  rn  z (default: )r   u    ⚕ *Update needs your input:*

u   ✓ Yeszupdate_prompt:ycallback_datau   ✗ Nozupdate_prompt:nNr   r_   rx  reply_markupr1  rv  Trp  z"[%s] send_update_prompt failed: %sr    )r   r+   r  r   r
   r#  r0  r  r+  r   r  r5  rt  r  rq  rG   r  rM  r   )r   r   rD   r   r  r   default_hintr_   keyboardr   r  r  r  s                r"   send_update_promptz"TelegramAdapter.send_update_prompt  s      y 	De?CCCC	;7>F33333BL&&'bF'bT`'b'bccD+(BSTTT(ARSSS-  H 00::I<<T8LLK?? G$0%$/  ..(3	 /   ++--       C ds3>7J7JKKKK 	; 	; 	;NN?ANNNe3q66:::::::::	;s   C<D 
E,#>E'!E,'E,dangerous commandcommanddescriptionc           	      Z  K   | j         st          dd          S 	 t          |          dk    r|dd         dz   n|}dt          j        |           dt          j        |           }|                     |          }d	dl}	t          | d
          s|	                    d          | _	        t          | j	                  }
t          t          dd|
           t          dd|
           gt          dd|
           t          dd|
           gg          }t          |          |t          j        |d|                                 }|                     d|          }||d<   |                    |                     ||||                      | j        di | d{V }|| j        |
<   t          dt-          |j                            S # t0          $ rI}t2                              d| j        |           t          dt-          |                    cY d}~S d}~ww xY w)u   Send an inline-keyboard approval prompt with interactive buttons.

        The buttons call ``resolve_gateway_approval()`` to unblock the waiting
        agent thread — same mechanism as the text ``/approve`` flow.
        Frm  rn    N...u.   ⚠️ <b>Command Approval Required</b>

<pre>z</pre>

Reason: r   _approval_counterrt   u   ✅ Allow Oncezea:once:r  u   ✅ Sessionzea:session:u
   ✅ Alwaysz
ea:always:u   ❌ Denyzea:deny:r   r_   rx  r  r1  rv  Trp  z"[%s] send_exec_approval failed: %sr    )r   r+   r   _htmlescaper#  	itertoolsr  countr  r   r   r
   r+  r   HTMLrt  r0  updater5  r  r   r  rq  rG   r  rM  r   )r   r   r  r  r  r   cmd_previewr_   r   r  approval_idr  r  r  r  r  s                   r"   send_exec_approvalz"TelegramAdapter.send_exec_approvalF  s      y 	De?CCCC8	;47LL44G4G'%4%.500WK7[117 7 <447 7  00::I
 4!455 <)2););&t566K+()9IaT_IaIabbb(FaT_FaFabbb
 )E_R]E_E_```(C[kC[C[\\\	- 	 	H w<<'n (	& &
 ++--&F <<T8LLK,7F()MM,,(3	 -     @?II&IIIIIIIIC 1<D -ds3>7J7JKKKK 	; 	; 	;NN?ANNNe3q66:::::::::	;s   F:G 
H*!>H%H*%H*titler  
confirm_idc                 X  K   | j         st          dd          S 	 t          |          dk    r|n|dd         dz   }t          t	          dd| 	          t	          d
d| 	          gt	          dd| 	          gg          }|                     |          }	t          |          |t          j        |d| 	                                }
| 
                    d|          }||
d<   |
                    |                     ||	||                      | j        di |
 d{V }|| j        |<   t          dt          |j                            S # t"          $ rI}t$                              d| j        |           t          dt          |                    cY d}~S d}~ww xY w)z8Render a three-button slash-command confirmation prompt.Frm  rn  r  Nr  u   ✅ Approve Oncezsc:once:r  u   🔒 Always Approvez
sc:always:u
   ❌ Cancelz
sc:cancel:r  r1  rv  Trp  z"[%s] send_slash_confirm failed: %sr    )r   r+   r   r   r
   r#  r+  r   MARKDOWNrt  r0  r  r5  r  r   r  rq  rG   r  rM  r   )r   r   r  r  r  r  r   previewr  r   r  r  r  r  s                 r"   send_slash_confirmz"TelegramAdapter.send_slash_confirm  s"     
 y 	De?CCCC'	; "%W!5!5gg75D5>E;QG+();KbV`KbKbccc()>Ng[eNgNghhh
 )E^R\E^E^___-  H 00::Iw<<'0 (	& &
 ++--&F <<T8LLK,7F()MM,,(3	 -     @?II&IIIIIIIIC4?D%j1ds3>7J7JKKKK 	; 	; 	;NN?ANNNe3q66:::::::::	;s   D9E 
F) >F$F)$F)questionchoices
clarify_idc           
        K   | j         st          dd          S 	 dt          j        |           }|                     |          }t          |          |t          j        d|                                 }	|rg }
t          |          D ]g\  }}t          |          }t          |          dk    r|dd         d	z   }|
                    t          |d
z    d| d| d|           g           h|
                    t          dd| d          g           t          |
          |	d<   |                     d|          }||	d<   |	                    |                     ||||                      | j        di |	 d{V }|| j        |<   t          dt          |j                            S # t*          $ rI}t,                              d| j        |           t          dt          |                    cY d}~S d}~ww xY w)u$  Render a clarify prompt with one inline button per choice.

        Multi-choice mode (``choices`` non-empty): renders one button per
        option plus a final "✏️ Other (type answer)" button.  Picking the
        "Other" button flips the entry into text-capture mode so the next
        message becomes the response.

        Open-ended mode (``choices`` empty): renders the question as plain
        text — no buttons.  The next message in the session is captured by
        the gateway's text-intercept and resolves the clarify.
        Frm  rn     ❓ r   r_   rx  r  N9   r  rt   z. cl:r  r  u   ✏️ Other (type answer)z:otherr  r1  rv  Trp  z[%s] send_clarify failed: %sr    )r   r+   r  r  r#  r+  r   r  rt  r   r  r   r   r
   r   r0  r  r5  r  r   rq  rG   r  rM  r   )r   r   r  r  r  r  r   r_   r   r  rowsidxchoicelabelr  r  r  s                    r"   send_clarifyzTelegramAdapter.send_clarify  s     ( y 	De?CCCC1	;2%,x0022D00::I w<<'n& & ++--	&F  D #,W#5#5 	 	KCKKE5zzB %crc
U 2KK,"Qw11%11*B
*B*BS*B*B  !     (4&>J&>&>&>      *>d)C)C~&<<T8LLK,7F()MM,,(3	 -     @?II&IIIIIIIIC.9D
+ds3>7J7JKKKK 	; 	; 	;NN949aHHHe3q66:::::::::	;s   FF6 6
H	 >H>H	H		providerscurrent_modelcurrent_providerc           
        K   | j         st          dd          S 	 ddlm} n# t          $ r d }Y nw xY w	 g |D ]}	|	                    dt          |	                    dg                               }
|	d	          d
|
 d}|	                    d          rd| }                    t          |d|	d                               fdt          dt                    d          D             }|                    t          dd          g           t          |          } ||          }|                     d|pd d| d          }|r|                    d          nd}|                     d|          } | j        d!t          |          |t          j        ||d|                     ||||          |                                  d{V }|j        |||||d| j        t+          |          <   t          dt+          |j                            S # t,          $ rI}t.                              d | j        |           t          dt+          |                    cY d}~S d}~ww xY w)"u   Send an interactive inline-keyboard model picker.

        Two-step drill-down: provider selection → model selection.
        Edits the same message in-place as the user navigates.
        Frm  rn  r   	get_labelc                     | S r   r    slugs    r"   r  z4TelegramAdapter.send_model_picker.<locals>.get_label	      r!   total_modelsmodelsr    (r  
is_current   ✓ mp:r  r  c                 *    g | ]}||d z            S r$   r    ry   r   buttonss     r"   r{   z5TelegramAdapter.send_model_picker.<locals>.<listcomp>-	  &    JJJ1GAAI&JJJr!   r$   
   ✗ Cancelmx+   ⚙ *Model Configuration*

Current model: `unknown`
Provider: 

Select a provider:r   Nr  rv  )msg_idr  r  on_model_selectedr  r  Trp  z![%s] send_model_picker failed: %sr    )r   r+   hermes_cli.providersr  rI   r   r   r   r
   rC  r   r  r0  r  r+  r   r  r5  rt  rq  r   r  rG   r  rM  r   )r   r   r  r  r  r  r  r   r  pr  r  r  r  provider_labelr_   r   r  r  r  r  s                       @r"   send_model_pickerz!TelegramAdapter.send_model_picker	  sJ      y 	De?CCCC	6666666 	 	 	    	9	;G  nc!%%"2E2E.F.FGGV90000055&& +*5NNE(>OAfI>O>OPPP    KJJJaWq0I0IJJJDKK-l$OOOPQQQ+D11H&Y'788N&&*'4'A	* *!/* * * D 6>G[1114I<<T8LLK?? G$0%$/  ..(3	 /   ++--       C" .&*%6!.$46 6D$S\\2 ds3>7J7JKKKK 	; 	; 	;NN>	1MMMe3q66:::::::::	;s*   $ 44G)H" "
I5,>I0*I50I5r  r  pagec                 V   | j         }t          |          }t          d||z   dz
  |z            }t          dt          ||dz
                      }||z  }t          ||z   |          }|||         }g t	          |          D ]r\  }	}
||	z   }d|
v r|
                    d          d         n|
}t          |          dk    r|dd         dz   }                    t          |d	| 
                     sfdt          dt                    d          D             }|dk    rg }|dk    r*|                    t          dd|dz
   
                     |                    t          |dz    d| d
                     ||dz
  k     r*|                    t          dd|dz    
                     |                    |           |                    t          dd
          t          dd
          g           |dk    rd|dz    d| d| dnd}t          |          |fS )zBBuild paginated model buttons. Returns (keyboard, page_info_text).rt   r   /ru   &   N#   r  mm:r  c                 *    g | ]}||d z            S r  r    r  s     r"   r{   z9TelegramAdapter._build_model_keyboard.<locals>.<listcomp>r	  s&    FFFqAE	"FFFr!   r$   u   ◀ Prevmg:zmx:noopu   Next ▶u   ◀ Backmbr  r  r  u   –z of r  r   )
_MODEL_PAGE_SIZEr   r   r   r   r~   r   r
   rC  r   )r   r  r  	page_sizetotaltotal_pagesr   endpage_modelsr   model_idabs_idxshortr  nav	page_infor  s                   @r"   _build_model_keyboardz%TelegramAdapter._build_model_keyboard]	  s   )	F!ei/!3	ABB1c$a0011y %)#U++U3Y'$[11 	 	KAxaiG/2hHNN3''++HE5zzBcrc
U*NN$U///JJJ    GFFFE!S\\1,E,EFFF ??Caxx

/
JZPTWXPXJZJZ[[[\\\JJ+tax,G,G+,G,GW`aaabbbkAo%%

/
JZPTWXPXJZJZ[[[\\\KK 4@@@ TBBB
 	 	 	
 =H!OO888s888888QS	#D))944r!   datac           
        K   | j                             |          }|s|                    d           d{V  dS 	 ddlm} n# t
          $ r d }Y nw xY w|                    d          r\|dd         t          fd	|d
         D             d          }|s|                    d           d{V  dS |                    dg           }|d<   |                    d          |d<   ||d<   d|d<   |                     |d          \  }}	|                    d          }
|                    dt          |                    }t          |          }||k    r	d||z
   dnd}|
                    |                     d|
 d|	 d|           t          j        |           d{V  |                                 d{V  dS |                    d          r|	 t          |dd                   }n-# t          $ r  |                    d           d{V  Y dS w xY w|                    dg           }||d<   |                     ||          \  }}	|                    dd          }
|                    dd          t          fd|d
         D             d          }|r#|                    dt          |                    nt          |          }t          |          }||k    r	d||z
   dnd}|
                    |                     d|
 d|	 d|           t          j        |           d{V  |                                 d{V  dS |                    d          r	 t          |dd                   }n-# t          $ r  |                    d           d{V  Y dS w xY w|                    dg           }|dk     s|t          |          k    r|                    d           d{V  dS ||         }|                    dd          |                    d           }|s|                    d!           d{V  dS 	  |||           d{V }n7# t           $ r*}t"                              d"|           d#| }Y d}~nd}~ww xY w	 |
                    |                     |          t          j        d           d{V  n@# t           $ r3 	 |
                    |dd           d{V  n# t           $ r Y nw xY wY nw xY w|                    d$           d{V  | j                             |d           dS |d%k    rg |d
         D ]}|                    dt          |                    dg                               }|d          d&| d'}|                    d(          rd)| }                    t+          |d|d*          +                     fd,t-          dt                    d-          D             }|                    t+          d.d/+          g           t/          |          }	  ||d0                   }n# t           $ r |d0         }Y nw xY w|
                    |                     d1|d2         pd3 d4| d5          t          j        |           d{V  |                                 d{V  dS |d/k    rT| j                             |d           |
                    d6d7           d{V  |                                 d{V  dS |                                 d{V  dS )8zDHandle model picker inline keyboard callbacks (mp:/mm:/mb:/mx:/mg:).u$   Picker expired — use /model again.rc   Nr   r  c                     | S r   r    r  s    r"   r  z@TelegramAdapter._handle_model_picker_callback.<locals>.get_label	  r  r!   r   r   c              3   4   K   | ]}|d          k    |V  dS r  Nr    ry   r  provider_slugs     r"   r   z@TelegramAdapter._handle_model_picker_callback.<locals>.<genexpr>	  1      MMq!F)}2L2L2L2L2L2LMMr!   r  zProvider not found.r  selected_providerr   selected_provider_name
model_list
model_pager  z
_u2    more available — type `/model <name>` directly_r   u&   ⚙ *Model Configuration*

Provider: *r  z
Select a model:r_   rx  r  r  zInvalid page.c              3   4   K   | ]}|d          k    |V  dS r+  r    r,  s     r"   r   z@TelegramAdapter._handle_model_picker_callback.<locals>.<genexpr>	  r.  r!   r  zInvalid selection.zInvalid model index.r  zPicker expired.zModel picker switch failed: %szError switching model: zModel switched!r  r  r  r  r  r  r  c                 *    g | ]}||d z            S r  r    r  s     r"   r{   zATelegramAdapter._handle_model_picker_callback.<locals>.<listcomp>
  r  r!   r$   r  r  r  r  r  r	  r
  r  zModel selection cancelled.)r_   r  )r   r   answerr  r  rI   r|   r   r&  r   r  r  r   r  r+  r   rG   r  r;  rO  r   r
   rC  r   )r   queryr'  r   stater  providerr  r  r%  pnamer  shownr   r  r  r1  r!  callbackresult_textexcr  r  r  r  r  r  r-  s                             @@r"   _handle_model_picker_callbackz-TelegramAdapter._handle_model_picker_callback	  sS
      (,,W55 	,,$J,KKKKKKKKKF	6666666 	 	 	    	 ??5!! h	! HMMMMME+.MMM H  ll(=l>>>>>>>>>\\(B//F)6E%&.6ll6=.Q.QE*+"(E,"#E,"&"<"<VQ"G"GHiLL77ELLV==EKKE_dgl_l_l[%%-[[[[rtE))((2&+2 2.72 2*/2 2  %0% * 
 
 
 
 
 
 
 
 
 ,,..         __U## C	!48}}   lll888888888 YY|R00F"&E,"&"<"<VT"J"JHiII6;;E!II&92>>MMMMME+.MMM H BJZHLLV===sSY{{EKKE_dgl_l_l[%%-[[[[rtE))((2&+2 2.72 2*/2 2  %0% * 
 
 
 
 
 
 
 
 
 ,,..         __U## _	!$qrr(mm   ll(<l========= <44JQww#Z00ll(>l?????????!#H!II&92>>Myy!455H ll(9l:::::::::>$,HWh$N$NNNNNNN > > >=sCCC===>
--,,[99(4!% .          
  	 	 	11(#'%) 2          
 !   D	 ,,$5,666666666 $(($77777T\\G;'  nc!%%"2E2E.F.FGGV90000055&& +*5NNE(>OAfI>O>OPPP    KJJJaWq0I0IJJJDKK-l$OOOPQQQ+D11H;!*51C+D!E!E ; ; ;!&'9!:; ))((.+0+A+NY. .%3. . .  %0% *          ,,..         T\\$(($777))1! *          ,,..          ,,..         s    A AA#G; ;&H%$H%5N &N76N7Q/ /
R#9 RR#';S# #
T .TT 
TT TT T Y- -ZZr  r   contextContextTypes.DEFAULT_TYPEc           
        K   |j         }|r|j        sdS |j        }t          |dd          }t          |dd          }t          |dd          }t          |dd          }t          |dd          }	t          |j        dd          }
|                    d          rC|j        rt          |j        j                  nd}|r|                     |||           d{V  dS |                    d	          r<|	                    d
d          }t          |          dk    r|d         }	 t          |d                   }n4# t          t          f$ r  |                    d           d{V  Y dS w xY wt          t          |j        dd                    }|                     |||t          |          nd|	t          |	          nd|
          s|                    d           d{V  dS | j                            |d          }|s|                    d           d{V  dS ddddd}t          |j        dd          }|                    |d          }|                    |           d{V  	 |                    |                     | d|           t*          j        d           d{V  n# t.          $ r Y nw xY w	 ddlm}  |||          }t4                              d ||||           n2# t.          $ r%}t4                              d!|           Y d}~nd}~ww xY wdS |                    d"          r.|	                    d
d          }t          |          dk    r|d         }|d         }t          t          |j        dd                    }|                     |||t          |          nd|	t          |	          nd|
          s|                    d#           d{V  dS | j                            |d          }|s|                    d$           d{V  dS dd%d&d'}t          |j        dd          }|                    |d          }|                    |           d{V  	 |                    |                     | d|           t*          j        d           d{V  n# t.          $ r Y nw xY w	 dd(lm} |                     |||           d{V }|r8|j        r0t          |j        dd          }t          |j        dd          }t          |dd          }t          |j        d)d          }t          |j        j                  |                     |          t*          j        d*| !                                }t          |d+|          }t          |          "                                d,t          tF          j$                  "                                t          t          tF          j$        d+tF          j$                            "                                hv } |y| rw|ut          |          }!|!|d-<   |%                    | &                    t          |j        j                  t          |          t          |          d.d/|!0                     n_|]|%                    | &                    t          |j        j                  t          |          d1t          |          i                      | j'        dWi | d{V  n:# t.          $ r-}t4                              d2| j(        |d.3           Y d}~nd}~ww xY wdS |                    d4          r|	                    d
d          }t          |          dk    r|d         }"|d         }#t          t          |j        dd                    }|                     |||t          |          nd|	t          |	          nd|
          s|                    d#           d{V  dS | j)                            |"          }|s|                    d$           d{V  dS t          |j        dd          }|#d5k    r	 dd6l*m+}$  |$|"           n8# t.          $ r+}t4          ,                    d7| j(        |           Y d}~nd}~ww xY w|                    d8           d{V  	 |                    d9|j        j-        pd d:t]          j/        |           d;t*          j0        d           d{V  n# t.          $ r Y nw xY wdS 	 t          |#          }%n4# t          tb          f$ r  |                    d<           d{V  Y dS w xY wd}&	 dd=l*m2}' |'                    |"          }(|(r6|(j3        r/d|%cxk    rt          |(j3                  k     rn n|(j3        |%         }&n# t.          $ r d}&Y nw xY w|&d>|%dz    }&| j)                            |"d           	 dd?l*m4})  |)|"|&          }*n:# t.          $ r-}t4                              d@| j(        |           dA}*Y d}~nd}~ww xY w|                    dB|&ddC                     d{V  	 |                    d9t]          j/        |j        j-        pd           dDt]          j/        |           dEt]          j/        |&           t*          j0        d           d{V  n# t.          $ r Y nw xY w|*rt4                              dF|"|&|           nt4          ,                    dG|"           dS |                    dH          sdS |	                    d
d          d         }+t          t          |j        dd                    }|                     |||t          |          nd|	t          |	          nd|
          s|                    dI           d{V  dS |                    dJ|+ dK           d{V  |+dLk    rdMndN}	 |                    |                     dO| dP          t*          j        d           d{V  n# t.          $ r Y nw xY w	 ddQl5m6},  |,            }-|-dRz  }.|.7                    dS          }/|/8                    |+           |/9                    |.           t4                              dT|+t          |j        ddU                     dS # t.          $ r&}t4                              dV|           Y d}~dS d}~ww xY w)Xz%Handle inline keyboard button clicks.Nr  r   chattyper   
first_name)r   r  r  r  r  zea:r  r$   r   rt   zInvalid approval data.rc   idr   r   u/   ⛔ You are not authorized to approve commands.z(This approval has already been resolved.u   ✅ Approved onceu   ✅ Approved for sessionu   ✅ Approved permanentlyu
   ❌ Denied)oncesessionalwaysdenyUserResolvedz by r3  r   )resolve_gateway_approvalzKTelegram button resolved %d approval(s) for session %s (choice=%s, user=%s)z;Failed to resolve gateway approval from Telegram button: %szsc:u1   ⛔ You are not authorized to answer this prompt.z&This prompt has already been resolved.u   🔒 Always approveu   ❌ Cancelled)rG  rI  ra  )slash_confirmrq  r  r   r  r1  T)r   r/  rv  r   z&[%s] slash-confirm callback failed: %sr	  r  other)mark_awaiting_textz"[%s] mark_awaiting_text failed: %su$   ✏️ Type your answer in the chat.r  z"

<i>Awaiting typed response from u   …</i>zInvalid choice.)_entrieszchoice )resolve_gateway_clarifyz'[%s] resolve_gateway_clarify failed: %sFr  r  z

<b>z:</b> z<Telegram clarify button resolved (id=%s, choice=%r, user=%s)zGTelegram clarify button: resolve_gateway_clarify returned False (id=%s)zupdate_prompt:u4   ⛔ You are not authorized to answer update prompts.zSent 'z' to the update process.yYesNou   ⚕ Update prompt answered: *r  r  z.update_responser  z/Telegram update prompt answered '%s' by user %sr	  z1Failed to write update response from callback: %sr    ):callback_queryr'  r   	from_userr|   r  r  r   r?  r~   r   r+  r   
IndexErrorr6  r  r   rO  r   r  r  r   r  rG   tools.approvalrM  r  r  r;  r   toolsrN  resolvert  r  r   PRIVATEr  r5  r  r   r   tools.clarify_gatewayrP  rM  r_   r  r  r  r   rQ  r  rR  r  r  with_suffix
write_textreplace)0r   r  r@  r7  r'  query_messagequery_chat_id
query_chatquery_chat_typequery_thread_idquery_user_namer   partsr  r  	caller_idr  	label_mapuser_displayr  rM  r  r>  r  _slash_confirm_modr=  r   rC  r   prompt_message_idrI  chat_type_valueis_private_chatr  r  choice_tokenrP  r  resolved_text_clarify_entriesentryrR  resolvedr6  r  homeresponse_pathtmps0                                                   r"   rB  z&TelegramAdapter._handle_callback_query?
  s      % 	EJ 	Fzy$77y$??]FD99
!*fd;;!-1DdKK!%/<FF ??<== 	49MKc%-/000tG O88gNNNNNNNNNF ??5!! ;	JJsA&&E5zzQq"%eAh--KK"J/   ,,,D,EEEEEEEEEFF
  r B BCC	88)6E6Qc/222W[6E6Qc/222W[- 9     ,,,],^^^^^^^^^F"266{DII" ,,,V,WWWWWWWWWF 098(	 	  'ufMM!fj99lll.........11!00E1M1M|1M1MNN#,#8%) 2          
 !   DeGGGGGG44[&IIEKKe{FL    ! e e eLL!^`cddddddddeF ??5!! _	JJsA&&E5zzQq"1X
r B BCC	88)6E6Qc/222W[6E6Qc/222W[- 9     ,,,_,`````````F"7;;JMM" ,,,T,UUUUUUUUUF 03- 	
  'ufMM!fj99lll.........11!00E1M1M|1M1MNN#,#8%) 2          
 !   D1jIIIIII(:(B(B#Z) ) # # # # # #K # *Uu} *U %,EM;NPT$U$U	&u}fdCC$+D&$$?$?	,3EM<QU,V,V)'*5=+@'A'A$($7$7$D$D*3*?7 7 #7799	7 +2)Wi*P*P*-o*>*>*D*D*F*F% 0117799(8'8CS T TUU[[]]K +
 %0_0IZIf*-.?*@*@KALK(=>'.. $ < <$'(=$>$>$'	NN58^^LP%& %& 9D != !" !"
 
 
 
 '2'.. $ < <$'(=$>$>$'	NN%0#i..$A!" !"   FdETTTTTTTTTTT  j j jLL!I49VYdhLiiiiiiiijF ??5!! f	JJsA&&E5zzQ"1X
$Qxr B BCC	88)6E6Qc/222W[6E6Qc/222W[- 9     ,,,_,`````````F"155jAA" ,,,T,UUUUUUUUUF&ufMM7**]LLLLLL**:6666$ ] ] ]'KTYX[\\\\\\\\]  ,,,R,SSSSSSSSS#55 "J(:(@b  "J  "Jfkfrs  gA  gA  "J  "J  "J'0~)- 6          
 %   Fl++CC"I.   ,,,=,>>>>>>>>>FF 04)RRRRRR,00<<E ; ;13P3P3P3Pc%->P>P3P3P3P3P3P(-c(:  ) ) )$(MMM) !( %8cAg$7$7M #''
D999%MMMMMM66z=QQHH  % % %LL!JDIWZ[[[$HHHHHH% ll(C}SbS/A(C(ClDDDDDDDDD11 SEL1C1Ir$J$J  S  SSXS_`lSmSm  S  Suz  vB  CP  vQ  vQ  S  S#,>%) 2          
 !   D  	KKV"M<   
 NNa"   F /00 	FC##A&r::;;	00!.=.Ic/***t.=.Ic/***t% 1 
 
 	 ,,$Z,[[[[[[[[[Fll I I I IlJJJJJJJJJ3D	))(()Q)Q)Q)QRR$0! *          
  	 	 	D	
	S888888"?$$D #55M++F33CNN6"""KK&&&KKIy I IK K K K K 	S 	S 	SLLLcRRRRRRRRR	Ss  (D> >-E/.E/:A J; ;
KK0K= =
L,L''L,A S	 	
SSI\9 9
]0#]++]0b+ +
c 5!cc  Ae 
ee!e1 1-f"!f"(Ag< <h
h4i 
i>#i99i>)A3l 
l*)l*?r 
rrBt 
u&uur  rE  c                 H    | d| }|                     d          r|dz  }|S )zBuild an actionable file-not-found error for gateway MEDIA delivery.

        Paths like /workspace/... or /output/... often only exist inside the
        Docker sandbox, while the gateway process runs on the host.
        z file not found: )z/workspace/z/output/z	/outputs/z (path may only exist inside the Docker sandbox. Bind-mount a host directory and emit the host-visible path in MEDIA: for gateway file delivery.))r|   )r   r  rE  r;  s       r"   _missing_media_path_errorz)TelegramAdapter._missing_media_path_error  sB     11411??CDD 	=E
 r!   
audio_pathcaptionc           
        K   | j         st          dd          S 	 t          j                            |          s%t          d|                     d|                    S t          |d          5 t          j                            |          d                                         }|dv r| 	                    |          }| 
                    ||          }	|                     ||||	          }
|                     | j         j        t          |          |r
|d	d
         nd	|	d|
|                     |          ||	dfd           d	{V }n|dv r| 	                    |          }| 
                    ||          }	|                     ||||	          }|                     | j         j        t          |          |r
|d	d
         nd	|	d||                     |          ||	dfd           d	{V }n,|                     |||||           d	{V cd	d	d	           S d	d	d	           n# 1 swxY w Y   t          dt%          |j                            S # t(          $ rY}t*                              d| j        |d           t1                                          |||||           d	{V cY d	}~S d	}~ww xY w)z<Send audio as a native Telegram voice message or audio file.Frm  rn  Audiorbrt   >   .opus.oggrv  Nr   )r   voicerz  r1  r  c                  .                          d          S Nr   seek
audio_files   r"   <lambda>z,TelegramAdapter.send_voice.<locals>.<lambda>      JOOA,>,> r!   rK  >   .m4a.mp3)r   audiorz  r1  r  c                  .                          d          S r  r  r  s   r"   r  z,TelegramAdapter.send_voice.<locals>.<lambda>  r  r!   )r   	file_pathrz  r,  r   Trp  zJ[%s] Failed to send Telegram voice/audio, falling back to base adapter: %sr	  r}  )r   r+   r   rE  r  rx  r  splitextr  r#  r0  r5  rR  
send_voicer+  r   
send_audiosend_documentr  rq  rG   r  r;  r   r   )r   r   ry  rz  r,  r   r  ext_voice_threadr  voice_thread_kwargsr  _audio_threadaudio_thread_kwargsr  r  r   s                  @r"   r  zTelegramAdapter.send_voice  s&      y 	De?CCCCJ	g7>>*-- l!%t7U7UV]_i7j7jkkkkj$'' =:g&&z2215;;==+++$($<$<X$F$FM"&"D"DXx"X"XK*.*F*F% ,7	 +G + +' !% K K	,'*7||%/9@'Jwuu~~d3>	 
 2 #77AA !#$>$>$>$> !L ! !      CC ,,,$($<$<X$F$FM"&"D"DXx"X"XK*.*F*F% ,7	 +G + +' !% K K	,'*7||%/9@'Jwuu~~d3>	 
 2 #77AA !#$>$>$>$> !L ! !      CC$ "&!3!3 '", '!)!) "4 " "      o= = = = = = = = = = = = = = = = = = = = = = =| ds3>7J7JKKKK 	g 	g 	gLL\		     ++GZ(]e+ffffffffffffff	gsP   AI( "I( 2F/H:!I( .I( :H>>I( H>%I( (
K2AK KK        imageshuman_delayc           
        K   | j         sdS |sdS 	 ddlm} nc# t          $ rV}t                              d| j        |           t                                          ||||           d{V  Y d}~dS d}~ww xY wg }g |D ]^\  }}	|	                    d          s-| 
                    |          r|                    ||	f           G                    ||	f           _|r+t                                          ||||           d{V  sdS ddlm}
 |                     |          }dfd	t          dt!                              D             }t#          |          D ]\  }}|dk    r |dk    rt%          j        |           d{V  g }g 	 |D ]\  }}	|	r
|	dd
         nd}|	                    d          r |
|dd                   }t(          j                            |          s"t                              d| j        |           |t/          |d          }                    |           |                     |||                     |                     |||                     |s-	 D ]'}	 |                                 # t          $ r Y $w xY wEt                              d| j        t!          |          |dz   t!          |                     |                     d|          }|                     ||||          }dfd}|                     | j         j        t=          |          ||d||                     |          ||d|           d{V  nw# t          $ rj}t                              d| j        |dz   t!          |          |d           t                                          ||||           d{V  Y d}~nd}~ww xY wD ]'}	 |                                 # t          $ r Y $w xY w# D ]'}	 |                                 # t          $ r Y $w xY ww xY wdS )a)  Send a batch of images natively via Telegram's media group API.

        Telegram's ``send_media_group`` bundles up to 10 photos/videos into
        a single album. Larger batches are chunked. Animated GIFs cannot
        go into a media group (they require ``send_animation``), so they
        are peeled off and sent individually via the base default path.

        URL-based photos go into the group directly; local files are
        opened as byte streams. On failure the whole batch falls back to
        the base adapter's per-image loop.
        Nr   )InputMediaPhotozD[%s] InputMediaPhoto unavailable, falling back to per-image send: %szfile://)r  )unquoter}  c                 *    g | ]}||z            S r    r    )ry   r   CHUNKphotoss     r"   r{   z8TelegramAdapter.send_multiple_images.<locals>.<listcomp>$  s&    LLL!&1u9%LLLr!   r      z.[%s] Skipping missing image in media group: %sr}  )mediarz  z5[%s] Sending media group of %d photo(s) (chunk %d/%d)rt   rv  rA   c                  ^    D ](} 	 |                      d           # t          $ r Y %w xY wd S r  )r  rG   )fhopened_filess    r"   _reset_opened_fileszATelegramAdapter.send_multiple_images.<locals>._reset_opened_filesL  sR    * ! !!GGAJJJJ( ! ! ! D!! !s   
**)r   r  r1  zmedia groupr  zI[%s] send_media_group failed (chunk %d/%d), falling back to per-image: %sTr	  rA   N) r   rH   r  rG   r  rM  r   r   send_multiple_imagesr|   _is_animation_urlr   rD  r  r#  rC  r   r   r  r  r   rE  r  r  closer  r0  r5  rR  send_media_groupr+  r   )r   r   r  r   r  r  r>  
animations	image_urlalt_text_unquote_threadr  	chunk_idxru  r  rz  
local_pathr  r  r  r  r  r  r  r  r   s                          @@@r"   r  z$TelegramAdapter.send_multiple_images  s5     $ y 	F 	F	0000000 	 	 	NNV	3   ''..w+VVVVVVVVVFFFFF	 #%
 #) 	5 	5Ix''	22 5t7M7Mi7X7X 5!!9h"78888y(34444  	''..X; /           	F444444**844 LLLLLuQFU/K/KLLL )& 1 1 J	 J	IuQ9q==mK000000000!E&(LD+0 X X'Ix19ChuuootG ++I66 X%-Xim%<%<
!w~~j99 %"NN P $	:   %!*d33$++B///__2w%O%O%OPPPP__9g%V%V%VWWWW \ '  B



$   Y KIs5zz9q=#f++   #@@xPP $ < <(3	 != ! !! ! ! ! ! ! AAI.#&w<<!&/:  (	
 33H== ! 3 B            	 	 	_Iy1}c&kk1!     gg22UH+ 3              	 '  B



$   ,  B



$   MJ	 J	s    
A<AA77A<C'M:
J
J,+J,1CM:9P:
O.A O)$P)O..P6P
PPQ	#P87Q	8
QQ	QQ	
image_pathc           
        K   | j         st          dd          S 	 t          j                            |          s%t          d|                     d|                    S |                     |          }|                     ||          }|                     ||||          }	t          |d          5 | 
                    | j         j        t          |          |r
|dd         nd|d	|	|                     |          ||d
fd           d{V }
ddd           n# 1 swxY w Y   t          dt          |
j                            S # t           $ r}t          |          }d|v pd|v }|r"t"                              d| j        |           n#t"                              d| j        |d           	 |                     |||t          j                            |          ||           d{V cY d}~S # t           $ r^}t"                              d| j        |d           t1                                          |||||           d{V cY d}~cY d}~S d}~ww xY wd}~ww xY w)z5Send a local image file natively as a Telegram photo.Frm  rn  Imagerv  r}  Nr   r   photorz  r1  r  c                  .                          d          S r  r  )
image_files   r"   r  z1TelegramAdapter.send_image_file.<locals>.<lambda>  s    
(:(: r!   r  Trp  Photo_invalid_dimensionsPHOTO_INVALID_DIMENSIONSzK[%s] Image dimensions exceed Telegram photo limits, sending as document: %szO[%s] Failed to send Telegram local image as photo, trying document fallback: %sr	  )r   r  rz  	file_namer,  r   zV[%s] Failed to send Telegram local image as document, falling back to base adapter: %sr}  )r   r+   r   rE  r  rx  r#  r0  r5  r  rR  
send_photor+  r   r  rq  rG   r  r  r   rM  r  basenamer;  r   send_image_file)r   r   r  rz  r,  r   r  r  r  r  r  r  	error_stris_dim_errordoc_errr  r   s                  @r"   r  zTelegramAdapter.send_image_filer  s      y 	De?CCCCM	p7>>*-- l!%t7U7UV]_i7j7jkkkk..x88G<<XxPPK 88$/	 9  M j$'' : GGI(#&w<<!+5<#F75D5>>$/:	 
 ( 33H==  : : : : H                        ds3>7J7JKKKK 0	p 0	p 0	pAI +i7 ;-:   .I	    3I!    p!//#(# g..z::%% 0                p p p7I!     #WW44Wj'S[fn4ooooooooooooooooooopQ0	ps|   AE "AE 6A$D&E &D**E -D*.%E I1AI,>=H;I1
I)AI$I)I,I1$I))I,,I1r  r  c           
        K   | j         st          dd          S 	 t          j                            |          s%t          d|                     d|                    S |pt          j                            |          }|                     |          }	|                     ||          }
| 	                    ||	||
          }t          |d          5 |                     | j         j        t          |          ||r
|dd         nd|
d	||                     |          ||
d
fd           d{V }ddd           n# 1 swxY w Y   t          dt          |j                            S # t"          $ rQ}t%          d| j         d|            t)                                          ||||||           d{V cY d}~S d}~ww xY w)z<Send a document/file natively as a Telegram file attachment.Frm  rn  Filerv  r}  Nr   )r   documentfilenamerz  r1  r  c                  .                          d          S r  r  r  s   r"   r  z/TelegramAdapter.send_document.<locals>.<lambda>      q		 r!   r  Trp  [z] Failed to send document: r}  )r   r+   r   rE  r  rx  r  r#  r0  r5  r  rR  r  r+  r   r  rq  rG   printr   r   )r   r   r  rz  r  r,  r   r  display_namer  r  r  r  r  r  r   s                 @r"   r  zTelegramAdapter.send_document  s      y 	De?CCCC"	t7>>),, j!%t7U7UV\^g7h7hiiii$C(8(8(C(CL..x88G<<XxPPK 88$/	 9  M i&& ! GGI+#&w<<$%$05<#F75D5>>$/:  ( 33H==  1 1 1 1 H                      " ds3>7J7JKKKK 	t 	t 	t?di??A??@@@..w	7IW_jr.ssssssssssssss	tsK   AE6 "A5E6 A%E<E6 EE6 E%E6 6
G AGGG
video_pathc           
      b  K   | j         st          dd          S 	 t          j                            |          s%t          d|                     d|                    S |                     |          }|                     ||          }|                     ||||          }	t          |d          5 | 
                    | j         j        t          |          |r
|dd         nd|d	|	|                     |          ||d
fd           d{V }
ddd           n# 1 swxY w Y   t          dt          |
j                            S # t           $ rP}t#          d| j         d|            t'                                          |||||           d{V cY d}~S d}~ww xY w)z2Send a video natively as a Telegram video message.Frm  rn  Videorv  r}  Nr   )r   videorz  r1  r  c                  .                          d          S r  r  r  s   r"   r  z,TelegramAdapter.send_video.<locals>.<lambda>'  r  r!   r  Trp  r  z] Failed to send video: r}  )r   r+   r   rE  r  rx  r#  r0  r5  r  rR  
send_videor+  r   r  rq  rG   r  r   r   )r   r   r  rz  r,  r   r  r  r  r  r  r  r  r   s               @r"   r  zTelegramAdapter.send_video   sk      y 	De?CCCC	g7>>*-- l!%t7U7UV]_i7j7jkkkk..x88G<<XxPPK 88$/	 9  M j$'' 1 GGI(#&w<<!"5<#F75D5>>$/:	 
 ( 33H==  1 1 1 1 H                        ds3>7J7JKKKK 	g 	g 	g<di<<<<===++GZ(]e+ffffffffffffff	gsK   AE "AE 6A$D&E &D**E -D*.%E 
F.AF)#F.)F.r  c           	        K   | j         st          dd          S ddlm}  ||          sLt                              d| j                   t                                          |||||           d{V S 	 | 	                    |          }| 
                    ||          }|                     ||||	          }	|                     | j         j        t          |          ||r
|dd
         nd|d|	|                     |          ||d           d{V }
t          dt!          |
j                            S # t$          $ r}t                              d| j        |d           	 ddl}|                    d          4 d{V }|                    |           d{V }|                                 |j        }ddd          d{V  n# 1 d{V swxY w Y   |                     ||||	          }|                     | j         j        t          |          ||r
|dd
         nd|d||                     |          ||d           d{V }
t          dt!          |
j                            cY d}~S # t$          $ r^}t                              d| j        |d           t                                          |||||           d{V cY d}~cY d}~S d}~ww xY wd}~ww xY w)zSend an image natively as a Telegram photo.
        
        Tries URL-based send first (fast, works for <5MB images).
        Falls back to downloading and uploading as file (supports up to 10MB).
        Frm  rn  r   )is_safe_urlz/[%s] Blocked unsafe image URL (SSRF protection)r}  Nrv  r   r  z	URL photoTrp  z8[%s] URL-based send_photo failed, trying file upload: %sr	  g      >@)timeoutzuploaded photoz+[%s] File upload send_photo also failed: %s)r   r+   tools.url_safetyr  r  rM  r   r   
send_imager#  r0  r5  rR  r  r+  r   r  rq  rG   httpxAsyncClientr   raise_for_statusrk  r;  )r   r   r  rz  r,  r   r  _photo_threadr  photo_thread_kwargsr  r  r  clientresp
image_dataupload_thread_kwargse2r   s                     r"   r  zTelegramAdapter.send_image.  s      y 	De?CCCC000000{9%% 	fNNLdiXXX++GY\d+eeeeeeeeeE	j 44X>>M<<XxPPK"&">">$/	 #? # # CC	$"7||&18Bwuu~~d+6	 
 * //99        C ds3>7J7JKKKK ,	j ,	j ,	jNNJ		    $j ,,T,:: . . . . . . .f!'I!6!6666666D))+++!%J. . . . . . . . . . . . . . . . . . . . . . . . . . .
 (,'C'C!(3	 (D ( ($ !GGI(#&w<<!+5<#F75D5>>$/:	 
 / 33H== $        "$3s~;N;NOOOOOOOOO j j jAI!	     #WW//GX`h/iiiiiiiiiiiiiiiiiiijI,	jsu   :CD? ?K7
#K2. J7GJ
G!	!J$G!	%BJK7
K/AK*K/ K2$K7*K//K22K7animation_urlc                 v  K   | j         st          dd          S 	 |                     |          }|                     ||          }|                     ||||          }|                     | j         j        t          |          ||r
|dd         nd|d||                     |          ||d           d{V }	t          d	t          |	j
                  
          S # t          $ rM}
t                              d| j        |
d	           |                     |||||           d{V cY d}
~
S d}
~
ww xY w)zJSend an animated GIF natively as a Telegram animation (auto-plays inline).Frm  rn  rv  Nr   )r   	animationrz  r1  r  Trp  zA[%s] Failed to send Telegram animation, falling back to photo: %sr	  r}  )r   r+   r#  r0  r5  rR  send_animationr+  r   r  rq  rG   r  r;  r   r  )r   r   r  rz  r,  r   _anim_threadr  animation_thread_kwargsr  r  s              r"   r  zTelegramAdapter.send_animation  s      y 	De?CCCC 	g33H==L<<XxPPK&*&B&B$/	 'C ' '# CC	("7||!.18Bwuu~~d+6	 
 . //99        C ds3>7J7JKKKK 	g 	g 	gLLS		     -(]effffffffffffff	gs   CC! !
D8+AD3-D83D8c                 H  K   | j         r	 |                     |          }|                     |          }| j                             t	          |          d|           d{V  dS # t
          $ r.}t                              d| j        |d           Y d}~dS d}~ww xY wdS )zSend typing indicator.typing)r   actionr   Nz1[%s] Failed to send Telegram typing indicator: %sTr	  )	r   r#  r:  send_chat_actionr+  rG   r  r  r   )r   r   r   _typing_threadr   r  s         r"   r  zTelegramAdapter.send_typing  s     9 	!%!9!9(!C!C$($F$F~$V$V! i00LL#&7 1           
    GI!	          	 	s   AA' '
B1#BBc                 @  K   | j         sdddS 	 | j                             t          |                     d{V }d}|j        t          j        k    rd}n8|j        t          j        k    rd}|j        rd}n|j        t          j        k    rd}|j	        p|j
        pt          |          ||j        t          |dd	          d
S # t          $ rN}t                              d| j        ||d           t          |          dt          |          dcY d}~S d}~ww xY w)z&Get information about a Telegram chat.Unknownr  )r   rD  Nr  r  channelis_forumF)r   rD  usernamer  z0[%s] Failed to get Telegram chat info for %s: %sTr	  )r   rD  r;  )r   get_chatr+  rD  r   GROUP
SUPERGROUPr  CHANNELr  	full_namer  r  r   rG   r  r;  r   )r   r   rC  r   r  s        r"   get_chat_infozTelegramAdapter.get_chat_info  sc     y 	5%t444	I++CLL99999999DIyHN**#		h111#	= ( 'Ih...%	 
DdnDG! M#D*e<<	    	I 	I 	ILLB	      LL$QHHHHHHHH	Is   B4C 
DADDDc                 @   |s|S i dgdt           dt           ffd|}t          |          }fd}t          j        d||          }t          j        dfd|          }fd	}t          j        d
||          }fd}t          j        d||t          j                  }t          j        dfd|          }t          j        dfd|          }t          j        dfd|          }t          j        dfd|          }fd}t          j        d||t          j                  }t          |          }t          t                                                              D ]}|	                    ||                   }t          j
        d|          }g }	t          |          D ]R\  }
}|
dz  dk    r|	                    |           $|fd}|	                    t          j        d||                     Sd                    |	          }|S )af  
        Convert standard markdown to Telegram MarkdownV2 format.

        Protected regions (code blocks, inline code) are extracted first so
        their contents are never modified.  Standard markdown constructs
        (headers, bold, italic, links) are translated to MarkdownV2 syntax,
        and all remaining special characters are escaped.
        r   r   rA   c                 J    dd          d}dxx         dz  cc<   | |<   |S )z@Stash *value* behind a placeholder token that survives escaping.z PHr    rt   r    )r   rf  counterplaceholderss     r"   _phz+TelegramAdapter.format_message.<locals>._ph  s;    +71:+++CAJJJ!OJJJ %LJr!   c                 ,   |                      d          }d|dd          v r|                    d          dz   nd}|d |         }||d          }|d d         }|                    dd                              dd	          } ||z   d
z             S )Nr   r   r   rt   \\\`z\`r   )r  r   r`  )mr   open_endopeningbody_and_closebodyr  s         r"   _protect_fencedz7TelegramAdapter.format_message.<locals>._protect_fenced  s    ''!**C.2c!""goosyy**1H)8)nG ^N!#2#&D<<f--55c5AAD3w~-...r!   z(```(?:[^\n]*\n)?[\s\S]*?```)z	(`[^`]+`)c                 h     |                      d                              dd                    S )Nr   r   r  )r  r`  r  r  s    r"   r  z0TelegramAdapter.format_message.<locals>.<lambda>%  s+    cc!''!**,,T6::;; r!   c                     t          |                     d                    }|                     d                              dd                              dd          } d| d| d          S )	Nrt   r$   r   r  r  z\)r  ]()rd   r  r`  )r  displayurlr  s      r"   _convert_linkz5TelegramAdapter.format_message.<locals>._convert_link+  sm    "1771::..G''!**$$T622::3FFC3,7,,c,,,---r!   z-\[([^\]]+)\]\(([^()]*(?:\([^()]*\)[^()]*)*)\)c                     |                      d                                          }t          j        dd|          } dt	          |           d          S )Nrt   \*\*(.+?)\*\*rf   r  )r  rn   rh   rb   rd   )r  innerr  s     r"   _convert_headerz7TelegramAdapter.format_message.<locals>._convert_header3  sV    GGAJJ$$&&EF+UE::E31<..111222r!   z^#{1,6}\s+(.+)$)flagsr  c                 b     dt          |                     d                     d          S )Nr  rt   rd   r  r
  s    r"   r  z0TelegramAdapter.format_message.<locals>.<lambda>@  /    cc9l1771::66999:: r!   z\*([^*\n]+)\*c                 b     dt          |                     d                     d          S )N_rt   r  r
  s    r"   r  z0TelegramAdapter.format_message.<locals>.<lambda>I  r  r!   z	~~(.+?)~~c                 b     dt          |                     d                     d          S )N~rt   r  r
  s    r"   r  z0TelegramAdapter.format_message.<locals>.<lambda>P  r  r!   z\|\|(.+?)\|\|c                 b     dt          |                     d                     d          S )N||rt   r  r
  s    r"   r  z0TelegramAdapter.format_message.<locals>.<lambda>W  s/    cc;|AGGAJJ77;;;<< r!   c           	      2   |                      d          }|                      d          }|                    d          r;|                    d          r& | dt          |d d                    d          S  | dt          |                     S )Nrt   r$   r   r   )r  r|   r}   rd   )r  r  rk  r  s      r"   _convert_blockquotez;TelegramAdapter.format_message.<locals>._convert_blockquote^  s    WWQZZFggajjG   && G7+;+;D+A+A GsfEE|GCRCL'A'AEEEFFF3&::<#8#8::;;;r!   z^((?:\*\*)?>{1,3}) (.+)$z(```[\s\S]*?```|`[^`]+`)r$   rt   c                    |                                  }|                     d          }|dk    r||dz
           dk    r|S |dk    r|dk    r||dz
           dk    r|S |dk    r|d |         }d|v sd|v rnd}t          |dz
  t          |d	z
  d
          d
          D ]F}||         dk    r'|dz  }|dk     r|dk    r||dz
           dk    r|c S  n5||         dk    r|dz  }Gd|z   S )Nr   rt   r   (]r  z](httpr  i  ru   )r   r  rC  r   )r  _segschbeforedepthr   s          r"   	_esc_barez1TelegramAdapter.format_message.<locals>._esc_bare  s1   		AB1uua!e!4!4!	SyyQUUtAE{c/A/A!	Syy!%bqb#v--$%E%*1q5#a$h2C2CR%H%H / /#'7c>>$)QJE',qyy+,q55T!a%[C5G5G35III(- (1 &*!W^^$)QJE"9$r!   z[(){}]r   )r  r   rh   rb   	MULTILINErd   reversedr_  keysr`  r~   r   r   r   )r   rk  r_   r  r  r  r!  rf  _code_split_safe_parts_idxr%  r*  r  r  r  s                @@@r"   r  zTelegramAdapter.format_message  s     	N#	s 	s 	 	 	 	 	 	 	  %T**	/ 	/ 	/ 	/ 	/ v,
 
 v;;;;
 
	. 	. 	. 	. 	.
 vFW[\\	3 	3 	3 	3 	3 vR\
 
 

 v::::
 
 v::::
 
 v::::
 
 v<<<<
 
	< 	< 	< 	< 	< v',	
 
 
 D!! D!2!2!4!45566 	8 	8C<<\#%677DD
 h:DAA#K00 	G 	GJD$ax1}}""4(((( '+ % % % %2 ""26)Y#E#EFFFFww{##r!   c                 
   | j         j                            d          }|:t          |t                    r|                                dv S t          |          S t          j        dd                                          dv S )zBReturn whether group chats should require an explicit bot trigger.require_mentionN>   r   rh  ri  rj  TELEGRAM_REQUIRE_MENTIONrn  	r   r   r   rC  r  r  ro   r   r   rW  s     r"   _telegram_require_mentionz)TelegramAdapter._telegram_require_mention  s    [&**+<==
!*c** H!''))-GGG
###y3W==CCEEIcccr!   c                 
   | j         j                            d          }|:t          |t                    r|                                dv S t          |          S t          j        dd                                          dv S )zFReturn whether non-allowlisted groups may trigger via direct @mention.
guest_modeN>   r   rh  ri  rj  TELEGRAM_GUEST_MODErn  r4  rW  s     r"   _telegram_guest_modez$TelegramAdapter._telegram_guest_mode  s~    [&**<88
!*c** H!''))-GGG
###y.88>>@@D^^^r!   c                    | j         j                            d          }|t          j        dd          }t          |t                    rd |D             S d t          |                              d          D             S )Nfree_response_chatsTELEGRAM_FREE_RESPONSE_CHATSr   c                     h | ]D}t          |                                          #t          |                                          ES r    r  rn   ry   parts     r"   r  z@TelegramAdapter._telegram_free_response_chats.<locals>.<setcomp>  =    KKK$T9J9JKCIIOO%%KKKr!   c                 ^    h | ]*}|                                 |                                 +S r    rw   r?  s     r"   r  z@TelegramAdapter._telegram_free_response_chats.<locals>.<setcomp>  -    MMM

M

MMMr!   r  	r   r   r   r   r   rC  r_  r  r~   r   r   s     r"   _telegram_free_response_chatsz-TelegramAdapter._telegram_free_response_chats  s    k##$9::;):B??Cc4   	LKK#KKKKMMS)<)<MMMMr!   c                    | j         j                            d          }|t          j        dd          }t          |t                    rd |D             S d t          |                              d          D             S )a_  Return the whitelist of group/supergroup chat IDs the bot will respond in.

        When non-empty, group messages from chats NOT in this set are
        silently ignored unless ``guest_mode`` is enabled and the bot is
        explicitly @mentioned.  DMs are never filtered.
        Empty set means no restriction (fully backward compatible).
        allowed_chatsNTELEGRAM_ALLOWED_CHATSr   c                     h | ]D}t          |                                          #t          |                                          ES r    r>  r?  s     r"   r  z:TelegramAdapter._telegram_allowed_chats.<locals>.<setcomp>  rA  r!   c                 ^    h | ]*}|                                 |                                 +S r    rw   r?  s     r"   r  z:TelegramAdapter._telegram_allowed_chats.<locals>.<setcomp>  rC  r!   r  rD  rE  s     r"   _telegram_allowed_chatsz'TelegramAdapter._telegram_allowed_chats  s     k##O44;)4b99Cc4   	LKK#KKKKMMS)<)<MMMMr!   c                 
   | j         j                            d          }|t          j        dd          }t          |t                    r|}n"t          |                              d          }t                      }|D ]}t          |          
                                }|s&	 |                    t          |                     J# t          t          f$ r$ t                              d| j        |           Y ~w xY w|S )Nignored_threadsTELEGRAM_IGNORED_THREADSr   r  z,[%s] Ignoring invalid Telegram thread id: %r)r   r   r   r   r   rC  r_  r  r~   setrn   r  r+  r   r   r  rM  r   )r   r   r`  ignoredr   r_   s         r"   _telegram_ignored_threadsz)TelegramAdapter._telegram_ignored_threads  s   k##$566;)6;;Cc4   	)FFXX^^C((FEE 	a 	aEu::##%%D aCII&&&&z* a a aMtyZ_`````as   ("C2D ?D c                    | j         j                            d          }|t          j        dd                                          }|rg	 t          j        |          }nO# t          $ rB d |	                                D             }|sd |
                    d          D             }Y nw xY w|}|g S t          |t                    r|g}t          |t                    s5t                              d| j        t#          |          j                   g S g }|D ]}t          |t                    r|                                s,	 |                    t)          j        |t(          j                             `# t(          j        $ r,}t                              d	| j        ||           Y d}~d}~ww xY w|r.t                              d
| j        t3          |                     |S )z=Compile optional regex wake-word patterns for group triggers.mention_patternsNTELEGRAM_MENTION_PATTERNSr   c                 ^    g | ]*}|                                 |                                 +S r    rw   r?  s     r"   r{   z=TelegramAdapter._compile_mention_patterns.<locals>.<listcomp>  s-    XXXt4::<<XdjjllXXXr!   c                 ^    g | ]*}|                                 |                                 +S r    rw   r?  s     r"   r{   z=TelegramAdapter._compile_mention_patterns.<locals>.<listcomp>  s-    !Z!Z!Z4TZZ\\!Z$**,,!Z!Z!Zr!   r  z?[%s] telegram mention_patterns must be a list or string; got %sz,[%s] Invalid Telegram mention pattern %r: %sz*[%s] Loaded %d Telegram mention pattern(s))r   r   r   r   r   rn   jsonloadsrG   
splitlinesr~   rC  r  r_  r  rM  r   rD  r   r   rh   compile
IGNORECASEr;  r  r   )r   patternsr   loadedcompiledpatternr>  s          r"   r   z)TelegramAdapter._compile_mention_patterns  s   ;$(();<<)7<<BBDDC "[!Z__FF  [ [ [XXs~~7G7GXXXF! [!Z!Z399S>>!Z!Z!Z[ "Ih$$ 	" zH(D)) 	NNQ	X'  
 I%' 	h 	hGgs++ 7==?? h
7BM B BCCCC8 h h hMtyZacfggggggggh 	`KKDdiQTU]Q^Q^___s+   A! !A	B-,B-	2E<<F7"F22F7c                     t          |dd           }|sdS t          t          |dd                                        d          d                                         }|dv S )NrC  FrD  r   .ru   >   r  r  )r   r  r~   r  )r   r  rC  r   s       r"   _is_group_chatzTelegramAdapter._is_group_chat  sd    w-- 	5fb112288==bAGGII	333r!   c                     | j         rt          |dd           sdS t          |j        dd           }t          |o(t          |dd           t          | j         dd           k              S )Nreply_to_messageFrW  rF  )r   r   re  ro   )r   r  
reply_users      r"   _is_reply_to_botz TelegramAdapter._is_reply_to_bot  sm    y 	1CT J J 	5W5{DII
Jd7:tT#B#BgdiY]_cFdFd#deeer!   c           	         | j         sdS t          | j         dd           pd                    d                                          }t          | j         dd           }|rd| nd }fd} |            D ]\  }}|D ]}t	          t          |dd                                        d          d	                                         }	|	d
k    r|rt          t          |dd	                    }
t          t          |dd                    }|
dk     s|dk    r||
|
|z                                                                            |k    r  dS |	dk    r.t          |dd           }|rt          |dd           |k    r  dS |	dk    r|rt          t          |dd	                    }
t          t          |dd                    }|
dk     s|dk    r]||
|
|z            }|                    d          }|dk     r||d                                                                          |k    r  dS dS )NFr  r   @rF  c               3      K   t           dd           pdt           dd           pg fV  t           dd           pdt           dd           pg fV  d S )Nr_   r   entitiesrz  caption_entities)r   )r  s   r"   _iter_sourcesz<TelegramAdapter._message_mentions_bot.<locals>._iter_sources  sx      '64006BUY8Z8Z8`^`````'9d339r77L^`d;e;e;kikkkkkkkr!   rD  rb  ru   mentionoffsetlengthr   Ttext_mentionuserbot_command)	r   r   r   r  r  r~   r+  rn   find)r   r  bot_usernamebot_idexpectedrm  source_textrk  entityentity_typero  rp  rr  command_textat_indexs    `             r"   _message_mentions_botz%TelegramAdapter._message_mentions_bot  s   y 	5	:t<<BJJ3OOUUWWD$//)5?%|%%%4	l 	l 	l 	l 	l &3]__ !	$ !	$!K"  $  $!'&&""="=>>DDSII"MSSUU)+++ 2!>!>??F 1!=!=>>FzzVq[[ "6&6/#9:@@BBHHJJhVV#ttt W N22"66488D $dD 9 9V C C#ttt M11h1 !2!>!>??F 1!=!=>>FzzVq[[ #.vfvo/E#FL+0055H!|| #HII.4466<<>>(JJ#tttA $B ur!   c                     | j         sdS t          |dd           t          |dd           fD ](}|s| j         D ]}|                    |          r  dS )dS )NFr_   rz  T)r   r   search)r   r  	candidater`  s       r"   !_message_matches_mention_patternsz1TelegramAdapter._message_matches_mention_patternsH  s    % 	5!'6488''9VZ:[:[\ 	  	 I 1    >>),,  444   ur!   c                 T    |                                  o|                     |          S )zReturn True for the narrow guest-mode bypass: explicit bot mention.

        The caller (:meth:`_should_process_message`) has already verified
        the message is a group chat, so that check is not repeated here.
        )r9  r}  )r   r  s     r"   _is_guest_mentionz!TelegramAdapter._is_guest_mentionS  s)     ((**Rt/I/I'/R/RRr!   r_   c                     |r| j         rt          | j         dd           s|S t          j        | j         j                  }t          j        d| dd|                                          }|p|S )Nr  z(?i)@z\b[,:\-]*\s*r   )r   r   rh   r  r  rb   rn   )r   r_   r  ri   s       r"   _clean_bot_trigger_textz'TelegramAdapter._clean_bot_trigger_text[  sv     	49 	GDIz4,P,P 	K9TY/00&8(888"dCCIIKK$r!   
is_commandr  c          	         |                      |          sdS t          |dd          }|_	 t          |          |                                 v rdS n8# t          t
          f$ r$ t                              d| j        |           Y nw xY wt          t          t          |dd          dd                    }| 
                    |          }|                                 }|r||vr|S |rdS ||                                 v rdS |                                 sdS |                     |          rdS |                                 s|                     |          rdS |                     |          S )	u  Apply Telegram group trigger rules.

        DMs remain unrestricted. Group/supergroup messages are accepted when:
        - the chat passes the ``allowed_chats`` whitelist (when set), or
          ``guest_mode`` is enabled and the bot is explicitly mentioned
        - the chat is explicitly allowlisted in ``free_response_chats``
        - ``require_mention`` is disabled
        - the message replies to the bot
        - the bot is @mentioned
        - the text/caption matches a configured regex wake-word pattern

        When ``allowed_chats`` is non-empty, it remains a hard gate except for
        the narrow ``guest_mode`` bypass: group/supergroup messages that
        explicitly @mention this bot. Replies and regex wake words do not bypass
        ``allowed_chats``. When ``require_mention`` is enabled, slash commands are not given
        special treatment — they must pass the same mention/reply checks
        as any other group message.  Users can still trigger commands via
        the Telegram bot menu (``/command@botname``) or by explicitly
        mentioning the bot (``@botname /command``), both of which are
        recognised as mentions by :meth:`_message_mentions_bot`.
        Tr   NFz8[%s] Ignoring non-numeric Telegram message_thread_id: %rrC  rF  r   )rc  r   r+  rR  r   r   r  rM  r   r  r  rL  rF  r5  rg  r9  r}  r  )r   r  r  r   chat_id_strguest_mentionalloweds          r"   _should_process_messagez'TelegramAdapter._should_process_messageb  s   , ""7++ 	4G%8$??	 qy>>T%C%C%E%EEE 5 Fz* q q qY[_[dfopppppq '''64"@"@$KKLL ..w77
 ..00 	!{'11   	4$<<>>>>4--// 	4  )) 	4 ((** 	t/I/I'/R/R 	455g>>>s   #A 2BBc                 ,  K   |j         r|j         j        sdS |                     |j                   sdS |                     |j         t          j        |j                  }|                     |j                  |_        |                     |           dS )zHandle incoming text messages.

        Telegram clients split long messages into multiple updates.  Buffer
        rapid successive text messages from the same user/chat and aggregate
        them into a single MessageEvent before dispatching.
        N	update_id)	r  r_   r  _build_message_eventr)   r4  r  r  _enqueue_text_eventr   r  r@  events       r"   r6  z$TelegramAdapter._handle_text_message  s       ~ 	V^%8 	F++FN;; 	F))&.+:JV\Vf)gg11%*==
  '''''r!   c                    K   |j         r|j         j        sdS |                     |j         d          sdS |                     |j         t          j        |j                  }|                     |           d{V  dS )z!Handle incoming command messages.NTr  r  )r  r_   r  r  r)   r5  r  handle_messager  s       r"   r7  zTelegramAdapter._handle_command  s      ~ 	V^%8 	F++FNt+LL 	F))&.+:MY_Yi)jj!!%(((((((((((r!   c                 R  K   |j         sdS |                     |j                   sdS |j         }t          |dd          }|rt          |dd          nt          |dd          }|sdS t          |dd          }t          |dd          }||dS dg}|rVt          |dd          }	t          |dd          }
|	r|                    d	|	            |
r|                    d
|
            |                    d|            |                    d|            |                    d| d|            |                    d           |                     |t
          j        |j                  }d                    |          |_	        | 
                    |           d{V  dS )z,Handle incoming location/venue pin messages.Nvenuelocationlatitude	longitudez![The user shared a location pin.]r  addresszVenue: z	Address: z
latitude: zlongitude: z5Map: https://www.google.com/maps/search/?api=1&query=r  zSAsk what they'd like to find nearby (restaurants, cafes, etc.) and any preferences.r  r   )r  r  r   r   r  r)   r8  r  r   r_   r  )r   r  r@  r  r  r  latlonrg  r  r  r  s               r"   r9  z(TelegramAdapter._handle_location_message  s     ~ 	F++FN;; 	FnWd++7<`75*d333'#z[_B`B` 	Fh
D11hT22;#+F 55 	4E7D11EeY55G 0.u../// 4222333'#''((((3(()))XSXXSVXXYYYjkkk))#{/CvO_)``YYu%%
!!%(((((((((((r!   r  c                     ddl m}  ||j        | j        j                            dd          | j        j                            dd                    S )z-Session-scoped key for text message batching.r   build_session_keygroup_sessions_per_userTthread_sessions_per_userFr  r  )r  r  r  r   r   r   )r   r  r  s      r"   _text_batch_keyzTelegramAdapter._text_batch_key  sg    555555  L$(K$5$9$9:SUY$Z$Z%)[%6%:%:;UW\%]%]
 
 
 	
r!   c                    |                      |          }| j                            |          }t          |j        pd          }|||_        || j        |<   nw|j        r$|j        r|j         d|j         n|j        |_        ||_        |j        r>|j                            |j                   |j                            |j                   | j	                            |          }|r(|
                                s|                                 t          j        |                     |                    | j	        |<   dS )a2  Buffer a text event and reset the flush timer.

        When Telegram splits a long user message into multiple updates,
        they arrive within a few hundred milliseconds.  This method
        concatenates them and waits for a short quiet period before
        dispatching the combined message.
        r   Nr   )r  r   r   r   r_   _last_chunk_len
media_urlsr   media_typesr   r   ra  r  r!  _flush_text_batch)r   r  rf  existing	chunk_len
prior_tasks         r"   r  z#TelegramAdapter._enqueue_text_event  sP    ""5))-11#66
(b))	$-E!.3D&s++ z bDLM a8= @ @EJ @ @ @W\Wa'0H$ ?#**5+;<<<$++E,=>>> 377<<
 	 joo// 	 .5.A""3''/
 /
&s+++r!   c                   K   t          j                    }	 | j                            |          }|rt	          |dd          nd}|r t          t	          |dd          pd          nd}|| j        k    r| j        }nS|| j        k    rt          | j
        | j                  }n-|| j        k    rt          | j
        | j                  }n| j
        }t          j        |           d{V  | j                            |d          }|s<	 | j                            |          |u r| j                            |d           dS dS t"                              d|t          |j        pd                     |                     |           d{V  | j                            |          |u r| j                            |d           dS dS # | j                            |          |u r| j                            |d           w w xY w)zWait for the quiet period then dispatch the aggregated text.

        Uses a longer delay when the latest chunk is near Telegram's 4096-char
        split point, since a continuation chunk is almost certain.
        r  r   r_   r   Nz,[Telegram] Flushing text batch %s (%d chars))r  current_taskr   r   r   r   _SPLIT_THRESHOLDr   _TEXT_BATCH_FAST_LENr   r   _TEXT_BATCH_FAST_DELAY_S_TEXT_BATCH_SHORT_LEN_TEXT_BATCH_SHORT_DELAY_Sr  rO  r   r  r  r_   r  )r   rf  r  pendinglast_len	total_lenr  r  s           r"   r  z!TelegramAdapter._flush_text_batch
  sI      +--#	> 044S99GAHOww(91===aHCJQGGVR88>B???PQI4000<d777D:D<YZZd888D:D<Z[[6-&&&&&&&&&.223==E  -11#66,FF.223===== GF KK>S)r**   %%e,,,,,,,,,-11#66,FF.223===== GFt-11#66,FF.223==== Gs   C0G	 AG	 	:Hr  c                     ddl m}  ||j        | j        j                            dd          | j        j                            dd                    }t          |dd	          }|r| d
| S | dS )z1Return a batching key for Telegram photos/albums.r   r  r  Tr  Fr  media_group_idNz:album:z:photo-burst)r  r  r  r   r   r   r   )r   r  r  r  r  r  s         r"   _photo_batch_keyz TelegramAdapter._photo_batch_key:  s    555555''L$(K$5$9$9:SUY$Z$Z%)[%6%:%:;UW\%]%]
 
 

 !&6== 	;!::.:::++++r!   	batch_keyc                   K   t          j                    }	 t          j        | j                   d{V  | j                            |d          }|s<	 | j                            |          |u r| j                            |d           dS dS t          	                    d|t          |j                             |                     |           d{V  | j                            |          |u r| j                            |d           dS dS # | j                            |          |u r| j                            |d           w w xY w)z;Send a buffered photo burst/album as a single MessageEvent.Nz3[Telegram] Flushing photo batch %s with %d image(s))r  r  r  r   r   rO  r   r   r  r  r   r  r  )r   r  r  r  s       r"   _flush_photo_batchz"TelegramAdapter._flush_photo_batchG  sy     +--		E- ?@@@@@@@@@/33ItDDE  .229==MM/33ItDDDDD NM KKMyZ]^c^nZoZoppp%%e,,,,,,,,,.229==MM/33ItDDDDD NMt.229==MM/33ItDDDD Ns   <D A	D :Ec                    | j                             |          }||| j         |<   nj|j                            |j                   |j                            |j                   |j        r%|                     |j        |j                  |_        | j                            |          }|r(|                                s|	                                 t          j        |                     |                    | j        |<   dS )z;Merge photo events into a pending batch and schedule flush.N)r   r   r  r   r  r_   _merge_captionr   r   ra  r  r!  r  )r   r  r  r  r  s        r"   _enqueue_photo_eventz$TelegramAdapter._enqueue_photo_eventU  s    .229==5:D'	22&&u'7888 ''(9:::z O $ 3 3HM5: N N488CC
 	 joo// 	 5<5HI`I`ajIkIk5l5l'	222r!   c                 "  K   |j         sdS |                     |j                   sdS |j         }|j        rt          j        }np|j        rt          j        }n\|j        rt          j        }nH|j	        rt          j
        }n4|j        rt          j        }n |j        rt          j        }nt          j        }|                     |||j                  }|j        r|                     |j                  |_        |j        r9|                     ||           d{V  |                     |           d{V  dS |j        r~	 |j        d         }|                                 d{V }|                                 d{V }d}	|j        r5dD ]2}
|j                                                            |
          r|
}	 n3t7          t9          |          |	          }|g|_        d|	                    d           g|_        t@          !                    d	|           tE          |d
d          }|r*| #                    tI          |          |           d{V  n,| %                    ||          }| &                    ||           dS # tN          $ r'}t@          (                    d|d           Y d}~nd}~ww xY w|j        r	 |j                                         d{V }|                                 d{V }tS          t9          |          d          }|g|_        dg|_        t@          !                    d|           n# tN          $ r(}t@          (                    d|d           Y d}~nd}~ww xY w|j	        r	 |j	                                         d{V }|                                 d{V }tS          t9          |          d          }|g|_        dg|_        t@          !                    d|           n-# tN          $ r(}t@          (                    d|d           Y d}~n d}~ww xY w|j        r	 |j                                         d{V }|                                 d{V }d}	tE          |dd          r:tT          D ]2}
|j                                                            |
          r|
}	 n3tW          t9          |          |	          }|g|_        tU          j,        |	d          g|_        t@          !                    d|           n# tN          $ r(}t@          (                    d|d           Y d}~nd}~ww xY w|j        r|j        }	 d}	|j-        pd}|r6t\          j/        0                    |          \  }}	|	                                }	|j1        pd                                }|	sR|rPtd          ,                    |d          }	|	s3d tg          j4                    D             }|,                    |d          }	d}|j5        r|j5        |k    rDd|_        t@          !                    d|j5                   |                     |           d{V  dS |	tl          v s|7                    d          r|                                 d{V }|                                 d{V }|	tl          v r|	ntd          ,                    |d          }	 t7          t9          |          |          }na# tp          $ rT}t@          (                    d |d           d!|p|p|	pd" d#|_        |                     |           d{V  Y d}~dS d}~ww xY wt          j        |_9        |g|_        |7                    d          r|ntt          ,                    |d$          g|_        t@          !                    d%|           tE          |d
d          }|r*| #                    tI          |          |           d{V  n,| %                    ||          }| &                    ||           dS |	s?|j1        r8d& tU          j4                    D             }|,                    |j1        d          }	|	tT          v r|                                 d{V }|                                 d{V }tW          t9          |          |	          }|g|_        tT          |	         g|_        t          j        |_9        t@          !                    d'|           |                     |           d{V  dS |	tf          vr|d(;                    ty          tg          j=                                        }d)|	pd" d*| |_        t@          !                    d+|	pd"           |                     |           d{V  dS |                                 d{V }|                                 d{V }t9          |          }t}          ||pd,|	           }tf          |	         }|g|_        |g|_        t@          !                    d-|           d.}|	d/v rt          |          |k    r	 |@                    d0          }|pd,|	 }t          jB        d1d2|          }d3| d4| } |j        r|  d5|j         |_        n| |_        n,# t          $ r t@          (                    d6d           Y nw xY wn4# tN          $ r'}t@          (                    d7|d           Y d}~nd}~ww xY wtE          |d
d          }|r+| #                    tI          |          |           d{V  dS |                     |           d{V  dS )8zBHandle incoming media messages, downloading images to local cache.Nr  ru   r9   )r:   r<   r8   r;   r9   r  zimage/rb  z"[Telegram] Cached user photo at %sr  z$[Telegram] Failed to cache photo: %sTr	  r  z	audio/oggz"[Telegram] Cached user voice at %sz$[Telegram] Failed to cache voice: %sr  z	audio/mp3z"[Telegram] Cached user audio at %sz$[Telegram] Failed to cache audio: %sz.mp4r  z	video/mp4z"[Telegram] Cached user video at %sz$[Telegram] Failed to cache video: %sr   c                     i | ]\  }}||	S r    r    ry   krV  s      r"   
<dictcomp>z9TelegramAdapter._handle_media_message.<locals>.<dictcomp>  s    &Y&Y&Y1q!&Y&Y&Yr!   i  @zLThe document is too large or its size could not be verified. Maximum: 20 MB.z'[Telegram] Document too large: %s bytesz-[Telegram] Failed to cache image document: %szImage document 'r	  z ' could not be read as an image.r>   z+[Telegram] Cached user image-document at %sc                     i | ]\  }}||	S r    r    r  s      r"   r  z9TelegramAdapter._handle_media_message.<locals>.<dictcomp>  s    (X(X(X$!QA(X(X(Xr!   z+[Telegram] Cached user video document at %sr  zUnsupported document type 'z'. Supported types: z([Telegram] Unsupported document type: %sr  z%[Telegram] Cached user document at %si  >   .md.txtr  z	[^\w.\- ]r  z[Content of z]:
r   zJ[Telegram] Could not decode text file as UTF-8, skipping content injectionz'[Telegram] Failed to cache document: %s)Dr  r  stickerr)   STICKERr  r:  r  r;  r  r<  r  r=  r  DOCUMENTr  r  rz  r  r_   _handle_stickerr  get_filedownload_as_bytearrayr  r  r}   r,   bytesr  r   r  r  r  r   _queue_media_group_eventr  r  r  rG   rM  r-   r1   r.   r   r  r   rE  r  	mime_type_TELEGRAM_IMAGE_MIME_TO_EXTr2   items	file_size_TELEGRAM_IMAGE_EXTENSIONSr|   r   message_type_TELEGRAM_IMAGE_EXT_TO_MIMEr   sortedr-  r/   r   decoderh   rb   UnicodeDecodeError)!r   r  r@  r  msg_typer  r  file_objimage_bytesr  r  cached_pathr  r  r  audio_bytesvideo_bytesdocoriginal_filenamer  doc_mimemime_to_extMAX_DOC_BYTES	image_extvideo_mime_to_extsupported_list	doc_bytes	raw_bytesr  MAX_TEXT_INJECT_BYTEStext_contentr  	injections!                                    r"   rA  z%TelegramAdapter._handle_media_messagef  s     ~ 	F++FN;; 	Fn ; 	,"*HHY 	,"(HHY 		,"(HHY 	,"(HHY 	,"(HH\ 	,"+HH"+H))#x6CS)TT ; 	C55ckBBEJ ; 	&&sE222222222%%e,,,,,,,,,F 9 	YY	"!&!1!1111111$,$B$B$D$DDDDDDD% "%O " "	#-3355>>yII ""+C!E" 5U;5G5GSQQQ$/= %?cjjoo%?%?$A!@+NNN!(.>!E!E! @77N8K8KUSSSSSSSSSS $ 5 5eS A AI--i??? Y Y YEqSWXXXXXXXXY 9 ^	\Y!$!3!3!5!5555555$,$B$B$D$DDDDDDD4U;5G5GVTTT$/= %0M!@+NNNN Y Y YEqSWXXXXXXXXYY T	\Y!$!3!3!5!5555555$,$B$B$D$DDDDDDD4U;5G5GVTTT$/= %0M!@+NNNN Y Y YEqSWXXXXXXXXY Y I	\Y!$!3!3!5!5555555$,$B$B$D$DDDDDDD8[$77 "%: " "	#-3355>>yII ""+C!E" 5U;5G5GSQQQ$/= %:%>sK%P%P$Q!@+NNNN Y Y YEqSWXXXXXXXXY \ w	\,Cu\$'M$7R!$ &W--.?@@FAs))++C  M/R6688  <x <599(BGGC <&Y&Y8P8V8X8X&Y&Y&Y)ooh;; !1} (E(E* J KK I3=YYY--e444444444F
 4448K8KH8U8U4%(\\^^333333H(0(F(F(H(H"H"H"H"H"H"HK'*.H'H'HNiNmNmnvx~NNI	&<U;=O=OU^&_&_&_%   'VXYdhiii=/@/`H/`PS/`W` = = = 
 #11%888888888 *5):E&(3}E$5=5H5H5R5R  *QXsXwXw  yB  DP  YQ  YQ  )RE%KK M{[[[%,S2BD%I%IN% D";;C<O<OQVWWWWWWWWWW$($9$9%$E$E	11)UCCCF Cs} C(X(X:O:U:W:W(X(X(X%+//rBBC///%(\\^^333333H(0(F(F(H(H"H"H"H"H"H"HK"8{9K9KQT"U"U"UK(3}E$)>s)C(DE%)4):E&KK M{[[[--e444444444F 666%)YYv6N6S6U6U/V/V%W%WN=c6FY = =,:= = J KK JCL\S\]]]--e444444444F "%//////"*"@"@"B"BBBBBBB	!),,	7	CTChXhcfXhXhii4S9	$/= %.K!C[QQQ )3%/))c)nn@U.U.U'0'7'7'@'@'8'L<Ls<L<L')vlC'N'N$S<$S$S\$S$S	 : 3,5)G)G5:)G)GEJJ)2EJ-   h%) '       \ \ \H!VZ[[[[[[[[\ !&6== 	//N0C0CUKKKKKKKKKF!!%(((((((((((s   ?EJ 
J9J44J9BM 
M:M55M:BP	 	
P;P66P;C"T+ +
U5UU0Dl :A9l 4\ l 
]1A	],&l ,]11Cl C>l Bl B)l :Ak l &k>;l =k>>l 
l3l..l3r  c                   K   | j                             |          }||| j         |<   nj|j                            |j                   |j                            |j                   |j        r%|                     |j        |j                  |_        | j                            |          }|r|                                 t          j
        |                     |                    | j        |<   dS )a  Buffer Telegram media-group items so albums arrive as one logical event.

        Telegram delivers albums as multiple updates with a shared media_group_id.
        If we forward each item immediately, the gateway thinks the second image is a
        new user message and interrupts the first. We debounce briefly and merge the
        attachments into a single MessageEvent.
        N)r   r   r  r   r  r_   r  r   ra  r  r!  _flush_media_group_event)r   r  r  r  r  s        r"   r  z(TelegramAdapter._queue_media_group_eventS  s       +//??7<D$^44&&u'7888 ''(9:::z O $ 3 3HM5: N N,00@@
 	 292E)).993
 3
///r!   c                   K   	 t          j        | j                   d {V  | j                            |d           }||                     |           d {V  n1# t           j        $ r Y | j                            |d            d S w xY w| j                            |d            d S # | j                            |d            w xY wr   )r  r  MEDIA_GROUP_WAIT_SECONDSr   rO  r  CancelledErrorr   )r   r  r  s      r"   r  z(TelegramAdapter._flush_media_group_eventl  s      	>- =>>>>>>>>>,00FFE ))%000000000% 	 	 	#''=====	 #''=====D#''====s*   AA B* B
+B* 	B

B* *Cr(   c                 (  K   ddl m}m}m}m}m} |j        }|j        pd}	|j        pd}
|j	        s|j
        r ||	          |_        dS  ||j                  }|rb ||d         |                    d|	          |                    d|
                    |_        t                              d|j                   dS 	 |                                 d{V }|                                 d{V }t%          t'          |          d	
          }t                              d|           ddlm}  |||           d{V }t-          j        |          }|                    d          r=|                    dd          } ||j        ||	|
            |||	|
          |_        dS  ||	rd|	 nd|	|
          |_        dS # t0          $ rA}t                              d|d            ||	rd|	 nd|	|
          |_        Y d}~dS d}~ww xY w)a  
        Describe a Telegram sticker via vision analysis, with caching.

        For static stickers (WEBP), we download, analyze with vision, and cache
        the description by file_unique_id. For animated/video stickers, we inject
        a placeholder noting the emoji.
        r   )get_cached_descriptioncache_sticker_descriptionbuild_sticker_injection build_animated_sticker_injectionSTICKER_VISION_PROMPTr   Nr  emojiset_namez [Telegram] Sticker cache hit: %sr<   r  z"[Telegram] Analyzing sticker at %s)vision_analyze_tool)r  user_promptro  analysisz	a stickerza sticker with emoji z%[Telegram] Sticker analysis error: %sTr	  )gateway.sticker_cacher  r  r  r  r  r  r  r  is_animatedis_videor_   file_unique_idr   r  r  r  r  r,   r  tools.vision_toolsr  rX  rY  rG   rM  )r   r  r  r  r  r  r  r  r  r  r  cachedr  r  r  r  result_jsonresultr  r  s                       r"   r  zTelegramAdapter._handle_stickerw  s     	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 +##)r  	'"2 	99%@@EJF ('(>?? 	00}%vzz'5'A'A6::jZbCcCc EJ KK:G<RSSSF	$--////////H ( > > @ @@@@@@@K0{1C1CQQQKKK<kJJJ>>>>>> 3 3%1! ! !      K Z,,Fzz)$$ 	$jj[AA))'*@+uV^___44[%RR


 547<M3E333+8 


  	 	 	NNBAPTNUUU0038I////kx EJJJJJJJ	s   ?C*G +G 
H6HHc                 j   	 ddl m}  |            dz  }|                                sdS ddl}t	          |dd          5 }|                    |          pi }ddd           n# 1 swxY w Y   |                    di                               d	i                               d
i                               dg           }|sdS || _        |D ]}|                    d          }|s|                    dg           D ]y}	|	                    d          }
|	                    d          }|
rK|rI| d| }|| j        vr9t          |
          | j        |<   t                              d| j        ||
           zdS # t          $ r,}t                              d| j        |           Y d}~dS d}~ww xY w)zRe-read dm_topics from config.yaml and load any new thread_ids into cache.

        This allows topics created externally (e.g. by the agent via API) to be
        recognized without a gateway restart.
        r   r  r  Nr  r  r  r  rH   r   r   r   r  r   r   r  z8[%s] Hot-loaded DM topic from config: %s -> thread_id=%sz/[%s] Failed to reload dm_topics from config: %s)r  r  r  r  r  r  r   r   r   r+  r  r  r   rG   r  )r   r  r  r  r  r   r   r  cidr  tidr   r  r  s                 r"   _reload_dm_topics_from_configz-TelegramAdapter._reload_dm_topics_from_config  sT   %	Z888888)/++m;K%%''     k3999 2Q++1r2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 

;++Z$$Wb!!["%%	    &/D"'  
 nnY// #"55 
 
A%%,,C55==D t '*OOTOO	$DO;;9<SDOI6"KK Z $	9c  
	   	Z 	Z 	ZLLJDIWXYYYYYYYYY	ZsH   'E< E< A%E< %A))E< ,A)-AE< B4E< <
F2!F--F2c                 x   |sdS t          |          }| j                                        D ]\  }}||k    r|                    | d          r|                    dd          d         }| j        D ]b}t          |                    d                    |k    r:|                    dg           D ]#}|                    d          |k    r|c c c S $cd|ic S |                                  | j                                        D ]\  }}||k    r|                    | d          r|                    dd          d         }| j        D ]b}t          |                    d                    |k    r:|                    dg           D ]#}|                    d          |k    r|c c c S $cd|ic S dS )zLook up DM topic config by chat_id and thread_id.

        Returns the topic config dict (name, skill, etc.) if this thread_id
        matches a known DM topic, or None.
        Nr  rt   r   r  r   )	r+  r   r  r|   r~   r   r  r   r	  )	r   r   r   thread_id_intrf  
cached_tidr  r  r  s	            r"   _get_dm_topic_infoz"TelegramAdapter._get_dm_topic_info  s     	4I  $4466 		, 		,OC]**s~~mmm/L/L* YYsA..q1
"&"8 ) )J:>>)4455@@!+"!=!= ) )A uuV}}
::'(  ;
++++ 	**,,,  $4466 	, 	,OC]**s~~mmm/L/L* YYsA..q1
"&"8 ) )J:>>)4455@@!+"!=!= ) )A uuV}}
::'(  ;
++++tr!   c                     | d| }|| j         vr;t          |          | j         |<   t                              d| j        ||           dS dS )zLCache a thread_id -> topic_name mapping discovered from an incoming message.r  z5[%s] Cached DM topic from message: %s -> thread_id=%sN)r   r+  r  r  r   )r   r   r   r  r  s        r"   _cache_dm_topic_from_messagez,TelegramAdapter._cache_dm_topic_from_message  sh    ----	DO++),YDOI&KKG	9i     ,+r!   r  r  c                    |j         }|j        }d}|j        t          j        t          j        hv rd}n|j        t          j        k    rd}|j        }t          t          |dd                    }d}	|-|dk    rt          |          }	n|dk    r|rt          |          }	|dk    r|	t          |dd          r| j        }	d}
d}|dk    r|	r|                     t          |j                  |	          }|r*|                    d          }
|                    d	          }t          |d
          rB|j        r;|j        j        }|r-|                     t          |j                  |	|           |
s|}
n|dk    r|	r| j        j                            dg           }|D ]}t          |                    dd                    t          |j                  k    rq|                    dg           D ]X}|                    d          }|?t          |          |	k    r,|                    d          }
|                    d	          } nY n|                     t          |j                  |j        pt          |d          r|j        nd||rt          |j                  n|dk    rt          |j                  nd|r|j        nt          |d          r|dk    r|j        nd|	|
          }d}d}|j        r^t          |j        j                  }t          |dd          }|t          |dd          nd}|r|}n|j        j        p|j        j        pd}ddlm} t          |j                  } || j        j        |	p||	r|nd          }t=          |j        pd|||t          |j                  ||||||j                  S )aO  Build a MessageEvent from a Telegram message.

        ``update_id`` is the ``Update.update_id`` from PTB; passing it through
        lets ``/restart`` record the triggering offset so the new gateway
        process can advance past it (prevents ``/restart`` being re-delivered
        when PTB's graceful-shutdown ACK fails).
        r  r  r  is_topic_messageFNr  r   skillforum_topic_createdgroup_topicsr   r   r  r   r  )r   	chat_namer   r   r   r   
chat_topicquoter_   r   )resolve_channel_prompt)r_   r  r  raw_messagerq  platform_update_idr1  reply_to_text
auto_skillchannel_prompt	timestamp) rC  rW  rD  r   r  r  r  r   ro   r   r  r7  r  rF  r   r  r  r   r  r   r   build_sourcer  r  re  rq  r_   rz  gateway.platforms.baser  r(   date)r   r  r  r  rC  rr  r   thread_id_rawr  thread_id_strr  topic_skill
topic_infocreated_namegroup_topics_configr  r  r  r  r  r  r  
quote_textr  _chat_id_str_channel_prompts                             r"   r  z$TelegramAdapter._build_message_event  sU    |  	9)<===IIY(***!I  11CU K KLL$G## #M 2 2d""'7" #M 2 2 M$9gdJX]>^>^$9 9M
00TW}MMJ 6'^^F33
(nnW55 w 566 27;V 2&:? 255c$'llMS_```% 2%1
'!!m!(,(9(=(=nb(Q(Q1  
z~~i4455TWEE!+"!=!= " "#ii44?s3xx=/H/H).6):):J*/))G*<*<K!EE F ""LLj\wt[7Q7Q%[T^^W[$([CLLLyD?P?Ps47|||VZ(,  AdnnWTS^E_E_3dmqududu4>>{#! # 
 
$ # 	g6ABBKGWd33E9>9J555PTJ  * ,1 /7  	BAAAAA47||00K)\)3LLt
 
 #!7-..( +'"*l
 
 
 	
r!   c                 T    t          j        dd                                          dvS )z6Check if message reactions are enabled via config/env.TELEGRAM_REACTIONSrn  >   rk  rl  rn  )r   r   r  r   s    r"   _reactions_enabledz"TelegramAdapter._reactions_enabled  s'    y-w77==??G[[[r!   r  c                   K   | j         sdS 	 | j                             t          |          t          |          |           d{V  dS # t          $ r-}t                              d| j        ||           Y d}~dS d}~ww xY w)z2Set a single emoji reaction on a Telegram message.Fr   rq  reactionNTz)[%s] set_message_reaction failed (%s): %sr   set_message_reactionr+  rG   r  r  r   )r   r   rq  r  r  s        r"   _set_reactionzTelegramAdapter._set_reaction  s      y 	5		)00Gz?? 1         
 4 	 	 	LLDdiQVXYZZZ55555	s   =A 
B"A>>Bc                 
  K   | j         sdS 	 | j                             t          |          t          |          d           d{V  dS # t          $ r,}t                              d| j        |           Y d}~dS d}~ww xY w)uN  Clear all reactions from a Telegram message.

        Calling ``set_message_reaction`` with ``reaction=None`` (or an empty
        sequence) is the documented Bot API way to remove all bot-set
        reactions on a message — equivalent to Bot API 10.0's
        ``deleteMessageReaction`` but supported in PTB 22.6 already.
        FNr/  Tz[%s] clear reactions failed: %sr1  r  s       r"   _clear_reactionsz TelegramAdapter._clear_reactions  s       y 	5		)00Gz?? 1         
 4 	 	 	LL:DIqIII55555	s   =A 
B!A==Bc                    K   |                                  sdS t          |j        dd          }t          |dd          }|r!|r!|                     ||d           d{V  dS dS dS )z;Add an in-progress reaction when message processing begins.Nr   rq  u   👀)r-  r   r  r3  )r   r  r   rq  s       r"   on_processing_startz#TelegramAdapter.on_processing_start  s      &&(( 	F%,	488UL$77
 	Hz 	H$$Wj,GGGGGGGGGGG	H 	H 	H 	Hr!   outcomec                 J  K   |                                  sdS t          |j        dd          }t          |dd          }|r|sdS |t          j        k    r|                     ||           d{V  dS |                     |||t          j        k    rdnd           d{V  dS )u  Swap the in-progress reaction for a final success/failure reaction.

        Unlike Discord (additive reactions), Telegram's set_message_reaction
        replaces all existing reactions in one call — no remove step needed.

        On CANCELLED outcomes (e.g. the user runs ``/stop``, or a session is
        interrupted mid-flight), we explicitly clear the 👀 in-progress
        reaction so it doesn't linger on the user's message indefinitely.
        Without this clear, the only way to remove the 👀 was to wait for
        another agent run to swap it to 👍/👎 — which never happens if the
        cancellation was the last activity in the chat.
        Nr   rq  u   👍u   👎)r-  r   r  r*   	CANCELLEDr5  r3  SUCCESS)r   r  r8  r   rq  s        r"   on_processing_completez&TelegramAdapter.on_processing_complete  s       &&(( 	F%,	488UL$77
 	J 	F'111''<<<<<<<<<<<$$ '+<+D D D,          r!   r   )NNr  r  )r   r   N)r  N)r  r   r@  rA  rA   N)NNN)Nr  )NNNN)r   r   r   __doc__r  r  r  r7  r   ro   __annotations__r  r  r  r  staticmethodr  r   r   r   propertyr   r&   r   r   r   r   r  classmethodr#  r(  r+  r-  r0  r5  r3  r:  rG   r=  rD  rG  rR  r_  rY  r\  re  r   rt  r{  r  r  r  r  r  r  r  r  r\  rf  rj  r+   r  r  r  r  r  r  r  r  r  r  r  r  r  r  r&  r?  rB  rx  r  r   r  r  r  r  r  r  r  r  r  r5  r9  rP  rF  rL  rR  rh   Patternr   r	   rc  rg  r}  r  r  r  r  r   r   r   r6  r7  r9  r(   r  r  r  r  r  r  rA  r  r  r  r	  r  r  r)   r  r-  r3  r5  r7  r*   r<  __classcell__)r   s   @r"   r   r   <  s,           "" $(D''' #  $
 &*%)   E?	
 E? 
   \6   X>4~ >4 >4 >4 >4 >4 >4@. c3h0.	c3h. . . .& "&#'#'#'.G .G .G.G #	.G
 C=.G C=.G C=.G 
.G .G .G .G` A8DcN+C AQT A A A [A ?(4S>:R ?W_`cWd ? ? ? [? ?Xd38n5M ?RZ[^R_ ? ? ? [?  .2	 	3-	 4S>*	 
#		 	 	 [	 
 .2-1Q QQ C=Q 4S>*	Q
 &c]Q 
c3hQ Q Q [Q< HSM hsm    [
 
hsm 
QT 
 
 
 [
 8) 8 8 8 8 \8 Y 4    \ 

 4S>*
 &c]	

 

 
 
 [
( &* 1  1 1 #s(^ 1 4S>*	 1
 &c] 1  1 c] 1 
 1  1  1  1DctCy c c c c 
I 
$ 
 
 
 \
 * *t * * * \* c D T    2d38n 2 2 2 2) ) ) )VNG NGt NG NG NG NG`*@ *@ *@ *@X9)I 9)$ 9) 9) 9) 9)~ %).21 11 1 SM	1
 'sm1 
#1 1 1 1f55 5 
#	5 5 5 5$

 
 	

 

 
 
 
.8j3 8jC 8jTW 8j\` 8j 8j 8j 8jtR R R RhRt R R R RhB B B BB$Xc] $ $QU $ $ $ $0 #'-1HU HUHU HU 3-	HU
 4S>*HU 
HU HU HU HU` i; i; i;i; i; 	i; i; 
i; i; i; i;VK
K
 K
 	K
 K
 
K
 K
 K
 K
ZC S T    6 $(-1> >C=> 4S>*> 
	> > > >0 .22; 2;2; 2; 	2;
 4S>*2; 
2; 2; 2; 2;h  B 9;-1(; (;(;$'(;25(;(; 4S>*(; 
	(; (; (; (;X /-1E; E;E;%(E;7:E;E; 4S>*E; 
	E; E; E; E;R ?C/; /;/;#&/;14/;CF/;/;#+DcN#;/; 
/; /; /; /;p .2H; H;H; H; $	H;
 H; H; 4S>*H; 
H; H; H; H;d .2Q; Q;Q; Q; 	Q;
 Q; Q; 4S>*Q; 
Q; Q; Q; Q;f '5D '5 '5 '5 '5 '5 '5Rw!w!),w!	w! w! w! w!rCSCS)DCS	CS CS CS CSJ
s # #    & "&"&-1Wg WgWg Wg #	Wg
 3-Wg 4S>*Wg 
Wg Wg Wg Wg Wg Wgz .2 D DD UD 4S>*	D
 D 
D D D D D DT "&"&-1Zp ZpZp Zp #	Zp
 3-Zp 4S>*Zp 
Zp Zp Zp Zp Zp Zp@ "&#'"&-10t 0t0t 0t #	0t
 C=0t 3-0t 4S>*0t 
0t 0t 0t 0t 0t 0tl "&"&-1,g ,g,g ,g #	,g
 3-,g 4S>*,g 
,g ,g ,g ,g ,g ,gd "&"&-1Zj ZjZj Zj #	Zj
 3-Zj 4S>*Zj 
Zj Zj Zj Zj Zj Zj@ "&"&-1,g ,g,g ,g #	,g
 3-,g 4S>*,g 
,g ,g ,g ,g\  c3h8P \`    2 I3  I4S>  I  I  I  IDjc jc j j j j\d4 d d d d_d _ _ _ _Ns3x N N N NNS N N N N3s8    *$4
+; $ $ $ $L4g 4$ 4 4 4 4f fD f f f f4W 4 4 4 4 4l	 	T 	 	 	 	S ST S S S SHSM hsm     OT :? :? :?w :?t :?X\ :? :? :? :?x( (,B[ (`d ( ( ( ( )F )\=V )[_ ) ) ) )#)V #)lF_ #)dh #) #) #) #)R
\ 
c 
 
 
 

 
$ 
 
 
 
@*>3 *>4 *> *> *> *>`,l , ,S , , , ,E# E$ E E E Emc m, m4 m m m m"k)& k)<C\ k)ae k) k) k) k)Z
S 
 
Z^ 
 
 
 
2	>S 	>T 	> 	> 	> 	>? ? ?D ? ? ? ?B+Z +Z +Z +ZZ%# %(3- %HUYZ]_bZbUcLd % % % %NC C UX ]a     $(	@
 @
@
 @
 C=	@

 
@
 @
 @
 @
H\D \ \ \ \3 C  PT    c s t    *H| H H H H H, IZ _c        r!   r   )Yr=  r  rX  loggingr   r  htmlr  rh   r  r   r   r   r   	getLoggerr   r  rH   r   r   r	   r
   r   r   rI   rJ   r   r   r   r   rM   r   r   rK   r   r   rL   r   rE   r   syspathlibr#   _PathrE  insertr  __file__r[  parentsgateway.configr%   r&   r   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   "gateway.platforms.telegram_networkr4   r5   r6   utilsr7   r  r  r  ro   r^   r[  ra   rd   rj   r   rr   r_  r   r   r   r   r    r!   r"   <module>rP     s       				      				 , , , , , , , , , , , ,		8	$	$&%YYYYYYYYYYYYYY"/////// " " "!"                76666666------ % % %F
CGKN LGIH       $LLL+%. 


 ! ! ! ! ! ! 33uuX..008;<< = = = 3 3 3 3 3 3 3 3                                      
 !          GGG      4T 4 4 4 4r "*<==.s .s . . . .
c c    @ !bj7  
. . . . . .:C :DI : : : :%&$s) %& %& %& %& %&P2 2 2 2 2 2jiF iF iF iF iF) iF iF iF iF iFs5   B  A B  AB  A#B   2B54B5