콘텐츠로 이동

OpenClaw Mattermost 연동: 5분 만에 봇 설정 완료하기

Mattermost는 플러그인 형태로 제공되며 코어 설치본에 포함되어 있지 않아요.

CLI를 통해 설치하세요 (npm registry):

Terminal window
openclaw plugins install @openclaw/mattermost

로컬 체크아웃 (git repo에서 실행할 때):

Terminal window
openclaw plugins install ./path/to/local/mattermost-plugin

설정 중에 Mattermost를 선택하고 git 체크아웃이 감지되면, OpenClaw가 자동으로 로컬 설치 경로를 제안해 줍니다.

상세 내용: Plugins

  1. Mattermost 플러그인을 설치하세요.
  2. Mattermost bot 계정을 만들고 bot token을 복사하세요.
  3. Mattermost base URL을 복사하세요 (예: https://chat.example.com).
  4. OpenClaw를 설정하고 Gateway를 시작하세요.

최소 설정:

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

네이티브 슬래시 커맨드 (Native slash commands)

섹션 제목: “네이티브 슬래시 커맨드 (Native slash commands)”

네이티브 슬래시 커맨드는 선택 사항이에요. 활성화하면 OpenClaw가 Mattermost API를 통해 oc_* 슬래시 커맨드를 등록하고, Gateway HTTP 서버에서 콜백 POST를 받아요.

{
channels: {
mattermost: {
commands: {
native: true,
nativeSkills: true,
callbackPath: "/api/channels/mattermost/command",
// Use when Mattermost cannot reach the gateway directly (reverse proxy/public URL).
callbackUrl: "https://gateway.example.com/api/channels/mattermost/command",
},
},
},
}

참고 사항:

  • Mattermost에서 native: "auto"는 기본적으로 비활성화 상태예요. 활성화하려면 native: true로 설정하세요.
  • callbackUrl을 생략하면 OpenClaw가 Gateway host/port와 callbackPath를 조합해서 자동으로 생성해요.
  • 다중 계정 설정의 경우, commands를 최상위 레벨이나 channels.mattermost.accounts.<id>.commands 아래에 설정할 수 있어요 (계정별 설정이 최상위 설정을 덮어써요).
  • 커맨드 콜백은 커맨드별 토큰으로 검증되며, 토큰 확인에 실패하면 요청을 차단해요.
  • 연결성 요구 사항: 콜백 엔드포인트는 Mattermost 서버에서 접속할 수 있어야 해요.
    • Mattermost가 OpenClaw와 동일한 호스트나 네트워크 네임스페이스에서 실행되지 않는 한 callbackUrl을 localhost로 설정하지 마세요.
    • 해당 URL이 /api/channels/mattermost/command를 OpenClaw로 리버스 프록시하지 않는다면 callbackUrl을 Mattermost base URL로 설정하지 마세요.
    • curl https://<gateway-host>/api/channels/mattermost/command 명령어로 간단히 확인할 수 있어요. GET 요청 시 OpenClaw에서 404가 아닌 405 Method Not Allowed를 반환해야 해요.
  • Mattermost egress 허용 목록 요구 사항:
    • 콜백 대상이 프라이빗/tailnet/내부 주소인 경우, Mattermost의 ServiceSettings.AllowedUntrustedInternalConnections에 콜백 host/domain을 포함하도록 설정하세요.
    • 전체 URL이 아닌 host/domain 항목을 사용하세요.
      • 올바른 예: gateway.tailnet-name.ts.net
      • 잘못된 예: https://gateway.tailnet-name.ts.net

환경 변수 (기본 계정) (Environment variables (default account))

섹션 제목: “환경 변수 (기본 계정) (Environment variables (default account))”

환경 변수를 선호한다면 Gateway 호스트에 다음 항목들을 설정하세요:

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

환경 변수는 기본 계정(default)에만 적용돼요. 다른 계정들은 반드시 설정 파일의 값을 사용해야 해요.

Mattermost는 DM에 자동으로 응답해요. 채널에서의 동작은 chatmode로 제어할 수 있어요.

  • oncall (기본값): 채널에서 @mention 되었을 때만 응답해요.
  • onmessage: 채널의 모든 메시지에 응답해요.
  • onchar: 메시지가 특정 트리거 접두사(trigger prefix)로 시작할 때 응답해요.

설정 예시:

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

참고 사항:

  • onchar 모드에서도 직접적인 @mention에는 여전히 응답해요.
  • 레거시 설정을 위해 channels.mattermost.requireMention도 지원하지만, chatmode를 사용하는 쪽을 더 추천해요.

channels.mattermost.replyToMode를 사용하면 채널이나 그룹의 답장을 메인 채널에 유지할지, 아니면 메시지 아래에 스레드를 새로 시작할지 결정할 수 있어요.

  • off (기본값): 수신된 메시지가 이미 스레드 안에 있는 경우에만 스레드로 답장해요.
  • first: 채널이나 그룹의 최상위 메시지에 대해 스레드를 시작하고, 대화를 스레드 범위의 세션으로 연결해요.
  • all: 현재 Mattermost에서는 first와 동일하게 동작해요.
  • DM은 이 설정을 무시하며 스레드를 사용하지 않는 상태를 유지해요.

설정 예시:

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

참고 사항:

  • 스레드 범위의 세션은 트리거가 된 메시지 ID를 스레드 루트로 사용해요.
  • Mattermost에서 일단 스레드 루트가 생성되면 이후의 메시지나 미디어는 해당 스레드에서 계속 이어지기 때문에, 현재 first와 all은 기능적으로 같아요.
  • 기본값: channels.mattermost.dmPolicy = "pairing" (알 수 없는 발신자에게는 페어링 코드를 보냅니다).
  • 승인 방법:
    • openclaw pairing list mattermost
    • openclaw pairing approve mattermost <CODE>
  • 공개 DM: channels.mattermost.dmPolicy="open" 설정과 함께 channels.mattermost.allowFrom=["*"]를 사용하세요.
  • 기본값: channels.mattermost.groupPolicy = "allowlist" (멘션이 있어야 접근 가능합니다).
  • channels.mattermost.groupAllowFrom을 통해 발신자를 허용 리스트에 추가하세요 (User ID 사용을 추천해요).
  • @username 매칭은 변경될 수 있으며, channels.mattermost.dangerouslyAllowNameMatching: true일 때만 활성화돼요.
  • 공개 채널: channels.mattermost.groupPolicy="open" (멘션 기반).
  • 런타임 참고 사항: 만약 channels.mattermost 설정이 완전히 누락된 경우, channels.defaults.groupPolicy가 설정되어 있더라도 그룹 체크를 위해 groupPolicy="allowlist"로 돌아가요.

외부 전송을 위한 대상(Target) 설정

섹션 제목: “외부 전송을 위한 대상(Target) 설정”

openclaw message send 명령어나 cron/webhooks를 사용할 때 다음과 같은 대상 형식을 사용하세요:

  • channel:<id>: 특정 채널을 대상으로 해요.
  • user:<id> 또는 @username: DM을 보낼 때 사용하며, username은 Mattermost API를 통해 확인돼요.

Mattermost에서 64ifufp...와 같은 단순한 ID는 사용자 ID인지 채널 ID인지 모호할 수 있어요.

OpenClaw는 이를 사용자 우선(user-first) 방식으로 해결해요:

  • 해당 ID가 사용자로 존재하면 (GET /api/v4/users/<id> 성공 시), OpenClaw는 /api/v4/channels/direct를 통해 다이렉트 채널을 확인하고 DM을 보내요.
  • 사용자가 존재하지 않으면 해당 ID를 채널 ID로 간주해요.

예측 가능한 동작을 원한다면 항상 명시적인 접두사(user:<id> 또는 channel:<id>)를 사용하는 것을 추천해요.

OpenClaw가 Mattermost DM 대상으로 메시지를 보낼 때 다이렉트 채널을 먼저 확인해야 하는 경우가 있어요. 이때 발생하는 일시적인 다이렉트 채널 생성 실패에 대해 기본적으로 재시도를 수행해요.

Mattermost 플러그인 전체에 대해 이 동작을 설정하려면 channels.mattermost.dmChannelRetry를 사용하고, 특정 계정에만 적용하려면 channels.mattermost.accounts.<id>.dmChannelRetry를 설정하세요.

{
channels: {
mattermost: {
dmChannelRetry: {
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 10000,
timeoutMs: 30000,
},
},
},
}

참고 사항:

  • 이 설정은 모든 Mattermost API 호출이 아니라 DM 채널 생성(/api/v4/channels/direct) 과정에서 발생하는 rate limits, 5xx 응답, 네트워크 오류 등의 일시적 실패에만 적용돼요.
  • 429를 제외한 4xx 클라이언트 오류는 영구적인 실패로 처리되어 재시도하지 않아요.
  • channel=mattermost와 함께 message action=react를 사용하세요.
  • messageId는 Mattermost의 포스트 ID예요.
  • emoji는 thumbsup이나 :+1: 같은 이름을 사용할 수 있어요 (콜론은 선택 사항이에요).
  • 리액션을 삭제하려면 remove=true (boolean)를 설정하세요.
  • 리액션 추가/삭제 이벤트는 시스템 이벤트로 라우팅된 에이전트 세션에 전달돼요.

Examples:

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: 리액션 액션 활성화/비활성화 (기본값 true).
  • 계정별 오버라이드: channels.mattermost.accounts.<id>.actions.reactions.

클릭 가능한 버튼이 포함된 메시지를 보내보세요. 사용자가 버튼을 클릭하면 에이전트가 선택 내용을 수신하고 응답할 수 있어요.

채널 capabilities에 inlineButtons를 추가하여 버튼을 활성화하세요:

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

buttons 파라미터와 함께 message action=send를 사용하세요. 버튼은 2D 배열(버튼의 행) 형태예요:

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

버튼 필드:

  • text (필수): 표시될 라벨이에요.
  • callback_data (필수): 클릭 시 다시 전송될 값이에요 (액션 ID로 사용돼요).
  • style (선택): "default", "primary", 또는 "danger"를 사용할 수 있어요.

사용자가 버튼을 클릭하면:

  1. 모든 버튼이 확인 문구로 교체돼요 (예: ”✓ Yes selected by @user”).
  2. 에이전트는 선택 내용을 인바운드 메시지로 수신하고 응답해요.

참고 사항:

  • 버튼 콜백은 HMAC-SHA256 검증을 사용해요 (자동으로 처리되며 별도 설정이 필요 없어요).
  • Mattermost는 보안을 위해 API 응답에서 콜백 데이터를 제거해요. 따라서 클릭 시 모든 버튼이 삭제되며, 일부만 삭제하는 것은 불가능해요.
  • 하이픈이나 언더바가 포함된 액션 ID는 자동으로 정리돼요 (Mattermost 라우팅 제한 때문이에요).

Config:

  • channels.mattermost.capabilities: capability 문자열 배열이에요. 에이전트 시스템 프롬프트에서 버튼 도구 설명을 활성화하려면 "inlineButtons"를 추가하세요.
  • channels.mattermost.interactions.callbackBaseUrl: 버튼 콜백을 위한 선택적인 외부 베이스 URL이에요 (예: https://gateway.example.com). Mattermost가 바인드 호스트에서 Gateway에 직접 도달할 수 없을 때 사용하세요.
  • 다중 계정 설정에서는 channels.mattermost.accounts.<id>.interactions.callbackBaseUrl 아래에 동일한 필드를 설정할 수 있어요.
  • interactions.callbackBaseUrl을 생략하면, OpenClaw는 gateway.customBindHost + gateway.port에서 콜백 URL을 도출하고, 그 다음으로 http://localhost:<port>를 시도해요.
  • 도달 가능성 규칙: 버튼 콜백 URL은 Mattermost 서버에서 접근 가능해야 해요. localhost는 Mattermost와 OpenClaw가 동일한 호스트나 네트워크 네임스페이스에서 실행될 때만 작동해요.
  • 콜백 대상이 프라이빗/tailnet/내부망인 경우, 해당 호스트나 도메인을 Mattermost의 ServiceSettings.AllowedUntrustedInternalConnections에 추가하세요.

외부 스크립트나 웹훅은 에이전트의 message 도구를 거치지 않고 Mattermost REST API를 통해 버튼을 직접 게시할 수 있어요. 가능하면 확장에서 제공하는 buildButtonAttachments()를 사용하세요. 원시 JSON을 게시할 때는 다음 규칙을 따르세요:

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
},
},
},
],
},
],
},
}

