
    .cjt             
       L   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	 ddl
mZ ddlmZ  ej        e          Z ej        d          Z ej        d          Z ej        d          Z ej        d	          Z ej        d
          Z ej        d          Z ej        d          Z ej        d          Z ej        d          ZeZ eh d          Z ej        d          Z ej        dej                  Z  ej        d          Z!ddiZ"h dZ#h dZ$h dZ%ddhZ&ddhZ'e#e$z  h dz  Z(dZ)dZ*d Z+ ej        d ej                  Z, ej        d!ej                  Z-d"e.fd#Z/d$e.d"e0fd%Z1d&e.d'e.d"e.fd(Z2d)e3d*e4d"e5dz  fd+Z6d,d-d.e4fd/Z7d0d1d2d3g d4d5d6d3d7d8d3d9d8d3d:d8d3d;d8d<g d=d>Z8d? Z9d@ Z:d[dBZ;dC Z<d&e.dDe.fdEZ=dF Z>dG Z?d&e.d'e.dHe.dz  fdIZ@dddAdJdKZAd\dLZBdMe3d"eCfdNZDd]dOZEd^dPZFdQ ZGd^dRZHd_dSZIdT ZJd^dUZKdV ZLdW ZMdX ZNd^dYZOddZlPmQZQ dS )`a$  Send Message Tool -- cross-channel messaging via platform APIs.

Sends a message to a user or channel on any connected messaging platform
(Telegram, Discord, Slack). Supports listing available targets and resolving
human-friendly channel names to IDs. Works in both CLI and gateway contexts.
    N)
formatdate)redact_sensitive_text)
get_secretz^\s*(-?\d+)(?::(\d+))?\s*$zD^\s*((?:oc|ou|on|chat|open)_[-A-Za-z0-9]+)(?::([-A-Za-z0-9_]+))?\s*$z^\s*([CGD][A-Z0-9]{8,})\s*$z^\s*(U[A-Z0-9]{8,})\s*$z^\s*@([A-Za-z0-9._-]{1,80})\s*$z&^\s*<@(U[A-Z0-9]{8,})(?:\|[^>]+)?>\s*$z%^\s*([CGD][A-Z0-9]{8,}):([^\s:]+)\s*$zS^\s*((?:wxid|gh|v\d+|wm|wb)_[A-Za-z0-9_-]+|[A-Za-z0-9._-]+@chatroom|filehelper)\s*$z ^\s*((?:group|direct):[^:]+)\s*$>   smsphotonsignalwhatsappz^\s*\+(\d{7,15})\s*$zB^\s*[\w-]+@(?:g\.us|s\.whatsapp\.net|lid|broadcast|newsletter)\s*$z6^\s*[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\s*$emailEMAIL_HOME_ADDRESS>   .gif.jpg.png.jpeg.webp>   .3gp.avi.mkv.mov.mp4>   .wav.flac.m4a.mp3.ogg.opusr   r   r   r   >   .md.csv.doc.pdf.txt.zip.docx.xlsxi      c                H   | pd                                 }|pg }|rt          |          dk    rd| fS |d         \  }}|rd| fS t          j                            |          d                                         }|t          vrd| fS t          |          |k    rd| fS |dfS )ux  Decide whether the accompanying text should ride on the media bubble.

    Single enforced chokepoint for the ``MEDIA:<path> caption`` behavior
    across every standalone sender. ``hermes send`` (and the send_message
    tool / cron) strips the ``MEDIA:`` tag and leaves the remaining prose as
    ``text``; historically each platform sent that ``text`` as a *separate*
    message before an uncaptioned media bubble, splitting the reported case
    ``hermes send --to whatsapp "MEDIA:/x.png This Caption"`` into two parts.

    Returns ``(caption, body_text)``:

    * ``(caption, "")`` — attach ``text`` to the media as its native caption
      and send *no* separate body message. Only when there is exactly one
      media file, it is a captionable kind (image/video/document, not a
      voice/audio note), and ``text`` fits ``max_caption_len``.
    * ``(None, text)`` — keep the historical behavior: ``text`` is a separate
      body message and the media carries no caption. Applies to multi-file
      sends (caption→file association is ambiguous), voice/audio notes, empty
      text, or text longer than the caption limit.
        Nr   )striplenospathsplitextlower_CAPTIONABLE_EXTS)textmedia_filesmax_caption_lenstrippedmedia
media_pathis_voiceexts           9/home/ice/.hermes/hermes-agent/tools/send_message_tool.py_media_caption_splitr8   Y   s    * 
!!##H2E s5zzQTz 8J Tz
'

:
&
&q
)
/
/
1
1C
###Tz 8}}&&TzR<    zP([?&](?:access_token|api[_-]?key|auth[_-]?token|token|signature|sig)=)([^&#\s]+)zJ\b(access_token|api[_-]?key|auth[_-]?token|signature|sig)\s*=\s*([^\s,;]+)returnc                     t          |           }t                              d |          }t                              d |          }|S )zCRedact secrets from error text before surfacing it to users/models.c                 2    |                      d           dS )Nr'   z***groupms    r7   <lambda>z&_sanitize_error_text.<locals>.<lambda>   s    QWWQZZ2D2D2D r9   c                 2    |                      d           dS )Nr'   z=***r=   r?   s    r7   rA   z&_sanitize_error_text.<locals>.<lambda>   s    !''!**7J7J7J r9   )r   _URL_SECRET_QUERY_REsub_GENERIC_SECRET_ASSIGN_RE)r/   redacteds     r7   _sanitize_error_textrG      sF    $T**H#''(D(DhOOH(,,-J-JHUUHOr9   messagec                 $    dt          |           iS )z9Build a standardized error payload with redacted content.error)rG   )rH   s    r7   _errorrK      s    )'2233r9   platform_namechat_idc                 Z    | dk    r$t          |                              d          rdS |S )zHReturn a result-safe chat identifier for tool transcripts/log consumers.r   group:z	group:***)str
startswith)rL   rM   s     r7   _display_chat_idrR      s0      S\\%<%<X%F%F {Nr9   excattemptc                 R   t          | dd           }|6	 t          t          |          d          S # t          t          f$ r Y dS w xY wt          |                                           }d|v sd|v rd S d|v sd|v sd|v sd	|v sd
|v sd|v sd|v sd|v rt          d|z            S d S )Nretry_afterg        g      ?z	timed outtimeoutzbad gateway502ztoo many requests429zservice unavailable503zgateway timeout504   )getattrmaxfloat	TypeError
ValueErrorrP   r-   )rS   rT   rV   r/   s       r7   _telegram_retry_delayrb      s    #}d33K	u[))3///:& 	 	 	33	 s88>>Ddi4//tD==$&&D== D((D==$$D==Q'\"""4s   2 AA   )attemptsrd   c                N  K   t          |          D ]}	  | j        di | d {V c S # t          $ rn}t          ||          }|	||dz
  k    r t                              d|dz   ||t          |                     t          j        |           d {V  Y d }~d }~ww xY wd S )Nr'   zFTransient Telegram send failure (attempt %d/%d), retrying in %.1fs: %s )	rangesend_message	Exceptionrb   loggerwarningrG   asynciosleep)botrd   kwargsrT   rS   delays         r7   !_send_telegram_message_with_retryrq      s     ?? ' '	'))33F33333333333 	' 	' 	')#w77E}8a< 7 7NNX!$S))   -&&&&&&&&&&&&&&	'' 's   *
B"A$BB"rh   a  Send a message to a connected messaging platform, or list available targets.

