Files
2026-05-05 06:05:47 +02:00

124 lines
4.4 KiB
Python

"""
Rocket.Chat Platform Plugin for Hermes Agent.
Registers a gateway platform adapter that connects to a self-hosted
Rocket.Chat server via DDP/WebSocket (real-time incoming messages + presence)
and REST API (outgoing replies).
"""
import logging
import os
logger = logging.getLogger(__name__)
# ── Interaction Templates (source of truth — zero yaml dependency) ──
_TEMPLATE_MAP = {
"yes_no": {
"label": "Ja/Nein",
"description": "Binary confirmation",
"attachment": {"color": "#1d74f5", "title": "Bitte bestätigen:"},
"buttons": [
{"text": "✅ Ja", "msg": "ja", "style": "primary"},
{"text": "❌ Nein", "msg": "nein", "style": "danger"},
],
},
"confirm_cancel": {
"label": "Bestätigen/Abbrechen",
"description": "Safe action with abort",
"attachment": {"color": "#1d74f5", "title": "Aktion bestätigen:"},
"buttons": [
{"text": "✅ Bestätigen", "msg": "bestätigen", "style": "primary"},
{"text": "🚫 Abbrechen", "msg": "abbrechen", "style": "danger"},
],
},
"ok": {
"label": "OK",
"description": "Single acknowledgement",
"attachment": {"color": "#1d74f5", "title": "Hinweis:"},
"buttons": [
{"text": "👍 OK", "msg": "ok", "style": "primary"},
],
},
"multi_choice_3": {
"label": "3 Optionen",
"description": "Three-option selection",
"attachment": {"color": "#1d74f5", "title": "Wähle eine Option:"},
"buttons": [
{"text": "1._Links", "msg": "links", "style": "default"},
{"text": "2._Rechts", "msg": "rechts", "style": "default"},
{"text": "3._Zwei_Runden", "msg": "zwei_runden", "style": "default"},
],
},
}
def _build_custom_buttons(labels: list, prefix: str = "") -> list:
"""Build Rocket.Chat action buttons from plain label strings.
Rocket.Chat UI truncates button text after ~11-12 chars.
Use short labels (emoji+text without spaces, or numbered refs).
"""
buttons = []
for idx, label in enumerate(labels):
# Sanitize label for button text (keep short)
display = label.strip()
# Payload may carry question_id prefix for correlation
msg_payload = display
if prefix:
msg_payload = f"{prefix}::{msg_payload}"
buttons.append({
"type": "button",
"text": display,
"msg": msg_payload,
"msg_in_chat_window": True,
"style": "default",
})
return buttons
def check_requirements():
"""Rocket.Chat adapter uses only stdlib (urllib, socket, ssl, threading)."""
return True
def validate_config(config) -> bool:
"""Check whether the platform is properly configured."""
extra = getattr(config, "extra", {}) or {}
base_url = os.getenv("ROCKETCHAT_BASE_URL") or extra.get("base_url", "")
user = os.getenv("ROCKETCHAT_USER") or extra.get("user", "")
password = os.getenv("ROCKETCHAT_PASSWORD") or extra.get("password", "")
return bool(base_url and user and password)
def is_connected(config) -> bool:
"""Check whether Rocket.Chat is configured (env or config.yaml)."""
return validate_config(config)
def register(ctx):
"""Plugin entry point — called by the Hermes plugin system at startup."""
from .adapter import RocketChatAdapter # lazy import avoids circular deps
ctx.register_platform(
name="rocketchat",
label="Rocket.Chat",
adapter_factory=lambda cfg: RocketChatAdapter(cfg),
check_fn=check_requirements,
validate_config=validate_config,
is_connected=is_connected,
required_env=["ROCKETCHAT_BASE_URL", "ROCKETCHAT_USER", "ROCKETCHAT_PASSWORD"],
install_hint="No extra packages needed (stdlib only)",
allowed_users_env="ROCKETCHAT_ALLOWED_USERS",
allow_all_env="ROCKETCHAT_ALLOW_ALL_USERS",
max_message_length=4000,
emoji="🚀",
pii_safe=False,
allow_update_command=True,
platform_hint=(
"You are chatting via Rocket.Chat. You support markdown formatting: "
"**bold**, *italic*, `inline code`, ```code blocks```, and [links](url). "
"Messages can be up to ~4000 characters. You have a green online dot "
"via DDP presence. Use professional but friendly tone."
),
)