중요 규칙:

  1. 첨부 파일은 최상위 attachments가 아니라 props.attachments에 넣어야 해요 (그렇지 않으면 무시돼요).
  2. 모든 액션에는 type: "button"이 필요해요. 이게 없으면 클릭이 무시돼요.
  3. 모든 액션에는 id 필드가 필요해요. Mattermost는 ID가 없는 액션을 무시해요.
  4. 액션 id는 반드시 영문자와 숫자([a-zA-Z0-9])로만 구성되어야 해요. 하이픈이나 언더바는 Mattermost의 서버 측 액션 라우팅을 깨뜨려 404 에러를 발생시켜요. 사용 전에 이를 제거하세요.
  5. context.action_id는 버튼의 id와 일치해야 확인 메시지에 원시 ID 대신 버튼 이름(예: “Approve”)이 표시돼요.
  6. context.action_id는 필수예요. 이게 없으면 인터랙션 핸들러가 400 에러를 반환해요.

HMAC 토큰 생성:

Gateway는 HMAC-SHA256으로 버튼 클릭을 검증해요. 외부 스크립트는 Gateway의 검증 로직과 일치하는 토큰을 생성해야 해요:

  1. 봇 토큰에서 비밀키를 도출하세요: HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)
  2. _token을 제외한 모든 필드로 context 객체를 만드세요.
  3. 키를 정렬하고 공백 없이 직렬화하세요 (Gateway는 키가 정렬된 JSON.stringify를 사용하여 콤팩트한 출력을 생성해요).
  4. 서명하세요: HMAC-SHA256(key=secret, data=serializedContext)
  5. 결과물인 hex digest를 context의 _token으로 추가하세요.

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}