IMPORTANT: When the user asks to send to a specific channel or person (not just a bare platform name), call send_message(action='list') FIRST to see available targets, then send to the correct one.
If the user just says a platform name like 'send to telegram', send directly to the home channel without listing first.objectstring)sendlistreactunreacta  Action to perform. 'send' (default) sends a message. 'list' returns all available channels/contacts across connected platforms. 'react' attaches an emoji reaction to a message (platforms that support it, e.g. photon/iMessage tapbacks). 'unreact' retracts a previously-added reaction.)typeenumdescriptiona  Delivery target. Format: 'platform' (uses home channel), 'platform:#channel-name', 'platform:chat_id', or 'platform:chat_id:thread_id' for Telegram topics and Discord threads. Examples: 'telegram', 'telegram:-1001234567890:17585', 'discord:999888777:555444333', 'discord:#bot-home', 'slack:#engineering', 'signal:+155****4567', 'matrix:!roomid:server.org', 'matrix:@user:server.org', 'ntfy:alerts-channel' (explicit ntfy topic), 'yuanbao:direct:<account_id>' (DM), 'yuanbao:group:<group_code>' (group chat))rx   rz   u   The message text to send. To send an image or file, include MEDIA:<local_path> (e.g. 'MEDIA:/tmp/report.pdf') in the message — the platform will deliver it as a native media attachment.u   For action='react': the emoji to react with (e.g. '❤️'). On iMessage, ❤️👍👎😂‼️❓ render as native tapbacks; other emoji use custom-emoji reactions.zFor action='react'/'unreact': id of the message to react to. Omit to target the most recent message received in that chat (usually the one being replied to).)actiontargetrH   emoji
message_id)rx   
propertiesrequired)namerz   
parametersc                     |                      dd          }|dk    rt                      S |dk    rt          |           S |dk    rt          | d          S t          |           S )z-Handle cross-channel send_message tool calls.r{   rt   ru   rv   rw   T)remove)get_handle_list_handle_react_handle_send)argskwr{   s      r7   send_message_toolr      so    XXh''F~~T"""T$////r9   c                      	 ddl m}  t          j        d |             i          S # t          $ r.}t          j        t          d|                     cY d}~S d}~ww xY w)z5Return formatted list of available messaging targets.r   )format_directory_for_displaytargetsz"Failed to load channel directory: N)gateway.channel_directoryr   jsondumpsri   rK   )r   es     r7   r   r     s    LJJJJJJz9&B&B&D&DEFFF L L Lz&!Ia!I!IJJKKKKKKKKLs   #& 
A#AAAFc                 8   |                      dd          }|                      d          pd                                }|                      d          pd                                pd}|r|s|st          |sdnd          S |                    dd	          }|d
                                                                         }t          |          d	k    r|d	                                         nd}d}|r@t          ||          \  }}	}
|s*	 d
dlm}  |||          }n# t          $ r d}Y nw xY w|p|}	 d
dl
m}m}  ||          }n)# t          t          f$ r t          d|           cY S w xY w|sR	  |            }|                    |          }n# t          $ r d}Y nw xY w|st          d| d| d          S |j        }d}	 d
dlm}  |            }n# t          $ r d}Y nw xY w||j                             |          nd}|t          d| d          S |rdnd}t)          ||d          }t+          |          st          d| d          S 	 d
dlm} |r | |||                    }n | ||||                    }n;# t          $ r.}t1          j        t5          d|                     cY d}~S d}~ww xY wt7          |t8                    rt1          j        |          S t1          j        dt;          |          i          S )u  Attach (or with ``remove=True`` retract) an emoji reaction on a message
    via a live gateway adapter.

    Only adapters that expose ``add_reaction(chat_id, emoji, message_id)`` /
    ``remove_reaction(chat_id, message_id)`` coroutines support this (e.g.
    photon/iMessage tapbacks). Requires the gateway to be running in this
    process — there is no standalone fallback, since reacting needs the
    adapter's live message-id state.
    r|   r&   r}   r~   Nz:Both 'target' and 'emoji' are required when action='react'z*'target' is required when action='unreact':r'   r   resolve_channel_name)Platformload_gateway_configUnknown platform: z.No chat specified and no home channel set for z. Use 'z
:chat_id'._gateway_runner_refzReactions require a live zN adapter in the running gateway (not available from cron/standalone contexts).remove_reactionadd_reaction
Platform 'z%' does not support message reactions.
_run_async)rM   r~   )rM   r}   r~   zReaction failed: success)r   r(   
tool_errorsplitr-   r)   _parse_target_refr   r   ri   gateway.configr   r   ra   KeyErrorget_home_channelrM   gateway.runr   adaptersr]   callablemodel_toolsr   r   r   rK   
isinstancedictbool)r   r   r|   r}   r~   partsrL   
target_refrM   
_thread_id_r   resolvedr   r   platformconfighomerunnerr   adapterfn_namereact_fnr   resultr   s                             r7   r   r   
  sr    XXh##FXXg$"++--E((<((.B5577?4J 
& 
 
>HH=
 
 	
 LLa  E!HNN$$**,,M%(ZZ!^^q!!!JG -!2=*!M!MQ 		- JJJJJJ//zJJ       
 ,*G@@@@@@@@@8M**! @ @ @>}>>?????@  	((**F**844DD 	 	 	DDD	 	2 2 2%2 2 2   ,F333333$$&&   /5/Afo!!(+++tGE E E E
 
 	
 $*=~Gw..HH 
MMMM
 
 	
;****** 	ZZ@@@ FF  Z*MMM F  ; ; ;z&!8Q!8!899::::::::;&$ "z&!!!:y$v,,/000sf   D# #D21D2:E #E43E4:F F)(F)G G.-G.6J 
K#KKKc                     |                      dd          }|                      dd          }|r|st          d          S |                    dd          }|d                                                                         }t          |          dk    r|d                                         nd}d}d}|rt          ||          \  }}}nd	}|rw|su	 dd
lm}	  |	||          }
|
rt          ||
          \  }}}nt          j
        dd| d| di          S n-# t          $ r  t          j
        dd| d| di          cY S w xY wddlm}  |            rt          d          S 	 ddlm}m}  |            }n;# t          $ r.}t          j
        t#          d|                     cY d}~S d}~ww xY w	  ||          }n)# t$          t&          f$ r t          d|           cY S w xY w|j                             |          }|r|j        s|dk    rt-          dd                                          }t-          dd                                          }|r[|rYddlm}  |d||t-          dd                                          t-          dd                                          d          }n&t          d| d          S t          d| d          S dd lm} d!|v }|                    |          \  }}|                    |          }|                                pt9          |          }d	}|s|                    |          }|sC|dk    r=t=          j        d"d                                          }|rdd#lm }  |||d$%          }|r
|j!        }d}nPtD                               ||#                                 d&          }t          j
        dd'| d(| d)| d*i          S tI          |||          } | rt          j
        |           S |d+k    r|r|}!|!%                    d,          rtL          '                    |!          rd-|! }!|!%                    d.          r?dd/l(m)}"  |"tU          |j+        |!                    \  }#}$|$rt          j
        |$          S |#}	 dd/l(m)}"  |"tY          |||||||0                    }%|r6t[          |%t\                    r!|%                     d1          rd2| d3| d4|%d5<   t[          |%t\                    re|%                     d1          rP|rN	 dd6l/m0}& dd7l1m2}'  |'d8d9          }( |'d:d          pd}) |&||||(||);          rd|%d<<   n# t          $ r Y nw xY wt[          |%t\                    rd|%v rtg          |%d                   |%d<   t          j
        |%          S # t          $ r.}t          j
        t#          d=|                     cY d}~S d}~ww xY w)>z$Send a message to a platform target.r|   r&   rH   z;Both 'target' and 'message' are required when action='send'r   r'   r   NFr   rJ   zCould not resolve 'z' on z;. Use send_message(action='list') to see available targets.z). Try using a numeric channel ID instead.)is_interruptedInterrupted)r   r   zFailed to load gateway config: r   weixinWEIXIN_TOKENWEIXIN_ACCOUNT_IDPlatformConfigTWEIXIN_BASE_URLWEIXIN_CDN_BASE_URL)
account_idbase_urlcdn_base_url)enabledtokenextrar   zZ' is not configured. Set up credentials in ~/.hermes/config.yaml or environment variables.)BasePlatformAdapterz[[as_document]]WEIXIN_HOME_CHANNEL)HomeChannelzWeixin Home)r   rM   r   _HOME_CHANNELzNo home channel set for zQ to determine where to send the message. Either specify a channel directly with 'z=:CHANNEL_NAME', or set a home channel via: hermes config set z <channel_id>slackUuser:)r   
user_name:r   	thread_idr0   force_documentr   zSent to z home channel (chat_id: )note)mirror_to_sessionget_session_envHERMES_SESSION_PLATFORMcliHERMES_SESSION_USER_ID)source_labelr   user_idmirroredzSend failed: )4r   r   r   r(   r-   r)   r   r   r   r   r   ri   tools.interruptr   r   r   r   rK   ra   r   	platformsr   r   r   gateway.platforms.baser   extract_mediafilter_media_delivery_paths_describe_media_for_mirrorr   r*   getenvr   rM   _HOME_CHANNEL_ENV_OVERRIDESupper_maybe_skip_cron_duplicate_sendrQ   _SLACK_USER_ID_RE	fullmatchr   r   _resolve_slack_user_targetr   _send_to_platformr   r   gateway.mirrorr   gateway.session_contextr   rG   )*r   r|   rH   r   rL   r   rM   r   is_explicitr   r   r   r   r   r   r   r   r   pconfigwx_token
wx_accountr   r   force_document_attachmentsr0   cleaned_messagemirror_textused_home_channelr   wx_homer   home_envduplicate_skip_slack_dm_targetr   	_resolved_resolve_errr   r   r   r   r   s*                                             r7   r   r   f  s   XXh##Fhhy"%%G Y YWXXXLLa  E!HNN$$**,,M%(ZZ!^^q!!!JGI *;M:*V*V'KK  + 	FFFFFF++M:FFH (9-(R(R%AAz Q: Q QM Q Q Q#    %&  	 	 	: ;z ; ; ; ; ;     	 /.....~ )-(((I@@@@@@@@$$&& I I Iz&!F1!F!FGGHHHHHHHHI
@8M**! @ @ @>}>>?????@ ""8,,G V'/ V H$$!."55;;==H#$7<<BBDDJ ZJ Z999999(. "&0$./@"$E$E$K$K$M$M(23H"(M(M(S(S(U(U    "  #Y}  #Y  #Y  #Y  Z  Z  Z  U=  U  U  U  V  V  V:::::: "3g!=#6#D#DW#M#M K%AA+NNK!''))T-G-T-TK &&x00 	[11i 5r::@@BBG [666666"{HgMZZZ 	lG $266-"5"5"7"7FFF H : XM X X;HX X@HX X X    5]GYWWN *z.))) G"&&s++ 	:0A0K0KL\0]0] 	:9'799&&'>?? 	 ......&0j*7=:JKK' '#I|  0z,///G'7******#'9  

 

  	ZFD!9!9 	Zfjj>S>S 	ZYYYwYYYF6N fd## 	

9(=(= 	+ 	<<<<<<CCCCCC./H%PP)/*BBGGO4$$!!-'#   . *.F:&    fd## 	D6(9(926'?CCF7Oz&!!! 7 7 7z&!4!4!455666666667s   AD 'EE'E: :
F2#F-'F2-F26G #G('G()B	W 3<U0 /W 0
U=:W <U==AW 
W=#W82W=8W=r   c                 `	   | dk    r_t                               |          }|r+|                    d          |                    d          dfS ddlm}  ||          }|r|ddfS | dk    rGt
                              |          }|r+|                    d          |                    d          dfS | d	k    rGt                              |          }|r+|                    d          |                    d          dfS | d
k    rt                              |          }|r+|                    d          |                    d          dfS t                              |          }|r|                    d          ddfS t                              |          pt                              |          }|rd|                    d           ddfS t                              |          }|rd|                    d           ddfS | dk    rG|                                }|                    d          }|dk    r|d|         ||dz   d         dfS | dk    r4t                              |          }|r|                    d          ddfS | dk    rvt                              |          }|r|                    d          ddfS |                                                                rd|                                 ddfS dS | dk    r|                                }|r|ddfS | dk    r3t"                              |          }|r|                                ddfS | dk    r1t$                              |          r|                                ddfS |                                }| dk    rJ|                    d          r5|t)          d          d                                         }	|	rd|	 ddfS dS | t*          v r3t,                              |          }|r|                                ddfS |                    d                                          r|ddfS | dk    r/|                    d          s|                    d          r|ddfS | dk    r	d|v r|ddfS dS )zFParse a tool target into chat_id/thread_id and whether it is explicit.telegramr'   r\   Tr   )parse_telegram_username_targetNfeishudiscordr   r   r   matrixz:$r   yuanbaorO   NNFntfyr
   r	   r   -!@xmpp)_TELEGRAM_TOPIC_TARGET_REr   r>   'plugins.platforms.telegram.telegram_idsr  _FEISHU_TARGET_RE_NUMERIC_TOPIC_RE_SLACK_THREAD_TARGET_RE_SLACK_TARGET_REr   _SLACK_MENTION_RE_SLACK_USER_NAME_REr(   rfind_WEIXIN_TARGET_RE_YUANBAO_TARGET_REisdigit_EMAIL_TARGET_RE_WHATSAPP_JID_RErQ   r)   _PHONE_PLATFORMS_E164_TARGET_RElstrip)
rL   r   matchr  usernametrimmed	split_idxtopicstripped_targetgroup_ids
             r7   r   r     s.   
"")33J?? 	8;;q>>5;;q>>477	
 	
 	
 	
 	
 	
 21*== 	(T4''  !++J77 	8;;q>>5;;q>>477	!!!++J77 	8;;q>>5;;q>>477'11*== 	8;;q>>5;;q>>477 **:66 	.;;q>>4--!++J77b;L;V;VWa;b;b 	8+5;;q>>++T477#--j99 	=0A00$<<  ""$$MM$''	q==:I:&	A(@$FF  !++J77 	.;;q>>4--	!!",,Z88 	.;;q>>4--%%'' 	=0J,,..00$<<    "" 	%$$$ **:66 	2##%%tT11
"" %%j11 	2##%%tT11 &&((O  _%?%?%I%I "3x==>>288:: 	3&H&&d22  ((())*55 	2 ##%%tT11%%'' &4%%  j&;&;C&@&@ JDYDYZ]D^D^ 4%%3*#4#44%%r9   c                 :   | sdS t          |           dk    rr| d         \  }}t          j                            |          d                                         }|r|t
          v rdS |t          v rdS |t          v rdS |t          v rdS dS d	t          |            d
S )zJReturn a human-readable mirror summary when a message only contains media.r&   r'   r   z[Sent voice message]z[Sent image attachment]z[Sent video attachment]z[Sent audio attachment]z[Sent document attachment]z[Sent z media attachments])	r)   r*   r+   r,   r-   _VOICE_EXTS_IMAGE_EXTS_VIDEO_EXTS_AUDIO_EXTS)r0   r4   r5   r6   s       r7   r   r   i  s     r
;1*1~
Hgz**1-3355 	*{**))+,,+,,+,,++9C$$9999r9   c                     ddl m}   | dd                                                                          } | dd                                          }|r|sdS  | dd                                          pd}|||dS )	zMReturn the cron scheduler's auto-delivery target for the current run, if any.r   r   !HERMES_CRON_AUTO_DELIVER_PLATFORMr&    HERMES_CRON_AUTO_DELIVER_CHAT_IDN"HERMES_CRON_AUTO_DELIVER_THREAD_ID)r   rM   r   )r   r   r(   r-   )r   r   rM   r   s       r7   _get_cron_auto_delivery_targetr.  |  s    777777BBGGMMOOUUWWHo@"EEKKMMG 7 t DbIIOOQQYUYI  r9   r   c                     t                      }|sdS |d         | k    o>t          |d                   t          |          k    o|                    d          |k    }|sdS |  d| }||d| z  }ddd|d| d	d
S )zRSkip redundant cron send_message calls when the scheduler will auto-deliver there.Nr   rM   r   r   T#cron_auto_delivery_duplicate_targetzSkipped send_message to z. This cron job will already auto-deliver its final response to that same target. Put the intended user-facing content in your final response instead, or use a different target if you want an additional message.)r   skippedreasonr|   r   )r.  rP   r   )rL   rM   r   auto_targetsame_targettarget_labels         r7   r   r     s    022K t 	J=0 	6I&''3w<<7	6OOK((I5 
  t#//g//LI' 7h| h h h
 
 
r9   r   c                "  K   t          | d          r| j        nt          |           }d}	 ddlm}	  |	            }n# t
          $ r d}Y nw xY w|	 |j                            |           }
n# t
          $ r d}
Y nw xY w|
	 i }|r||d<   |dk    r|r||d<   |sd}|
                    |||           d{V }n-# t          j
        $ r  t
          $ r}d	d
| icY d}~S d}~ww xY w|j        r
d|j        dS d	d|j         iS d}	 ddlm} |                    |          }n# t
          $ r d}Y nw xY w||j        	 |                    ||||||           d{V }nJ# t          j
        $ r  t
          $ r.}t"                              d|d           d	d| icY d}~S d}~ww xY wt'          |t(                    r,|                    d          s|                    d	          r|S d	d| dt+          |          j         iS d	d| diS )a  Send a message via a live gateway adapter, with a standalone fallback
    for out-of-process callers (e.g. cron running separately from the gateway).

    Order of attempts:
      1. Live in-process adapter via ``_gateway_runner_ref()`` (the path that
         existed before this change).
      2. The plugin's ``standalone_sender_fn`` registered on its
         ``PlatformEntry`` (used when the gateway is not in this process, so
         the runner weakref is ``None``).
      3. A descriptive error explaining both options.
    valueNr   r   r   r  publish_topic)rM   contentmetadatarJ   zPlugin platform send failed: T)r   r~   zAdapter send failed: platform_registryr   z$Plugin standalone send for %s raisedexc_infozPlugin standalone send failed: r   zPlugin standalone send for 'zR' returned an invalid result: expected a dict with 'success' or 'error' keys, got zNo live adapter for platform 'z'. Is the gateway running with this platform connected? For out-of-process delivery (e.g. cron in a separate process), the platform plugin must register a standalone_sender_fn on its PlatformEntry.)hasattrr7  rP   r   r   ri   r   r   rt   rl   CancelledErrorr   r~   rJ   gateway.platform_registryr<  standalone_sender_fnrj   debugr   r   rx   __name__)r   r   rM   chunkr   r0   r   rL   r   r   r   r:  r   r   entryr<  s                   r7   _send_via_adapterrG    s     * '.h&@&@SHNNc(mmMF333333$$&&    	o))(33GG 	 	 	GGG	F 6,5H[) F**w*07H_- $#H&||GUU]|^^^^^^^^)    F F F!D!D!DEEEEEEEF~ J#'v7HIIICV\CCDDE??????!%%m44    U7C	D 55#'- 6        FF % 	 	 	 	D 	D 	DLL?Y]L^^^BqBBCCCCCCC	D fd## 	I)>)> 	&**WBUBU 	M5} 5 5!&\\25 5
 	
 	E] E E E so   = AAA- -A<;A<8B; ;C%C C% C%D$ $D32D3 !E" "F);#F$F)$F)c           
        K   ddl m} |pg }| |j        k    rt          ||||           d{V S ddlm}m}	 	 ddlm}
 d}n# t          $ r d}Y nw xY w|pg }|j
        |r|
j        nd	i}| |vrI	 dd
lm} |                    | j                  }|r|j        dk    r
|j        || <   n# t"          $ r Y nw xY w|                    |           }|r(| |j
        k    r|	nd}|                    |||          }n|g}| |j
        k    rYt'          t)          |di           o|j                            d                    }t-          |j        ||||||           d{V S | |j        k    rdd
lm} |                    d          }||j        ddiS t5          |||pt6                    \  }}|O|                    ||d|||           d{V }t9          |t:                    r|                    d          r|S |S d}t=          |          D ]o\  }}|t?          |          dz
  k    }|                    |||||r|ng            d{V }t9          |t:                    r|                    d          r|c S |}p|S | |j         k    r}d}t=          |          D ]i\  }}|t?          |          dz
  k    }tC          ||||r|ng |           d{V }t9          |t:                    r|                    d          r|c S |}j|S | |j"        k    r|rd}t=          |          D ]m\  }}|t?          |          dz
  k    }tG          |j        |||r|ng            d{V }t9          |t:                    r|                    d          r|c S |}n|S | |j$        k    r}|r{d}t=          |          D ]g\  }}|t?          |          dz
  k    }tK          |||r|nd           d{V }t9          |t:                    r|                    d          r|c S |}h|S | |j&        k    r|rdd
lm} ddl'm(}  |             |                    d          }||j        ddiS d}t=          |          D ]o\  }}|t?          |          dz
  k    }|                    ||||r|nd|           d{V }t9          |t:                    r|                    d          r|c S |}p|S | |j)        k    r+|r(dd
lm} ddl'm(}  |             |                    d          }||j        ddiS t5          |||pt6                    \  } }| O|                    ||d|||            d{V }t9          |t:                    r|                    d          r|S |S d}t=          |          D ]o\  }}|t?          |          dz
  k    }|                    |||||r|ng            d{V }t9          |t:                    r|                    d          r|c S |}p|S | |j*        k    r-|r*dd
lm}! ddl'm(}"  |"             |!                    d          }#|#|#j        ddiS t5          |||pt6                    \  }$}d}|$P|#                    ||d||||$           d{V }t9          |t:                    r|                    d          r|S |S t=          |          D ]p\  }}|t?          |          dz
  k    }|#                    ||||r|nd||            d{V }t9          |t:                    r|                    d          r|c S |}q|S | |j)        k    rd}t=          |          D ]k\  }}|t?          |          dz
  k    }tW          | |||||r|ng |!           d{V }t9          |t:                    r|                    d          r|c S |}l|S |r!|,                                sdd"| j         d#iS d}%|rd$| j         d%}%d}|D ]}| |j*        k    rt[          d||||           d{V }nl| |j"        k    rtG          |j        ||           d{V }nC| |j.        k    rt[          d&||||           d{V }n| |j/        k    rt[          d'||||           d{V }n| |j0        k    rt[          d(||||           d{V }n| |j&        k    rt[          d||||           d{V }n| |j1        k    rt[          d)||||           d{V }n| |j2        k    rtg          |j        ||           d{V }na| |j4        k    rtk          |||           d{V }n>| |j$        k    rtK          ||           d{V }ntW          | ||||||!           d{V }t9          |t:                    r|                    d          r|c S |}|%rgt9          |t:                    rR|                    d*          r=tm          |                    d+g                     }&|&7                    |%           |&|d+<   |S ),a  Route a message to the appropriate platform sender.

    Long messages are automatically chunked to fit within platform limits
    using the same smart-splitting algorithm as the gateway adapters
    (preserves code-block boundaries, adds part indicators).
    r   r   r0   Nr   	utf16_lenTelegramAdapterTFr$   r;  len_fnr   disable_link_previews)r0   r   rQ  r   r  rJ   z=Discord plugin not registered or missing standalone_sender_fnr1   r&   )r   r0   captionr'   )r   r0   )r0   r   discover_pluginsr  z<Feishu plugin not registered or missing standalone_sender_fnr   z;Slack plugin not registered or missing standalone_sender_fnr	   z>WhatsApp plugin not registered or missing standalone_sender_fn)r0   r   r   rS  )r0   r   r   r   zsend_message MEDIA delivery is currently only supported for telegram, discord, matrix, weixin, signal, yuanbao, feishu, whatsapp and slack; target z had only media attachmentsz#MEDIA attachments were omitted for z; native send_message media delivery is currently only supported for telegram, discord, matrix, weixin, signal, yuanbao, feishu, whatsapp and slackr
   r   dingtalkwecomr   warnings)8r   r   WEIXIN_send_weixinr   r   rL  "plugins.platforms.telegram.adapterrN  ImportErrorTELEGRAMMAX_MESSAGE_LENGTHrA  r<  r   r7  max_message_lengthri   truncate_messager   r]   r   _send_telegramr   DISCORDrB  r8   _DEFAULT_CAPTION_LIMITr   r   	enumerater)   MATRIX_send_matrix_via_adapterSIGNAL_send_signalYUANBAO_send_yuanbaoFEISHUhermes_cli.pluginsrU  SLACKWHATSAPPrG  r(   _registry_standalone_sendEMAILSMSDINGTALKWECOMBLUEBUBBLES_send_bluebubblesQQBOT_send_qqbotru   append)'r   r   rM   rH   r   r0   r   r   r   rL  rN  _telegram_available_MAX_LENGTHSr<  rF  max_len_len_fnchunksrQ  _dc_captionr   r   last_resultirE  is_last
_pr_feishu
_dp_feishu_feishu_entry	_pr_slack	_dp_slack_slack_entry_sl_caption_pr_wa_dp_wa	_wa_entry_wa_captionrk   rX  s'                                          r7   r   r   	  s      ('''''#K 8?""!'7GUUUUUUUUUUEEEEEEEE$FFFFFF" $ $ $#$ #K 	AT^?==Z^L
 |##	CCCCCC%))(.99E B1A55).)AX& 	 	 	D	 x((G '8+<<<))$$55gww5WW 8$$$ $WWgr%B%B%qw}GXGXYpGqGq r r#M#"7)
 
 
 
 
 
 
 
 
 	
" 8###??????!%%i00=E6>\]]
 .[$>(>
 
 
Q " 55#'# 6        F &$'' FJJw,?,? M!&)) 	! 	!HAuCKK!O+G 55#+2:KK 6        F &$'' FJJw,?,?  KK 8?""!&)) 	! 	!HAuCKK!O+G3+2:KK#        F &$'' FJJw,?,?  KK 8?""{"!&)) 
	! 
	!HAuCKK!O+G'+2:KK	        F &$'' FJJw,?,?  KK 8####!&)) 		! 		!HAuCKK!O+G(+2<KK        F
 &$'' FJJw,?,?  KK 8?""{"MMMMMMEEEEEE
"x00 M$F$N[\\!&)) 	! 	!HAuCKK!O+G(==+2<KK# >        F &$'' FJJw,?,?  KK 8>!!k!LLLLLLDDDDDD	 }}W--<#D#LZ[[-[$>(>
 
 
Q "'<<#'# =        F &$'' FJJw,?,? M!&)) 	! 	!HAuCKK!O+G'<<#+2:KK =        F &$'' FJJw,?,?  KK 8$$$$IIIIIIAAAAAAJJz**		 > F]^^
 .[$>(>
 
 
Q " %99'#-# :        F &$'' FJJw,?,? M!&)) 	! 	!HAuCKK!O+G$99+2<KK#- :        F &$'' FJJw,?,?  KK 8>!!!&)) 	! 	!HAu3v;;?*G,#+2:KK-        F &$'' FJJw,?,?  KK  
7==?? 
F".F F F
 	
 G 
`(. ` ` ` 	
 K $ $x(((4Z'SXZcddddddddFF(('wFFFFFFFFFF''4WgwPUW`aaaaaaaaFF%%4UGWeU^________FF***4Z'SXZcddddddddFF((4XwQVXabbbbbbbbFF''4WgwPUW`aaaaaaaaFF---,W]GUKKKKKKKKFF''&w????????FF)))(%88888888FF -#'-        F fd## 	

7(;(; 	MMM +:k400 +[__Y5O5O +
B7788   "*Js#   A AA07B( (
B54B5rJ   c                 H    dt          |                                           v S )zCheck if a Telegram error is a thread-not-found failure.

    Matches the gateway adapter's ``_is_thread_not_found_error`` for
    the standalone ``_send_telegram`` path (issue #27012).
    zthread not found)rP   r-   )rJ   s    r7   _is_telegram_thread_not_foundr    s!     U!1!1!3!333r9   c           	      z  K   	 ddl m} ddlm} t	          t          j        d|                    }	|	r
|}
|j        }nK	 ddlm	} |
                    |          }|                    |          }
n# t          $ r |}
Y nw xY w|j        }	 ddlm}  |ddg	          }n# t          $ r d
}Y nw xY w|r	 ddlm} t$                              d|            ||  ||           ||                    }nJ# t          $ r1}t$                              d|            ||           }Y d
}~nd
}~ww xY w ||           }ddlm}  ||          }|pg }i }|e	 ddlm	} |                    t1          |                    }n4# t          $ r' t1          |          dk    rd
nt3          |          }Y nw xY w|||d<   t5          |          }|rd|d<   d
}g }d
}ddlm} t9          ||t:                    \  }}| ||
          t:          k    r|
}d}
|
                                rddlm}m} |                     |
d|          } | D ]}!	 tC          |f||!|d| d
{V }# t          $ r_}"tE          |"          rq|#                    d          \t$                              d|#                    d                     |$                    dd
           tC          |f||!|d| d
{V }ndt1          |"          %                                v sFdt1          |"          %                                v s#d t1          |"          %                                v rjt$                              d!|tM          |"                     |	s%	 dd"lm'}#  |#|!          }$n# t          $ r |!}$Y nw xY w|!}$tC          |f||$d
d| d
{V }n Y d
}"~"d
}"~"ww xY w|D ]\  }%}&tP          j)        *                    |%          sd#|% }'t$                              |'           |+                    |'           |\|Z	 tC          |f|||d| d
{V }d
}n?# t          $ r2}(t$                              d$tM          |(                     Y d
}(~(nd
}(~(ww xY wtP          j)        ,                    |%          d%         %                                })	 t[          |%d&          5 }*t5          |          }+||)t\          v r|&s
||+d'<   ||+d(<   |)t\          v r|&s	|)t^          v r:	 dd)lm0}, tc          j2        |,|%           d
{V }-|-|-|+d*<   n# t          $ r Y nw xY w	 |)tf          v r|s |j4        d<||*d+|+ d
{V }nx|)tj          v r |j6        d<||*d,|+ d
{V }nX|)t\          v r|&r |j7        d<||*d-|+ d
{V }n6|)t^          v r |j8        d<||*d.|+ d
{V }n |j9        d<||*d/|+ d
{V }nu# t          $ rg}.tE          |.          r|+#                    d          rt$                              d0|+d                    |*:                    d           |+$                    dd
           |)tf          v r|s |j4        d<||*d+|+ d
{V }n|)tj          v r |j6        d<||*d,|+ d
{V }n|)t\          v r|&r |j7        d<||*d-|+ d
{V }n|)t^          v r |j8        d<||*d.|+ d
{V }nd |j9        d<||*d/|+ d
{V }nL|+#                    d(          r5dt1          |.          %                                v s#d't1          |.          %                                v rt$                              d1tM          |.                     |*:                    d           |+$                    d(d
           |	sA|+#                    d'          r,	 dd"lm'}#  |#|+d'                   |+d'<   n# t          $ r Y nw xY w|)tf          v r|s |j4        d<||*d+|+ d
{V }n8|)tj          v r |j6        d<||*d,|+ d
{V }n |j9        d<||*d/|+ d
{V }n Y d
}.~.nd
}.~.ww xY wd
d
d
           n# 1 swxY w Y   # t          $ rO}/tM          d2|% d3|/           }'t$          ;                    |'           |+                    |'           Y d
}/~/d
}/~/ww xY w|d4}0|r|0|d5S d6|0iS dd7|t1          |j<                  d8}1|r||1d9<   |1S # tz          $ r d6d:icY S t          $ r}/t}          d;|/           cY d
}/~/S d
}/~/ww xY w)=uD  Send via Telegram Bot API (one-shot, no polling needed).

    Applies markdown→MarkdownV2 formatting (same as the gateway adapter)
    so that bold, links, and headers render correctly.  If the message
    already contains HTML tags, it is sent with ``parse_mode='HTML'``
    instead, bypassing MarkdownV2 conversion.
    r   )Bot)	ParseModez<[a-zA-Z/][^>]*>rM  )resolve_proxy_urlTELEGRAM_PROXYzapi.telegram.org)target_hostsN)HTTPXRequestz>send_message: standalone Telegram send routed through proxy %s)proxy)r   requestget_updates_requestzUsend_message: failed to attach Telegram proxy (%s), falling back to direct connection)r   )normalize_telegram_chat_id1message_thread_idTdisable_web_page_preview)rL  rR  r&   rK  r$   rO  )rM   r/   
parse_modezIThread %s not found in _send_telegram, retrying without message_thread_idparsemarkdownhtmlzFParse mode %s failed in _send_telegram, falling back to plain text: %s)_strip_mdv2z Media file not found, skipping: z;Telegram caption-fallback send failed for missing media: %sr'   rbrS  r  )_probe_voice_duration_secondsduration)rM   photo)rM   video)rM   voice)rM   audio)rM   documentzFThread %s not found for media send, retrying without message_thread_idz7Caption parse failed for media send, retrying plain: %szFailed to send media : ANo deliverable text or media remained after processing MEDIA tags)rJ   rX  rJ   r  r   r   rM   r~   rX  zGpython-telegram-bot not installed. Run: pip install python-telegram-botzTelegram send failed: rf   )?r  r  telegram.constantsr  r   researchHTMLr[  rN  __new__format_messageri   MARKDOWN_V2r   r  telegram.requestr  rj   infork   r  r  _message_thread_id_for_sendrP   intr   rL  r8   _TELEGRAM_CAPTION_LIMITr(   r   r`  rq   r  r   popr-   rG   r  r*   r+   existsrx  r,   openr&  _TELEGRAM_SEND_AUDIO_EXTSr  rl   	to_threadr'  
send_photor(  
send_video
send_voice
send_audiosend_documentseekrJ   r~   r\  rK   )2r   rM   rH   r0   r   rQ  r   r  r  	_has_html	formattedsend_parse_moderN  _adapterr  	_tg_proxyr  rn   
_proxy_errr  int_chat_idthread_kwargseffective_thread_idtext_kwargslast_msgrX  _tg_caption
_utf16_len_capr   r   rL  text_chunksrE  md_errorr  plainr4   r5   rk   _cap_errr6   fmedia_kwargsr  r  	media_errr   rJ   r   s2                                                     r7   ra  ra    s0     u4      000000 #6@@AA	 	4I'nOO$NNNNNN*22?CC$33G<<		 $ $ $#			$ (3O	@@@@@@))*:J\I]^^^II 	 	 	III	 	#
'999999\^ghhhc(Ly999(49(E(E(E  
  ' ' 'v  yC  D  D  Dc&&&' #E"""C	
 	
 	
 	
 	
 	
 1099!'R 
NNNNNN&5&Q&Q	NN' '##      	NNc11DDs9~~ $## #.5H12 =))  	;6:K23 BBBBBB&[2I
 
 
a 

9 5 59P P P#KI?? 6	 NMMMMMMM->>4	 ?  K % * *)%F& +%#2& & 7B& &            HH
 ! # # # 5X>> ;??SfCgCgCsg'OO,?@@   $(;TBBB)J*$/e'6* * ;F* * $ $ $ $ $ $
 !CMM$7$7$9$999Z3x==K^K^K`K`=`=`djnqrzn{n{  oB  oB  oD  oD  eD  eDd+0::  
  ) *. Z Z Z Z Z Z(3E(:(:#, . . .(-. %*E)J*$/e'+* * 0;* * $ $ $ $ $ $ G#J %0 A	) A	) J7>>*-- IZIIw'''((( *x/?
)J*)4;'6* *:E* * $ $ $ $ $ $ '+$   Y0::       
 '"":..q17799Cj)*d++ e"q#'#6#6L
 #.{8J8Jx8J2=Y/5D\2{**x*CC\<\<\!hhhhhh-4->?\^h-i-i'i'i'i'i'i'iH'3;CZ 8( ! ! ! D!T"+--n--;S^ .(31. .@L. . ( ( ( ( ( (HH !K//-;S^ .(31. .@L. . ( ( ( ( ( (HH !K//H/-;S^ .(31. .@L. . ( ( ( ( ( (HH !$===-;S^ .(31. .@L. . ( ( ( ( ( (HH .?S-> .(3a. .CO. . ( ( ( ( ( (H % ?" ?" ?"8CC >"HXHXYlHmHm >" #NN h ,-@ A  
 FF1III(,,-@$GGG"k11.11? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," "%!3!31? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," "%!3!3!31? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," "%(A!A!A1? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," 2C1B 2",7!2" 2"GS2" 2" ," ," ," ," ," ," *--l;;  "#s9~~';';'='===(C	NN,@,@,B,BBB
 #NN Y 4Y ? ?   FF1III(,,\4@@@#, )1A1A)1L1L )!)$^$^$^$^$^$^>Ik,W`Ja>b>bL$;$;'0 !) !) !)$(D!)"k11.11? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," "%!3!31? 2",7q2" 2"DP2" 2" ," ," ," ," ," ," 2C1B 2",7!2" 2"GS2" 2" ," ," ," ," ," ," "?"Me" e" e" e" e" e" e" e" e" e" e" e" e" e" e"L  ) ) )./Xz/X/XUV/X/XYYW%%%(((((((()
 WE >!&H===U## "h122	
 
  	*!)F: d d dbcccc 4 4 42q22333333334s  :d 0A0 /d 0A?<d >A??
d 
B d B.+d -B..d 4AC8 7d 8
D3'D.)d .D33(d (F d .F63d 5F66Bd I)(d )O4DO	NON*'O)N**Od OA"d 6Qd 
R(Rd R;d a8;a+(T=<a+=
U
a+	U

a+BW*(a+*a5Ga_('a(
_5	2a4_5	5Aaa+aa+a8+a/	/a82a/	3a86d 8
cAcd cd d #!d d:	d:d5/d:5d:c                    K   ddl m} ddlm}  |             |                    |           }||j        d|  diS |                    ||||           d{V S )ak  Dispatch a one-shot send through a migrated platform plugin's
    standalone_sender_fn (registry hook).  Used for platforms whose adapter
    moved out of gateway/platforms/ into plugins/platforms/<name>/ (#41112):
    the legacy inline ``_send_<platform>`` helper now lives in the plugin as
    ``_standalone_send`` and is reached via the platform registry.
    r   r;  rT  NrJ   z6 plugin not registered or missing standalone_sender_fn)r   )rA  r<  rl  rU  r   rB  )rL   r   rM   rH   r   r<  rU  rF  s           r7   ro  ro    s       <;;;;;333333!!-00E}2:Maaabb++GWgQZ+[[[[[[[[[r9   c           	      t  K   |                     d          s|                     d          s|dfS 	 ddl}n# t          $ r	 dddifcY S w xY w	 ddlm}m}  |            } ||          \  }dd	|  d
dfdfd} |j        dd|                    d          i|4 d{V }|                     d          rT |||t          d          d                    d{V \  }	}
|
r#dt          |
          fcddd          d{V  S d|	 }|t          d          d         }	 |dd|	i           d{V }|
                    d          s;dt          d|
                    dd           d          fcddd          d{V  S |
                    d          pi 
                    d          }|s#dt          d          fcddd          d{V  S |dfcddd          d{V  S # 1 d{V swxY w Y   dS # t          $ r}dt          d|           fcY d}~S d}~ww xY w)u  Resolve a Slack user target to a D... DM conversation ID.

    ``chat_id`` may be a Slack conversation ID (C/G/D...) — returned unchanged —
    or an internal user target (``user:U...`` / ``user_name:<handle>``). User
    targets are opened as DMs via conversations.open because Slack
    chat.postMessage requires a conversation ID. ``user_name:`` targets are
    first resolved to a user ID through users.list (stable handle match only).

    Returns ``(chat_id, None)`` on success or ``(None, error_dict)`` on failure.
    r   r   Nr   rJ   z/aiohttp not installed. Run: pip install aiohttp)r  proxy_kwargs_for_aiohttpzhttps://slack.com/apizBearer application/jsonAuthorizationzContent-Typec                    K    | j          d| f|d4 d {V }|                                 d {V cd d d           d {V  S # 1 d {V swxY w Y   d S )N/)headersr   )postr   )sessionmethodpayloadresp_req_kwr   r  s       r7   post_apiz,_resolve_slack_user_target.<locals>.post_api  s     #w|x$:$:&$:$:eGRYee]dee ) ) ) ) ) ) )im!YY[[(((((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )s   A
AAc                 r  K   |                                                     d                                          }g }d }t          d          D ]%}ddi}|r||d<    	| d|           d {V }|                    d          sd d|                    d	d
           fc S |                    dg           D ]}|                    d          s|                    d          r-t          |                    dd                                                                                     |k    r|                    |           |                    d          pi                     d          }|s n'|sd d| dfS t          |          dk    rd d| dfS |d                             d          d fS )Nr     limit   cursorz
