Mattermost (plugin)

Estado: compatible vía plugin (token del bot + eventos WebSocket). Canales, grupos y mensajes directos son compatibles. Mattermost es una plataforma de mensajería de equipo auto-hospedable; consulta el sitio oficial en mattermost.com para más detalles y descargas.

Plugin obligatorio

Mattermost se distribuye como plugin y no viene incluido en la instalación base.

Instalación por CLI (registro npm):

openclaw plugins install @openclaw/mattermost

Checkout local (cuando se ejecuta desde un repositorio git):

openclaw plugins install ./extensions/mattermost

Si eliges Mattermost durante la configuración/onboarding y se detecta un checkout de git, OpenClaw ofrecerá la ruta de instalación local automáticamente.

Detalles: Plugins

Configuración rápida

  1. Instala el plugin de Mattermost.
  2. Crea una cuenta de bot en Mattermost y copia el token del bot.
  3. Copia la URL base de Mattermost (p. ej., https://chat.example.com).
  4. Configura OpenClaw e inicia el gateway.

Configuración mínima:

{
  channels: {
    mattermost: {
      enabled: true,
      botToken: "mm-token",
      baseUrl: "https://chat.example.com",
      dmPolicy: "pairing",
    },
  },
}

Comandos slash nativos

Los comandos slash nativos son opcionales. Cuando se activan, OpenClaw registra comandos slash oc_* vía la API de Mattermost y recibe callbacks POST en el servidor HTTP del gateway.

{
  channels: {
    mattermost: {
      commands: {
        native: true,
        nativeSkills: true,
        callbackPath: "/api/channels/mattermost/command",
        // Usa cuando Mattermost no puede alcanzar el gateway directamente (proxy inverso/URL pública).
        callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
      },
    },
  },
}

Notas:

  • native: "auto" está desactivado por defecto para Mattermost. Establece native: true para activarlo.
  • Si se omite callbackUrl, OpenClaw lo deriva del host/puerto del gateway + callbackPath.
  • Para configuraciones multicuenta, commands puede establecerse a nivel superior o bajo channels.mattermost.accounts.<id>.commands (los valores de cuenta sobrescriben los de nivel superior).
  • Los callbacks de comandos se validan con tokens por comando y fallan de forma cerrada cuando las verificaciones de token fallan.
  • Requisito de alcanzabilidad: el endpoint de callback debe ser alcanzable desde el servidor Mattermost.
    • No establezcas callbackUrl a localhost a menos que Mattermost se ejecute en el mismo host/espacio de red que OpenClaw.
    • No establezcas callbackUrl a tu URL base de Mattermost a menos que esa URL haga proxy inverso de /api/channels/mattermost/command hacia OpenClaw.
    • Una verificación rápida es curl https://<gateway-host>/api/channels/mattermost/command; un GET debería devolver 405 Method Not Allowed de OpenClaw, no 404.
  • Requisito de lista de permitidos de egreso de Mattermost:
    • Si tu callback apunta a direcciones privadas/tailnet/internas, establece ServiceSettings.AllowedUntrustedInternalConnections de Mattermost para incluir el host/dominio del callback.
    • Usa entradas de host/dominio, no URLs completas.
      • Correcto: gateway.tailnet-name.ts.net
      • Incorrecto: https://gateway.tailnet-name.ts.net

Variables de entorno (cuenta predeterminada)

Establece estas en el host del gateway si prefieres variables de entorno:

  • MATTERMOST_BOT_TOKEN=...
  • MATTERMOST_URL=https://chat.example.com

Las variables de entorno aplican solo a la cuenta predeterminada (default). Otras cuentas deben usar valores de configuración.

Modos de chat

Mattermost responde a mensajes directos automáticamente. El comportamiento en canales se controla con chatmode:

  • oncall (predeterminado): responde solo cuando se le @menciona en canales.
  • onmessage: responde a todos los mensajes del canal.
  • onchar: responde cuando un mensaje comienza con un prefijo activador.

Ejemplo de configuración:

{
  channels: {
    mattermost: {
      chatmode: "onchar",
      oncharPrefixes: [">", "!"],
    },
  },
}

Notas:

  • onchar sigue respondiendo a @menciones explícitas.
  • channels.mattermost.requireMention se respeta para configuraciones legacy, pero se prefiere chatmode.

Hilos y sesiones

Usa channels.mattermost.replyToMode para controlar si las respuestas en canales y grupos permanecen en el canal principal o inician un hilo bajo el post que activó la respuesta.

  • off (predeterminado): solo responde en un hilo cuando el post entrante ya está en uno.
  • first: para posts de nivel superior en canales/grupos, inicia un hilo bajo ese post y enruta la conversación a una sesión con alcance de hilo.
  • all: mismo comportamiento que first para Mattermost actualmente.
  • Los mensajes directos ignoran esta configuración y permanecen sin hilos.

Ejemplo de configuración:

{
  channels: {
    mattermost: {
      replyToMode: "all",
    },
  },
}

Notas:

  • Las sesiones con alcance de hilo usan el ID del post activador como raíz del hilo.
  • first y all son actualmente equivalentes porque una vez que Mattermost tiene una raíz de hilo, los fragmentos y medios de seguimiento continúan en ese mismo hilo.

Control de acceso (mensajes directos)

  • Por defecto: channels.mattermost.dmPolicy = "pairing" (los remitentes desconocidos reciben un código de emparejamiento).
  • Aprueba mediante:
    • openclaw pairing list mattermost
    • openclaw pairing approve mattermost <CODE>
  • Mensajes directos públicos: channels.mattermost.dmPolicy="open" más channels.mattermost.allowFrom=["*"].

Canales (grupos)

  • Por defecto: channels.mattermost.groupPolicy = "allowlist" (controlado por mención).
  • Lista de permitidos de remitentes con channels.mattermost.groupAllowFrom (se recomiendan IDs de usuario).
  • La coincidencia por @username es mutable y solo se activa cuando channels.mattermost.dangerouslyAllowNameMatching: true.
  • Canales abiertos: channels.mattermost.groupPolicy="open" (controlado por mención).
  • Nota de runtime: si channels.mattermost está completamente ausente, el runtime recurre a groupPolicy="allowlist" para verificaciones de grupo (incluso si channels.defaults.groupPolicy está establecido).

Destinos para entrega saliente

Usa estos formatos de destino con openclaw message send o cron/webhooks:

  • channel:<id> para un canal
  • user:<id> para un mensaje directo
  • @username para un mensaje directo (resuelto vía la API de Mattermost)

Los IDs opacos solos (como 64ifufp...) son ambiguos en Mattermost (ID de usuario vs ID de canal).

OpenClaw los resuelve dando prioridad al usuario:

  • Si el ID existe como usuario (GET /api/v4/users/<id> tiene éxito), OpenClaw envía un mensaje directo resolviendo el canal directo vía /api/v4/channels/direct.
  • De lo contrario, el ID se trata como un ID de canal.

Si necesitas un comportamiento determinístico, usa siempre los prefijos explícitos (user:<id> / channel:<id>).

Reacciones (herramienta de mensaje)

  • Usa message action=react con channel=mattermost.
  • messageId es el ID del post de Mattermost.
  • emoji acepta nombres como thumbsup o :+1: (los dos puntos son opcionales).
  • Establece remove=true (booleano) para eliminar una reacción.
  • Los eventos de añadir/eliminar reacciones se reenvían como eventos del sistema a la sesión del agente enrutado.

Ejemplos:

message action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup
message action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup remove=true

Configuración:

  • channels.mattermost.actions.reactions: activar/desactivar acciones de reacción (por defecto true).
  • Excepción por cuenta: channels.mattermost.accounts.<id>.actions.reactions.

Botones interactivos (herramienta de mensaje)

Envía mensajes con botones clicables. Cuando un usuario hace clic en un botón, el agente recibe la selección y puede responder.

Activa los botones añadiendo inlineButtons a las capacidades del canal:

{
  channels: {
    mattermost: {
      capabilities: ["inlineButtons"],
    },
  },
}

Usa message action=send con un parámetro buttons. Los botones son un array 2D (filas de botones):

message action=send channel=mattermost target=channel:<channelId> buttons=[[{"text":"Yes","callback_data":"yes"},{"text":"No","callback_data":"no"}]]

Campos de botón:

  • text (obligatorio): etiqueta a mostrar.
  • callback_data (obligatorio): valor enviado de vuelta al hacer clic (usado como ID de acción).
  • style (opcional): "default", "primary" o "danger".

Cuando un usuario hace clic en un botón:

  1. Todos los botones se reemplazan con una línea de confirmación (p. ej., “Yes seleccionado por @user”).
  2. El agente recibe la selección como un mensaje entrante y responde.

Notas:

  • Los callbacks de botones usan verificación HMAC-SHA256 (automática, sin configuración necesaria).
  • Mattermost elimina los datos de callback de sus respuestas de API (función de seguridad), por lo que todos los botones se eliminan al hacer clic — la eliminación parcial no es posible.
  • Los IDs de acción que contienen guiones o guiones bajos se sanean automáticamente (limitación de enrutamiento de Mattermost).

Configuración:

  • channels.mattermost.capabilities: array de cadenas de capacidad. Añade "inlineButtons" para activar la descripción de la herramienta de botones en el prompt del sistema del agente.
  • channels.mattermost.interactions.callbackBaseUrl: URL base externa opcional para callbacks de botones (por ejemplo https://gateway.example.com). Úsala cuando Mattermost no pueda alcanzar el gateway en su host de enlace directamente.
  • En configuraciones multicuenta, también puedes establecer el mismo campo bajo channels.mattermost.accounts.<id>.interactions.callbackBaseUrl.
  • Si se omite interactions.callbackBaseUrl, OpenClaw deriva la URL de callback de gateway.customBindHost + gateway.port, luego recurre a http://localhost:<port>.
  • Regla de alcanzabilidad: la URL de callback del botón debe ser alcanzable desde el servidor Mattermost. localhost solo funciona cuando Mattermost y OpenClaw se ejecutan en el mismo host/espacio de red.
  • Si tu destino de callback es privado/tailnet/interno, añade su host/dominio a ServiceSettings.AllowedUntrustedInternalConnections de Mattermost.

Integración directa de API (scripts externos)

Los scripts externos y webhooks pueden publicar botones directamente vía la API REST de Mattermost en lugar de usar la herramienta message del agente. Usa buildButtonAttachments() de la extensión cuando sea posible; si publicas JSON sin procesar, sigue estas reglas:

Estructura del payload:

{
  channel_id: "<channelId>",
  message: "Choose an option:",
  props: {
    attachments: [
      {
        actions: [
          {
            id: "mybutton01", // solo alfanumérico — ver abajo
            type: "button", // obligatorio, o los clics se ignoran silenciosamente
            name: "Approve", // etiqueta a mostrar
            style: "primary", // opcional: "default", "primary", "danger"
            integration: {
              url: "https://gateway.example.com/mattermost/interactions/default",
              context: {
                action_id: "mybutton01", // debe coincidir con el id del botón (para búsqueda de nombre)
                action: "approve",
                // ... campos personalizados ...
                _token: "<hmac>", // ver sección HMAC abajo
              },
            },
          },
        ],
      },
    ],
  },
}

Reglas críticas:

  1. Los adjuntos van en props.attachments, no en attachments de nivel superior (ignorado silenciosamente).
  2. Cada acción necesita type: "button" — sin esto, los clics se pierden silenciosamente.
  3. Cada acción necesita un campo id — Mattermost ignora acciones sin ID.
  4. El id de acción debe ser solo alfanumérico ([a-zA-Z0-9]). Los guiones y guiones bajos rompen el enrutamiento de acciones del lado del servidor de Mattermost (devuelve 404).
  5. context.action_id debe coincidir con el id del botón para que el mensaje de confirmación muestre el nombre del botón (p. ej., “Approve”) en vez de un ID sin procesar.
  6. context.action_id es obligatorio — el manejador de interacciones devuelve 400 sin él.

Generación de token HMAC:

El gateway verifica los clics de botón con HMAC-SHA256. Los scripts externos deben generar tokens que coincidan con la lógica de verificación del gateway:

  1. Deriva el secreto del token del bot: HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)
  2. Construye el objeto de contexto con todos los campos excepto _token.
  3. Serializa con claves ordenadas y sin espacios (el gateway usa JSON.stringify con claves ordenadas, que produce salida compacta).
  4. Firma: HMAC-SHA256(key=secret, data=serializedContext)
  5. Añade el digest hexadecimal resultante como _token en el contexto.

Ejemplo en Python:

import hmac, hashlib, json

secret = hmac.new(
    b"openclaw-mattermost-interactions",
    bot_token.encode(), hashlib.sha256
).hexdigest()

ctx = {"action_id": "mybutton01", "action": "approve"}
payload = json.dumps(ctx, sort_keys=True, separators=(",", ":"))
token = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()

context = {**ctx, "_token": token}

Errores comunes de HMAC:

  • json.dumps de Python añade espacios por defecto ({"key": "val"}). Usa separators=(",", ":") para coincidir con la salida compacta de JavaScript ({"key":"val"}).
  • Firma siempre todos los campos del contexto (menos _token). El gateway elimina _token y luego firma todo lo restante. Firmar un subconjunto causa una falla silenciosa de verificación.
  • Usa sort_keys=True — el gateway ordena las claves antes de firmar, y Mattermost puede reordenar los campos del contexto al almacenar el payload.
  • Deriva el secreto del token del bot (determinístico), no de bytes aleatorios. El secreto debe ser el mismo en el proceso que crea botones y el gateway que los verifica.

Adaptador de directorio

El plugin de Mattermost incluye un adaptador de directorio que resuelve nombres de canales y usuarios vía la API de Mattermost. Esto habilita destinos #channel-name y @username en openclaw message send y entregas de cron/webhooks.

No se necesita configuración — el adaptador usa el token del bot de la configuración de la cuenta.

Multicuenta

Mattermost admite múltiples cuentas bajo channels.mattermost.accounts:

{
  channels: {
    mattermost: {
      accounts: {
        default: { name: "Primary", botToken: "mm-token", baseUrl: "https://chat.example.com" },
        alerts: { name: "Alerts", botToken: "mm-token-2", baseUrl: "https://alerts.example.com" },
      },
    },
  },
}

Solución de problemas

  • Sin respuestas en canales: asegúrate de que el bot esté en el canal y menciónalo (oncall), usa un prefijo activador (onchar) o establece chatmode: "onmessage".
  • Errores de autenticación: verifica el token del bot, la URL base y si la cuenta está activada.
  • Problemas multicuenta: las variables de entorno solo aplican a la cuenta default.
  • Los botones aparecen como cajas blancas: el agente puede estar enviando datos de botón malformados. Verifica que cada botón tenga los campos text y callback_data.
  • Los botones se muestran pero los clics no hacen nada: verifica que AllowedUntrustedInternalConnections en la configuración del servidor Mattermost incluya 127.0.0.1 localhost, y que EnablePostActionIntegration sea true en ServiceSettings.
  • Los botones devuelven 404 al hacer clic: el id del botón probablemente contiene guiones o guiones bajos. El enrutador de acciones de Mattermost falla con IDs no alfanuméricos. Usa solo [a-zA-Z0-9].
  • El gateway registra invalid _token: discrepancia HMAC. Verifica que firmes todos los campos del contexto (no un subconjunto), uses claves ordenadas y uses JSON compacto (sin espacios). Consulta la sección HMAC arriba.
  • El gateway registra missing _token in context: el campo _token no está en el contexto del botón. Asegúrate de incluirlo al construir el payload de integración.
  • La confirmación muestra el ID sin procesar en vez del nombre del botón: context.action_id no coincide con el id del botón. Establece ambos con el mismo valor saneado.
  • El agente no conoce los botones: añade capabilities: ["inlineButtons"] a la configuración del canal Mattermost.