OpenClaw Mattermost 연동: 5분 만에 봇 설정 완료하기
플러그인 설치 (Plugin required)
섹션 제목: “플러그인 설치 (Plugin required)”Mattermost는 플러그인 형태로 제공되며 코어 설치본에 포함되어 있지 않아요.
CLI를 통해 설치하세요 (npm registry):
openclaw plugins install @openclaw/mattermost로컬 체크아웃 (git repo에서 실행할 때):
openclaw plugins install ./path/to/local/mattermost-plugin설정 중에 Mattermost를 선택하고 git 체크아웃이 감지되면, OpenClaw가 자동으로 로컬 설치 경로를 제안해 줍니다.
상세 내용: Plugins
빠른 설정 (Quick setup)
섹션 제목: “빠른 설정 (Quick setup)”- Mattermost 플러그인을 설치하세요.
- Mattermost bot 계정을 만들고 bot token을 복사하세요.
- Mattermost base URL을 복사하세요 (예:
https://chat.example.com). - 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가 OpenClaw와 동일한 호스트나 네트워크 네임스페이스에서 실행되지 않는 한
- Mattermost egress 허용 목록 요구 사항:
- 콜백 대상이 프라이빗/tailnet/내부 주소인 경우, Mattermost의
ServiceSettings.AllowedUntrustedInternalConnections에 콜백 host/domain을 포함하도록 설정하세요. - 전체 URL이 아닌 host/domain 항목을 사용하세요.
- 올바른 예:
gateway.tailnet-name.ts.net - 잘못된 예:
https://gateway.tailnet-name.ts.net
- 올바른 예:
- 콜백 대상이 프라이빗/tailnet/내부 주소인 경우, Mattermost의
환경 변수 (기본 계정) (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은 기능적으로 같아요.
액세스 제어 (DMs)
섹션 제목: “액세스 제어 (DMs)”- 기본값:
channels.mattermost.dmPolicy = "pairing"(알 수 없는 발신자에게는 페어링 코드를 보냅니다). - 승인 방법:
openclaw pairing list mattermostopenclaw 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>)를 사용하는 것을 추천해요.
DM 채널 재시도 설정
섹션 제목: “DM 채널 재시도 설정”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 클라이언트 오류는 영구적인 실패로 처리되어 재시도하지 않아요.
리액션 (message tool)
섹션 제목: “리액션 (message tool)”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=thumbsupmessage action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup remove=trueConfig:
channels.mattermost.actions.reactions: 리액션 액션 활성화/비활성화 (기본값 true).- 계정별 오버라이드:
channels.mattermost.accounts.<id>.actions.reactions.
인터랙티브 버튼 (message tool)
섹션 제목: “인터랙티브 버튼 (message tool)”클릭 가능한 버튼이 포함된 메시지를 보내보세요. 사용자가 버튼을 클릭하면 에이전트가 선택 내용을 수신하고 응답할 수 있어요.
채널 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"를 사용할 수 있어요.
사용자가 버튼을 클릭하면:
- 모든 버튼이 확인 문구로 교체돼요 (예: ”✓ Yes selected by @user”).
- 에이전트는 선택 내용을 인바운드 메시지로 수신하고 응답해요.
참고 사항:
- 버튼 콜백은 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에 추가하세요.
직접 API 연동 (외부 스크립트)
섹션 제목: “직접 API 연동 (외부 스크립트)”외부 스크립트나 웹훅은 에이전트의 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 }, }, }, ], }, ], },}중요 규칙:
- 첨부 파일은 최상위
attachments가 아니라props.attachments에 넣어야 해요 (그렇지 않으면 무시돼요). - 모든 액션에는
type: "button"이 필요해요. 이게 없으면 클릭이 무시돼요. - 모든 액션에는
id필드가 필요해요. Mattermost는 ID가 없는 액션을 무시해요. - 액션
id는 반드시 영문자와 숫자([a-zA-Z0-9])로만 구성되어야 해요. 하이픈이나 언더바는 Mattermost의 서버 측 액션 라우팅을 깨뜨려 404 에러를 발생시켜요. 사용 전에 이를 제거하세요. context.action_id는 버튼의id와 일치해야 확인 메시지에 원시 ID 대신 버튼 이름(예: “Approve”)이 표시돼요.context.action_id는 필수예요. 이게 없으면 인터랙션 핸들러가 400 에러를 반환해요.
HMAC 토큰 생성:
Gateway는 HMAC-SHA256으로 버튼 클릭을 검증해요. 외부 스크립트는 Gateway의 검증 로직과 일치하는 토큰을 생성해야 해요:
- 봇 토큰에서 비밀키를 도출하세요:
HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken) _token을 제외한 모든 필드로 context 객체를 만드세요.- 키를 정렬하고 공백 없이 직렬화하세요 (Gateway는 키가 정렬된
JSON.stringify를 사용하여 콤팩트한 출력을 생성해요). - 서명하세요:
HMAC-SHA256(key=secret, data=serializedContext) - 결과물인 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 간에 비밀키가 동일해야 해요.
Directory adapter
섹션 제목: “Directory adapter”Mattermost 플러그인에는 Directory adapter가 포함되어 있어요. 이 어댑터는 Mattermost API를 통해 채널과 사용자 이름을 확인해 줍니다. 덕분에 openclaw message send 명령어나 cron, webhook으로 메시지를 보낼 때 복잡한 ID 대신 #channel-name이나 @username을 직접 대상으로 지정할 수 있어 정말 편해요.
따로 설정할 부분은 없어요. 어댑터가 계정 설정에 등록된 bot token을 자동으로 사용하거든요.
Multi-account
섹션 제목: “Multi-account”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"]를 추가해 보세요.
관련 문서
섹션 제목: “관련 문서”- Channels Overview — 지원되는 모든 채널
- Pairing — DM 인증 및 페어링 흐름
- Groups — 그룹 채팅 동작 및 멘션 제어
- Channel Routing — 메시지 세션 라우팅
- Security — 액세스 모델 및 보안 강화
OpenClaw Expert
아직 막혀 있나요?
이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.