Mattermost (plugin)

Status: suportado via plugin (token de bot + eventos WebSocket). Canais, grupos e DMs são suportados. O Mattermost é uma plataforma de mensagens para equipes auto-hospedável; consulte o site oficial em mattermost.com para detalhes do produto e downloads.

Plugin necessário

O Mattermost é distribuído como plugin e não vem incluído na instalação principal.

Instale via CLI (registro npm):

openclaw plugins install @openclaw/mattermost

Checkout local (ao executar de um repositório git):

openclaw plugins install ./extensions/mattermost

Se você escolher o Mattermost durante a configuração/onboarding e um checkout git for detectado, o OpenClaw oferecerá o caminho de instalação local automaticamente.

Detalhes: Plugins

Configuração rápida

  1. Instale o plugin do Mattermost.
  2. Crie uma conta de bot no Mattermost e copie o token do bot.
  3. Copie a URL base do Mattermost (ex.: https://chat.example.com).
  4. Configure o OpenClaw e inicie o gateway.

Configuração mínima:

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

Comandos slash nativos

Comandos slash nativos são opt-in. Quando ativados, o OpenClaw registra comandos slash oc_* via API do Mattermost e recebe POSTs de callback no servidor HTTP do gateway.

{
  channels: {
    mattermost: {
      commands: {
        native: true,
        nativeSkills: true,
        callbackPath: "/api/channels/mattermost/command",
        // Use quando o Mattermost não consegue acessar o gateway diretamente (proxy reverso/URL pública).
        callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
      },
    },
  },
}

Observações:

  • native: "auto" tem como padrão desativado para o Mattermost. Defina native: true para ativar.
  • Se callbackUrl for omitido, o OpenClaw deriva um a partir de host/porta do gateway + callbackPath.
  • Para configurações multiconta, commands pode ser definido no nível superior ou em channels.mattermost.accounts.<id>.commands (valores da conta sobrescrevem campos de nível superior).
  • Callbacks de comando são validados com tokens por comando e falham fechados quando a verificação de token falha.
  • Requisito de acessibilidade: o endpoint de callback deve ser acessível pelo servidor Mattermost.
    • Não defina callbackUrl como localhost a menos que o Mattermost rode no mesmo host/namespace de rede que o OpenClaw.
    • Não defina callbackUrl como sua URL base do Mattermost a menos que essa URL faça proxy reverso de /api/channels/mattermost/command para o OpenClaw.
    • Uma verificação rápida é curl https://<gateway-host>/api/channels/mattermost/command; um GET deve retornar 405 Method Not Allowed do OpenClaw, não 404.
  • Requisito de egress allowlist do Mattermost:
    • Se seu callback aponta para endereços privados/tailnet/internos, defina ServiceSettings.AllowedUntrustedInternalConnections do Mattermost para incluir o host/domínio do callback.
    • Use entradas de host/domínio, não URLs completas.
      • Correto: gateway.tailnet-name.ts.net
      • Incorreto: https://gateway.tailnet-name.ts.net

Variáveis de ambiente (conta padrão)

Defina estas no host do gateway se preferir variáveis de ambiente:

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

Variáveis de ambiente se aplicam apenas à conta padrão (default). Outras contas devem usar valores de configuração.

Modos de chat

O Mattermost responde a DMs automaticamente. O comportamento em canais é controlado por chatmode:

  • oncall (padrão): responder apenas quando @mencionado em canais.
  • onmessage: responder a todas as mensagens em canais.
  • onchar: responder quando uma mensagem começa com um prefixo gatilho.

Exemplo de configuração:

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

Observações:

  • onchar ainda responde a @menções explícitas.
  • channels.mattermost.requireMention é respeitado para configs legadas, mas chatmode é preferido.

Threading e sessões

Use channels.mattermost.replyToMode para controlar se as respostas em canais e grupos ficam no canal principal ou iniciam uma thread sob o post de acionamento.

  • off (padrão): só responde em thread quando o post de entrada já está em uma.
  • first: para posts de nível superior em canais/grupos, inicia uma thread sob esse post e roteia a conversa para uma sessão com escopo de thread.
  • all: mesmo comportamento que first para o Mattermost atualmente.
  • Mensagens diretas ignoram essa configuração e permanecem sem threading.

Exemplo de configuração:

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

Observações:

  • Sessões com escopo de thread usam o ID do post de acionamento como raiz da thread.
  • first e all são atualmente equivalentes porque, uma vez que o Mattermost tem uma raiz de thread, os chunks subsequentes e mídias continuam nessa mesma thread.

Controle de acesso (DMs)

  • Padrão: channels.mattermost.dmPolicy = "pairing" (remetentes desconhecidos recebem um código de pareamento).
  • Aprove via:
    • openclaw pairing list mattermost
    • openclaw pairing approve mattermost <CODE>
  • DMs públicas: channels.mattermost.dmPolicy="open" mais channels.mattermost.allowFrom=["*"].

Canais (grupos)

  • Padrão: channels.mattermost.groupPolicy = "allowlist" (filtrado por menção).
  • Permita remetentes com channels.mattermost.groupAllowFrom (IDs de usuário recomendados).
  • Correspondência por @username é mutável e só é ativada quando channels.mattermost.dangerouslyAllowNameMatching: true.
  • Canais abertos: channels.mattermost.groupPolicy="open" (filtrado por menção).
  • Observação de runtime: se channels.mattermost estiver completamente ausente, o runtime recorre a groupPolicy="allowlist" para verificações de grupo (mesmo que channels.defaults.groupPolicy esteja definido).

Alvos para entrega de saída

Use estes formatos de alvo com openclaw message send ou cron/webhooks:

  • channel:<id> para um canal
  • user:<id> para uma DM
  • @username para uma DM (resolvido via API do Mattermost)

IDs opacos soltos (como 64ifufp...) são ambíguos no Mattermost (ID de usuário vs ID de canal).

O OpenClaw os resolve priorizando usuário:

  • Se o ID existir como usuário (GET /api/v4/users/<id> retorna sucesso), o OpenClaw envia uma DM resolvendo o canal direto via /api/v4/channels/direct.
  • Caso contrário, o ID é tratado como ID de canal.

Se precisar de comportamento determinístico, sempre use os prefixos explícitos (user:<id> / channel:<id>).

Reações (ferramenta de mensagem)

  • Use message action=react com channel=mattermost.
  • messageId é o ID do post no Mattermost.
  • emoji aceita nomes como thumbsup ou :+1: (dois pontos são opcionais).
  • Defina remove=true (booleano) para remover uma reação.
  • Eventos de adicionar/remover reação são encaminhados como eventos de sistema para a sessão roteada do agente.

Exemplos:

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

Config:

  • channels.mattermost.actions.reactions: ativar/desativar ações de reação (padrão true).
  • Override por conta: channels.mattermost.accounts.<id>.actions.reactions.

Botões interativos (ferramenta de mensagem)

Envie mensagens com botões clicáveis. Quando um usuário clica em um botão, o agente recebe a seleção e pode responder.

Ative botões adicionando inlineButtons às capacidades do canal:

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

Use message action=send com um parâmetro buttons. Botões são um array 2D (linhas de botões):

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

Campos do botão:

  • text (obrigatório): rótulo de exibição.
  • callback_data (obrigatório): valor enviado de volta ao clicar (usado como ID da ação).
  • style (opcional): "default", "primary" ou "danger".

Quando um usuário clica em um botão:

  1. Todos os botões são substituídos por uma linha de confirmação (ex.: ”✓ Yes selected by @user”).
  2. O agente recebe a seleção como mensagem de entrada e responde.

Observações:

  • Callbacks de botão usam verificação HMAC-SHA256 (automática, sem configuração necessária).
  • O Mattermost remove os dados de callback de suas respostas de API (recurso de segurança), então todos os botões são removidos ao clicar — remoção parcial não é possível.
  • IDs de ação contendo hífens ou underscores são sanitizados automaticamente (limitação de roteamento do Mattermost).

Config:

  • channels.mattermost.capabilities: array de strings de capacidade. Adicione "inlineButtons" para ativar a descrição da ferramenta de botões no prompt de sistema do agente.
  • channels.mattermost.interactions.callbackBaseUrl: URL base externa opcional para callbacks de botão (por exemplo https://gateway.example.com). Use quando o Mattermost não consegue acessar o gateway no seu host de bind diretamente.
  • Em configurações multiconta, você também pode definir o mesmo campo em channels.mattermost.accounts.<id>.interactions.callbackBaseUrl.
  • Se interactions.callbackBaseUrl for omitido, o OpenClaw deriva a URL de callback de gateway.customBindHost + gateway.port, depois recorre a http://localhost:<port>.
  • Regra de acessibilidade: a URL de callback do botão deve ser acessível pelo servidor Mattermost. localhost só funciona quando Mattermost e OpenClaw rodam no mesmo host/namespace de rede.
  • Se seu alvo de callback é privado/tailnet/interno, adicione seu host/domínio ao ServiceSettings.AllowedUntrustedInternalConnections do Mattermost.

Integração direta com API (scripts externos)

Scripts externos e webhooks podem postar botões diretamente via API REST do Mattermost em vez de passar pela ferramenta message do agente. Use buildButtonAttachments() da extensão quando possível; se postar JSON bruto, siga estas regras:

Estrutura do payload:

{
  channel_id: "<channelId>",
  message: "Choose an option:",
  props: {
    attachments: [
      {
        actions: [
          {
            id: "mybutton01", // alphanumeric only — see below
            type: "button", // required, or clicks are silently ignored
            name: "Approve", // display label
            style: "primary", // optional: "default", "primary", "danger"
            integration: {
              url: "https://gateway.example.com/mattermost/interactions/default",
              context: {
                action_id: "mybutton01", // must match button id (for name lookup)
                action: "approve",
                // ... any custom fields ...
                _token: "<hmac>", // see HMAC section below
              },
            },
          },
        ],
      },
    ],
  },
}

Regras críticas:

  1. Attachments vão em props.attachments, não em attachments de nível superior (ignorado silenciosamente).
  2. Toda ação precisa de type: "button" — sem isso, cliques são engolidos silenciosamente.
  3. Toda ação precisa de um campo id — o Mattermost ignora ações sem IDs.
  4. O id da ação deve ser apenas alfanumérico ([a-zA-Z0-9]). Hífens e underscores quebram o roteamento de ações do lado do servidor do Mattermost (retorna 404). Remova-os antes de usar.
  5. context.action_id deve corresponder ao id do botão para que a mensagem de confirmação mostre o nome do botão (ex.: “Approve”) em vez de um ID bruto.
  6. context.action_id é obrigatório — o handler de interação retorna 400 sem ele.

Geração de token HMAC:

O gateway verifica cliques de botão com HMAC-SHA256. Scripts externos devem gerar tokens que correspondam à lógica de verificação do gateway:

  1. Derive o segredo do token do bot: HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)
  2. Construa o objeto de contexto com todos os campos exceto _token.
  3. Serialize com chaves ordenadas e sem espaços (o gateway usa JSON.stringify com chaves ordenadas, que produz saída compacta).
  4. Assine: HMAC-SHA256(key=secret, data=serializedContext)
  5. Adicione o digest hex resultante como _token no contexto.

Exemplo em 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}