users.listokzSlack users.list error: rJ   unknownmembersdeletedis_botr   r&   response_metadatanext_cursorzCould not resolve Slack user '@z'.r'   zSlack user '@z<' matched multiple Slack users. Use a Slack user ID instead.r   id)r(   r  r-   rg   r   rP   rx  r)   )
r  r   querymatchesr  _pager  datamemberr  s
            r7   resolve_user_namez5_resolve_slack_user_target.<locals>.resolve_user_name  s     JJLL'',,2244EGFr  "C. /(.GH%%Xg|WEEEEEEEExx~~ [!ZDHHWi<X<X!Z!ZZZZZ"hhy"55 / /Fzz),, !

80D0D !  6::fb112288::@@BBeKKv...((#677=2BB=QQ E HGtGGGGG7||aoTooooo1:>>$''--r9   rW      )totalzconversations.openusersr  z Slack conversations.open error: r  z#. Check bot permissions (im:write).channelr  z7Slack conversations.open did not return a DM channel IDzSlack DM resolution failed: rf   )rQ   aiohttpr\  r   r  r  ClientSessionClientTimeoutr)   rK   r   ri   )r   rM   r   r  r  _proxy_sess_kwr  r  r   rJ   openeddm_idr   r  r   r  r  s                 @@@@r7   r   r     sz      w'' 7+=+=l+K+K }R R R RgPQQQQQR:@VVVVVVVV""$$44V<<'*$5e$5$5GYZZ	) 	) 	) 	) 	) 	) 	)	. 	. 	. 	. 	.8 )7(]]1F1FR1F1P1P]T\]] 	 	 	 	 	 	 	ah!!,// ,'8'8'#lJ[J[J\J\B]'^'^!^!^!^!^!^!^ /.		 	 	 	 	 	 	 	 	 	 	 	 	 	
 ,'++c'llmm,G#8G-AGWCUVVVVVVVVF::d## V8vzz'97U7U 8 8 8  	 	 	 	 	 	 	 	 	 	 	 	 	 	 ZZ	**0b55d;;E _V$]^^^!	 	 	 	 	 	 	 	 	 	 	 	 	 	" $;#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	$  @ @ @V>1>>?????????@sx   ; AAAH 1AG<H A.G<H <G<H &G<)H <
HH 	H
H 
H7H2,H72H7c                 
  "#$%&'(K   	 ddl 'n# t          $ r ddicY S w xY wddlm}m"m}m}m}m}m	#m
}	 ddlm}
 	 |                     dd                              d	          &|                     d
