Skip to content

feat(gateway): add Rocket.Chat platform adapter - #14869

Closed
cyb0rgk1tty wants to merge 2 commits into
NousResearch:mainfrom
cyb0rgk1tty:feat/rocketchat-adapter
Closed

feat(gateway): add Rocket.Chat platform adapter#14869
cyb0rgk1tty wants to merge 2 commits into
NousResearch:mainfrom
cyb0rgk1tty:feat/rocketchat-adapter

Conversation

@cyb0rgk1tty

Copy link
Copy Markdown
Contributor

Summary

Adds a Rocket.Chat adapter so Hermes can chat from self-hosted Rocket.Chat alongside the existing platforms. Full parity with the Mattermost adapter: REST (v1) for outbound writes, DDP WebSocket for inbound receive, PAT auth, threaded replies via tmid, file upload via the two-step rooms.media flow, mention gating, DM/channel/group detection, and system-message / self-message filtering.

No new Python dependency — the adapter is built on aiohttp, which is already required by the Mattermost / HomeAssistant / SMS adapters.

Motivation

Rocket.Chat is a common self-hosted choice alongside Mattermost and Matrix. Users who run both wanted a single Hermes profile model across platforms.

What's in the diff

  • Adapter: gateway/platforms/rocketchat.py (~560 lines) with RocketchatAdapter(BasePlatformAdapter).
  • Wiring through the full 16-point ADDING_A_PLATFORM.md checklist:
    • Platform.ROCKETCHAT enum + _apply_env_overrides() block in gateway/config.py
    • Adapter factory branch + platform_env_map / platform_allow_all_map entries in gateway/run.py
    • PLATFORM_HINTS entry in agent/prompt_builder.py (so the agent knows Markdown + threads + MEDIA: hints apply)
    • hermes-rocketchat toolset + inclusion in hermes-gateway composite (toolsets.py)
    • Cron delivery platform_map + _HOME_TARGET_ENV_VARS entry (cron/scheduler.py)
    • Standalone _send_rocketchat() + platform_map + dispatch in tools/send_message_tool.py
    • deliver schema description bump in tools/cronjob_tools.py
    • PLATFORMS registry entry in hermes_cli/platforms.py, status display row in hermes_cli/status.py, full wizard entry in hermes_cli/gateway.py
  • Tests: tests/gateway/test_rocketchat.py — 46 tests mirroring test_mattermost.py coverage (config loading, format/truncate, send with mocked aiohttp, DDP frame dispatch including ping/pong + ready + stream-room-messages, dedup, mention gating, threading, media-type MIME propagation, requirements check).
  • Docs: new website/docs/user-guide/messaging/rocketchat.md setup guide, plus table/diagram/next-steps updates in messaging/index.md and entries in reference/environment-variables.md.

Env var contract

