
    `gjJA                        d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 dZ
dZdZ	 	 	 	 	 	 ddZdd	Z	 	 	 	 	 	 	 	 dd
ZddZddZddZddZddgZy)u  CLI subcommand: ``hermes send`` — pipe text from shell scripts to any
configured messaging platform (Telegram, Discord, Slack, Signal, SMS, etc.).

This is a thin wrapper around ``tools.send_message_tool.send_message_tool``
that exposes its functionality as a standalone CLI entry point so ops
scripts, cron jobs, CI hooks, and monitoring daemons can reuse the gateway's
already-configured credentials without having to reimplement each platform's
REST API client.

Design notes:

* No LLM, no agent loop — the subcommand just resolves arguments, reads the
  message body, calls the shared tool function, and prints/returns the
  result. It is intentionally fast, cheap, and side-effect-only.
* For platforms that send via bot token (Telegram, Discord, Slack, Signal,
  SMS, WhatsApp-CloudAPI, …) no running gateway is required. The tool
  talks directly to each platform's REST endpoint. For platforms that rely
  on a persistent adapter connection (plugin platforms, Matrix in some
  modes, …) a live gateway is needed; the underlying tool surfaces that
  error to the caller.
* Exit codes follow the classic Unix convention:
    0 — delivery (or list) succeeded
    1 — delivery failed at the platform level
    2 — usage / argument / config error (argparse already uses 2)
    )annotationsN)Path)Optional      c                D   | r| S |r?|dk(  rt         j                  j                         S 	 t        |      j	                  d      S t         j                  j                         s"t         j                  j                         }|r|S y# t
        $ rD t        d| d| d| d| d	t         j                  	       t        j                  t               Y t        $ rD}t        d
| d| t         j                  	       t        j                  t               Y d}~d}~ww xY w)u8  Resolve the message body from (in order):

    1. An explicit positional message argument.
    2. ``--file PATH`` or ``--file -`` (where ``-`` means stdin).
    3. Piped stdin when it is not attached to a TTY.

    Returns ``None`` when nothing is available — callers must treat that as
    a usage error.
    -utf-8encodinghermes send: z is not a text file. --file reads the message *body* (logs, reports, markdown).
To send an image/document/audio file as a native attachment, reference it with MEDIA: in the message text instead:
  hermes send --to telegram "MEDIA:z6"
  hermes send --to telegram "optional caption MEDIA:zv"
Add [[as_document]] to deliver an image as an uncompressed file:
  hermes send --to telegram "[[as_document]] MEDIA:"filezhermes send: cannot read z: N)sysstdinreadr   	read_textUnicodeDecodeErrorprintstderrexit_USAGE_EXITOSErrorisatty)
positional	file_pathexcdatas       F/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/send_cmd.py_read_message_bodyr!   )   s    99>>##	"	?,,g,>>* 99yy~~K3 " 	"	{ +6 7@[ AGGPk RFFO[PQS ZZ
 HH[! 	"-i[3%@szzRHH[!!	"s   B A
DD:DDc                H    | r | j                         r| j                         S y)zAReturn a cleaned ``--to`` value, or ``None`` when nothing is set.N)strip)arg_tos    r    _resolve_targetr%   Z   s    &,,.||~    c               t   	 | rt        j                  |       ni }|r!t        t        j                  |d             n|rn|j                  d      r"t        d|d    t        j                         n\|j                  d      r+|j                  d	      }|rt        |       n,t        d
       n t        t        j                  |d             |j                  d      rt        S |j                  d      rt        S |j                  d      rt        S t        S # t         j                  $ r	 d| d}Y w xY w)zPrint the tool result in the requested format and return the exit code.

    The underlying ``send_message_tool`` always returns a JSON string. We
    parse it, decide success/failure, and format accordingly.
    z#invalid JSON from send_message_tool)errorrawr   )indentr(   r   r   successnotesentskipped)
jsonloadsJSONDecodeErrorr   dumpsgetr   r   _FAILURE_EXIT_SUCCESS_EXIT)result_json	json_modequietpayloadr,   s        r    _emit_resultr:   a   s   W-8$**[)b djj+,	;;wM''"2!343::F[[#;;v&Ddf $**WQ/0{{7{{9{{9;  W B+VWs   D D76D7c          
        	 ddl m}m} 	  |       }t        |j                  d      xs i       }| r| j                         j                         }|j                         D 	ci c]  \  }}	|j                         |k(  s||	 }
}}	|
sCt	        d|  d	d
j                  t        |            xs d t
        j                         t        S |
}|r-t	        t        j                   d|idt"                     t$        S t'        |j)                               s't	        d       t	        d       t	        d       t$        S | t	         |              t$        S t        |      D ]  }||   }t	        | d       |st	        d       $|D ]^  }|j                  dd      }|j                  d      xs |j                  d      xs d}|r||k7  rd| dnd}t	        d| d| |        ` t	                 t$        S # t        $ r.}t	        d| t
        j                         t        cY d}~S d}~ww xY w# t        $ r.}t	        d| t
        j                         t        cY d}~S d}~ww xY wc c}	}w )u4  Print the channel directory (all configured targets across platforms).

    Uses ``load_directory()`` for structured JSON output and
    ``format_directory_for_display()`` for the human-readable rendering that
    the send_message tool itself shows to the model — keeps the two surfaces
    identical.
    r   )format_directory_for_displayload_directoryz/hermes send: failed to load channel directory: r   Nz/hermes send: failed to read channel directory: 	platformsz,hermes send: no targets found for platform 'z'. Configured: z, z(none)r   )r*   defaultz@No messaging platforms configured or no channels discovered yet.zBSet one up with `hermes gateway setup`, or run the gateway once soz@channel discovery can populate ~/.hermes/channel_directory.json.:z  (no channels discovered yet)name?idchat_id z  []z  )gateway.channel_directoryr<   r=   	Exceptionr   r   r   r4   dictr3   r#   loweritemsjoinsortedr/   r2   strr5   anyvalues)platform_filterr7   r<   r=   r   r)   r>   keykvfiltered	plat_namechannelschrA   rD   suffixs                    r    _list_targetsrZ      sR   	

 SWW[)/R0I##%++-%.__%6KTQ!'')s:JAqDKK>>O P#yy	):;GxHJZZ
 ! 	djj+y1!SIJy!"PQRSPQ *,-I& 	Y'1o23 	3B66&#&DffTl=bffY&7=2G)0W_s7)1%"FByk4&12		3
 	 k  ?uECJJW  ?uECJJW LsF   G9 H3 #I-?I-9	H0#H+%H0+H03	I*<#I%I*%I*c                    	 ddl m}  	 ddlm}  |       }|dz  }| r%|j                         r	  | t        |      dd       ddl}|d