d          $$sddiS |pg }g %|D ]U\  }}t          j                            |          r%                    |           :t&                              d|           V%r,"%fdt+          dt-          %          "          D             }ng g} |
|          \  }(#$&'(fd}dt.          ddf$&'fd} |	            }t&                              d|                                t-          %          t-          |                     g }t5          |          D ]\  }}t-          |          }|dk    rO|                    |          }||k    r4 |d ||           d|dz    d	t-          |           d           d{V  |dk    r|nd}t+          d|dz             D ]}	 |                    |           d{V  t;          j                    } |||           d{V }t;          j                    |z
  }d|vr|                    ||           d{V   n|d         } ||          s,tA          d|dz    d	t-          |           d|           c c S  ||          }|!                    ||           ||k    rP|                    |dz              t&          "                    d|dz   t-          |          ||r|ddnd            nt&                              d|dz   t-          |          |||r|ddnd           l# tF          $ r}||k    rY|                    |dz              t&          "                    d|dz   t-          |          |t/          |                     Y d}~ nJt&                              d |dz   t-          |          ||t/          |                     Y d}~d}~ww xY wg } t-          %          t-          |          k     r|                     d!           |rF|                     d"t-          |           d#d$$                    d% |D                        d&           |r@t-          |          t-          |          k    r tA          d't-          |           d(          S d)d*tK          d*          d+}!| r| |!d,<   |!S # tF          $ r}tA          d-|           cY d}~S d}~ww xY w).u  Send via signal-cli JSON-RPC API.

    Supports both text-only and text-with-attachments (images/audio/documents).
    Multi-attachment sends are chunked into batches of
    SIGNAL_MAX_ATTACHMENTS_PER_MSG and metered by the process-wide
    SignalAttachmentScheduler — same bucket the gateway adapter uses, so
    sends from this tool and inbound-driven replies share rate-limit state.
    r   NrJ   zhttpx not installed)$SIGNAL_BATCH_PACING_NOTICE_THRESHOLDSIGNAL_MAX_ATTACHMENTS_PER_MSGSIGNAL_RATE_LIMIT_MAX_ATTEMPTS_extract_retry_after_seconds_format_wait_is_signal_rate_limit_error_signal_send_timeoutget_scheduler)markdown_to_signalhttp_urlzhttp://127.0.0.1:8080r  accountr&   zSignal account not configuredz)Signal media file not found, skipping: %sc                 *    g | ]}||z            S rf   rf   ).0r  r	  attachment_pathss     r7   