흔히 발생하는 HMAC 실수:

  • Python의 json.dumps는 기본적으로 공백을 추가해요 ({"key": "val"}). JavaScript의 콤팩트한 출력({"key":"val"})과 맞추기 위해 separators=(",", ":")를 사용하세요.
  • 항상 _token을 제외한 모든 context 필드에 서명하세요. Gateway는 _token을 제거한 후 남은 모든 것에 서명해요. 일부만 서명하면 검증에 실패해요.
  • sort_keys=True를 사용하세요. Gateway는 서명 전에 키를 정렬하며, Mattermost가 페이로드를 저장할 때 context 필드 순서를 바꿀 수도 있어요.
  • 비밀키는 랜덤 바이트가 아니라 봇 토큰에서 도출하세요 (결정론적 방식). 버튼을 생성하는 프로세스와 이를 검증하는 Gateway 간에 비밀키가 동일해야 해요.

Mattermost 플러그인에는 Directory adapter가 포함되어 있어요. 이 어댑터는 Mattermost API를 통해 채널과 사용자 이름을 확인해 줍니다. 덕분에 openclaw message send 명령어나 cron, webhook으로 메시지를 보낼 때 복잡한 ID 대신 #channel-name이나 @username을 직접 대상으로 지정할 수 있어 정말 편해요.

