Developer and self-hosted
Mattermost
ステータス: ダウンロード可能な Plugin (ボットトークン + WebSocket イベント)。チャンネル、グループ、DM に対応しています。Mattermost はセルフホスト可能なチームメッセージングプラットフォームです。製品詳細とダウンロードについては、公式サイト mattermost.com を参照してください。
インストール
チャンネルを設定する前に Mattermost をインストールしてください:
npm レジストリ
openclaw plugins install @openclaw/mattermost
ローカルチェックアウト
openclaw plugins install ./path/to/local/mattermost-plugin
詳細: Plugin
クイックセットアップ
Plugin が利用可能であることを確認する
現在パッケージ化されている OpenClaw リリースには、すでに同梱されています。古いインストールやカスタムインストールでは、上記のコマンドで手動追加できます。
Mattermost ボットを作成する
Mattermost ボットアカウントを作成し、ボットトークンをコピーします。
ベース URL をコピーする
Mattermost のベース URL (例: https://chat.example.com) をコピーします。
OpenClaw を設定して Gateway を起動する
最小構成:
{
channels: {
mattermost: {
enabled: true,
botToken: "mm-token",
baseUrl: "https://chat.example.com",
dmPolicy: "pairing",
},
},
}
ネイティブスラッシュコマンド
ネイティブスラッシュコマンドはオプトインです。有効にすると、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",
},
},
},
}
動作メモ
native: "auto"は Mattermost ではデフォルトで無効です。有効にするにはnative: trueを設定します。callbackUrlが省略された場合、OpenClaw は Gateway のホスト/ポート +callbackPathから導出します。- 複数アカウントのセットアップでは、
commandsはトップレベルまたはchannels.mattermost.accounts.<id>.commandsの下に設定できます (アカウント値がトップレベルフィールドを上書きします)。 - コマンドコールバックは、OpenClaw が
oc_*コマンドを登録したときに Mattermost から返されるコマンドごとのトークンで検証されます。 - OpenClaw は各コールバックを受け入れる前に現在の Mattermost コマンド登録を更新するため、削除または再生成されたスラッシュコマンドの古いトークンは、Gateway の再起動なしで受け入れられなくなります。
- Mattermost API がコマンドがまだ最新であることを確認できない場合、コールバック検証はフェイルクローズします。失敗した検証は短時間キャッシュされ、同時ルックアップはまとめられ、新しいルックアップ開始はリプレイ負荷を制限するためにコマンドごとにレート制限されます。
- 登録に失敗した場合、起動が部分的だった場合、またはコールバックトークンが解決されたコマンドの登録済みトークンと一致しない場合、スラッシュコールバックはフェイルクローズします (あるコマンドに有効なトークンが、別のコマンドのアップストリーム検証に到達することはできません)。
到達性要件
コールバックエンドポイントは Mattermost サーバーから到達可能である必要があります。
- Mattermost が OpenClaw と同じホスト/ネットワーク名前空間で実行されていない限り、
callbackUrlをlocalhostに設定しないでください。 - その URL が
/api/channels/mattermost/commandを OpenClaw にリバースプロキシしない限り、callbackUrlを Mattermost のベース URL に設定しないでください。 - 簡単な確認方法は
curl https://<gateway-host>/api/channels/mattermost/commandです。GET は404ではなく、OpenClaw から405 Method Not Allowedを返すはずです。
Mattermost 送信許可リスト
コールバック先がプライベート/tailnet/内部アドレスの場合は、Mattermost の ServiceSettings.AllowedUntrustedInternalConnections にコールバックのホスト/ドメインを含めるよう設定します。
完全な URL ではなく、ホスト/ドメインのエントリを使用してください。
- 良い例:
gateway.tailnet-name.ts.net - 悪い例:
https://gateway.tailnet-name.ts.net
環境変数 (デフォルトアカウント)
環境変数を使用する場合は、Gateway ホストでこれらを設定します:
MATTERMOST_BOT_TOKEN=...MATTERMOST_URL=https://chat.example.com
チャットモード
Mattermost は DM に自動で応答します。チャンネルの動作は chatmode で制御されます:
oncall (デフォルト)
チャンネルで @メンションされた場合にのみ応答します。
onmessage
すべてのチャンネルメッセージに応答します。
onchar
メッセージがトリガープレフィックスで始まる場合に応答します。
設定例:
{
channels: {
mattermost: {
chatmode: "onchar",
oncharPrefixes: [">", "!"],
},
},
}
メモ:
oncharは明示的な @メンションにも引き続き応答します。channels.mattermost.requireMentionはレガシー設定では尊重されますが、chatmodeが推奨されます。
スレッドとセッション
channels.mattermost.replyToMode を使用して、チャンネルとグループの返信をメインチャンネルに残すか、トリガーとなった投稿の下にスレッドを開始するかを制御します。
off(デフォルト): 受信投稿がすでにスレッド内にある場合にのみ、スレッド内で返信します。first: トップレベルのチャンネル/グループ投稿の場合、その投稿の下にスレッドを開始し、会話をスレッドスコープのセッションにルーティングします。all: 現在の Mattermost ではfirstと同じ動作です。- ダイレクトメッセージはこの設定を無視し、スレッド化されません。
設定例:
{
channels: {
mattermost: {
replyToMode: "all",
},
},
}
メモ:
- スレッドスコープのセッションは、トリガーとなった投稿 ID をスレッドルートとして使用します。
firstとallは現在同等です。Mattermost にスレッドルートがあると、後続のチャンクとメディアは同じスレッド内で継続されるためです。
アクセス制御 (DM)
- デフォルト:
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で送信者を許可リストに登録します (ユーザー ID 推奨)。- チャンネルごとのメンション上書きは、
channels.mattermost.groups.<channelId>.requireMentionの下、またはデフォルトとしてchannels.mattermost.groups["*"].requireMentionの下に置きます。 @usernameマッチングは変更可能であり、channels.mattermost.dangerouslyAllowNameMatching: trueの場合にのみ有効です。- オープンチャンネル:
channels.mattermost.groupPolicy="open"(メンションゲート付き)。 - ランタイムメモ:
channels.mattermostが完全に存在しない場合、ランタイムはグループチェックでgroupPolicy="allowlist"にフォールバックします (channels.defaults.groupPolicyが設定されている場合でも)。
例:
{
channels: {
mattermost: {
groupPolicy: "open",
groups: {
"*": { requireMention: true },
"team-channel-id": { requireMention: false },
},
},
},
}
アウトバウンド配信のターゲット
openclaw message send または Cron/Webhook では、これらのターゲット形式を使用します:
- チャンネルには
channel:<id> - DM には
user:<id> - DM には
@username(Mattermost API 経由で解決)
DM チャンネルの再試行
OpenClaw が Mattermost の DM ターゲットに送信し、先にダイレクトチャンネルを解決する必要がある場合、デフォルトで一時的なダイレクトチャンネル作成失敗を再試行します。
Mattermost Plugin 全体でその動作を調整するには channels.mattermost.dmChannelRetry を使用し、1 つのアカウントでは channels.mattermost.accounts.<id>.dmChannelRetry を使用します。
{
channels: {
mattermost: {
dmChannelRetry: {
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 10000,
timeoutMs: 30000,
},
},
},
}
メモ:
- これはすべての Mattermost API 呼び出しではなく、DM チャンネル作成 (
/api/v4/channels/direct) にのみ適用されます。 - 再試行は、レート制限、5xx レスポンス、ネットワークエラー、タイムアウトエラーなどの一時的な失敗に適用されます。
429以外の 4xx クライアントエラーは永続的なものとして扱われ、再試行されません。
プレビューストリーミング
Mattermost は思考、ツールアクティビティ、部分的な返信テキストを 1 つの下書きプレビュー投稿にストリーミングし、最終回答を安全に送信できるようになった時点でその場で確定します。プレビューはチャンクごとのメッセージでチャンネルを埋めるのではなく、同じ投稿 ID 上で更新されます。メディア/エラーの最終応答では、保留中のプレビュー編集をキャンセルし、使い捨てのプレビュー投稿をフラッシュする代わりに通常の配信を使用します。
channels.mattermost.streaming で有効にします:
{
channels: {
mattermost: {
streaming: "partial", // off | partial | block | progress
},
},
}
ストリーミングモード
partialは通常の選択肢です。返信が伸びるにつれて編集される 1 つのプレビュー投稿を使用し、完全な回答で確定します。blockは、プレビュー投稿内で追記スタイルの下書きチャンクを使用します。progressは生成中にステータスプレビューを表示し、完了時に最終回答のみを投稿します。offはプレビューストリーミングを無効にします。
ストリーミング動作メモ
- ストリームをその場で確定できない場合 (たとえば投稿がストリーム途中で削除された場合)、OpenClaw は新しい最終投稿の送信にフォールバックするため、返信が失われることはありません。
- 推論のみのペイロードは、
> Reasoning:ブロック引用として届くテキストを含め、チャンネル投稿から抑制されます。他のサーフェスで思考を確認するには/reasoning onを設定します。Mattermost の最終投稿には回答のみが保持されます。 - チャンネルマッピングのマトリックスについては、ストリーミングを参照してください。
リアクション (メッセージツール)
channel=mattermostでmessage action=reactを使用します。messageIdは Mattermost の投稿 ID です。emojiはthumbsupや:+1:のような名前を受け付けます (コロンは任意です)。- リアクションを削除するには
remove=true(ブール値) を設定します。 - リアクションの追加/削除イベントは、システムイベントとしてルーティング先のエージェントセッションに転送されます。
例:
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
設定:
channels.mattermost.actions.reactions: リアクションアクションを有効/無効にします (デフォルトは true)。- アカウントごとの上書き:
channels.mattermost.accounts.<id>.actions.reactions。
インタラクティブボタン (メッセージツール)
クリック可能なボタン付きでメッセージを送信します。ユーザーがボタンをクリックすると、エージェントは選択を受信して応答できます。
チャンネル機能に 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"}]]
ボタンフィールド:
textstringrequired表示ラベル。
callback_datastringrequiredクリック時に送り返される値(アクション ID として使用)。
style"default" | "primary" | "danger"ボタンスタイル。
ユーザーがボタンをクリックした場合:
ボタンが確認表示に置き換えられる
すべてのボタンが確認行に置き換えられます(例: 「✓ Yes selected by @user」)。
エージェントが選択を受信する
エージェントは選択を受信メッセージとして受け取り、応答します。
実装メモ
- ボタンのコールバックは HMAC-SHA256 検証を使用します(自動、設定不要)。
- Mattermost は API レスポンスからコールバックデータを削除します(セキュリティ機能)。そのため、クリック時にはすべてのボタンが削除されます - 部分的な削除はできません。
- ハイフンまたはアンダースコアを含むアクション ID は自動的にサニタイズされます(Mattermost のルーティング制限)。
設定と到達性
channels.mattermost.capabilities: ケイパビリティ文字列の配列。エージェントのシステムプロンプトでボタンツールの説明を有効にするには"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 連携(外部スクリプト)
外部スクリプトと Webhook は、エージェントの message ツールを経由する代わりに、Mattermost REST API を使ってボタンを直接投稿できます。可能な場合は Plugin の buildButtonAttachments() を使用してください。生の JSON を投稿する場合は、次のルールに従ってください:
ペイロード構造:
{
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
},
},
},
],
},
],
},
}
HMAC トークン生成
Gateway は HMAC-SHA256 でボタンクリックを検証します。外部スクリプトは、Gateway の検証ロジックと一致するトークンを生成する必要があります:
bot トークンからシークレットを導出する
HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken)
コンテキストオブジェクトを構築する
_token 以外のすべてのフィールドでコンテキストオブジェクトを構築します。
ソート済みキーでシリアライズする
ソート済みキーかつスペースなしでシリアライズします(Gateway はソート済みキーで JSON.stringify を使用し、コンパクトな出力を生成します)。
ペイロードに署名する
HMAC-SHA256(key=secret, data=serializedContext)
トークンを追加する
結果の 16 進ダイジェストをコンテキスト内の _token として追加します。
Python の例:
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を除く)に署名してください。Gateway は_tokenを取り除いてから残りすべてに署名します。一部だけに署名すると、検証が黙って失敗します。 sort_keys=Trueを使用してください - Gateway は署名前にキーをソートし、Mattermost はペイロード保存時にコンテキストフィールドを並べ替える場合があります。- ランダムバイトではなく、bot トークンからシークレットを導出してください(決定的)。ボタンを作成するプロセスと検証する Gateway の間で、シークレットは同じである必要があります。
ディレクトリアダプター
Mattermost Plugin には、Mattermost API 経由でチャンネル名とユーザー名を解決するディレクトリアダプターが含まれています。これにより、openclaw message send と Cron/Webhook 配信で #channel-name と @username の宛先を使用できます。
設定は不要です - アダプターはアカウント設定の bot トークンを使用します。
複数アカウント
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" },
},
},
},
}
トラブルシューティング
チャンネルに返信がない
bot がチャンネル内にいることを確認してメンションする(oncall)、トリガープレフィックスを使用する(onchar)、または chatmode: "onmessage" を設定してください。
認証または複数アカウントのエラー
- bot トークン、ベース URL、アカウントが有効かどうかを確認してください。
- 複数アカウントの問題: env var は
defaultアカウントにのみ適用されます。
ネイティブスラッシュコマンドが失敗する
Unauthorized: invalid command token.: OpenClaw がコールバックトークンを受け入れませんでした。典型的な原因:- スラッシュコマンドの登録が起動時に失敗した、または一部しか完了していない
- コールバックが誤った Gateway/アカウントに到達している
- Mattermost に、以前のコールバック先を指す古いコマンドがまだ残っている
- Gateway がスラッシュコマンドを再有効化せずに再起動された
- ネイティブスラッシュコマンドが動作しなくなった場合は、ログで
mattermost: failed to register slash commandsまたはmattermost: native slash commands enabled but no commands could be registeredを確認してください。 callbackUrlが省略され、ログでコールバックがhttp://127.0.0.1:18789/...に解決されたという警告が出ている場合、その URL はおそらく Mattermost が OpenClaw と同じホスト/ネットワーク名前空間で実行されている場合にのみ到達可能です。代わりに、外部から到達可能なcommands.callbackUrlを明示的に設定してください。
ボタンの問題
- ボタンが白いボックスとして表示される: エージェントが不正な形式のボタンデータを送信している可能性があります。各ボタンに
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 の不一致です。すべてのコンテキストフィールド(一部ではない)に署名し、ソート済みキーを使用し、コンパクト JSON(スペースなし)を使用していることを確認してください。上記の HMAC セクションを参照してください。 - Gateway ログに
missing _token in contextが出る:_tokenフィールドがボタンのコンテキスト内にありません。インテグレーションペイロードを構築する際に含めてください。 - 確認表示にボタン名ではなく生の ID が表示される:
context.action_idがボタンのidと一致していません。両方を同じサニタイズ済み値に設定してください。 - エージェントがボタンを認識しない: Mattermost チャンネル設定に
capabilities: ["inlineButtons"]を追加してください。
関連
- チャンネルルーティング - メッセージのセッションルーティング
- チャンネル概要 - サポートされているすべてのチャンネル
- グループ - グループチャットの動作とメンションゲート
- ペアリング - DM 認証とペアリングフロー
- セキュリティ - アクセスモデルと堅牢化