<listcomp>z _send_signal.<locals>.<listcomp>d  s9        !1'E#E!EF  r9   c                 d  K   |d}|r&r$t                    dk    rd         |d<   n|d<   	                    d          r	dd          |d<   n	g|d	<   | r| |d
<   dd|dt          t          j                    dz             d} | rt          |           nd          }                    |          4 d {V }|                    
 d|           d {V }|                                 |                                cd d d           d {V  S # 1 d {V swxY w Y   d S )Nr  rH   r'   r   	textStyle
textStylesrO      groupId	recipientattachments2.0rt   send_  jsonrpcr  paramsr  rW   /api/v1/rpcr   )r)   rQ   r  timeAsyncClientr  raise_for_statusr   )batch_attachmentsbatch_messager$  r  rW   clientr  r  r  rM   r  httpxtext_styless          r7   _postz_send_signal.<locals>._postm  s     !(]CCF 7 7{##q((*5a.F;''+6F<(!!(++ 0$+ABBKy!!'.i{#  :(9}% !  7c$)++"45577	 G +*EV+]3/@+A+A+A\]^^G(((99 # # # # # # #V#[[H)A)A)A[PPPPPPPP%%'''yy{{# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #s   AD
D),D)r/   r:   c                   K   | d}                     d          rdd         |d<   ng|d<   	                     d          4 d{V }|                     d	d
d|dt          t	          j                    dz             d           d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)z9Best-effort one-shot RPC for a user-facing pacing notice.r  rO   r  Nr  r  g      >@r%  r&  r  rt   notice_r!  r"  r'  z Signal: inline notice failed: %s)rQ   r)  r  r  r(  ri   rj   rk   )r/   notice_params_client_er  rM   r  r.  s       r7   _send_inline_noticez)_send_signal.<locals>._send_inline_notice  s     (/DAAM!!(++ 7+2122;i((.5Yk*G ,,T,:: 	 	 	 	 	 	 	g!,,#000',&,&3"EC	d0B,C,C"E"E	  '         	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	  G G GA2FFFFFFFFFGs<   B? A
B,B? ,
B66B? 9B6:B? ?
C/	C**C/zIsend_message Signal: scheduler state=%s, %d attachment(s) in %d batch(es)u!   (More images coming — pausing ~z for Signal rate limit, batch r'   z.)zSignal RPC error on batch r  z`Signal: rate-limit retries exhausted on batch %d/%d (%d attachments lost, server retry_after=%s)z.0fsr  ziSignal: rate-limited on batch %d/%d (attempt %d/%d, server retry_after=%s); scheduler will pace the retryz7Signal: send error on batch %d/%d after %d attempts: %szFSignal: transient error on batch %d/%d (attempt %d/%d): %s; will retryz1Some media files were skipped (not found on disk)zSignal rate-limited z batch(es) (#z, #c              3   4   K   | ]}t          |          V  d S N)rP   )r  bs     r7   	<genexpr>z_send_signal.<locals>.<genexpr>  s(      ??1A??????r9   r   zSignal: every batch (z*) hit rate limit; no attachments deliveredTr   )r   r   rM   rX  zSignal send failed: )&r.  r\  #gateway.platforms.signal_rate_limitr  r	  r
  r  r  r  r  r  gateway.platforms.signal_formatr  r   rstripr*   r+   r  rx  rj   rk   rg   r)   rP   r  staterd  estimate_waitacquirer(  	monotonicreport_rpc_durationrK   feedbackrJ   ri   joinrR   ))r   rM   rH   r0   r  r
  r  r  r  r  r  valid_mediar4   	_is_voiceatt_batches
