diff --git a/gateway/config.py b/gateway/config.py index 76f6ed098fcf..01184fd271c1 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -998,98 +998,9 @@ def load_gateway_config() -> GatewayConfig: _tg_extra = _tg_plat.setdefault("extra", {}) _tg_extra.setdefault("require_mention", _tl_require_mention) - # Telegram settings β†’ env vars (env vars take precedence) - telegram_cfg = yaml_cfg.get("telegram", {}) - if isinstance(telegram_cfg, dict): - # Bridge top-level legacy `telegram.disable_topic_auto_rename` into - # gateway.platforms.telegram.extra so the runtime config sees it. - # Read as a runtime-config flag, not env-var (no need for env override). - if "disable_topic_auto_rename" in telegram_cfg: - _tg_plat = platforms_data.setdefault(Platform.TELEGRAM.value, {}) - _tg_extra = _tg_plat.setdefault("extra", {}) - _tg_extra.setdefault( - "disable_topic_auto_rename", - telegram_cfg["disable_topic_auto_rename"], - ) - # Prefer telegram.require_mention; fall back to the top-level shorthand. - _effective_rm = telegram_cfg.get("require_mention", yaml_cfg.get("require_mention")) - if _effective_rm is not None and not os.getenv("TELEGRAM_REQUIRE_MENTION"): - os.environ["TELEGRAM_REQUIRE_MENTION"] = str(_effective_rm).lower() - if "mention_patterns" in telegram_cfg and not os.getenv("TELEGRAM_MENTION_PATTERNS"): - os.environ["TELEGRAM_MENTION_PATTERNS"] = json.dumps(telegram_cfg["mention_patterns"]) - if "exclusive_bot_mentions" in telegram_cfg and not os.getenv("TELEGRAM_EXCLUSIVE_BOT_MENTIONS"): - os.environ["TELEGRAM_EXCLUSIVE_BOT_MENTIONS"] = str(telegram_cfg["exclusive_bot_mentions"]).lower() - if "guest_mode" in telegram_cfg and not os.getenv("TELEGRAM_GUEST_MODE"): - os.environ["TELEGRAM_GUEST_MODE"] = str(telegram_cfg["guest_mode"]).lower() - if "observe_unmentioned_group_messages" in telegram_cfg and not os.getenv("TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES"): - os.environ["TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES"] = str(telegram_cfg["observe_unmentioned_group_messages"]).lower() - frc = telegram_cfg.get("free_response_chats") - if frc is not None and not os.getenv("TELEGRAM_FREE_RESPONSE_CHATS"): - if isinstance(frc, list): - frc = ",".join(str(v) for v in frc) - os.environ["TELEGRAM_FREE_RESPONSE_CHATS"] = str(frc) - # allowed_chats: if set, bot ONLY responds in these group chats (whitelist) - ac = telegram_cfg.get("allowed_chats") - if ac is not None and not os.getenv("TELEGRAM_ALLOWED_CHATS"): - if isinstance(ac, list): - ac = ",".join(str(v) for v in ac) - os.environ["TELEGRAM_ALLOWED_CHATS"] = str(ac) - allowed_topics = telegram_cfg.get("allowed_topics") - if allowed_topics is not None and not os.getenv("TELEGRAM_ALLOWED_TOPICS"): - if isinstance(allowed_topics, list): - allowed_topics = ",".join(str(v) for v in allowed_topics) - os.environ["TELEGRAM_ALLOWED_TOPICS"] = str(allowed_topics) - ignored_threads = telegram_cfg.get("ignored_threads") - if ignored_threads is not None and not os.getenv("TELEGRAM_IGNORED_THREADS"): - if isinstance(ignored_threads, list): - ignored_threads = ",".join(str(v) for v in ignored_threads) - os.environ["TELEGRAM_IGNORED_THREADS"] = str(ignored_threads) - if "reactions" in telegram_cfg and not os.getenv("TELEGRAM_REACTIONS"): - os.environ["TELEGRAM_REACTIONS"] = str(telegram_cfg["reactions"]).lower() - if "proxy_url" in telegram_cfg and not os.getenv("TELEGRAM_PROXY"): - os.environ["TELEGRAM_PROXY"] = str(telegram_cfg["proxy_url"]).strip() - # reply_to_mode: top-level preferred, falls back to extra.reply_to_mode - # YAML 1.1 parses bare 'off' as boolean False β€” coerce to string "off". - _telegram_extra = telegram_cfg.get("extra") if isinstance(telegram_cfg.get("extra"), dict) else {} - _telegram_rtm = ( - telegram_cfg["reply_to_mode"] if "reply_to_mode" in telegram_cfg - else _telegram_extra.get("reply_to_mode") - ) - if _telegram_rtm is not None and not os.getenv("TELEGRAM_REPLY_TO_MODE"): - _rtm_str = "off" if _telegram_rtm is False else str(_telegram_rtm).lower() - os.environ["TELEGRAM_REPLY_TO_MODE"] = _rtm_str - allowed_users = telegram_cfg.get("allow_from") - if allowed_users is not None and not os.getenv("TELEGRAM_ALLOWED_USERS"): - if isinstance(allowed_users, list): - allowed_users = ",".join(str(v) for v in allowed_users) - os.environ["TELEGRAM_ALLOWED_USERS"] = str(allowed_users) - group_allowed_users = telegram_cfg.get("group_allow_from") - if group_allowed_users is not None and not os.getenv("TELEGRAM_GROUP_ALLOWED_USERS"): - if isinstance(group_allowed_users, list): - group_allowed_users = ",".join(str(v) for v in group_allowed_users) - os.environ["TELEGRAM_GROUP_ALLOWED_USERS"] = str(group_allowed_users) - group_allowed_chats = telegram_cfg.get("group_allowed_chats") - if group_allowed_chats is not None and not os.getenv("TELEGRAM_GROUP_ALLOWED_CHATS"): - if isinstance(group_allowed_chats, list): - group_allowed_chats = ",".join(str(v) for v in group_allowed_chats) - os.environ["TELEGRAM_GROUP_ALLOWED_CHATS"] = str(group_allowed_chats) - for _telegram_extra_key in ("guest_mode", "disable_link_previews", "observe_unmentioned_group_messages"): - if _telegram_extra_key in telegram_cfg: - plat_data = platforms_data.setdefault(Platform.TELEGRAM.value, {}) - if not isinstance(plat_data, dict): - plat_data = {} - platforms_data[Platform.TELEGRAM.value] = plat_data - extra = plat_data.setdefault("extra", {}) - if not isinstance(extra, dict): - extra = {} - plat_data["extra"] = extra - extra[_telegram_extra_key] = telegram_cfg[_telegram_extra_key] - if _telegram_extra: - _plat_data, _plat_extra = _ensure_platform_extra_dict( - platforms_data, Platform.TELEGRAM.value - ) - for _telegram_extra_key, _telegram_extra_value in _telegram_extra.items(): - _plat_extra.setdefault(_telegram_extra_key, _telegram_extra_value) + # Telegram settings β†’ env vars / extra: migrated to the telegram + # plugin's apply_yaml_config_fn hook + # (plugins/platforms/telegram/adapter.py). #41112 / #3823. # WhatsApp settings β†’ env vars: migrated to the whatsapp plugin's # apply_yaml_config_fn hook (plugins/platforms/whatsapp/adapter.py). diff --git a/gateway/run.py b/gateway/run.py index 9b79e7597a5b..31936cd79e33 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -6864,36 +6864,7 @@ class GatewayRunner: logger.debug("Platform registry lookup for '%s' failed: %s", platform.value, e) # Fall through to built-in adapters below - if platform == Platform.TELEGRAM: - from gateway.platforms.telegram import TelegramAdapter, check_telegram_requirements - if not check_telegram_requirements(): - logger.warning("Telegram: python-telegram-bot not installed") - return None - adapter = TelegramAdapter(config) - # Apply Telegram notification mode from config. Controls whether - # intermediate messages (tool progress, streaming, status) trigger - # push notifications. Supports ENV override for quick testing. - _notify_mode = os.getenv("HERMES_TELEGRAM_NOTIFICATIONS", "") - if not _notify_mode: - try: - _gw_cfg = _load_gateway_config() - _raw = cfg_get(_gw_cfg, "display", "platforms", "telegram", "notifications") - if _raw not in {None, ""}: - _notify_mode = str(_raw).strip().lower() - except Exception: - pass - _notify_mode = _notify_mode or "important" - if _notify_mode not in {"all", "important"}: - logger.warning( - "Unknown telegram notifications mode '%s', " - "defaulting to 'important' (valid: all, important)", - _notify_mode, - ) - _notify_mode = "important" - adapter._notifications_mode = _notify_mode - return adapter - - elif platform == Platform.SLACK: + if platform == Platform.SLACK: from gateway.platforms.slack import SlackAdapter, check_slack_requirements if not check_slack_requirements(): logger.warning("Slack: slack-bolt not installed. Run: pip install 'hermes-agent[slack]'") diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index ffee053eff5d..e8ece9d54727 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -3797,39 +3797,9 @@ def run_gateway(verbose: int = 0, quiet: bool = False, replace: bool = False): # Per-platform config: each entry defines the env vars, setup instructions, # and prompts needed to configure a messaging platform. _PLATFORMS = [ - { - "key": "telegram", - "label": "Telegram", - "emoji": "πŸ“±", - "token_var": "TELEGRAM_BOT_TOKEN", - "setup_instructions": [ - "1. Open Telegram and message @BotFather", - "2. Send /newbot and follow the prompts to create your bot", - "3. Copy the bot token BotFather gives you", - "4. To find your user ID: message @userinfobot β€” it replies with your numeric ID", - ], - "vars": [ - { - "name": "TELEGRAM_BOT_TOKEN", - "prompt": "Bot token", - "password": True, - "help": "Paste the token from @BotFather (step 3 above).", - }, - { - "name": "TELEGRAM_ALLOWED_USERS", - "prompt": "Allowed user IDs (comma-separated)", - "password": False, - "is_allowlist": True, - "help": "Paste your user ID from step 4 above.", - }, - { - "name": "TELEGRAM_HOME_CHANNEL", - "prompt": "Home channel ID (for cron/notification delivery, or empty to set later with /set-home)", - "password": False, - "help": "For DMs, this is your user ID. You can set it later by typing /set-home in chat.", - }, - ], - }, + # Telegram moved to plugins/platforms/telegram/ β€” setup metadata discovered + # dynamically via the platform registry entry registered by + # plugins/platforms/telegram/adapter.py::register(). #41112. # Discord moved to plugins/platforms/discord/ β€” its setup metadata is # discovered dynamically via _all_platforms() from the platform registry # entry registered by plugins/platforms/discord/adapter.py::register(). @@ -5227,7 +5197,8 @@ def _builtin_setup_fn(key: str): from hermes_cli import setup as _s return { - "telegram": _s._setup_telegram, + # telegram moved into the plugin: setup_fn registered by + # plugins/platforms/telegram/adapter.py::register(). #41112. # discord moved into the plugin: setup_fn is registered by # plugins/platforms/discord/adapter.py::register() and dispatched # via the plugin path in _configure_platform(). diff --git a/plugins/platforms/telegram/__init__.py b/plugins/platforms/telegram/__init__.py new file mode 100644 index 000000000000..d4f1d7bf0e3f --- /dev/null +++ b/plugins/platforms/telegram/__init__.py @@ -0,0 +1,3 @@ +from .adapter import register + +__all__ = ["register"] diff --git a/gateway/platforms/telegram.py b/plugins/platforms/telegram/adapter.py similarity index 96% rename from gateway/platforms/telegram.py rename to plugins/platforms/telegram/adapter.py index d2b425b52b9f..408097b80355 100644 --- a/gateway/platforms/telegram.py +++ b/plugins/platforms/telegram/adapter.py @@ -81,7 +81,7 @@ from gateway.platforms.base import ( SUPPORTED_IMAGE_DOCUMENT_TYPES, utf16_len, ) -from gateway.platforms.telegram_network import ( +from plugins.platforms.telegram.telegram_network import ( TelegramFallbackTransport, discover_fallback_ips, parse_fallback_ip_env, @@ -6079,3 +6079,201 @@ class TelegramAdapter(BasePlatformAdapter): message_id, "\U0001f44d" if outcome == ProcessingOutcome.SUCCESS else "\U0001f44e", ) + + +# ────────────────────────────────────────────────────────────────────────── +# Plugin migration glue (#41112 / #3823) +# +# Added when the Telegram adapter (+ its telegram_network satellite) moved from +# gateway/platforms/ into this bundled plugin. Mirrors the Discord (#24356) / +# Slack migrations: a register(ctx) entry point plus hook implementations that +# replace the per-platform core touchpoints (the Platform.TELEGRAM branch in +# gateway/run.py, the telegram_cfg YAMLβ†’env/extra block in gateway/config.py, +# the _setup_telegram wizard + _PLATFORMS["telegram"] static dict in +# hermes_cli/{setup,gateway}.py, and the _send_telegram dispatch in +# tools/send_message_tool.py). Telegram uses the generic token connected +# check, so no is_connected override is needed. +# ────────────────────────────────────────────────────────────────────────── + + +def _resolve_notifications_mode() -> str: + """Resolve the Telegram notification mode (all/important) from env or + config.yaml display.platforms.telegram.notifications, defaulting to + 'important'. Mirrors the post-construction logic that used to live in + gateway/run.py::_create_adapter().""" + mode = os.getenv("HERMES_TELEGRAM_NOTIFICATIONS", "") + if not mode: + try: + from gateway.config import load_gateway_config + from gateway.run import cfg_get + _gw_cfg = load_gateway_config() + _raw = cfg_get(_gw_cfg, "display", "platforms", "telegram", "notifications") + if _raw not in {None, ""}: + mode = str(_raw).strip().lower() + except Exception: + pass + mode = mode or "important" + if mode not in {"all", "important"}: + logger.warning( + "Unknown telegram notifications mode '%s', defaulting to 'important' " + "(valid: all, important)", mode, + ) + mode = "important" + return mode + + +def _build_adapter(config): + """Factory wrapper that constructs TelegramAdapter and applies the + notification mode (preserving the gateway/run.py post-construction step).""" + adapter = TelegramAdapter(config) + try: + adapter._notifications_mode = _resolve_notifications_mode() + except Exception: + adapter._notifications_mode = "important" + return adapter + + +async def _standalone_send( + pconfig, + chat_id, + message, + *, + thread_id=None, + media_files=None, + force_document=False, +): + """Out-of-process Telegram delivery. Delegates to the standalone + ``_send_telegram`` REST sender in tools/send_message_tool.py (which already + handles chunking-agnostic single sends, threads, media, retries, and + parse-mode fallback). Implements the standalone_sender_fn contract so + deliver=telegram cron jobs succeed when cron runs separately from the + gateway.""" + token = getattr(pconfig, "token", None) or os.getenv("TELEGRAM_BOT_TOKEN", "") + disable_link_previews = bool( + getattr(pconfig, "extra", {}) and pconfig.extra.get("disable_link_previews") + ) + from tools.send_message_tool import _send_telegram + return await _send_telegram( + token, + chat_id, + message, + media_files=media_files, + thread_id=thread_id, + disable_link_previews=disable_link_previews, + force_document=force_document, + ) + + +def interactive_setup() -> None: + """Configure Telegram bot credentials and allowlist. + + Delegates to the existing CLI setup helpers (managed-bot QR onboarding, + token validation, allowlist capture) via lazy import so the full wizard + behavior is preserved without duplicating ~150 lines. Replaces the + _PLATFORMS["telegram"] static dict dispatch in hermes_cli/gateway.py. + """ + from hermes_cli import setup as _setup_mod + _setup_mod._setup_telegram() + + +def _apply_yaml_config(yaml_cfg: dict, telegram_cfg: dict) -> dict | None: + """Translate config.yaml telegram: keys into TELEGRAM_* env vars and + PlatformConfig.extra entries. + + Implements the apply_yaml_config_fn contract (#24849). Mirrors the legacy + telegram_cfg block from gateway/config.py::load_gateway_config(). Env vars + take precedence over YAML. Returns a dict of extras to merge into + PlatformConfig.extra (disable_topic_auto_rename + runtime flags), or None. + """ + import json as _json + extras: dict = {} + + if "disable_topic_auto_rename" in telegram_cfg: + extras.setdefault("disable_topic_auto_rename", telegram_cfg["disable_topic_auto_rename"]) + + _effective_rm = telegram_cfg.get("require_mention", yaml_cfg.get("require_mention")) + if _effective_rm is not None and not os.getenv("TELEGRAM_REQUIRE_MENTION"): + os.environ["TELEGRAM_REQUIRE_MENTION"] = str(_effective_rm).lower() + if "mention_patterns" in telegram_cfg and not os.getenv("TELEGRAM_MENTION_PATTERNS"): + os.environ["TELEGRAM_MENTION_PATTERNS"] = _json.dumps(telegram_cfg["mention_patterns"]) + if "exclusive_bot_mentions" in telegram_cfg and not os.getenv("TELEGRAM_EXCLUSIVE_BOT_MENTIONS"): + os.environ["TELEGRAM_EXCLUSIVE_BOT_MENTIONS"] = str(telegram_cfg["exclusive_bot_mentions"]).lower() + if "guest_mode" in telegram_cfg and not os.getenv("TELEGRAM_GUEST_MODE"): + os.environ["TELEGRAM_GUEST_MODE"] = str(telegram_cfg["guest_mode"]).lower() + if "observe_unmentioned_group_messages" in telegram_cfg and not os.getenv("TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES"): + os.environ["TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES"] = str(telegram_cfg["observe_unmentioned_group_messages"]).lower() + frc = telegram_cfg.get("free_response_chats") + if frc is not None and not os.getenv("TELEGRAM_FREE_RESPONSE_CHATS"): + if isinstance(frc, list): + frc = ",".join(str(v) for v in frc) + os.environ["TELEGRAM_FREE_RESPONSE_CHATS"] = str(frc) + ac = telegram_cfg.get("allowed_chats") + if ac is not None and not os.getenv("TELEGRAM_ALLOWED_CHATS"): + if isinstance(ac, list): + ac = ",".join(str(v) for v in ac) + os.environ["TELEGRAM_ALLOWED_CHATS"] = str(ac) + allowed_topics = telegram_cfg.get("allowed_topics") + if allowed_topics is not None and not os.getenv("TELEGRAM_ALLOWED_TOPICS"): + if isinstance(allowed_topics, list): + allowed_topics = ",".join(str(v) for v in allowed_topics) + os.environ["TELEGRAM_ALLOWED_TOPICS"] = str(allowed_topics) + ignored_threads = telegram_cfg.get("ignored_threads") + if ignored_threads is not None and not os.getenv("TELEGRAM_IGNORED_THREADS"): + if isinstance(ignored_threads, list): + ignored_threads = ",".join(str(v) for v in ignored_threads) + os.environ["TELEGRAM_IGNORED_THREADS"] = str(ignored_threads) + if "reactions" in telegram_cfg and not os.getenv("TELEGRAM_REACTIONS"): + os.environ["TELEGRAM_REACTIONS"] = str(telegram_cfg["reactions"]).lower() + if "proxy_url" in telegram_cfg and not os.getenv("TELEGRAM_PROXY"): + os.environ["TELEGRAM_PROXY"] = str(telegram_cfg["proxy_url"]).strip() + _telegram_extra = telegram_cfg.get("extra") if isinstance(telegram_cfg.get("extra"), dict) else {} + _telegram_rtm = ( + telegram_cfg["reply_to_mode"] if "reply_to_mode" in telegram_cfg + else _telegram_extra.get("reply_to_mode") + ) + if _telegram_rtm is not None and not os.getenv("TELEGRAM_REPLY_TO_MODE"): + _rtm_str = "off" if _telegram_rtm is False else str(_telegram_rtm).lower() + os.environ["TELEGRAM_REPLY_TO_MODE"] = _rtm_str + allowed_users = telegram_cfg.get("allow_from") + if allowed_users is not None and not os.getenv("TELEGRAM_ALLOWED_USERS"): + if isinstance(allowed_users, list): + allowed_users = ",".join(str(v) for v in allowed_users) + os.environ["TELEGRAM_ALLOWED_USERS"] = str(allowed_users) + group_allowed_users = telegram_cfg.get("group_allow_from") + if group_allowed_users is not None and not os.getenv("TELEGRAM_GROUP_ALLOWED_USERS"): + if isinstance(group_allowed_users, list): + group_allowed_users = ",".join(str(v) for v in group_allowed_users) + os.environ["TELEGRAM_GROUP_ALLOWED_USERS"] = str(group_allowed_users) + group_allowed_chats = telegram_cfg.get("group_allowed_chats") + if group_allowed_chats is not None and not os.getenv("TELEGRAM_GROUP_ALLOWED_CHATS"): + if isinstance(group_allowed_chats, list): + group_allowed_chats = ",".join(str(v) for v in group_allowed_chats) + os.environ["TELEGRAM_GROUP_ALLOWED_CHATS"] = str(group_allowed_chats) + for _key in ("guest_mode", "disable_link_previews", "observe_unmentioned_group_messages"): + if _key in telegram_cfg: + extras.setdefault(_key, telegram_cfg[_key]) + for _k, _v in _telegram_extra.items(): + extras.setdefault(_k, _v) + + return extras or None + + +def register(ctx) -> None: + """Plugin entry point β€” called by the Hermes plugin system.""" + ctx.register_platform( + name="telegram", + label="Telegram", + adapter_factory=_build_adapter, + check_fn=check_telegram_requirements, + required_env=["TELEGRAM_BOT_TOKEN"], + install_hint="pip install 'hermes-agent[telegram]'", + setup_fn=interactive_setup, + apply_yaml_config_fn=_apply_yaml_config, + allowed_users_env="TELEGRAM_ALLOWED_USERS", + allow_all_env="TELEGRAM_ALLOW_ALL_USERS", + cron_deliver_env_var="TELEGRAM_HOME_CHANNEL", + standalone_sender_fn=_standalone_send, + max_message_length=4096, + emoji="✈️", + allow_update_command=True, + ) diff --git a/plugins/platforms/telegram/plugin.yaml b/plugins/platforms/telegram/plugin.yaml new file mode 100644 index 000000000000..468081d2d383 --- /dev/null +++ b/plugins/platforms/telegram/plugin.yaml @@ -0,0 +1,35 @@ +name: telegram-platform +label: Telegram +kind: platform +version: 1.0.0 +description: > + Telegram gateway adapter for Hermes Agent. + Connects to Telegram via python-telegram-bot and relays messages between + Telegram chats/groups/topics and the Hermes agent. Supports threads/topics, + streaming edits, native media, inline keyboards, slash commands, fallback + network transport (direct-IP failover), notification modes, mention gating, + and per-user/chat allowlists. +author: NousResearch +requires_env: + - name: TELEGRAM_BOT_TOKEN + description: "Telegram bot token from @BotFather" + prompt: "Telegram bot token" + url: "https://t.me/BotFather" + password: true +optional_env: + - name: TELEGRAM_ALLOWED_USERS + description: "Comma-separated Telegram user IDs allowed to talk to the bot" + prompt: "Allowed users (comma-separated)" + password: false + - name: TELEGRAM_ALLOW_ALL_USERS + description: "Allow any Telegram user to trigger the bot (dev only)" + prompt: "Allow all users? (true/false)" + password: false + - name: TELEGRAM_HOME_CHANNEL + description: "Default chat ID for cron / notification delivery" + prompt: "Home channel ID" + password: false + - name: TELEGRAM_HOME_CHANNEL_NAME + description: "Display name for the Telegram home channel" + prompt: "Home channel display name" + password: false diff --git a/gateway/platforms/telegram_network.py b/plugins/platforms/telegram/telegram_network.py similarity index 100% rename from gateway/platforms/telegram_network.py rename to plugins/platforms/telegram/telegram_network.py diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index 3e866dda956d..99da51b8d2b4 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -593,7 +593,7 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, # Telegram adapter import is optional (requires python-telegram-bot) try: - from gateway.platforms.telegram import TelegramAdapter + from plugins.platforms.telegram.adapter import TelegramAdapter _telegram_available = True except ImportError: _telegram_available = False @@ -857,7 +857,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No else: # Reuse the gateway adapter's format_message for markdownβ†’MarkdownV2 try: - from gateway.platforms.telegram import TelegramAdapter + from plugins.platforms.telegram.adapter import TelegramAdapter _adapter = TelegramAdapter.__new__(TelegramAdapter) formatted = _adapter.format_message(message) except Exception: @@ -902,7 +902,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No # send to a forum group's General topic always errors out # (see issue #22267). try: - from gateway.platforms.telegram import TelegramAdapter + from plugins.platforms.telegram.adapter import TelegramAdapter effective_thread_id = TelegramAdapter._message_thread_id_for_send( str(thread_id) ) @@ -954,7 +954,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No ) if not _has_html: try: - from gateway.platforms.telegram import _strip_mdv2 + from plugins.platforms.telegram.adapter import _strip_mdv2 plain = _strip_mdv2(formatted) except Exception: plain = message