Armadilhas comuns de HMAC:

  • O json.dumps do Python adiciona espaços por padrão ({"key": "val"}). Use separators=(",", ":") para corresponder à saída compacta do JavaScript ({"key":"val"}).
  • Sempre assine todos os campos de contexto (menos _token). O gateway remove _token e assina tudo que resta. Assinar um subconjunto causa falha de verificação silenciosa.
  • Use sort_keys=True — o gateway ordena as chaves antes de assinar, e o Mattermost pode reordenar campos de contexto ao armazenar o payload.
  • Derive o segredo do token do bot (determinístico), não de bytes aleatórios. O segredo deve ser o mesmo entre o processo que cria botões e o gateway que verifica.

Adaptador de diretório

O plugin do Mattermost inclui um adaptador de diretório que resolve nomes de canais e usuários via API do Mattermost. Isso permite alvos #channel-name e @username em openclaw message send e entregas por cron/webhook.

Nenhuma configuração é necessária — o adaptador usa o token do bot da configuração da conta.

Multiconta

O Mattermost suporta múltiplas contas em 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" },
      },
    },
  },
}

Solução de problemas

  • Sem respostas em canais: verifique se o bot está no canal e mencione-o (oncall), use um prefixo gatilho (onchar) ou defina chatmode: "onmessage".
  • Erros de autenticação: verifique o token do bot, a URL base e se a conta está ativada.
  • Problemas multiconta: variáveis de ambiente se aplicam apenas à conta default.
  • Botões aparecem como caixas brancas: o agente pode estar enviando dados de botão malformados. Verifique se cada botão tem ambos os campos text e callback_data.
  • Botões renderizam mas cliques não fazem nada: verifique se AllowedUntrustedInternalConnections na configuração do servidor Mattermost inclui 127.0.0.1 localhost, e que EnablePostActionIntegration é true em ServiceSettings.
  • Botões retornam 404 ao clicar: o id do botão provavelmente contém hífens ou underscores. O roteador de ações do Mattermost quebra com IDs não alfanuméricos. Use apenas [a-zA-Z0-9].
  • Gateway registra invalid _token: incompatibilidade de HMAC. Verifique se você assina todos os campos de contexto (não um subconjunto), usa chaves ordenadas e usa JSON compacto (sem espaços). Veja a seção de HMAC acima.
  • Gateway registra missing _token in context: o campo _token não está no contexto do botão. Certifique-se de que está incluído ao construir o payload de integração.
  • Confirmação mostra ID bruto em vez do nome do botão: context.action_id não corresponde ao id do botão. Defina ambos com o mesmo valor sanitizado.
  • Agente não sabe sobre botões: adicione capabilities: ["inlineButtons"] à configuração do canal Mattermost.