z  }|j                         sy	 ddl	}	 t        |dd      5 }|j                  |      xs i }ddd       	 ddlm}	  |	      }	 ddlm}
 |
j                  t!        t"              r|ni       }t!        t"              sy|j%                         D ]M  \  }}t!        |t        t&        t(        t*        f      s'||j,                  v r6t        |      |j,                  |<   O y# t        $ r d} Y ?w xY w# t        $ r Y yw xY w# t        $ r) 	  | t        |      dd	       n# t        $ r Y nw xY wY It        $ r Y Tw xY w# t        $ r Y yw xY w# 1 sw Y   %xY w# t        $ r Y yw xY w# t        $ r Y 4w xY w# t        $ r Y w xY w)u  Populate ``os.environ`` from ``~/.hermes/.env`` AND bridge top-level
    ``config.yaml`` keys into the environment so the underlying gateway
    config loader sees platform credentials and home channel IDs.

    ``send_message_tool`` reads tokens and home-channel IDs via
    ``os.getenv(...)`` on each call. The gateway process does two things at
    startup that ``hermes send`` must replicate when invoked standalone:

    1. ``load_dotenv(~/.hermes/.env)`` — brings bot tokens into the env.
    2. Bridge top-level simple values from ``~/.hermes/config.yaml`` into
       ``os.environ`` (without overriding existing env vars). This is where
       ``TELEGRAM_HOME_CHANNEL`` and friends live when the user saved them
       via ``hermes config set``.

    See ``gateway/run.py`` for the canonical version of this bridge — we
    intentionally reimplement the minimum needed here so ``hermes send``
    doesn't pull in the full gateway module just to resolve a home channel.
    r   )load_dotenvN)get_hermes_homez.envTr
   )overrider   zlatin-1zconfig.yamlrr   )_expand_env_vars)managed_scope)dotenvr\   rH   hermes_cli.configr]   existsrN   r   osyamlopen	safe_loadr`   
hermes_clira   apply_managed_overlay
isinstancerI   rK   intfloatboolenviron)r\   r]   homeenv_pathre   config_pathrf   fhr)   r`   ra   rR   vals                r    _load_hermes_envru      s   (&5  f}Hx(	HwG &K+sW5 	+..$*C	+
6s#,11C9N#TVW c4 IIK #S#S%67"**c(

3#s     " 	CMD9M  		  	+ 	+     s   D9 E E F "F6 0F)F6 G )G 9EE	EE	F$E98F9	FFFFFF	F&%F&)F3.F6 6	GG	GG	G"!G"c           	        t                t        | dd      r:t        | dd      }t        |t        | dd            }t        j                  |       t        t        | dd            }|s4t        dt        j                  	       t        j                  t               t        t        | dd      t        | d
d            }||j                         s4t        dt        j                  	       t        j                  t               t        | dd      }|r| d|j                          }ddlm} d||d} ||      }t        |t        | dd      t        | dd            }t        j                  |       y)z9Entry point wired into the top-level argparse dispatcher.list_targetsFmessageNr/   )r7   tozhermes send: --to PLATFORM[:channel[:thread]] is required
Examples:
  hermes send --to telegram "hello"
  hermes send --to discord:#ops --file report.md
  hermes send --list      # list available targetsr   r   znhermes send: no message provided. Pass text as a positional argument, use --file PATH, or pipe data via stdin.subjectz