따로 설정할 부분은 없어요. 어댑터가 계정 설정에 등록된 bot token을 자동으로 사용하거든요.

Mattermost는 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" },
},
},
},
}
  • 채널에서 응답이 없는 경우: 봇이 채널에 들어와 있는지 확인하고 멘션(oncall)하거나, 트리거 접두사(onchar)를 사용해 보세요. 또는 chatmode: "onmessage"로 설정되어 있는지 확인이 필요해요.
  • 인증 에러(Auth errors): 봇 토큰과 base URL을 확인하고, 계정이 활성화 상태인지 체크해 주세요.
  • 다중 계정 문제: 환경 변수는 default 계정에만 적용된다는 점을 유의하세요.
  • 버튼이 하얀색 박스로 보이는 경우: 에이전트가 잘못된 버튼 데이터를 보내고 있을 수 있어요. 각 버튼에 text와 callback_data 필드가 모두 포함되어 있는지 확인해 보세요.
  • 버튼은 렌더링되지만 클릭해도 반응이 없는 경우: Mattermost 서버 설정의 AllowedUntrustedInternalConnections에 127.0.0.1 localhost가 포함되어 있는지, 그리고 ServiceSettings에서 EnablePostActionIntegration이 true인지 확인해 주세요.
  • 클릭 시 404 에러가 발생하는 경우: 버튼 id에 하이픈(-)이나 언더바(_)가 포함되었을 가능성이 커요. Mattermost의 액션 라우터는 알파벳과 숫자가 아닌 ID에서 오류가 발생하거든요. [a-zA-Z0-9]만 사용해 주세요.
  • Gateway 로그에 invalid _token이 찍히는 경우: HMAC 불일치 문제예요. 모든 context 필드에 서명했는지, 키를 정렬했는지, 공백 없는 compact JSON을 사용했는지, 그리고 위쪽의 HMAC 섹션 내용을 잘 따랐는지 확인해 보세요.
  • Gateway 로그에 missing _token in context가 찍히는 경우: 버튼의 context에 _token 필드가 누락되었어요. 통합 페이로드를 빌드할 때 이 필드가 포함되었는지 확인해 주세요.
  • 확인 창에 버튼 이름 대신 가공되지 않은 ID가 표시되는 경우: context.action_id가 버튼의 id와 일치하지 않아서 생기는 문제예요. 두 값을 동일하고 깔끔한(sanitized) 값으로 설정해 주세요.
  • 에이전트가 버튼 기능을 인식하지 못하는 경우: Mattermost 채널 설정에 capabilities: ["inlineButtons"]를 추가해 보세요.

AI Setup Assistant

OpenClaw

OpenClaw Expert

아직 막혀 있나요?

이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.