diff --git a/tools/react_to_message_tool.py b/tools/react_to_message_tool.py index c4fe424769..138147198d 100644 --- a/tools/react_to_message_tool.py +++ b/tools/react_to_message_tool.py @@ -15,7 +15,6 @@ def _open_session_db(): """Open the SessionDB for the profile owning this turn, or ``None``.""" try: from hermes_state import get_shared_session_db - return get_shared_session_db() except Exception: return None diff --git a/tools/send_message_senders.py b/tools/send_message_senders.py index 54a6ebed51..6e4ed17ade 100644 --- a/tools/send_message_senders.py +++ b/tools/send_message_senders.py @@ -131,7 +131,6 @@ def _telegram_bot(token): """Bot honouring TELEGRAM_PROXY (``telegram.proxy_url``) — without it the standalone path times out where api.telegram.org is blocked. Falls back to a direct connection.""" from telegram import Bot - try: from gateway.platforms.base import resolve_proxy_url proxy = resolve_proxy_url("TELEGRAM_PROXY", target_hosts=["api.telegram.org"]) @@ -259,7 +258,6 @@ def _telegram_format(message): """``(formatted, parse_mode, has_html)``: text already containing HTML tags is sent as HTML; otherwise Markdown -> MarkdownV2 via the adapter's ``format_message``.""" from telegram.constants import ParseMode - has_html = bool(re.search(r'<[a-zA-Z/][^>]*>', message)) if has_html: return message, ParseMode.HTML, True @@ -372,7 +370,6 @@ def _plugin_standalone_sender(platform_name, *, label=None, discover=True): """``(standalone_sender_fn, None)`` for a registered plugin or ``(None, error_dict)``; ``discover`` runs the idempotent plugin scan first.""" from gateway.platform_registry import platform_registry - if discover: from hermes_cli.plugins import discover_plugins discover_plugins() @@ -505,7 +502,6 @@ async def _send_signal(extra, chat_id, message, media_files=None): from gateway.platforms import signal_rate_limit as rl from gateway.platforms.signal_format import markdown_to_signal - try: http_url = extra.get("http_url", "http://127.0.0.1:8080").rstrip("/") account = extra.get("account", "") @@ -517,35 +513,24 @@ async def _send_signal(extra, chat_id, message, media_files=None): for media_path, _is_voice in valid_media: if not os.path.exists(media_path): logger.warning("Signal media file not found, skipping: %s", media_path) - # No attachments still means one (text-only) batch; with attachments # the text rides on batch #0 so it isn't repeated per batch. per_batch = rl.SIGNAL_MAX_ATTACHMENTS_PER_MSG - att_batches = [ - attachment_paths[i:i + per_batch] for i in range(0, len(attachment_paths), per_batch) - ] or [[]] + att_batches = [attachment_paths[i:i + per_batch] + for i in range(0, len(attachment_paths), per_batch)] or [[]] n_batches = len(att_batches) - plain_text, text_styles = markdown_to_signal(message) + recipient = {"groupId": chat_id[6:]} if chat_id.startswith("group:") else {"recipient": [chat_id]} async def _rpc_send(text, *, id_prefix, timeout, attachments=None, styled=False): - params = {"account": account, "message": text} - if chat_id.startswith("group:"): - params["groupId"] = chat_id[6:] - else: - params["recipient"] = [chat_id] + params = {"account": account, "message": text, **recipient} if styled and text and text_styles: - if len(text_styles) == 1: - params["textStyle"] = text_styles[0] - else: - params["textStyles"] = text_styles + params["textStyle" if len(text_styles) == 1 else "textStyles"] = ( + text_styles[0] if len(text_styles) == 1 else text_styles) if attachments: params["attachments"] = attachments - payload = { - "jsonrpc": "2.0", - "method": "send", - "params": params, - "id": f"{id_prefix}_{int(time.time() * 1000)}"} + payload = {"jsonrpc": "2.0", "method": "send", "params": params, + "id": f"{id_prefix}_{int(time.time() * 1000)}"} async with httpx.AsyncClient(timeout=timeout) as client: return await client.post(f"{http_url}/api/v1/rpc", json=payload) @@ -563,18 +548,15 @@ async def _send_signal(extra, chat_id, message, media_files=None): failed_batches: list[int] = [] for idx, att_batch in enumerate(att_batches): n = len(att_batch) - if n > 0: - estimated = scheduler.estimate_wait(n) - if estimated >= rl.SIGNAL_BATCH_PACING_NOTICE_THRESHOLD: - # Best-effort one-shot RPC for a user-facing pacing notice. - notice = ( - f"(More images coming — pausing ~{rl._format_wait(estimated)} " - f"for Signal rate limit, batch {idx + 1}/{n_batches}.)") - try: - await _rpc_send(notice, id_prefix="notice", timeout=30.0) - except Exception as _e: - logger.warning("Signal: inline notice failed: %s", _e) - + estimated = scheduler.estimate_wait(n) if n > 0 else 0.0 + if n > 0 and estimated >= rl.SIGNAL_BATCH_PACING_NOTICE_THRESHOLD: + # Best-effort one-shot RPC for a user-facing pacing notice. + notice = (f"(More images coming — pausing ~{rl._format_wait(estimated)} " + f"for Signal rate limit, batch {idx + 1}/{n_batches}.)") + try: + await _rpc_send(notice, id_prefix="notice", timeout=30.0) + except Exception as _e: + logger.warning("Signal: inline notice failed: %s", _e) outcome = await _signal_send_batch( _post, scheduler, rl, idx, n_batches, att_batch, plain_text if idx == 0 else "") if outcome is False: @@ -586,14 +568,10 @@ async def _send_signal(extra, chat_id, message, media_files=None): if len(attachment_paths) < len(valid_media): warnings.append("Some media files were skipped (not found on disk)") if failed_batches: - warnings.append( - f"Signal rate-limited {len(failed_batches)} batch(es) " - f"(#{', #'.join(str(b) for b in failed_batches)})") - + warnings.append(f"Signal rate-limited {len(failed_batches)} batch(es) " + f"(#{', #'.join(str(b) for b in failed_batches)})") if failed_batches and len(failed_batches) == n_batches: - return _error( - f"Signal: every batch ({n_batches}) hit rate limit; no attachments delivered") - + return _error(f"Signal: every batch ({n_batches}) hit rate limit; no attachments delivered") return _success("signal", _display_chat_id("signal", chat_id), warnings) except Exception as e: return _error(f"Signal send failed: {e}") @@ -720,7 +698,6 @@ async def _send_qqbot(pconfig, chat_id, message): # Profile-scoped lookup so a multiplex profile never borrows another's QQ credentials. from gateway.config import _getenv - extra = pconfig.extra or {} appid = extra.get("app_id") or _getenv("QQ_APP_ID", "") secret = (pconfig.token or extra.get("client_secret") diff --git a/tools/send_message_targets.py b/tools/send_message_targets.py index 1b80c59c15..0da2160255 100644 --- a/tools/send_message_targets.py +++ b/tools/send_message_targets.py @@ -66,7 +66,6 @@ def _parse_telegram(ref): if parsed: return parsed from plugins.platforms.telegram.telegram_ids import parse_telegram_username_target - username = parse_telegram_username_target(ref) return (username, None) if username else None @@ -179,7 +178,6 @@ def resolve_send_target( """ from gateway.config import Platform from gateway.platform_registry import platform_registry - entry = platform_registry.get(platform_name) def _validate(candidate: str) -> str | None: @@ -225,7 +223,6 @@ def resolve_send_target( resolution_failed = False try: from gateway.channel_directory import resolve_channel_name - resolved = resolve_channel_name(platform_name, target_ref) except Exception: resolved = None diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index c1377ee67b..6374fccc86 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -29,7 +29,6 @@ from tools.registry import tool_error def prepare_send_message_platforms() -> None: """Load enabled standalone plugins before tool schemas/cache keys are built.""" from hermes_cli.plugins import discover_plugins - discover_plugins() @@ -209,7 +208,6 @@ def _resolve_platform_config(platform_name, config): purely via .env (synthesized pconfig so cron delivery works without gateway.yaml).""" from gateway.config import Platform from gateway.platform_registry import platform_registry - entry = platform_registry.get(platform_name) if entry is None and platform_name not in {member.value for member in Platform}: return None, None, None, f"Unknown or unregistered plugin platform: {platform_name}" @@ -357,7 +355,6 @@ async def _send_live_adapter_media( return {"error": f"Adapter send failed: {_bounded_send_error(last_result.error)}"} from gateway.platforms.base import BasePlatformAdapter - total = len(media_files) for index, descriptor in enumerate(media_files): media_path = descriptor[0] if isinstance(descriptor, (list, tuple)) and descriptor else None @@ -494,7 +491,6 @@ def _platform_max_length(platform): SignalAdapter's own chunking), the registry's ``max_message_length`` for plugins, else None (no chunking).""" from gateway.config import Platform - if platform == Platform.SIGNAL: try: from gateway.platforms.signal import MAX_MESSAGE_LENGTH @@ -595,7 +591,6 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, unrelated optional imports such as lark-oapi), Telegram (chunks itself), plugin standalone media routes, native chunked routes, then the generic text path.""" from gateway.config import Platform - platform_name = platform.value if hasattr(platform, "value") else str(platform) media_files = media_files or [] @@ -611,7 +606,6 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, disable_link_previews=disable_link_previews, force_document=force_document) from gateway.platforms.base import BasePlatformAdapter - max_len = _platform_max_length(platform) chunks = BasePlatformAdapter.truncate_message(message, max_len) if max_len else [message] @@ -644,14 +638,12 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, send_one = lambda chunk, is_last: text_sender(pconfig, chat_id, chunk, thread_id) # noqa: E731 else: from gateway.platform_registry import platform_registry - entry = platform_registry.get(platform_name) handler = entry.send_message_handler if entry is not None else None if handler is not None: # Custom handler receives the full typed request once (not per chunk). try: import inspect - result = handler(args or {}, chat_id, platform_name, pconfig) if inspect.isawaitable(result): result = await result