r   )send_message_toolsend)actiontargetrx   r8   )r7   r8   )ru   getattrrZ   r   r   r%   r   r   r   r!   r#   lstriptools.send_message_toolr{   r:   )	argsrQ   	exit_coder~   rx   rz   r{   	tool_argsresults	            r    cmd_sendr   *  sZ     t^U+ "$	48!/WT6SX=YZ	WT467FA
 	
 	 i&fd#G gmmoA	

 	 dIt,GIT'.."2!34 : I y)F$.dGU+I
 HHYr&   c                   | j                  ddddt        j                        }|j                  dddd	d
       |j                  ddd	d       |j                  dddd	d       |j                  dddd	d       |j                  dddddd       |j                  dd ddd!"       |j                  d#ddd$"       |j	                  t
        %       |S )&zCreate the ``send`` subparser and return it.

    Kept as a standalone function so the top-level parser builder can wire
    it in next to the other messaging subcommands without cluttering
    ``_parser.py`` or ``main.py``.
    r|   zASend a message to a configured platform (scripts, cron jobs, CI).u!  Pipe text from any shell script to any messaging platform Hermes is already configured for. Reuses the gateway's platform credentials (~/.hermes/.env + ~/.hermes/config.yaml) — no LLM, no agent loop, no running gateway required for bot-token platforms like Telegram/Discord/Slack/Signal.a  Examples:
  hermes send --to telegram "deploy finished"
  echo "RAM 92%" | hermes send --to telegram:-1001234567890
  hermes send --to discord:#ops --file /tmp/report.md
  hermes send --to slack:#eng --subject "[CI]" --file build.log
  hermes send --to telegram "MEDIA:/tmp/chart.png"   # send a media attachment
  hermes send --list                  # all platforms
  hermes send --list telegram         # filter by platform

Exit codes: 0 ok, 1 delivery/backend error, 2 usage error.)helpdescriptionepilogformatter_classz-tz--toTARGETNzDelivery target. Format: 'platform' (home channel), 'platform:chat_id', 'platform:chat_id:thread_id', or 'platform:#channel-name'. Examples: telegram, telegram:-1001234567890:17585, discord:#ops, slack:C0123ABCD, signal:+15551234567.)metavarr?   r   rx   rB   z4Message text. If omitted, read from --file or stdin.)nargsr?   r   z-fz--filePATHzRead message body from PATH (text only). Use '-' to force stdin. To send an image/document as an attachment, use MEDIA:<path> in the message text instead.z-sz	--subjectLINEz6Prepend a subject/header line before the message body.z-lz--listrw   
store_trueFzRList available targets. Optional positional filter: `hermes send --list telegram`.)destr}   r?   r   z-qz--quietz,Suppress stdout on success (exit code only).)r}   r?   r   z--jsonz6Emit raw JSON result instead of human-readable output.)func)
add_parserargparseRawDescriptionHelpFormatteradd_argumentset_defaultsr   )
subparsersparsers     r    register_send_subparserr   p  sJ    ""P<	I !<<- # F2 #   C	   (  
 E   a   ;   E	   X&Mr&   r   r   )r   Optional[str]r   r   returnr   )r$   r   r   r   )r6   rN   r7   rn   r8   rn   r   rl   )rQ   r   r7   rn   r   rl   )r   None)r   zargparse.Namespacer   r   )r   zargparse.ArgumentParser)__doc__
__future__r   r   r/   r   pathlibr   typingr   r   r4   r5   r!   r%   r:   rZ   ru   r   r   __all__ r&   r    <module>r      s   4 #   
   ... .b** * 	*
 	*ZBJT#nCLdN 0
1r&   