plain_textr0  r6  	schedulerfailed_batchesidx	att_batchn	estimatedr,  rT   _rpc_t0r  _rpc_durationerrserver_retry_afterr   rX  r   r	  r  r  r  r  r.  r/  s)    `                                @@@@@@@r7   rh  rh  8  s     0 0 0 0.////0	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 CBBBBB`299Z)@AAHHMM))Ir** 	><==!'R%0 	X 	X!J	w~~j)) X ''
3333JJWWWW
  	    q#&6"7"79WXX  KK
 $K"4"4W"="=
K	# 	# 	# 	# 	# 	# 	# 	# 	# 	#4	GC 	GD 	G 	G 	G 	G 	G 	G 	G 	G 	G* "MOO	WOOs#344c+6F6F	
 	
 	
 %''44 :	 :	NCIA1uu%33A66	 DDD--WLL<S<S W W8;aW WBEkBRBRW W W        
 +.((JJM $BQ$FGG . .-#++A........."n..G!&y-!@!@@@@@@@D$(N$4$4w$>Md**';;M1MMMMMMMMMw-C66s;; h%&f37&f&fSQ\M]M]&f&fad&f&fggggggg)E)Ec)J)J&&&'91==="@@@&--cAg666K!GS%5%5q<N]188888T]	   NN8 a[!1!1!?8JY-44444PY    !   "@@@&--cAg666U!GS%5%5wA   NN`a[!1!17<Z\_`a\b\b          3{#3#333OOOPPP 	OOCs>':': C CZZ???????C C C  
  	c.11S5E5EEE,K(8(8 , , ,  
 "xDTU]_fDgDghh 	*!)F: 2 2 20Q00111111112s    "" AT( F'T( -A6N#T( &:N T( %A5NT( 7NT( 
QAQ 8T( ><Q :T(  QCT( T( (
U2U	U	Uc                   K   |pg }|rd|ind}d}d}	 ddl m}  |            }n# t          $ r d}Y nw xY w|U	 ddlm}	 |j                            |	j                  }n.# t          $ r! t          	                    dd           d}Y nw xY w|t          |||||           d{V S 	 dd	lm}
 n# t          $ r d
dicY S w xY w |
|           }	 |                                 d{V }|s:t          d          	 |                                 d{V  S # t          $ r Y S w xY wt          |||||           d{V 	 	 |                                 d{V  S # t          $ r Y S w xY w# t          $ rG}t          d|           cY d}~	 |                                 d{V  S # t          $ r Y S w xY wd}~ww xY w# 	 |                                 d{V  w # t          $ r Y w w xY wxY w)u  Send via the Matrix adapter so native Matrix media uploads are preserved.

    When a live gateway adapter is available (i.e. the tool runs inside a
    running gateway), the persistent connection is reused — one olm/megolm
    session for all sends.  This avoids per-message E2EE re-init storms
    that exhaust recipient OTKs and silently drop messages (issue #46310).

    Falls back to an ephemeral connect/disconnect cycle only when no gateway
    is running (standalone cron, ``hermes send`` CLI).
    r   Nr   r   rI  zxMatrix: live gateway adapter lookup failed; falling back to an ephemeral connect (may re-init E2EE per send, see #46310)Tr=  )MatrixAdapterrJ   zIMatrix dependencies not installed. Run: pip install 'mautrix[encryption]'zMatrix connect failedMatrix send failed: )r   r   ri   r   r   r   r   re  rj   rk   _matrix_send_core plugins.platforms.matrix.adapterrU  r\  connectrK   
disconnect)r   rM   rH   r0   r   r:  live_adapterr   r   r   rU  r   	connectedr   s                 r7   rf  rf    s      #K+4>Y''$H LF333333$$&&   		 //////!?..x??LL 	  	  	 NNL    
  LLL	  
 ''7K
 
 
 
 
 
 
 
 	

fBBBBBBB f f fdeeeef mG$$G!//++++++++	 	3122	$$&&&&&&&&&& 	 	 	D	 'Wg{H
 
 
 
 
 
 
 
 	
	$$&&&&&&&&&& 	 	 	D	  2 2 20Q001111111	$$&&&&&&&&&& 	 	 	D	2	$$&&&&&&&&&& 	 	 	D	s   % 44%A   (B
B*B1 1CC*E, <D
D$#D$'E, E
E)(E),
F=6F8F=G  F((
F54F58F==G   G-GG-
G*'G-)G**G-c                 v  K   d}|                                 r<|                     |||           d{V }|j        st          d|j                   S |D ]P\  }}t
          j                            |          st          d|           c S t
          j                            |          d         	                                }|t          v r|                     |||           d{V }n|t          v r|                     |||           d{V }np|t          v r!|r|                     |||           d{V }nF|t           v r|                     |||           d{V }n|                     |||           d{V }|j        st          d|j                   c S R|ddiS d	d
||j        dS )z=Core send logic shared by live and ephemeral Matrix adapters.N)r:  rV  zMedia file not found: r'   zMatrix media send failed: rJ   r  Tr  r  )r(   rt   r   rK   rJ   r*   r+   r  r,   r-   r'  send_image_filer(  r  r&  r  r)  r  r~   )	r   rM   rH   r0   r:  r  r4   r5   r6   s	            r7   rW  rW  I  s[     K}} F#LL'HLMMMMMMMM" 	FD1BDDEEE + L L
Hw~~j)) 	A?:??@@@@@gz**1-3355+ ' 7 7V^ 7 _ _______KKK ' 2 27JQY 2 Z ZZZZZZZKKKH ' 2 27JQY 2 Z ZZZZZZZKKK ' 2 27JQY 2 Z ZZZZZZZKK ' 5 5gzT\ 5 ] ]]]]]]]K" 	LJ{7HJJKKKKK	L \]] !,	  r9   c                    K   	 ddl m}m}  |            sddiS n# t          $ r ddicY S w xY w	  || j        | j        |||           d{V S # t          $ r}t          d|           cY d}~S d}~ww xY w)	z6Send via Weixin iLink using the native adapter helper.r   )check_weixin_requirementssend_weixin_directrJ   z9Weixin requirements not met. Need aiohttp + cryptography.zWeixin adapter not available.)r   r   rM   rH   r0   NzWeixin send failed: )gateway.platforms.weixinr`  ra  r\  r   r   ri   rK   )r   rM   rH   r0   r`  ra  r   s          r7   rZ  rZ  x  s     :ZZZZZZZZ((** 	ZXYY	Z : : :89999:	2''--#
 
 
 
 
 
 
 
 
 	
  2 2 20Q00111111112s)    ,,A 