ROCKETCHAT_URL                      required  — server URL (e.g. https://rc.example.com)
ROCKETCHAT_TOKEN                    required  — Personal Access Token ("Ignore Two Factor" must be checked)
ROCKETCHAT_USER_ID                  required  — bot user _id (shown next to the PAT at creation)
ROCKETCHAT_ALLOWED_USERS            optional  — comma-separated user _ids
ROCKETCHAT_ALLOW_ALL_USERS          optional  — "true" to skip allowlist
ROCKETCHAT_HOME_CHANNEL             optional  — room _id for cron delivery
ROCKETCHAT_HOME_CHANNEL_NAME        optional  — display name (default "Home")
ROCKETCHAT_REQUIRE_MENTION          optional  — default "true"; set "false" to respond to all channel msgs
ROCKETCHAT_FREE_RESPONSE_CHANNELS   optional  — comma-separated room _ids exempt from the above
ROCKETCHAT_REPLY_MODE               optional  — "thread" (uses tmid) or "off" (flat, default)

Mirrors the Mattermost env shape so existing profile tooling maps directly.

Test plan

  • pytest tests/gateway/test_rocketchat.py -q → 46 passed
  • pytest tests/ -q → no new failures (the existing dingtalk/matrix/agent_cache failures on main are pre-existing and unchanged)
  • hermes gateway setup lists Rocket.Chat in the wizard with the correct prompts
  • Live verification against a self-hosted Rocket.Chat 8.2 workspace:
    • REST /api/v1/me authentication
    • DDP WS connect + stream-room-messages:__my_messages__ subscription
    • Inbound DM parse → agent dispatch
    • Outbound chat.postMessage with tmid (threaded reply visible in client)
    • Full LLM roundtrip (glm-5.1 via ollama-cloud) — "2 + 2 = 4 👍" delivered as thread reply
  • Live file upload (unit-tested; not yet exercised end-to-end)
  • DDP reconnect after a server-side restart (handler exists and unit-tested)

Notes

  • The adapter subscribes to the __my_messages__ virtual room id (covers every room the bot is a member of with one sub). DDP subscriptions don't resume across reconnect, so the adapter re-logs-in and re-subscribes on every reconnect.
  • rooms.media + rooms.mediaConfirm is used for uploads (the older rooms.upload is deprecated in 6.x+).
  • Writes go via REST; receive goes via DDP. This matches what Rocket.Chat's developer docs recommend ("DDP methods no longer actively tested — prefer REST for new work") while keeping the only viable push path for incoming messages.

🤖 Generated with Claude Code

Connects Hermes to self-hosted Rocket.Chat (tested against 8.2) via the
REST API (v1) for outbound writes and the Realtime (DDP) WebSocket for
inbound messages. Single subscription to stream-room-messages on the
__my_messages__ virtual room id covers every channel/DM/group the bot
is a member of — no per-room enumeration required.

Authentication uses a Personal Access Token (generate with "Ignore Two
Factor Authentication" checked), which doubles as the DDP resume token.
No new Python dependency: the adapter runs on aiohttp, already a core
dep for the Mattermost / HomeAssistant / SMS adapters.

Feature parity with the Mattermost adapter:
- Send / edit / delete messages (chat.postMessage, chat.update)
- Threaded replies via tmid (ROCKETCHAT_REPLY_MODE=thread)
- File upload via the two-step rooms.media + rooms.mediaConfirm flow
  (replaces the deprecated rooms.upload endpoint)
- Inbound file attachment download + cache into media_urls/media_types
- Typing indicator via DDP stream-notify-room method
- Mention gating with ROCKETCHAT_REQUIRE_MENTION and
  ROCKETCHAT_FREE_RESPONSE_CHANNELS; authoritative mentions[] array
  plus a text-scan fallback for edits
- DM vs channel vs group type detection (t: d/c/p/l)
- System-message filtering (skips messages with a non-empty t field)
- Self-message filtering (skips msgs where u._id == bot_user_id)
- Per-room ephemeral channel prompts via config.extra.channel_prompts
- MessageDeduplicator-backed dedup on Rocket.Chat _id
- Exponential-backoff reconnect (2s -> 60s with jitter); re-login
  and re-subscribe on every reconnect because DDP subs do not resume

Full 16-point ADDING_A_PLATFORM.md wire-up:
- Platform.ROCKETCHAT enum entry + env-override loading in config.py
- Adapter factory branch + auth allowlist maps in run.py
- PLATFORM_HINTS entry so the agent knows it is on Rocket.Chat
- hermes-rocketchat toolset + include in hermes-gateway composite
- Cron delivery platform_map + HOME_CHANNEL env var map
- send_message_tool _send_rocketchat() standalone helper + routing
- hermes_cli PLATFORMS registry, status display, gateway setup wizard
- cronjob_tools deliver schema description

Env var contract (ROCKETCHAT_URL, _TOKEN, _USER_ID, _ALLOWED_USERS,
_ALLOW_ALL_USERS, _HOME_CHANNEL, _HOME_CHANNEL_NAME, _REQUIRE_MENTION,
_FREE_RESPONSE_CHANNELS, _REPLY_MODE) mirrors the Mattermost shape so
profile operators can map existing setup patterns directly.

Tests: 46 new tests in tests/gateway/test_rocketchat.py covering config
loading, format/truncate, send (with mocked aiohttp), DDP message
parsing, dedup, mention behavior, threading, media type detection, and
DDP framing (ping/pong, ready, stream-room-messages dispatch). Full
pytest run shows no regressions against upstream main.

Docs: new website/docs/user-guide/messaging/rocketchat.md setup guide,
plus env-var reference table entries and messaging-index updates
(platform capabilities table, mermaid diagram, next-steps list).

Verified live against a self-hosted Rocket.Chat 8.2 workspace: REST
auth, DDP subscribe, inbound DM parse, outbound chat.postMessage, and
threaded replies all confirmed end-to-end with a real LLM roundtrip.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists labels Apr 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Likely duplicate of #4637 — both add a Rocket.Chat platform adapter. Also addresses feature request #3725.

@cyb0rgk1tty
cyb0rgk1tty force-pushed the feat/rocketchat-adapter branch from cb7ba94 to 368ebe0 Compare April 24, 2026 03:39
Rocket.Chat 6.x replaced the legacy ``<rid>/typing`` DDP method call with
``<rid>/user-activity``, which takes an activity list (``["typing"]`` /
``["recording"]`` / ``["uploading"]`` / ``[]`` to clear). The server still
silently accepts calls to the old ``/typing`` stream — returns
``msg: "result"`` with no error — but modern clients (Rocket.Chat 6.x+,
confirmed against 8.2) no longer subscribe to it, so the "is typing…"
indicator never rendered on the other side.

Switch ``send_typing`` to emit ``[<rid>/user-activity, username, ["typing"]]``
and implement ``stop_typing`` to emit an empty activity list, matching the
current Rocket.Chat client behavior. Verified live against a self-hosted
Rocket.Chat 8.2 instance by inspecting the server's DDP responses to both
variants.

Existing tests still pass unchanged.
@HearthCore

Copy link
Copy Markdown
Contributor

Hey @cyb0rgk1tty, thanks a lot for your work on the Rocket.Chat adapter — your PR #14869 was the foundation we built on. 🙏

We refactored your approach into the modern plugin format (plugins/platforms/rocketchat/, kind: platform) so it requires zero core Hermes changes. It also bundles several fixes and enhancements that we developed while running it live:

Key additions:

  • ✅ DDP reconnect with exponential backoff (2s–60s)
  • ✅ TTS audio pipeline (ffmpeg MP3 conversion for voice messages)
  • ✅ Bidirectional Hermes session title ↔ RC room topic sync
  • ✅ Emoji reactions (👀✅❌) on channel messages
  • ✅ Slash command position-0 fix (no false positives mid-sentence)
  • ✅ is_gateway_known_command() gate to prevent unnecessary 400s
  • ✅ Deferred attachments (file-only uploads merged with next text msg)
  • ✅ AGENTS.md for AI assistant devs + full README
  • ✅ Fixes both bugs reported on the parallel PR feat(gateway): add Rocket.Chat platform adapter #4637 (empty init.py + discover_plugins)

Our PR: #30463

Would love your thoughts on the approach! Happy to collaborate further. 🔥

Also tagging @meron1122 since your PR #4637 was a valuable reference for the plugin structure.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the substantial Rocket.Chat adapter work.

Automated hermes-sweeper review found that this change matches the standing in-tree-provider-integration policy: new third-party/vendor integrations must ship as standalone plugins, rather than add adapter, config, cron, sender, toolset, and setup branches throughout Hermes core.

  • The PR adds the adapter at gateway/platforms/rocketchat.py and core wiring in gateway/config.py, gateway/run.py, cron/scheduler.py, and tools/send_message_tool.py.
  • Current platform guidance explicitly recommends ~/.hermes/plugins/ for community/third-party adapters and states that route requires zero core changes: gateway/platforms/ADDING_A_PLATFORM.md:5-15.
  • Current main already resolves plugin platforms through platform_registry before the legacy adapter chain: gateway/run.py:8652-8688.
  • The linked discussion around feat(plugins): add Rocket.Chat platform adapter as bundled plugin #30463 is useful prior work; the appropriate next step is to publish and maintain Rocket.Chat as a standalone installable plugin repository, promoted through #plugins-skills-and-skins.

This is an automated hermes-sweeper review.


Closed as not-planned per standing maintainer policy (in-tree-provider-integration). This is a design-direction decision, not a code-quality judgment — see the Contribution Rubric in AGENTS.md for what the project is looking for. If you believe this policy was misapplied to your change, comment here and a maintainer will take a look.

@teknium1 teknium1 closed this Jul 12, 2026
@teknium1 teknium1 added the sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) label Jul 12, 2026
@meron1122

Copy link
Copy Markdown

I published plugin powerup with amazing features from @HearthCore and a few more. Feel free to use and contribute
https://github.com/HalfbitStudio/hermes-plugin-rocketchat

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants