Developer and self-hosted
Mattermost
สถานะ: Plugin ที่ดาวน์โหลดได้ (โทเค็นบอต + เหตุการณ์ WebSocket) รองรับแชนเนล แชนเนลส่วนตัว DM แบบกลุ่ม และ DM โดย Mattermost เป็นแพลตฟอร์มรับส่งข้อความสำหรับทีมที่โฮสต์เองได้ (mattermost.com)
ติดตั้ง
รีจิสทรี npm
openclaw plugins install @openclaw/mattermostเช็กเอาต์ในเครื่อง
openclaw plugins install ./path/to/local/mattermost-pluginรายละเอียด: Plugin
ตั้งค่าอย่างรวดเร็ว
ตรวจสอบว่า Plugin พร้อมใช้งาน
ติดตั้ง @openclaw/mattermost ด้วยคำสั่งด้านบน แล้วรีสตาร์ต Gateway หากกำลังทำงานอยู่
สร้างบอต Mattermost
สร้างบัญชีบอต Mattermost คัดลอก โทเค็นบอต และเพิ่มบอตไปยังทีมและแชนเนลที่ต้องการให้บอตอ่าน
คัดลอก URL ฐาน
คัดลอก URL ฐาน ของ Mattermost (เช่น https://chat.example.com) ระบบจะตัด /api/v4 ที่ท้าย URL ออกโดยอัตโนมัติ
กำหนดค่า OpenClaw และเริ่ม Gateway
การกำหนดค่าขั้นต่ำ:
{ channels: { mattermost: { enabled: true, botToken: "mm-token", baseUrl: "https://chat.example.com", dmPolicy: "pairing", }, },}ทางเลือกแบบไม่โต้ตอบ:
openclaw channels add --channel mattermost --bot-token <token> --http-url https://chat.example.comคำสั่งสแลชแบบเนทีฟ
คำสั่งสแลชแบบเนทีฟต้องเลือกเปิดใช้ เมื่อเปิดใช้แล้ว OpenClaw จะลงทะเบียนคำสั่งสแลช oc_* ในทุกทีมที่บอตเป็นสมาชิก และรับ callback แบบ POST บนเซิร์ฟเวอร์ HTTP ของ Gateway
{ channels: { mattermost: { commands: { native: true, nativeSkills: true, callbackPath: "/api/channels/mattermost/command", // ใช้เมื่อ Mattermost ไม่สามารถเข้าถึง Gateway ได้โดยตรง (พร็อกซีย้อนกลับ/URL สาธารณะ) callbackUrl: "https://gateway.example.com/api/channels/mattermost/command", }, }, },}คำสั่งที่ลงทะเบียน: /oc_status, /oc_model, /oc_models, /oc_new, /oc_help, /oc_think, /oc_reasoning, /oc_verbose, /oc_queue เมื่อใช้ nativeSkills: true ระบบจะลงทะเบียนคำสั่ง Skills เป็น /oc_<skill> ด้วย
หมายเหตุเกี่ยวกับลักษณะการทำงาน
nativeและnativeSkillsมีค่าเริ่มต้นเป็น"auto"ซึ่งจะถูกตีความว่าปิดใช้งานสำหรับ Mattermost ให้ตั้งค่าเป็นtrueอย่างชัดเจนcallbackPathมีค่าเริ่มต้นเป็น/api/channels/mattermost/command- หากไม่ระบุ
callbackUrlOpenClaw จะสร้างhttp://<gateway.customBindHost or localhost>:<gateway.port, default 18789><callbackPath>ขึ้นมา โฮสต์ที่ผูกแบบไวลด์การ์ด (0.0.0.0,::) จะใช้localhostเป็นค่าทดแทน - สำหรับการตั้งค่าหลายบัญชี สามารถตั้ง
commandsที่ระดับบนสุดหรือภายใต้channels.mattermost.accounts.<id>.commandsได้ (ค่าของบัญชีมีลำดับความสำคัญเหนือฟิลด์ระดับบนสุด) - คำสั่งสแลชที่มีทริกเกอร์เดียวกันและสร้างโดยการผสานรวมอื่นจะไม่ถูกแก้ไข (ระบบจะข้ามการลงทะเบียน) ส่วนคำสั่งที่บอตสร้างจะถูกอัปเดตหรือสร้างใหม่เมื่อ URL callback เปลี่ยนไป
- callback ของคำสั่งจะได้รับการตรวจสอบด้วยโทเค็นประจำคำสั่งที่ Mattermost ส่งคืนเมื่อ OpenClaw ลงทะเบียนคำสั่ง
oc_* - OpenClaw รีเฟรชการลงทะเบียนคำสั่ง Mattermost ปัจจุบันก่อนยอมรับ callback แต่ละครั้ง ดังนั้นโทเค็นเก่าจากคำสั่งสแลชที่ถูกลบหรือสร้างใหม่จะหยุดได้รับการยอมรับโดยไม่ต้องรีสตาร์ต Gateway
- การตรวจสอบ callback จะปฏิเสธโดยค่าเริ่มต้นหาก API ของ Mattermost ไม่สามารถยืนยันได้ว่าคำสั่งยังเป็นปัจจุบัน ระบบจะแคชผลการตรวจสอบที่ล้มเหลวไว้ชั่วครู่ รวมการค้นหาที่เกิดพร้อมกัน และจำกัดอัตราการเริ่มค้นหาใหม่ต่อคำสั่งเพื่อควบคุมแรงกดดันจากการเล่นซ้ำ
- callback ของคำสั่งสแลชจะปฏิเสธโดยค่าเริ่มต้นเมื่อการลงทะเบียนล้มเหลว การเริ่มต้นระบบสำเร็จเพียงบางส่วน หรือโทเค็น callback ไม่ตรงกับโทเค็นที่ลงทะเบียนของคำสั่งที่ระบุได้ (โทเค็นที่ใช้ได้กับคำสั่งหนึ่งจะไม่สามารถผ่านไปยังการตรวจสอบต้นทางสำหรับอีกคำสั่งหนึ่ง)
- callback ที่ยอมรับแล้วจะได้รับการตอบรับด้วยข้อความชั่วคราว "กำลังประมวลผล..." โดยคำตอบจริงจะส่งมาเป็นข้อความปกติ
ข้อกำหนดด้านการเข้าถึง
เซิร์ฟเวอร์ Mattermost ต้องสามารถเข้าถึงปลายทาง callback ได้
- อย่าตั้ง
callbackUrlเป็นlocalhostเว้นแต่ Mattermost จะทำงานบนโฮสต์/เนมสเปซเครือข่ายเดียวกับ OpenClaw - อย่าตั้ง
callbackUrlเป็น URL ฐานของ Mattermost เว้นแต่ URL นั้นจะทำพร็อกซีย้อนกลับ/api/channels/mattermost/commandไปยัง OpenClaw - การตรวจสอบอย่างรวดเร็วทำได้ด้วย
curl https://<gateway-host>/api/channels/mattermost/command; GET ควรได้รับ405 Method Not Allowedจาก OpenClaw ไม่ใช่404
รายการอนุญาตขาออกของ Mattermost
หาก callback มุ่งไปยังที่อยู่ส่วนตัว/tailnet/ภายใน ให้ตั้งค่า ServiceSettings.AllowedUntrustedInternalConnections ของ Mattermost ให้รวมโฮสต์/โดเมนของ callback
ใช้รายการโฮสต์/โดเมน ไม่ใช่ 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การตั้งค่าgroups.<channelId>.requireMentionต่อแชนเนลมีลำดับความสำคัญเหนือทั้งสองค่า - หลังจากบอตส่งการตอบกลับที่มองเห็นได้ในเธรดของแชนเนล ข้อความภายหลังในเธรดเดียวกันจะได้รับการตอบโดยไม่ต้อง @กล่าวถึงใหม่หรือใช้คำนำหน้า
oncharทำให้การสนทนาหลายรอบในเธรดดำเนินต่อเนื่องได้ ระบบจะจดจำการเข้าร่วมเป็นเวลา 7 วันหลังจากที่บอตตอบกลับในเธรดนั้นครั้งล่าสุด และข้อมูลจะยังคงอยู่หลังรีสตาร์ต Gateway เธรดที่บอตเพียงสังเกตการณ์จะไม่ได้รับผลกระทบ หากต้องการให้ต้องกล่าวถึงอย่างชัดเจนอีกครั้ง ให้เริ่มข้อความระดับบนสุดใหม่
เธรดและเซสชัน
ใช้ channels.mattermost.replyToMode เพื่อควบคุมว่าการตอบกลับในแชนเนลและกลุ่มจะอยู่ในแชนเนลหลักหรือเริ่มเธรดใต้โพสต์ที่ทริกเกอร์
off(ค่าเริ่มต้น): ตอบกลับในเธรดเฉพาะเมื่อโพสต์ขาเข้าอยู่ในเธรดอยู่แล้วfirst: สำหรับโพสต์ระดับบนสุดในแชนเนล/กลุ่ม ให้เริ่มเธรดใต้โพสต์นั้นและกำหนดเส้นทางการสนทนาไปยังเซสชันที่มีขอบเขตเฉพาะเธรดallและbatched: ปัจจุบันมีลักษณะการทำงานเหมือนfirstสำหรับ Mattermost เนื่องจากเมื่อ Mattermost มีรากเธรดแล้ว ส่วนข้อความและสื่อที่ตามมาจะอยู่ในเธรดเดิมต่อไป- ข้อความโดยตรงมีค่าเริ่มต้นเป็น
offแม้จะตั้งreplyToModeไว้
ใช้ channels.mattermost.replyToModeByChatType เพื่อลบล้างโหมดสำหรับแชตประเภท direct, group หรือ channel ตั้ง direct เพื่อเลือกให้ข้อความโดยตรงใช้เธรด:
off(ค่าเริ่มต้น): ข้อความโดยตรงจะไม่มีเธรดและอยู่ในเซสชันต่อเนื่องเดียวfirst,allหรือbatched: ข้อความโดยตรงระดับบนสุดแต่ละข้อความจะเริ่มเธรด Mattermost ที่รองรับด้วยเซสชันใหม่ซึ่งเป็นอิสระจากกัน
{ channels: { mattermost: { replyToMode: "all", replyToModeByChatType: { direct: "first", }, }, },}หมายเหตุ:
- เซสชันที่มีขอบเขตเฉพาะเธรดใช้ ID ของโพสต์ที่ทริกเกอร์เป็นรากเธรด
- ปัจจุบัน
firstและallเทียบเท่ากัน เนื่องจากเมื่อ Mattermost มีรากเธรดแล้ว ส่วนข้อความและสื่อที่ตามมาจะอยู่ในเธรดเดิมต่อไป - การลบล้างตามประเภทแชตมีลำดับความสำคัญเหนือ
replyToModeหากไม่มีการลบล้างdirectระบบที่ติดตั้งใช้งานอยู่จะคง DM แบบราบและไม่มีเธรดไว้
การควบคุมการเข้าถึง (DM)
- ค่าเริ่มต้น:
channels.mattermost.dmPolicy = "pairing"(ผู้ส่งที่ไม่รู้จักจะได้รับรหัสจับคู่) ค่าอื่นๆ ได้แก่allowlist,open,disabled - อนุมัติผ่าน:
openclaw pairing list mattermostopenclaw pairing approve mattermost <CODE>
- DM สาธารณะ:
channels.mattermost.dmPolicy="open"ร่วมกับchannels.mattermost.allowFrom=["*"](สคีมาการกำหนดค่าบังคับให้ใช้ไวลด์การ์ด) channels.mattermost.allowFromรองรับ ID ผู้ใช้ (แนะนำ) และรายการaccessGroup:<name>โปรดดู กลุ่มการเข้าถึง
แชนเนล (กลุ่ม)
- ค่าเริ่มต้น:
channels.mattermost.groupPolicy = "allowlist"(ต้องกล่าวถึง) - กำหนดรายการผู้ส่งที่อนุญาตด้วย
channels.mattermost.groupAllowFrom(แนะนำให้ใช้ ID ผู้ใช้) channels.mattermost.groupAllowFromรองรับรายการaccessGroup:<name>โปรดดู กลุ่มการเข้าถึง- การลบล้างข้อกำหนดการกล่าวถึงต่อแชนเนลอยู่ภายใต้
channels.mattermost.groups.<channelId>.requireMentionหรือใช้channels.mattermost.groups["*"].requireMentionเป็นค่าเริ่มต้น - การจับคู่
@usernameสามารถเปลี่ยนแปลงได้และจะเปิดใช้เฉพาะเมื่อchannels.mattermost.dangerouslyAllowNameMatching: true - แชนเนลแบบเปิด:
channels.mattermost.groupPolicy="open"(ต้องกล่าวถึง) - ลำดับการระบุ:
channels.mattermost.groupPolicyจากนั้นchannels.defaults.groupPolicyแล้วจึง"allowlist" - หมายเหตุเกี่ยวกับรันไทม์: หากไม่มีส่วน
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> |
แชนเนลตาม ID |
channel:<name> หรือ #channel-name |
แชนเนลตามชื่อ โดยค้นหาในทุกทีมที่บอตเป็นสมาชิก |
user:<id> หรือ mattermost:<id> |
DM กับผู้ใช้รายนั้น |
@username |
DM (ระบุชื่อผู้ใช้ผ่าน API ของ Mattermost) |
การส่งขาออกรองรับไฟล์แนบได้สูงสุดหนึ่งไฟล์ต่อข้อความ หากมีหลายไฟล์ ให้แยกส่งเป็นคนละข้อความ
การลองแชนเนล DM ใหม่
เมื่อ OpenClaw ส่งข้อความไปยังเป้าหมาย DM ของ Mattermost และจำเป็นต้องระบุช่องทางโดยตรงก่อน ระบบจะลองสร้างช่องทางโดยตรงใหม่โดยค่าเริ่มต้นหากเกิดความล้มเหลวชั่วคราว
ใช้ channels.mattermost.dmChannelRetry เพื่อปรับลักษณะการทำงานนี้โดยรวมสำหรับ Plugin Mattermost หรือใช้ channels.mattermost.accounts.<id>.dmChannelRetry สำหรับบัญชีเดียว ค่าเริ่มต้น:
{ channels: { mattermost: { dmChannelRetry: { maxRetries: 3, initialDelayMs: 1000, maxDelayMs: 10000, timeoutMs: 30000, }, }, },}หมายเหตุ:
- การตั้งค่านี้ใช้เฉพาะกับการสร้างช่องทาง DM (
/api/v4/channels/direct) ไม่ใช่ทุกการเรียกใช้ API ของ Mattermost - การลองใหม่ใช้การหน่วงเวลาแบบเอ็กซ์โพเนนเชียลร่วมกับค่าคลาดเคลื่อนแบบสุ่ม และใช้กับความล้มเหลวชั่วคราว เช่น การจำกัดอัตรา การตอบกลับ 5xx รวมถึงข้อผิดพลาดของเครือข่ายหรือการหมดเวลา
- ข้อผิดพลาดไคลเอนต์ 4xx นอกเหนือจาก
429จะถือเป็นข้อผิดพลาดถาวรและจะไม่มีการลองใหม่
การสตรีมตัวอย่าง
Mattermost สตรีมการคิด กิจกรรมของเครื่องมือ และข้อความตอบกลับบางส่วนลงใน โพสต์ตัวอย่างฉบับร่าง ซึ่งจะถูกปรับให้สมบูรณ์ในตำแหน่งเดิมเมื่อส่งคำตอบสุดท้ายได้อย่างปลอดภัย ในโหมด partial ตัวอย่างจะอัปเดตโดยใช้รหัสโพสต์เดิมแทนที่จะส่งข้อความแยกสำหรับทุกส่วนจนรบกวนช่องทาง ในโหมด block ตัวอย่างจะสลับระหว่างข้อความที่เสร็จสมบูรณ์กับบล็อกกิจกรรมของเครื่องมือ ทำให้บล็อกก่อนหน้ายังคงมองเห็นเป็นโพสต์แยกของตนเองแทนที่จะถูกบล็อกถัดไปเขียนทับ ผลลัพธ์สุดท้ายที่เป็นสื่อหรือข้อผิดพลาดจะยกเลิกการแก้ไขตัวอย่างที่รอดำเนินการ และใช้การนำส่งตามปกติแทนการส่งโพสต์ตัวอย่างชั่วคราวที่ไม่จำเป็น
การสตรีมตัวอย่าง เปิดอยู่โดยค่าเริ่มต้น ในโหมด partial กำหนดค่าผ่าน channels.mattermost.streaming.mode (ค่าแบบสเกลาร์/บูลีนเดิมของ streaming จะถูกย้ายโดย openclaw doctor --fix):
{ channels: { mattermost: { streaming: { mode: "partial" }, // off | partial | block | progress }, },}โหมดการสตรีม
partial(ค่าเริ่มต้น): โพสต์ตัวอย่างหนึ่งโพสต์ซึ่งถูกแก้ไขตามคำตอบที่เพิ่มขึ้น จากนั้นปรับให้สมบูรณ์ด้วยคำตอบฉบับเต็มblockจะสลับตัวอย่างระหว่างข้อความที่เสร็จสมบูรณ์กับบล็อกกิจกรรมของเครื่องมือ ทำให้แต่ละบล็อกยังคงมองเห็นเป็นโพสต์แยกของตนเองแทนที่จะถูกเขียนทับในตำแหน่งเดิม การอัปเดตเครื่องมือทั้งแบบขนานและต่อเนื่องจะใช้โพสต์กิจกรรมของเครื่องมือปัจจุบันร่วมกันprogressแสดงตัวอย่างสถานะระหว่างการสร้าง และโพสต์เฉพาะคำตอบสุดท้ายเมื่อเสร็จสมบูรณ์offปิดใช้การสตรีมตัวอย่าง เมื่อใช้streaming.block.enabled: trueบล็อกของผู้ช่วยที่เสร็จสมบูรณ์จะยังคงถูกนำส่งเป็นการตอบกลับแบบบล็อกตามปกติ (โพสต์แยกกัน) แทนที่จะรวมเป็นโพสต์สุดท้ายเพียงโพสต์เดียว
หมายเหตุเกี่ยวกับลักษณะการสตรีม
- หากไม่สามารถปรับสตรีมให้สมบูรณ์ในตำแหน่งเดิมได้ (เช่น โพสต์ถูกลบระหว่างการสตรีม) OpenClaw จะเปลี่ยนไปส่งโพสต์สุดท้ายใหม่ เพื่อไม่ให้คำตอบสูญหาย
- เพย์โหลดที่มีเฉพาะการคิดจะถูกระงับไม่ให้แสดงในโพสต์ของช่องทาง รวมถึงข้อความที่เข้ามาเป็นบล็อกคำพูด
> Thinkingตั้งค่า/reasoning onเพื่อดูการคิดในพื้นผิวอื่น ส่วนโพสต์สุดท้ายของ Mattermost จะเก็บเฉพาะคำตอบ - ดูเมทริกซ์การแมปช่องทางได้ที่ การสตรีม
รีแอ็กชัน (เครื่องมือข้อความ)
- ใช้
message action=reactร่วมกับchannel=mattermost messageIdคือรหัสโพสต์ Mattermostemojiรองรับชื่อ เช่นthumbsupหรือ:+1:(ไม่จำเป็นต้องใส่เครื่องหมายทวิภาค)- ตั้งค่า
remove=true(บูลีน) เพื่อลบรีแอ็กชัน - เหตุการณ์เพิ่ม/ลบรีแอ็กชันจะถูกส่งต่อเป็นเหตุการณ์ระบบไปยังเซสชันเอเจนต์ที่กำหนดเส้นทาง โดยอยู่ภายใต้การตรวจสอบนโยบาย DM/กลุ่มแบบเดียวกับข้อความ
ตัวอย่าง:
message action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsupmessage action=react channel=mattermost target=channel:<channelId> messageId=<postId> emoji=thumbsup remove=trueการกำหนดค่า:
channels.mattermost.actions.reactions: เปิด/ปิดการดำเนินการเกี่ยวกับรีแอ็กชัน (ค่าเริ่มต้น true)- การแทนที่สำหรับแต่ละบัญชี:
channels.mattermost.accounts.<id>.actions.reactions
ปุ่มโต้ตอบ (เครื่องมือข้อความ)
ส่งข้อความพร้อมปุ่มที่คลิกได้ เมื่อผู้ใช้คลิกปุ่ม เอเจนต์จะได้รับตัวเลือกและสามารถตอบสนองได้
ปุ่มมาจากเพย์โหลดเชิงความหมาย presentation (ในคำตอบปกติของเอเจนต์และใน message action=send) OpenClaw เรนเดอร์ปุ่มค่าเป็นปุ่มโต้ตอบของ Mattermost ทำให้ปุ่ม URL ยังคงมองเห็นได้ในข้อความ และลดระดับเมนูตัวเลือกเป็นข้อความที่อ่านได้
message action=send channel=mattermost target=channel:<channelId> presentation={"blocks":[{"type":"buttons","buttons":[{"label":"Yes","value":"yes"},{"label":"No","value":"no"}]}]}ฟิลด์ของปุ่มการนำเสนอ:
labelstringrequiredป้ายชื่อที่แสดง (ชื่อแทน: text)
valuestringค่าที่ส่งกลับเมื่อคลิก ซึ่งใช้เป็นรหัสการดำเนินการ (ชื่อแทน: callback_data, callbackData) จำเป็นสำหรับปุ่มที่คลิกได้ เว้นแต่จะตั้งค่า url
urlstringปุ่มลิงก์ ซึ่งเรนเดอร์เป็นข้อความ label: url ในเนื้อหาข้อความแทนปุ่มโต้ตอบ
style"primary" | "secondary" | "success" | "danger"รูปแบบปุ่ม Mattermost จะใช้รูปแบบเริ่มต้นกับค่าที่ไม่รองรับ
หากต้องการประกาศการรองรับปุ่มในพรอมต์ระบบของเอเจนต์ ให้เพิ่ม inlineButtons ลงในความสามารถของช่องทาง:
{ channels: { mattermost: { capabilities: ["inlineButtons"], }, },}เมื่อผู้ใช้คลิกปุ่ม:
การตรวจสอบสิทธิ์เข้าถึง
ผู้คลิกต้องผ่านการตรวจสอบนโยบาย DM/กลุ่มแบบเดียวกับผู้ส่งข้อความ การคลิกที่ไม่ได้รับอนุญาตจะได้รับการแจ้งเตือนชั่วคราวและถูกเพิกเฉย
แทนที่ปุ่มด้วยการยืนยัน
ปุ่มทั้งหมดจะถูกแทนที่ด้วยบรรทัดยืนยัน (เช่น "✓ Yes ถูกเลือกโดย @user")
เอเจนต์ได้รับตัวเลือก
เอเจนต์จะได้รับตัวเลือกเป็นข้อความขาเข้า (พร้อมเหตุการณ์ระบบ) และตอบสนอง
หมายเหตุการนำไปใช้
- คอลแบ็กของปุ่มใช้การตรวจสอบ HMAC-SHA256 (อัตโนมัติ ไม่ต้องกำหนดค่า)
- บล็อกไฟล์แนบทั้งหมดจะถูกแทนที่เมื่อคลิก ดังนั้นปุ่มทั้งหมดจะถูกลบพร้อมกัน จึงไม่สามารถลบเพียงบางส่วนได้
- รหัสการดำเนินการที่มีเครื่องหมายยัติภังค์หรือขีดล่างจะถูกปรับรูปแบบโดยอัตโนมัติ (ข้อจำกัดการกำหนดเส้นทางของ Mattermost)
- การคลิกที่มี
action_idไม่ตรงกับการดำเนินการในโพสต์ต้นฉบับจะถูกปฏิเสธด้วย403("การดำเนินการที่ไม่รู้จัก")
การกำหนดค่าและการเข้าถึง
channels.mattermost.capabilities: อาร์เรย์ของสตริงความสามารถ เพิ่ม"inlineButtons"เพื่อเปิดใช้คำอธิบายเครื่องมือปุ่มในพรอมต์ระบบของเอเจนต์channels.mattermost.interactions.callbackBaseUrl: URL ฐานภายนอกแบบไม่บังคับสำหรับคอลแบ็กของปุ่ม (เช่นhttps://gateway.example.com) ใช้ค่านี้เมื่อ Mattermost ไม่สามารถเข้าถึง Gateway ที่โฮสต์การผูกของ Gateway ได้โดยตรง- ในการตั้งค่าหลายบัญชี สามารถตั้งค่าฟิลด์เดียวกันภายใต้
channels.mattermost.accounts.<id>.interactions.callbackBaseUrlได้เช่นกัน - หากละเว้น
interactions.callbackBaseUrlOpenClaw จะสร้าง URL คอลแบ็กจากgateway.customBindHost+gateway.port(ค่าเริ่มต้น 18789) แล้วจึงเปลี่ยนไปใช้http://localhost:<port>หากไม่สำเร็จ พาธคอลแบ็กคือ/mattermost/interactions/<accountId> - กฎการเข้าถึง: เซิร์ฟเวอร์ Mattermost ต้องเข้าถึง URL คอลแบ็กของปุ่มได้
localhostใช้งานได้เฉพาะเมื่อ Mattermost และ OpenClaw ทำงานบนโฮสต์/เนมสเปซเครือข่ายเดียวกัน channels.mattermost.interactions.allowedSourceIps: รายการอนุญาต IP ต้นทางสำหรับคอลแบ็กของปุ่ม หากไม่มีค่านี้ ระบบจะยอมรับเฉพาะต้นทางลูปแบ็ก (127.0.0.1,::1) ดังนั้นต้องเพิ่มเซิร์ฟเวอร์ Mattermost ระยะไกลลงในรายการอนุญาตนี้ มิฉะนั้นการคลิกจะถูกปฏิเสธด้วย403หากอยู่หลังพร็อกซีย้อนกลับ ให้ตั้งค่าgateway.trustedProxiesด้วย เพื่อให้ระบบระบุ IP จริงของไคลเอนต์จากส่วนหัวที่ส่งต่อ- หากเป้าหมายคอลแบ็กเป็นแบบส่วนตัว/tailnet/ภายใน ให้เพิ่มโฮสต์/โดเมนของเป้าหมายลงใน
ServiceSettings.AllowedUntrustedInternalConnectionsของ Mattermost
การผสานรวม API โดยตรง (สคริปต์ภายนอก)
สคริปต์และ Webhook ภายนอกสามารถโพสต์ปุ่มโดยตรงผ่าน Mattermost REST API แทนการผ่านเครื่องมือ message ของเอเจนต์ ควรใช้เครื่องมือ message ของ OpenClaw สำหรับการผสานรวมโดยตรง ให้นำเข้า buildButtonAttachments จาก @openclaw/mattermost/api.js หากโพสต์ 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 action: "approve", // ... any custom fields ... _token: "<hmac>", // see HMAC section below }, }, }, ], }, ], },}การสร้างโทเค็น HMAC
Gateway ตรวจสอบการคลิกปุ่มด้วย HMAC-SHA256 สคริปต์ภายนอกต้องสร้างโทเค็นที่ตรงกับตรรกะการตรวจสอบของ Gateway:
สร้างข้อมูลลับจากโทเค็นบอต
HMAC-SHA256(key="openclaw-mattermost-interactions", data=botToken) โดยเข้ารหัสเป็นเลขฐานสิบหก
สร้างออบเจ็กต์บริบท
สร้างออบเจ็กต์บริบทด้วยทุกฟิลด์ ยกเว้น _token
ทำให้เป็นอนุกรมด้วยคีย์ที่เรียงลำดับ
ทำให้เป็นอนุกรมโดยใช้ คีย์ที่เรียงลำดับแบบเวียนเกิด และ ไม่มีช่องว่าง (Gateway ปรับออบเจ็กต์ที่ซ้อนกันให้เป็นรูปแบบมาตรฐานด้วย และสร้าง JSON แบบกระชับ)
ลงนามเพย์โหลด
HMAC-SHA256(key=secret, data=serializedContext)
เพิ่มโทเค็น
เพิ่มไดเจสต์เลขฐานสิบหกที่ได้เป็น _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
json.dumpsของ Python จะเพิ่มช่องว่างโดยค่าเริ่มต้น ({"key": "val"}) ใช้separators=(",", ":")เพื่อให้ตรงกับเอาต์พุตแบบกระชับของ JavaScript ({"key":"val"})- ลงนามฟิลด์บริบท ทั้งหมด เสมอ (ยกเว้น
_token) Gateway จะตัด_tokenออก แล้วลงนามทุกอย่างที่เหลือ การลงนามเพียงบางส่วนทำให้การตรวจสอบล้มเหลวโดยไม่มีข้อความแจ้ง - ใช้
sort_keys=Trueเนื่องจาก Gateway จะเรียงลำดับคีย์ก่อนลงนาม และ Mattermost อาจจัดลำดับฟิลด์บริบทใหม่เมื่อจัดเก็บเพย์โหลด - สร้างค่าลับจากโทเค็นบอต (แบบกำหนดผลลัพธ์ได้แน่นอน) ไม่ใช่จากไบต์แบบสุ่ม ค่าลับต้องเหมือนกันทั้งในกระบวนการที่สร้างปุ่มและ Gateway ที่ทำการตรวจสอบ
อะแดปเตอร์ไดเรกทอรี
Plugin Mattermost มีอะแดปเตอร์ไดเรกทอรีที่แปลงชื่อช่องและชื่อผู้ใช้ผ่าน Mattermost API ซึ่งทำให้สามารถใช้เป้าหมาย #channel-name และ @username ใน openclaw message send รวมถึงการส่งผ่าน cron/webhook ได้
ไม่จำเป็นต้องกำหนดค่า เนื่องจากอะแดปเตอร์จะใช้โทเค็นบอตจากการกำหนดค่าบัญชี
หลายบัญชี
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" }, }, }, },}ค่าของบัญชีจะแทนที่ฟิลด์ระดับบนสุด ส่วน channels.mattermost.defaultAccount จะเลือกบัญชีที่จะใช้เมื่อไม่ได้ระบุบัญชี
การแก้ไขปัญหา
ไม่มีการตอบกลับในช่อง
ตรวจสอบว่าบอตอยู่ในช่องและกล่าวถึงบอต (oncall), ใช้คำนำหน้าทริกเกอร์ (onchar) หรือตั้งค่า chatmode: "onmessage"
ข้อผิดพลาดในการยืนยันตัวตนหรือหลายบัญชี
- ตรวจสอบโทเค็นบอต, URL ฐาน และตรวจสอบว่าบัญชีเปิดใช้งานอยู่หรือไม่
- ปัญหาเกี่ยวกับหลายบัญชี: ตัวแปรสภาพแวดล้อมมีผลเฉพาะกับบัญชี
default - โฮสต์ Mattermost แบบส่วนตัว/LAN ต้องใช้
network.dangerouslyAllowPrivateNetwork: true(การป้องกัน SSRF จะบล็อก IP ส่วนตัวโดยค่าเริ่มต้น)
คำสั่งเครื่องหมายทับแบบเนทีฟล้มเหลว
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และบันทึกเตือนว่าคอลแบ็กถูกแปลงเป็น URL ลูปแบ็ก เช่นhttp://localhost:18789/...URL ดังกล่าวอาจเข้าถึงได้เฉพาะเมื่อ Mattermost ทำงานอยู่บนโฮสต์/เนมสเปซเครือข่ายเดียวกับ OpenClaw ให้ตั้งค่าcommands.callbackUrlที่เข้าถึงได้จากภายนอกอย่างชัดเจนแทน
ปัญหาเกี่ยวกับปุ่ม
- ปุ่มปรากฏเป็นกล่องสีขาวหรือไม่ปรากฏเลย: ข้อมูลปุ่มมีรูปแบบไม่ถูกต้อง ปุ่มนำเสนอแต่ละปุ่มต้องมี
labelและvalue(ปุ่มที่ขาดรายการใดรายการหนึ่งจะถูกทิ้ง) - ปุ่มแสดงผลแต่เมื่อคลิกแล้วไม่มีการดำเนินการ: ตรวจสอบว่าสามารถเข้าถึง Gateway จากเซิร์ฟเวอร์ Mattermost ได้, IP ของเซิร์ฟเวอร์ Mattermost รวมอยู่ใน
channels.mattermost.interactions.allowedSourceIps(หากไม่มีค่านี้จะยอมรับเฉพาะลูปแบ็ก) และServiceSettings.AllowedUntrustedInternalConnectionsมีโฮสต์คอลแบ็กสำหรับเป้าหมายส่วนตัว - ปุ่มส่งคืน 404 เมื่อคลิก:
idของปุ่มอาจมีเครื่องหมายขีดกลางหรือขีดล่าง เราเตอร์การดำเนินการของ Mattermost ไม่รองรับ ID ที่มีอักขระอื่นนอกจากตัวอักษรและตัวเลข ใช้เฉพาะ[a-zA-Z0-9] - Gateway บันทึก
rejected callback source: การคลิกมาจาก IP ที่อยู่นอกinteractions.allowedSourceIpsเพิ่มเซิร์ฟเวอร์ Mattermost หรือขาเข้าของคุณลงในรายการอนุญาต และตั้งค่าgateway.trustedProxiesเมื่ออยู่หลังพร็อกซีย้อนกลับ - Gateway บันทึก
invalid _token: HMAC ไม่ตรงกัน ตรวจสอบว่าคุณลงนามฟิลด์บริบททั้งหมด (ไม่ใช่เพียงบางส่วน), ใช้คีย์ที่เรียงลำดับแล้ว และใช้ JSON แบบกระชับ (ไม่มีช่องว่าง) ดูส่วน HMAC ด้านบน - Gateway บันทึก
missing _token in context: ฟิลด์_tokenไม่อยู่ในบริบทของปุ่ม ตรวจสอบว่าได้รวมฟิลด์นี้ไว้เมื่อสร้างเพย์โหลดการผสานรวม - Gateway ปฏิเสธการคลิกด้วย
Unknown action:context.action_idไม่ตรงกับidของการดำเนินการใดๆ ในโพสต์ ตั้งค่าทั้งสองให้เป็นค่าที่ผ่านการปรับรูปแบบเดียวกัน - เอเจนต์ไม่เสนอปุ่ม: เพิ่ม
capabilities: ["inlineButtons"]ลงในการกำหนดค่าช่อง Mattermost
ที่เกี่ยวข้อง
- การกำหนดเส้นทางช่อง - การกำหนดเส้นทางเซสชันสำหรับข้อความ
- ภาพรวมช่อง - ช่องทั้งหมดที่รองรับ
- กลุ่ม - ลักษณะการทำงานของแชตกลุ่มและการควบคุมด้วยการกล่าวถึง
- การจับคู่ - การยืนยันตัวตนผ่าน DM และขั้นตอนการจับคู่
- ความปลอดภัย - โมเดลการเข้าถึงและการเสริมความปลอดภัย