A6A1+A61A6c                 v  K   	 ddl m}m}  |            sddiS n# t          $ r ddicY S w xY w	 ddlm}  ||           } ||          }|                                 d{V }|st          d	          S 	 |                    ||           d{V }	|	j	        s1t          d
|	j
                   |                                 d{V  S dd||	j        d|                                 d{V  S # |                                 d{V  w xY w# t          $ r}
t          d
|
           cY d}
~
S d}
~
ww xY w)zBSend via BlueBubbles iMessage server using the adapter's REST API.r   )BlueBubblesAdaptercheck_bluebubbles_requirementsrJ   z8BlueBubbles requirements not met (need aiohttp + httpx).z"BlueBubbles adapter not available.r   )r   Nz(BlueBubbles: failed to connect to serverzBlueBubbles send failed: Tbluebubblesr  )gateway.platforms.bluebubblesrd  re  r\  r   r   rY  rK   rt   r   rJ   rZ  r~   ri   )r   rM   rH   rd  re  r   r   r   r\  r   r   s              r7   ru  ru    s     ?dddddddd--// 	YWXX	Y ? ? ?=>>>>?7111111 .u---$$W--!//++++++++	 	FDEEE	'"<<99999999F> JH&,HHII $$&&&&&&&&&&  $7bhbstt$$&&&&&&&&&&'$$&&&&&&&&&& 7 7 75!55666666667sN    ,,AD 99C3 2D C3 D 3DD 
D8D3-D83D8c                      t           j                            d          rdS ddlm}   | dd          }|r|dk    rdS 	 ddlm}  |            S # t          $ r Y d	S w xY w)
u  Gate send_message on gateway running (always available on messaging platforms).

    Also passes for kanban workers — the dispatcher sets ``HERMES_KANBAN_TASK``
    on every spawned worker, but those workers run with the assignee profile's
    ``HERMES_HOME`` which has no ``gateway.pid``, so the gateway-running check
    would fail even though the parent gateway is alive. Honoring the env var
    lets workers call ``send_message`` to deliver rich content directly to the
    originating chat (paired with ``kanban_complete`` for the short notifier
    summary), which is the canonical pattern for any worker that needs to
    reply with more than the ~200-char first-line truncation the kanban
    notifier applies.
    HERMES_KANBAN_TASKTr   r   r   r&   local)is_gateway_runningF)r*   environr   r   r   gateway.statusrk  ri   )r   r   rk  s      r7   _check_send_messagern    s     
z~~*++ t7777778"==H H''t555555!!###   uus   A 
AAc           	        K   	 ddl }n# t          $ r t          d          cY S w xY w| j        pi }|                    d          pt          j        dd          }| j        p)|                    d          pt          j        dd          }|r|st          d	          S 	 |                    d
          4 d{V }|	                    dt          |          t          |          d           d{V }|j        dk    r)t          d|j                   cddd          d{V  S |                                }	|	                    d          }
|
s!t          d          cddd          d{V  S d|
 dd}|dd         dd}d| d}|	                    |||           d{V }|j        dv r@|                                }dd||                    d          dcddd          d{V  S d | d}|	                    |||           d{V }|j        dv r@|                                }dd||                    d          dcddd          d{V  S d!| d}|	                    |||           d{V }|j        dv r@|                                }dd||                    d          dcddd          d{V  S t          d"|j         d#|j         d$|j                   cddd          d{V  S # 1 d{V swxY w Y   dS # t          $ r}t          d%|           cY d}~S d}~ww xY w)&a  Send via QQBot using the REST API directly (no WebSocket needed).

    Uses the QQ Bot Open Platform REST endpoints to get an access token
    and post a message. Supports guild channels, C2C (private) chats,
    and group chats by trying the appropriate endpoints.
    r   Nz8QQBot direct send requires httpx. Run: pip install httpxapp_id	QQ_APP_IDr&   client_secretQQ_CLIENT_SECRETz3QQBot: QQ_APP_ID / QQ_CLIENT_SECRET not configured.   r%  z)https://bots.qq.com/app/getAppAccessToken)appIdclientSecretr'  r  zQQBot token request failed: access_tokenz"QQBot: no access_token in responsezQQBot r  r  i  )r9  msg_typez#https://api.sgroup.qq.com/channels/z	/messages)r   r  >   r     Tqqbotr  r  z#https://api.sgroup.qq.com/v2/users/z$https://api.sgroup.qq.com/v2/groups/zQQBot send failed: channel=z c2c=z group=zQQBot send failed: )r.  r\  rK   r   r   r*   r   r   r)  r  rP   status_coder   ri   )r   rM   rH   r.  r   appidsecretr-  
token_resp
token_datarw  r  r  urlr  r  url_c2cresp_c2c	url_group
resp_groupr   s                        r7   rw  rw    sK     R R R RPQQQQQR MREIIh=29["#=#=Em 3uyy99 3-r22  M MKLLL21$$R$00 /	F /	F /	F /	F /	F /	F /	FF%{{;"5zz3v;;GG  +          J %,,UZ=SUUVV/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F $**J%>>.99L DBCC/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F" "9,!8!8 2 G #*%4%.a@@G KJJJCSwHHHHHHHHD:--yy{{#'W&*hhtnn6 67/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F> OGNNNG#[[w[PPPPPPPPH#z11}}#'W&*hhtnn6 6G/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	FN RwQQQI%{{97G{TTTTTTTTJ%33!((#'W&*hhtnn6 6W/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F^   E8H  E  ExOc  E  Elv  mC  E  E  F  F_/	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F /	F`  1 1 1/A//000000001s   	 %% L <ALL +9L$L 7A/L&L 9ALL &AL L &L9L 
LL LL 
M)M :M Mc                   K   	 ddl m}m} n# t          $ r t	          d          cY S w xY w |            }|t	          d          S 	  ||| ||           d{V S # t
          $ r}t	          d|           cY d}~S d}~ww xY w)u  Send via Yuanbao using the running gateway adapter's WebSocket connection.

    Yuanbao uses a persistent WebSocket — unlike HTTP-based platforms, we
    cannot create a throwaway client.  We obtain the running singleton from
    the adapter module itself (``get_active_adapter``).

    chat_id format:
      - Group: "group:<group_code>"
      - DM:    "direct:<account_id>" or just "<account_id>"
    r   )get_active_adaptersend_yuanbao_directz%Yuanbao adapter module not available.NzVYuanbao adapter is not running. Start the gateway with yuanbao platform enabled first.rJ  zYuanbao send failed: )gateway.platforms.yuanbaor  r  r\  rK   ri   )rM   rH   r0   r  r  r   r   s          r7   rj  rj    s      ?UUUUUUUUU ? ? ?=>>>>>? ! ""GE
 
 	

3(('7P[\\\\\\\\\\ 3 3 31a11222222223s*    ))A 
B'A>8B>B)r   )Fr  )NNFFr9  )NN)R__doc__rl   r   loggingr*   r  sslr(  email.utilsr   agent.redactr   agent.secret_scoper   	getLoggerrD  rj   compiler  r  r  r   r  r  r  r  r  r  	frozensetr  r  
IGNORECASEr  r  r   r'  r(  r)  r&  r  r.   r  rc  r8   rC   rE   rP   rG   r   rK   rR   ri   r  r_   rb   rq   SEND_MESSAGE_SCHEMAr   r   r   r   r   r   r.  r   rG  r   r   r  ra  ro  r   rh  rf  rW  rZ  ru  rn  rw  rj  tools.registryr   rf   r9   r7   <module>r     s       				 				 



  " " " " " " . . . . . . ) ) ) ) ) )		8	$	$&BJ'DEE BJfgg  2:<== BJ9::  bj!CDD BJHII $"*%MNN BJuvv RZ CDD -  9DDDEE "*455
 2:IM   2:WXX 
  '(<= 888666@@@w $V,   +- 1 1 1     & & &N "rzWM   'BJQM  #    4C 4D 4 4 4 4
C # #    y 3 54<    2 EF ' ' 'c ' ' ' '& 	5  !<<<  }  !  \ 
 !  ] 
 !  J 
 !   %
 
. 3 % % P   L L LY1 Y1 Y1 Y1xi7 i7 i7XTS Tc T T T Tn: : :&  3  QTW[Q[    J \ \ \ \ \~} } } }@4 4t 4 4 4 4}4 }4 }4 }4H
\ \ \ \(K@ K@ K@\z2 z2 z2 z2NG G G GT$ $ $^2 2 2 2*7 7 7@  4E1 E1 E1P3 3 3 3< & % % % % % % %r9   