Mainstream messaging
Microsoft Teams
สถานะ: รองรับข้อความ + ไฟล์แนบ DM แล้ว; การส่งไฟล์ในช่องทาง/กลุ่มต้องใช้ sharePointSiteId + สิทธิ์ Graph (ดู การส่งไฟล์ในแชทกลุ่ม) Poll จะถูกส่งผ่าน Adaptive Cards การดำเนินการกับข้อความเปิดเผย upload-file อย่างชัดเจนสำหรับการส่งที่เน้นไฟล์ก่อน
Plugin ที่มาพร้อมชุด
Microsoft Teams จัดส่งเป็น Plugin ที่มาพร้อมชุดใน OpenClaw รุ่นปัจจุบัน ดังนั้นจึงไม่จำเป็นต้องติดตั้งแยกต่างหากในบิลด์แพ็กเกจปกติ
หากคุณใช้บิลด์เก่ากว่าหรือการติดตั้งแบบกำหนดเองที่ไม่รวม Teams ที่มาพร้อมชุด ให้ติดตั้งแพ็กเกจ npm โดยตรง:
openclaw plugins install @openclaw/msteamsใช้แพ็กเกจเปล่าเพื่อติดตามแท็กรุ่นทางการปัจจุบัน ปักหมุดเวอร์ชันที่แน่นอนเฉพาะเมื่อคุณต้องการการติดตั้งที่ทำซ้ำได้เท่านั้น
เช็กเอาต์ในเครื่อง (เมื่อรันจากรีโป git):
openclaw plugins install ./path/to/local/msteams-pluginรายละเอียด: Plugins
การตั้งค่าอย่างรวดเร็ว
@microsoft/teams.cli จัดการการลงทะเบียนบอท การสร้าง manifest และการสร้างข้อมูลประจำตัวได้ในคำสั่งเดียว
1. ติดตั้งและเข้าสู่ระบบ
npm install -g @microsoft/teams.cli@previewteams loginteams status # verify you're logged in and see your tenant info2. เริ่ม tunnel (Teams เข้าถึง localhost ไม่ได้)
ติดตั้งและยืนยันตัวตนกับ devtunnel CLI หากคุณยังไม่ได้ทำ (คู่มือเริ่มต้นใช้งาน)
# One-time setup (persistent URL across sessions):devtunnel create my-openclaw-bot --allow-anonymousdevtunnel port create my-openclaw-bot -p 3978 --protocol auto # Each dev session:devtunnel host my-openclaw-bot# Your endpoint: https://<tunnel-id>.devtunnels.ms/api/messagesทางเลือกอื่น: ngrok http 3978 หรือ tailscale funnel 3978 (แต่อาจเปลี่ยน URL ในแต่ละเซสชัน)
3. สร้างแอป
teams app create \ --name "OpenClaw" \ --endpoint "https://<your-tunnel-url>/api/messages"คำสั่งเดียวนี้จะ:
- สร้างแอปพลิเคชัน Entra ID (Azure AD)
- สร้าง client secret
- สร้างและอัปโหลด Teams app manifest (พร้อมไอคอน)
- ลงทะเบียนบอท (ค่าเริ่มต้นคือ Teams-managed - ไม่ต้องมี Azure subscription)
เอาต์พุตจะแสดง CLIENT_ID, CLIENT_SECRET, TENANT_ID และ Teams App ID - จดค่าเหล่านี้ไว้สำหรับขั้นตอนถัดไป และยังเสนอให้ติดตั้งแอปใน Teams โดยตรงด้วย
4. กำหนดค่า OpenClaw โดยใช้ข้อมูลประจำตัวจากเอาต์พุต:
{ channels: { msteams: { enabled: true, appId: "<CLIENT_ID>", appPassword: "<CLIENT_SECRET>", tenantId: "<TENANT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}หรือใช้ตัวแปรสภาพแวดล้อมโดยตรง: MSTEAMS_APP_ID, MSTEAMS_APP_PASSWORD, MSTEAMS_TENANT_ID
5. ติดตั้งแอปใน Teams
teams app create จะถามให้คุณติดตั้งแอป - เลือก "ติดตั้งใน Teams" หากคุณข้ามไป คุณสามารถรับลิงก์ภายหลังได้:
teams app get <teamsAppId> --install-link6. ตรวจสอบว่าทุกอย่างทำงานได้
teams app doctor <teamsAppId>คำสั่งนี้รันการวินิจฉัยครอบคลุมการลงทะเบียนบอท การกำหนดค่าแอป AAD ความถูกต้องของ manifest และการตั้งค่า SSO
สำหรับการปรับใช้ใน production ให้พิจารณาใช้ การยืนยันตัวตนแบบ federated (ใบรับรองหรือ managed identity) แทน client secret
เป้าหมาย
- คุยกับ OpenClaw ผ่าน DM, แชทกลุ่ม หรือช่องทางของ Teams
- รักษาการกำหนดเส้นทางให้กำหนดได้แน่นอน: การตอบกลับจะกลับไปยังช่องทางที่เข้ามาเสมอ
- ใช้พฤติกรรมช่องทางที่ปลอดภัยเป็นค่าเริ่มต้น (ต้องกล่าวถึง เว้นแต่กำหนดค่าเป็นอย่างอื่น)
การเขียนค่า config
ตามค่าเริ่มต้น Microsoft Teams ได้รับอนุญาตให้เขียนการอัปเดต config ที่เรียกโดย /config set|unset (ต้องใช้ commands.config: true)
ปิดใช้งานด้วย:
{ channels: { msteams: { configWrites: false } },}การควบคุมการเข้าถึง (DM + กลุ่ม)
การเข้าถึง DM
- ค่าเริ่มต้น:
channels.msteams.dmPolicy = "pairing"ผู้ส่งที่ไม่รู้จักจะถูกละเว้นจนกว่าจะได้รับอนุมัติ channels.msteams.allowFromควรใช้ AAD object ID ที่เสถียรหรือกลุ่มการเข้าถึงผู้ส่งแบบคงที่ เช่นaccessGroup:core-team- อย่าพึ่งพาการจับคู่ UPN/display-name สำหรับ allowlist - ค่าเหล่านี้เปลี่ยนได้ OpenClaw ปิดการจับคู่ชื่อโดยตรงตามค่าเริ่มต้น; เลือกเปิดใช้อย่างชัดเจนด้วย
channels.msteams.dangerouslyAllowNameMatching: true - ตัวช่วยสร้างสามารถแปลงชื่อเป็น ID ผ่าน Microsoft Graph เมื่อข้อมูลประจำตัวอนุญาต
การเข้าถึงกลุ่ม
- ค่าเริ่มต้น:
channels.msteams.groupPolicy = "allowlist"(ถูกบล็อก เว้นแต่คุณเพิ่มgroupAllowFrom) ใช้channels.defaults.groupPolicyเพื่อแทนที่ค่าเริ่มต้นเมื่อยังไม่ได้ตั้งค่า channels.msteams.groupAllowFromควบคุมว่าผู้ส่งหรือกลุ่มการเข้าถึงผู้ส่งแบบคงที่ใดสามารถทริกเกอร์ในแชทกลุ่ม/ช่องทางได้ (fallback ไปที่channels.msteams.allowFrom)- ตั้งค่า
groupPolicy: "open"เพื่ออนุญาตสมาชิกใดก็ได้ (ยังคงกั้นด้วยการกล่าวถึงตามค่าเริ่มต้น) - หากต้องการไม่อนุญาต ช่องทางใดเลย ให้ตั้งค่า
channels.msteams.groupPolicy: "disabled"
ตัวอย่าง:
{ channels: { msteams: { groupPolicy: "allowlist", groupAllowFrom: ["00000000-0000-0000-0000-000000000000", "accessGroup:core-team"], }, },}Teams + channel allowlist
- จำกัดขอบเขตการตอบกลับของกลุ่ม/ช่องทางด้วยการระบุทีมและช่องทางภายใต้
channels.msteams.teams - คีย์ควรใช้ Teams conversation ID ที่เสถียรจากลิงก์ Teams ไม่ใช่ชื่อที่แสดงซึ่งเปลี่ยนแปลงได้
- เมื่อ
groupPolicy="allowlist"และมี teams allowlist อยู่ จะยอมรับเฉพาะทีม/ช่องทางที่ระบุไว้เท่านั้น (มีการกั้นด้วยการกล่าวถึง) - ตัวช่วยกำหนดค่ารับรายการ
Team/Channelและจัดเก็บให้คุณ - เมื่อเริ่มต้น OpenClaw จะแปลงชื่อทีม/ช่องทางและชื่อ allowlist ผู้ใช้เป็น ID (เมื่อสิทธิ์ Graph อนุญาต)
และบันทึกการแมปลง log; ชื่อทีม/ช่องทางที่แปลงไม่ได้จะคงไว้ตามที่พิมพ์ แต่ตามค่าเริ่มต้นจะถูกละเว้นสำหรับการกำหนดเส้นทาง เว้นแต่เปิดใช้งาน
channels.msteams.dangerouslyAllowNameMatching: true
ตัวอย่าง:
{ channels: { msteams: { groupPolicy: "allowlist", teams: { "My Team": { channels: { General: { requireMention: true }, }, }, }, }, },}การตั้งค่าด้วยตนเอง (โดยไม่ใช้ Teams CLI)
หากคุณใช้ Teams CLI ไม่ได้ คุณสามารถตั้งค่าบอทด้วยตนเองผ่าน Azure Portal
วิธีการทำงาน
- ตรวจสอบว่า Microsoft Teams Plugin พร้อมใช้งาน (มาพร้อมชุดในรุ่นปัจจุบัน)
- สร้าง Azure Bot (App ID + secret + tenant ID)
- สร้าง แพ็กเกจแอป Teams ที่อ้างอิงบอทและรวมสิทธิ์ RSC ด้านล่าง
- อัปโหลด/ติดตั้งแอป Teams ลงในทีม (หรือขอบเขตส่วนบุคคลสำหรับ DM)
- กำหนดค่า
msteamsใน~/.openclaw/openclaw.json(หรือตัวแปร env) แล้วเริ่ม Gateway - Gateway จะฟังทราฟฟิก Webhook ของ Bot Framework บน
/api/messagesตามค่าเริ่มต้น
ขั้นตอนที่ 1: สร้าง Azure Bot
-
ไปที่ สร้าง Azure Bot
-
กรอกแท็บ พื้นฐาน:
ฟิลด์ ค่า Bot handle ชื่อบอทของคุณ เช่น openclaw-msteams(ต้องไม่ซ้ำ)Subscription เลือก Azure subscription ของคุณ Resource group สร้างใหม่หรือใช้ที่มีอยู่ Pricing tier Free สำหรับ dev/testing Type of App Single Tenant (แนะนำ - ดูหมายเหตุด้านล่าง) Creation type Create new Microsoft App ID
- คลิก Review + create → Create (รอประมาณ 1-2 นาที)
ขั้นตอนที่ 2: รับข้อมูลประจำตัว
- ไปที่ทรัพยากร Azure Bot ของคุณ → Configuration
- คัดลอก Microsoft App ID → นี่คือ
appIdของคุณ - คลิก Manage Password → ไปที่ App Registration
- ภายใต้ Certificates & secrets → New client secret → คัดลอก Value → นี่คือ
appPasswordของคุณ - ไปที่ Overview → คัดลอก Directory (tenant) ID → นี่คือ
tenantIdของคุณ
ขั้นตอนที่ 3: กำหนดค่า Messaging Endpoint
- ใน Azure Bot → Configuration
- ตั้งค่า Messaging endpoint เป็น URL Webhook ของคุณ:
- Production:
https://your-domain.com/api/messages - Local dev: ใช้ tunnel (ดู การพัฒนาในเครื่อง ด้านล่าง)
- Production:
ขั้นตอนที่ 4: เปิดใช้งานช่องทาง Teams
- ใน Azure Bot → Channels
- คลิก Microsoft Teams → Configure → Save
- ยอมรับ Terms of Service
ขั้นตอนที่ 5: สร้าง Teams App Manifest
- รวมรายการ
botพร้อมbotId = <App ID> - ขอบเขต:
personal,team,groupChat supportsFiles: true(จำเป็นสำหรับการจัดการไฟล์ในขอบเขตส่วนบุคคล)- เพิ่มสิทธิ์ RSC (ดู สิทธิ์ RSC)
- สร้างไอคอน:
outline.png(32x32) และcolor.png(192x192) - Zip ทั้งสามไฟล์เข้าด้วยกัน:
manifest.json,outline.png,color.png
ขั้นตอนที่ 6: กำหนดค่า OpenClaw
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", appPassword: "<APP_PASSWORD>", tenantId: "<TENANT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}ตัวแปรสภาพแวดล้อม: MSTEAMS_APP_ID, MSTEAMS_APP_PASSWORD, MSTEAMS_TENANT_ID
ขั้นตอนที่ 7: รัน Gateway
ช่องทาง Teams จะเริ่มโดยอัตโนมัติเมื่อ Plugin พร้อมใช้งานและมี config msteams พร้อมข้อมูลประจำตัว
การยืนยันตัวตนแบบ federated (ใบรับรองพร้อม managed identity)
เพิ่มใน 2026.4.11
สำหรับการปรับใช้ใน production OpenClaw รองรับ การยืนยันตัวตนแบบ federated เป็นทางเลือกที่ปลอดภัยกว่า client secret มีสองวิธีให้ใช้:
ตัวเลือก A: การยืนยันตัวตนโดยใช้ใบรับรอง
ใช้ใบรับรอง PEM ที่ลงทะเบียนกับ Entra ID app registration ของคุณ
การตั้งค่า:
- สร้างหรือรับใบรับรอง (รูปแบบ PEM พร้อม private key)
- ใน Entra ID → App Registration → Certificates & secrets → Certificates → อัปโหลดใบรับรองสาธารณะ
Config:
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", certificatePath: "/path/to/cert.pem", webhook: { port: 3978, path: "/api/messages" }, }, },}ตัวแปร env:
MSTEAMS_AUTH_TYPE=federatedMSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem
ตัวเลือก B: Azure Managed Identity
ใช้ Azure Managed Identity สำหรับการยืนยันตัวตนแบบไม่ใช้รหัสผ่าน เหมาะอย่างยิ่งสำหรับการปรับใช้บนโครงสร้างพื้นฐาน Azure (AKS, App Service, Azure VMs) ที่มี managed identity พร้อมใช้งาน
วิธีการทำงาน:
- pod/VM ของบอทมี managed identity (system-assigned หรือ user-assigned)
- federated identity credential เชื่อม managed identity กับ Entra ID app registration
- ขณะรัน OpenClaw ใช้
@azure/identityเพื่อรับ token จาก Azure IMDS endpoint (169.254.169.254) - token ถูกส่งต่อให้ Teams SDK สำหรับการยืนยันตัวตนของบอท
ข้อกำหนดเบื้องต้น:
- โครงสร้างพื้นฐาน Azure ที่เปิดใช้ managed identity (AKS workload identity, App Service, VM)
- federated identity credential ที่สร้างบน Entra ID app registration
- การเข้าถึงเครือข่ายไปยัง IMDS (
169.254.169.254:80) จาก pod/VM
Config (system-assigned managed identity):
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", useManagedIdentity: true, webhook: { port: 3978, path: "/api/messages" }, }, },}การกำหนดค่า (ข้อมูลประจำตัวที่มีการจัดการที่ผู้ใช้กำหนด):
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", useManagedIdentity: true, managedIdentityClientId: "<MI_CLIENT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}ตัวแปรสภาพแวดล้อม:
MSTEAMS_AUTH_TYPE=federatedMSTEAMS_USE_MANAGED_IDENTITY=trueMSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>(เฉพาะแบบที่ผู้ใช้กำหนด)
การตั้งค่า AKS Workload Identity
สำหรับการปรับใช้ AKS ที่ใช้ workload identity:
-
เปิดใช้งาน workload identity บนคลัสเตอร์ AKS ของคุณ
-
สร้างข้อมูลประจำตัวแบบสหพันธรัฐ บนการลงทะเบียนแอป Entra ID:
bash az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{ "name": "my-bot-workload-identity", "issuer": "<AKS_OIDC_ISSUER_URL>", "subject": "system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT>", "audiences": ["api://AzureADTokenExchange"]}' -
ใส่คำอธิบายประกอบให้บัญชีบริการ Kubernetes ด้วยรหัสไคลเอนต์ของแอป:
yaml apiVersion: v1kind: ServiceAccountmetadata: name: my-bot-sa annotations: azure.workload.identity/client-id: "<APP_CLIENT_ID>" -
ติดป้ายกำกับให้พ็อด สำหรับการฉีด workload identity:
yaml metadata: labels: azure.workload.identity/use: "true" -
ตรวจสอบให้แน่ใจว่ามีการเข้าถึงเครือข่าย ไปยัง IMDS (
169.254.169.254) - หากใช้ NetworkPolicy ให้เพิ่มกฎ egress ที่อนุญาตทราฟฟิกไปยัง169.254.169.254/32บนพอร์ต 80
การเปรียบเทียบประเภทการยืนยันตัวตน
| วิธี | การกำหนดค่า | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Client secret | appPassword |
ตั้งค่าง่าย | ต้องหมุนเวียนความลับ ปลอดภัยน้อยกว่า |
| ใบรับรอง | authType: "federated" + certificatePath |
ไม่มีความลับร่วมส่งผ่านเครือข่าย | มีภาระในการจัดการใบรับรอง |
| Managed Identity | authType: "federated" + useManagedIdentity |
ไม่ต้องใช้รหัสผ่าน ไม่มีความลับให้จัดการ | ต้องใช้โครงสร้างพื้นฐาน Azure |
พฤติกรรมเริ่มต้น: เมื่อไม่ได้ตั้งค่า authType OpenClaw จะใช้การยืนยันตัวตนด้วย client secret เป็นค่าเริ่มต้น การกำหนดค่าที่มีอยู่ยังคงทำงานได้โดยไม่ต้องเปลี่ยนแปลง
การพัฒนาในเครื่อง (การทำ tunneling)
Teams ไม่สามารถเข้าถึง localhost ได้ ใช้ dev tunnel แบบคงอยู่เพื่อให้ URL ของคุณคงเดิมในแต่ละเซสชัน:
# One-time setup:devtunnel create my-openclaw-bot --allow-anonymousdevtunnel port create my-openclaw-bot -p 3978 --protocol auto # Each dev session:devtunnel host my-openclaw-botทางเลือกอื่น: ngrok http 3978 หรือ tailscale funnel 3978 (URL อาจเปลี่ยนในแต่ละเซสชัน)
หาก URL tunnel ของคุณเปลี่ยน ให้อัปเดตปลายทาง:
teams app update <teamsAppId> --endpoint "https://<new-url>/api/messages"การทดสอบบอต
เรียกใช้การวินิจฉัย:
teams app doctor <teamsAppId>ตรวจสอบการลงทะเบียนบอต แอป AAD ไฟล์ manifest และการกำหนดค่า SSO ในรอบเดียว
ส่งข้อความทดสอบ:
- ติดตั้งแอป Teams (ใช้ลิงก์ติดตั้งจาก
teams app get <id> --install-link) - ค้นหาบอตใน Teams และส่ง DM
- ตรวจสอบบันทึก Gateway สำหรับกิจกรรมขาเข้า
ตัวแปรสภาพแวดล้อม
คีย์การกำหนดค่าทั้งหมดสามารถตั้งค่าผ่านตัวแปรสภาพแวดล้อมแทนได้:
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_IDMSTEAMS_AUTH_TYPE(ไม่บังคับ:"secret"หรือ"federated")MSTEAMS_CERTIFICATE_PATH(federated + certificate)MSTEAMS_CERTIFICATE_THUMBPRINT(ไม่บังคับ ไม่จำเป็นสำหรับการยืนยันตัวตน)MSTEAMS_USE_MANAGED_IDENTITY(federated + managed identity)MSTEAMS_MANAGED_IDENTITY_CLIENT_ID(เฉพาะ MI ที่ผู้ใช้กำหนด)
การดำเนินการข้อมูลสมาชิก
OpenClaw เปิดเผยการดำเนินการ member-info ที่ใช้ Graph สำหรับ Microsoft Teams เพื่อให้เอเจนต์และระบบอัตโนมัติสามารถระบุรายละเอียดสมาชิกของช่อง (ชื่อที่แสดง อีเมล บทบาท) ได้โดยตรงจาก Microsoft Graph
ข้อกำหนด:
- สิทธิ์ RSC
Member.Read.Group(มีอยู่แล้วใน manifest ที่แนะนำ) - สำหรับการค้นหาข้ามทีม: สิทธิ์ Graph Application
User.Read.Allพร้อมความยินยอมของผู้ดูแลระบบ
การดำเนินการนี้ถูกควบคุมด้วย channels.msteams.actions.memberInfo (ค่าเริ่มต้น: เปิดใช้งานเมื่อมีข้อมูลประจำตัว Graph)
บริบทประวัติ
channels.msteams.historyLimitควบคุมจำนวนข้อความช่อง/กลุ่มล่าสุดที่จะถูกห่อเข้าไปในพรอมต์- ย้อนกลับไปใช้
messages.groupChat.historyLimitตั้งค่าเป็น0เพื่อปิดใช้งาน (ค่าเริ่มต้น 50) - ประวัติเธรดที่ดึงมาจะถูกกรองด้วยรายการอนุญาตผู้ส่ง (
allowFrom/groupAllowFrom) ดังนั้นการเติมบริบทเธรดเริ่มต้นจะรวมเฉพาะข้อความจากผู้ส่งที่ได้รับอนุญาต - บริบทไฟล์แนบที่อ้างอิง (
ReplyTo*ที่ได้มาจาก HTML การตอบกลับของ Teams) ปัจจุบันจะถูกส่งต่อไปตามที่ได้รับ - กล่าวอีกอย่างคือ รายการอนุญาตควบคุมว่าใครสามารถทริกเกอร์เอเจนต์ได้ ปัจจุบันมีเพียงเส้นทางบริบทเสริมบางรายการเท่านั้นที่ถูกกรอง
- ประวัติ DM สามารถจำกัดได้ด้วย
channels.msteams.dmHistoryLimit(รอบของผู้ใช้) การ override รายผู้ใช้:channels.msteams.dms["<user_id>"].historyLimit
สิทธิ์ RSC ของ Teams ปัจจุบัน (manifest)
ต่อไปนี้คือ สิทธิ์ resourceSpecific ที่มีอยู่ ใน manifest แอป Teams ของเรา สิทธิ์เหล่านี้ใช้เฉพาะภายในทีม/แชทที่ติดตั้งแอปเท่านั้น
สำหรับช่อง (ขอบเขตทีม):
ChannelMessage.Read.Group(Application) - รับข้อความช่องทั้งหมดโดยไม่ต้อง @mentionChannelMessage.Send.Group(Application)Member.Read.Group(Application)Owner.Read.Group(Application)ChannelSettings.Read.Group(Application)TeamMember.Read.Group(Application)TeamSettings.Read.Group(Application)
สำหรับแชทกลุ่ม:
ChatMessage.Read.Chat(Application) - รับข้อความแชทกลุ่มทั้งหมดโดยไม่ต้อง @mention
หากต้องการเพิ่มสิทธิ์ RSC ผ่าน Teams CLI:
teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type Applicationตัวอย่าง manifest ของ Teams (ปกปิดข้อมูลแล้ว)
ตัวอย่างที่น้อยที่สุดและถูกต้องพร้อมฟิลด์ที่จำเป็น แทนที่ ID และ URL
{ $schema: "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json", manifestVersion: "1.23", version: "1.0.0", id: "00000000-0000-0000-0000-000000000000", name: { short: "OpenClaw" }, developer: { name: "Your Org", websiteUrl: "https://example.com", privacyUrl: "https://example.com/privacy", termsOfUseUrl: "https://example.com/terms", }, description: { short: "OpenClaw in Teams", full: "OpenClaw in Teams" }, icons: { outline: "outline.png", color: "color.png" }, accentColor: "#5B6DEF", bots: [ { botId: "11111111-1111-1111-1111-111111111111", scopes: ["personal", "team", "groupChat"], isNotificationOnly: false, supportsCalling: false, supportsVideo: false, supportsFiles: true, }, ], webApplicationInfo: { id: "11111111-1111-1111-1111-111111111111", }, authorization: { permissions: { resourceSpecific: [ { name: "ChannelMessage.Read.Group", type: "Application" }, { name: "ChannelMessage.Send.Group", type: "Application" }, { name: "Member.Read.Group", type: "Application" }, { name: "Owner.Read.Group", type: "Application" }, { name: "ChannelSettings.Read.Group", type: "Application" }, { name: "TeamMember.Read.Group", type: "Application" }, { name: "TeamSettings.Read.Group", type: "Application" }, { name: "ChatMessage.Read.Chat", type: "Application" }, ], }, },}ข้อควรระวังของ manifest (ฟิลด์ที่ต้องมี)
bots[].botIdต้อง ตรงกับ Azure Bot App IDwebApplicationInfo.idต้อง ตรงกับ Azure Bot App IDbots[].scopesต้องรวมพื้นผิวที่คุณวางแผนจะใช้ (personal,team,groupChat)- ต้องมี
bots[].supportsFiles: trueสำหรับการจัดการไฟล์ในขอบเขตส่วนบุคคล authorization.permissions.resourceSpecificต้องรวมสิทธิ์อ่าน/ส่งของช่องหากคุณต้องการทราฟฟิกช่อง
การอัปเดตแอปที่มีอยู่
หากต้องการอัปเดตแอป Teams ที่ติดตั้งอยู่แล้ว (เช่น เพื่อเพิ่มสิทธิ์ RSC):
# Download, edit, and re-upload the manifestteams app manifest download <teamsAppId> manifest.json# Edit manifest.json locally...teams app manifest upload manifest.json <teamsAppId># Version is auto-bumped if content changedหลังจากอัปเดตแล้ว ให้ติดตั้งแอปใหม่ในแต่ละทีมเพื่อให้สิทธิ์ใหม่มีผล และ ออกจาก Teams ทั้งหมดแล้วเปิดใหม่ (ไม่ใช่แค่ปิดหน้าต่าง) เพื่อล้างเมทาดาทาแอปที่แคชไว้
การอัปเดต manifest ด้วยตนเอง (ไม่ใช้ CLI)
- อัปเดต
manifest.jsonของคุณด้วยการตั้งค่าใหม่ - เพิ่มค่าในฟิลด์
version(เช่น1.0.0→1.1.0) - บีบอัด manifest ใหม่ พร้อมไอคอน (
manifest.json,outline.png,color.png) - อัปโหลด zip ใหม่:
- Teams Admin Center: แอป Teams → จัดการแอป → ค้นหาแอปของคุณ → อัปโหลดเวอร์ชันใหม่
- Sideload: ใน Teams → แอป → จัดการแอปของคุณ → อัปโหลดแอปแบบกำหนดเอง
ความสามารถ: RSC เท่านั้น เทียบกับ Graph
ด้วย Teams RSC เท่านั้น (ติดตั้งแอปแล้ว ไม่มีสิทธิ์ Graph API)
ทำงานได้:
- อ่านเนื้อหา ข้อความ ของข้อความช่อง
- ส่งเนื้อหา ข้อความ ของข้อความช่อง
- รับไฟล์แนบใน ส่วนบุคคล (DM)
ไม่ทำงาน:
- เนื้อหารูปภาพหรือไฟล์ ของช่อง/กลุ่ม (เพย์โหลดมีเพียง HTML stub)
- ดาวน์โหลดไฟล์แนบที่เก็บไว้ใน SharePoint/OneDrive
- อ่านประวัติข้อความ (นอกเหนือจากเหตุการณ์ webhook สด)
ด้วย Teams RSC + สิทธิ์ Microsoft Graph Application
เพิ่ม:
- ดาวน์โหลดเนื้อหาที่โฮสต์ไว้ (รูปภาพที่วางลงในข้อความ)
- ดาวน์โหลดไฟล์แนบที่เก็บไว้ใน SharePoint/OneDrive
- อ่านประวัติข้อความช่อง/แชทผ่าน Graph
RSC เทียบกับ Graph API
| ความสามารถ | สิทธิ์ RSC | Graph API |
|---|---|---|
| ข้อความแบบเรียลไทม์ | ใช่ (ผ่าน webhook) | ไม่ (เฉพาะการ polling) |
| ข้อความในอดีต | ไม่ | ใช่ (สามารถ query ประวัติได้) |
| ความซับซ้อนในการตั้งค่า | เฉพาะ manifest แอป | ต้องใช้ความยินยอมของผู้ดูแลระบบ + โฟลว์โทเค็น |
| ทำงานแบบออฟไลน์ | ไม่ (ต้องรันอยู่) | ใช่ (query ได้ทุกเวลา) |
สรุป: RSC ใช้สำหรับการฟังแบบเรียลไทม์ ส่วน Graph API ใช้สำหรับการเข้าถึงข้อมูลย้อนหลัง หากต้องการตามอ่านข้อความที่พลาดไปขณะออฟไลน์ คุณต้องใช้ Graph API พร้อม ChannelMessage.Read.All (ต้องได้รับความยินยอมจากผู้ดูแลระบบ)
สื่อและประวัติที่เปิดใช้งาน Graph (จำเป็นสำหรับช่อง)
หากคุณต้องการรูปภาพ/ไฟล์ใน ช่อง หรือต้องการดึง ประวัติข้อความ คุณต้องเปิดใช้งานสิทธิ์ Microsoft Graph และให้ความยินยอมของผู้ดูแลระบบ
- ใน Entra ID (Azure AD) App Registration ให้เพิ่ม สิทธิ์ Application ของ Microsoft Graph:
ChannelMessage.Read.All(ไฟล์แนบของช่อง + ประวัติ)Chat.Read.AllหรือChatMessage.Read.All(แชทกลุ่ม)
- ให้ความยินยอมของผู้ดูแลระบบ สำหรับ tenant
- เพิ่มค่า เวอร์ชัน manifest ของแอป Teams อัปโหลดใหม่ และ ติดตั้งแอปใหม่ใน Teams
- ออกจาก Teams ทั้งหมดแล้วเปิดใหม่ เพื่อล้างเมทาดาทาแอปที่แคชไว้
สิทธิ์เพิ่มเติมสำหรับการ mention ผู้ใช้: การ @mention ผู้ใช้ทำงานได้ทันทีสำหรับผู้ใช้ในการสนทนา อย่างไรก็ตาม หากคุณต้องการค้นหาและ mention ผู้ใช้ที่ ไม่ได้อยู่ในการสนทนาปัจจุบัน แบบไดนามิก ให้เพิ่มสิทธิ์ User.Read.All (Application) และให้ความยินยอมของผู้ดูแลระบบ
ข้อจำกัดที่ทราบ
การหมดเวลาของ Webhook
Teams ส่งข้อความผ่าน HTTP webhook หากการประมวลผลใช้เวลานานเกินไป (เช่น การตอบกลับของ LLM ที่ช้า) คุณอาจเห็น:
- Gateway หมดเวลา
- Teams ลองส่งข้อความซ้ำ (ทำให้เกิดข้อความซ้ำ)
- การตอบกลับถูกทิ้ง
OpenClaw จัดการเรื่องนี้โดยตอบกลับอย่างรวดเร็วและส่งคำตอบเชิงรุก แต่คำตอบที่ช้ามากยังอาจทำให้เกิดปัญหาได้
การจัดรูปแบบ
Markdown ของ Teams มีข้อจำกัดมากกว่า Slack หรือ Discord:
- การจัดรูปแบบพื้นฐานใช้งานได้: ตัวหนา, ตัวเอียง,
code, ลิงก์ - Markdown ที่ซับซ้อน (ตาราง, รายการซ้อน) อาจแสดงผลไม่ถูกต้อง
- รองรับ Adaptive Cards สำหรับโพลและการส่งการนำเสนอเชิงความหมาย (ดูด้านล่าง)
การกำหนดค่า
การตั้งค่าหลัก (ดู /gateway/configuration สำหรับรูปแบบช่องทางที่ใช้ร่วมกัน):
channels.msteams.enabled: เปิด/ปิดช่องทางchannels.msteams.appId,channels.msteams.appPassword,channels.msteams.tenantId: ข้อมูลรับรองของบอตchannels.msteams.webhook.port(ค่าเริ่มต้น3978)channels.msteams.webhook.path(ค่าเริ่มต้น/api/messages)channels.msteams.dmPolicy:pairing | allowlist | open | disabled(ค่าเริ่มต้น: pairing)channels.msteams.allowFrom: รายการอนุญาตสำหรับ DM (แนะนำให้ใช้ AAD object IDs) วิซาร์ดจะแปลงชื่อเป็น IDs ระหว่างการตั้งค่าเมื่อมีสิทธิ์เข้าถึง Graphchannels.msteams.dangerouslyAllowNameMatching: สวิตช์ฉุกเฉินเพื่อเปิดใช้งานการจับคู่ UPN/ชื่อที่แสดงซึ่งเปลี่ยนแปลงได้ และการกำหนดเส้นทางโดยใช้ชื่อทีม/ช่องทางโดยตรงอีกครั้งchannels.msteams.textChunkLimit: ขนาดชิ้นข้อความขาออกchannels.msteams.chunkMode:length(ค่าเริ่มต้น) หรือnewlineเพื่อแยกตามบรรทัดว่าง (ขอบเขตย่อหน้า) ก่อนแบ่งชิ้นตามความยาวchannels.msteams.mediaAllowHosts: รายการอนุญาตสำหรับโฮสต์ไฟล์แนบขาเข้า (ค่าเริ่มต้นเป็นโดเมนของ Microsoft/Teams)channels.msteams.mediaAuthAllowHosts: รายการอนุญาตสำหรับแนบส่วนหัว Authorization เมื่อลองสื่อซ้ำ (ค่าเริ่มต้นเป็นโฮสต์ของ Graph + Bot Framework)channels.msteams.requireMention: ต้องมี @mention ในช่องทาง/กลุ่ม (ค่าเริ่มต้น true)channels.msteams.replyStyle:thread | top-level(ดู สไตล์การตอบกลับ)channels.msteams.teams.<teamId>.replyStyle: การแทนที่ต่อทีมchannels.msteams.teams.<teamId>.requireMention: การแทนที่ต่อทีมchannels.msteams.teams.<teamId>.tools: การแทนที่นโยบายเครื่องมือเริ่มต้นต่อทีม (allow/deny/alsoAllow) ที่ใช้เมื่อไม่มีการแทนที่ระดับช่องทางchannels.msteams.teams.<teamId>.toolsBySender: การแทนที่นโยบายเครื่องมือต่อทีมต่อผู้ส่งโดยค่าเริ่มต้น (รองรับไวลด์การ์ด"*")channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: การแทนที่ต่อช่องทางchannels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: การแทนที่ต่อช่องทางchannels.msteams.teams.<teamId>.channels.<conversationId>.tools: การแทนที่นโยบายเครื่องมือต่อช่องทาง (allow/deny/alsoAllow)channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: การแทนที่นโยบายเครื่องมือต่อช่องทางต่อผู้ส่ง (รองรับไวลด์การ์ด"*")- คีย์
toolsBySenderควรใช้คำนำหน้าที่ชัดเจน:channel:,id:,e164:,username:,name:(คีย์เดิมที่ไม่มีคำนำหน้ายังคงแมปไปที่id:เท่านั้น) channels.msteams.actions.memberInfo: เปิดหรือปิดแอ็กชันข้อมูลสมาชิกที่รองรับด้วย Graph (ค่าเริ่มต้น: เปิดใช้งานเมื่อมีข้อมูลรับรอง Graph)channels.msteams.authType: ประเภทการยืนยันตัวตน -"secret"(ค่าเริ่มต้น) หรือ"federated"channels.msteams.certificatePath: พาธไปยังไฟล์ใบรับรอง PEM (federated + certificate auth)channels.msteams.certificateThumbprint: ลายนิ้วมือใบรับรอง (ไม่บังคับ, ไม่จำเป็นสำหรับ auth)channels.msteams.useManagedIdentity: เปิดใช้งาน auth แบบ managed identity (โหมด federated)channels.msteams.managedIdentityClientId: client ID สำหรับ managed identity ที่ผู้ใช้กำหนดchannels.msteams.sharePointSiteId: SharePoint site ID สำหรับการอัปโหลดไฟล์ในแชตกลุ่ม/ช่องทาง (ดู การส่งไฟล์ในแชตกลุ่ม)
การกำหนดเส้นทางและเซสชัน
- คีย์เซสชันใช้รูปแบบเอเจนต์มาตรฐาน (ดู /concepts/session):
- ข้อความโดยตรงใช้เซสชันหลักร่วมกัน (
agent:<agentId>:<mainKey>) - ข้อความช่องทาง/กลุ่มใช้ conversation id:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- ข้อความโดยตรงใช้เซสชันหลักร่วมกัน (
สไตล์การตอบกลับ: เธรดเทียบกับโพสต์
Teams เพิ่งเพิ่มสไตล์ UI ของช่องทางสองแบบบนโมเดลข้อมูลพื้นฐานเดียวกัน:
| สไตล์ | คำอธิบาย | replyStyle ที่แนะนำ |
|---|---|---|
| โพสต์ (แบบคลาสสิก) | ข้อความปรากฏเป็นการ์ดพร้อมคำตอบแบบเธรดอยู่ด้านล่าง | thread (ค่าเริ่มต้น) |
| เธรด (คล้าย Slack) | ข้อความไหลต่อกันเป็นเส้นตรง คล้าย Slack มากกว่า | top-level |
ปัญหา: Teams API ไม่เปิดเผยว่าช่องทางใช้สไตล์ UI แบบใด หากคุณใช้ replyStyle ผิด:
threadในช่องทางสไตล์ Threads → คำตอบจะปรากฏซ้อนกันอย่างไม่เหมาะสมtop-levelในช่องทางสไตล์ Posts → คำตอบจะปรากฏเป็นโพสต์ระดับบนแยกต่างหากแทนที่จะอยู่ในเธรด
วิธีแก้: กำหนดค่า replyStyle ต่อช่องทางตามวิธีตั้งค่าช่องทางนั้น:
{ channels: { msteams: { replyStyle: "thread", teams: { "19:[email protected]": { channels: { "19:[email protected]": { replyStyle: "top-level", }, }, }, }, }, },}ลำดับความสำคัญในการแก้ค่า
เมื่อบอตส่งคำตอบเข้าไปในช่องทาง ระบบจะแก้ค่า replyStyle จากการแทนที่ที่เฉพาะเจาะจงที่สุดลงไปถึงค่าเริ่มต้น ค่าแรกที่ไม่ใช่ undefined จะชนะ:
- ต่อช่องทาง —
channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle - ต่อทีม —
channels.msteams.teams.<teamId>.replyStyle - ส่วนกลาง —
channels.msteams.replyStyle - ค่าเริ่มต้นโดยนัย — ได้มาจาก
requireMention:requireMention: true→threadrequireMention: false→top-level
หากคุณตั้ง requireMention: false แบบส่วนกลางโดยไม่มี replyStyle ที่ชัดเจน การกล่าวถึงในช่องทางสไตล์ Posts จะปรากฏเป็นโพสต์ระดับบน แม้ขาเข้าจะเป็นคำตอบในเธรดก็ตาม ปัก replyStyle: "thread" ไว้ที่ระดับส่วนกลาง ทีม หรือช่องทางเพื่อหลีกเลี่ยงผลลัพธ์ที่ไม่คาดคิด
การรักษาบริบทของเธรด
เมื่อ replyStyle: "thread" มีผล และบอตถูก @mention จากภายในเธรดของช่องทาง OpenClaw จะแนบรากเธรดเดิมกลับเข้ากับการอ้างอิงบทสนทนาขาออก (19:…@thread.tacv2;messageid=<root>) เพื่อให้คำตอบไปอยู่ในเธรดเดียวกัน สิ่งนี้ใช้ได้ทั้งกับการส่งแบบสด (ภายในเทิร์น) และการส่งเชิงรุกที่เกิดขึ้นหลังจากบริบทเทิร์นของ Bot Framework หมดอายุแล้ว (เช่น เอเจนต์ที่รันนาน, คำตอบการเรียกเครื่องมือที่เข้าคิวผ่าน mcp__openclaw__message)
รากเธรดมาจาก threadId ที่จัดเก็บไว้ในการอ้างอิงบทสนทนา การอ้างอิงที่จัดเก็บไว้เก่ากว่าซึ่งมีมาก่อน threadId จะย้อนกลับไปใช้ activityId (กิจกรรมขาเข้าที่เพาะบทสนทนาครั้งล่าสุด) ดังนั้นการปรับใช้ที่มีอยู่จึงยังทำงานได้โดยไม่ต้องเพาะใหม่
เมื่อ replyStyle: "top-level" มีผล ขาเข้าจากเธรดช่องทางจะถูกตอบเป็นโพสต์ระดับบนใหม่โดยตั้งใจ โดยไม่แนบส่วนต่อท้ายเธรด นี่คือพฤติกรรมที่ถูกต้องสำหรับช่องทางสไตล์ Threads หากคุณเห็นโพสต์ระดับบนในที่ที่คาดว่าจะเป็นคำตอบแบบเธรด แสดงว่า replyStyle ของคุณตั้งค่าไม่ถูกต้องสำหรับช่องทางนั้น
ไฟล์แนบและรูปภาพ
ข้อจำกัดปัจจุบัน:
- DM: รูปภาพและไฟล์แนบทำงานผ่าน Teams bot file APIs
- ช่องทาง/กลุ่ม: ไฟล์แนบอยู่ในพื้นที่จัดเก็บ M365 (SharePoint/OneDrive) เพย์โหลด Webhook มีเพียง HTML stub ไม่ใช่ไบต์ไฟล์จริง ต้องมีสิทธิ์ Graph API เพื่อดาวน์โหลดไฟล์แนบของช่องทาง
- สำหรับการส่งแบบเน้นไฟล์อย่างชัดเจน ให้ใช้
action=upload-fileพร้อมmedia/filePath/path;messageที่ไม่บังคับจะกลายเป็นข้อความ/ความคิดเห็นประกอบ และfilenameจะแทนที่ชื่อที่อัปโหลด
หากไม่มีสิทธิ์ Graph ข้อความช่องทางที่มีรูปภาพจะถูกรับเป็นข้อความเท่านั้น (บอตไม่สามารถเข้าถึงเนื้อหารูปภาพได้)
โดยค่าเริ่มต้น OpenClaw ดาวน์โหลดสื่อจากชื่อโฮสต์ Microsoft/Teams เท่านั้น แทนที่ด้วย channels.msteams.mediaAllowHosts (ใช้ ["*"] เพื่ออนุญาตโฮสต์ใดก็ได้)
ส่วนหัว Authorization จะถูกแนบเฉพาะสำหรับโฮสต์ใน channels.msteams.mediaAuthAllowHosts เท่านั้น (ค่าเริ่มต้นเป็นโฮสต์ Graph + Bot Framework) รักษารายการนี้ให้เข้มงวด (หลีกเลี่ยงส่วนต่อท้ายแบบหลายผู้เช่า)
การส่งไฟล์ในแชตกลุ่ม
บอตสามารถส่งไฟล์ใน DM โดยใช้โฟลว์ FileConsentCard (ในตัว) อย่างไรก็ตาม การส่งไฟล์ในแชตกลุ่ม/ช่องทาง ต้องมีการตั้งค่าเพิ่มเติม:
| บริบท | วิธีส่งไฟล์ | การตั้งค่าที่ต้องมี |
|---|---|---|
| DM | FileConsentCard → ผู้ใช้ยอมรับ → บอตอัปโหลด | ใช้งานได้ทันที |
| แชตกลุ่ม/ช่องทาง | อัปโหลดไปยัง SharePoint → แชร์ลิงก์ | ต้องมี sharePointSiteId + สิทธิ์ Graph |
| รูปภาพ (ทุกบริบท) | อินไลน์ที่เข้ารหัส Base64 | ใช้งานได้ทันที |
เหตุผลที่แชตกลุ่มต้องใช้ SharePoint
บอตไม่มีไดรฟ์ OneDrive ส่วนตัว (ปลายทาง Graph API /me/drive ใช้งานไม่ได้กับข้อมูลประจำตัวของแอปพลิเคชัน) ในการส่งไฟล์ในแชตกลุ่ม/ช่องทาง บอตจะอัปโหลดไปยัง ไซต์ SharePoint และสร้างลิงก์แชร์
การตั้งค่า
-
เพิ่มสิทธิ์ Graph API ใน Entra ID (Azure AD) → App Registration:
Sites.ReadWrite.All(Application) - อัปโหลดไฟล์ไปยัง SharePointChat.Read.All(Application) - ไม่บังคับ, เปิดใช้งานลิงก์แชร์ต่อผู้ใช้
-
ให้ความยินยอมของผู้ดูแลระบบ สำหรับผู้เช่า
-
รับ SharePoint site ID ของคุณ:
bash # Via Graph Explorer or curl with a valid token:curl -H "Authorization: Bearer $TOKEN" \ "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}" # Example: for a site at "contoso.sharepoint.com/sites/BotFiles"curl -H "Authorization: Bearer $TOKEN" \ "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles" # Response includes: "id": "contoso.sharepoint.com,guid1,guid2" -
กำหนดค่า OpenClaw:
json5 { channels: { msteams: { // ... other config ... sharePointSiteId: "contoso.sharepoint.com,guid1,guid2", }, },}
พฤติกรรมการแชร์
| สิทธิ์ | พฤติกรรมการแชร์ |
|---|---|
เฉพาะ Sites.ReadWrite.All |
ลิงก์แชร์ทั่วทั้งองค์กร (ทุกคนในองค์กรเข้าถึงได้) |
Sites.ReadWrite.All + Chat.Read.All |
ลิงก์แชร์ต่อผู้ใช้ (เฉพาะสมาชิกแชตเข้าถึงได้) |
การแชร์ต่อผู้ใช้ปลอดภัยกว่า เนื่องจากเฉพาะผู้เข้าร่วมแชตเท่านั้นที่เข้าถึงไฟล์ได้ หากไม่มีสิทธิ์ Chat.Read.All บอตจะย้อนกลับไปใช้การแชร์ทั่วทั้งองค์กร
พฤติกรรมสำรอง
| สถานการณ์ | ผลลัพธ์ |
|---|---|
แชตกลุ่ม + ไฟล์ + กำหนดค่า sharePointSiteId แล้ว |
อัปโหลดไปยัง SharePoint, ส่งลิงก์แชร์ |
แชตกลุ่ม + ไฟล์ + ไม่มี sharePointSiteId |
พยายามอัปโหลดไปยัง OneDrive (อาจล้มเหลว), ส่งเฉพาะข้อความ |
| แชตส่วนตัว + ไฟล์ | โฟลว์ FileConsentCard (ทำงานได้โดยไม่ต้องใช้ SharePoint) |
| ทุกบริบท + รูปภาพ | อินไลน์ที่เข้ารหัส Base64 (ทำงานได้โดยไม่ต้องใช้ SharePoint) |
ตำแหน่งที่จัดเก็บไฟล์
ไฟล์ที่อัปโหลดจะถูกจัดเก็บในโฟลเดอร์ /OpenClawShared/ ในไลบรารีเอกสารเริ่มต้นของไซต์ SharePoint ที่กำหนดค่าไว้
โพล (Adaptive Cards)
OpenClaw ส่งโพล Teams เป็น Adaptive Cards (ไม่มี Teams poll API แบบเนทีฟ)
- CLI:
openclaw message poll --channel msteams --target conversation:<id> ... - Gateway บันทึกคะแนนโหวตไว้ใน
~/.openclaw/msteams-polls.json - Gateway ต้องออนไลน์อยู่เพื่อบันทึกคะแนนโหวต
- โพลยังไม่โพสต์สรุปผลโดยอัตโนมัติ (ตรวจสอบไฟล์ที่เก็บข้อมูลหากจำเป็น)
การ์ดนำเสนอ
ส่ง payload การนำเสนอเชิงความหมายไปยังผู้ใช้หรือการสนทนาของ Teams โดยใช้เครื่องมือ message หรือ CLI OpenClaw จะแสดงผลเป็น Teams Adaptive Cards จากสัญญาการนำเสนอทั่วไป
พารามิเตอร์ presentation รับบล็อกเชิงความหมาย เมื่อระบุ presentation แล้ว ข้อความของ message จะเป็นตัวเลือก
เครื่องมือเอเจนต์:
{ action: "send", channel: "msteams", target: "user:<id>", presentation: { title: "Hello", blocks: [{ type: "text", text: "Hello!" }], },}CLI:
openclaw message send --channel msteams \ --target "conversation:19:[email protected]" \ --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'สำหรับรายละเอียดรูปแบบ target โปรดดู รูปแบบ target ด้านล่าง
รูปแบบ target
target ของ MSTeams ใช้คำนำหน้าเพื่อแยกระหว่างผู้ใช้และการสนทนา:
| ประเภท target | รูปแบบ | ตัวอย่าง |
|---|---|---|
| ผู้ใช้ (ตาม ID) | user:<aad-object-id> |
user:40a1a0ed-4ff2-4164-a219-55518990c197 |
| ผู้ใช้ (ตามชื่อ) | user:<display-name> |
user:John Smith (ต้องใช้ Graph API) |
| กลุ่ม/ช่อง | conversation:<conversation-id> |
conversation:19:[email protected] |
| กลุ่ม/ช่อง (ดิบ) | <conversation-id> |
19:[email protected] (หากมี @thread) |
ตัวอย่าง CLI:
# Send to a user by IDopenclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello" # Send to a user by display name (triggers Graph API lookup)openclaw message send --channel msteams --target "user:John Smith" --message "Hello" # Send to a group chat or channelopenclaw message send --channel msteams --target "conversation:19:[email protected]" --message "Hello" # Send a presentation card to a conversationopenclaw message send --channel msteams --target "conversation:19:[email protected]" \ --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello"}]}'ตัวอย่างเครื่องมือเอเจนต์:
{ action: "send", channel: "msteams", target: "user:John Smith", message: "Hello!",}{ action: "send", channel: "msteams", target: "conversation:19:[email protected]", presentation: { title: "Hello", blocks: [{ type: "text", text: "Hello" }], },}การส่งข้อความเชิงรุก
- ข้อความเชิงรุกทำได้เฉพาะ หลังจาก ผู้ใช้มีการโต้ตอบแล้วเท่านั้น เพราะเราจะเก็บการอ้างอิงการสนทนา ณ จุดนั้น
- ดู
/gateway/configurationสำหรับdmPolicyและการควบคุมด้วย allowlist
ID ของทีมและช่อง (ข้อผิดพลาดที่พบบ่อย)
พารามิเตอร์ query groupId ใน URL ของ Teams ไม่ใช่ ID ทีมที่ใช้สำหรับการกำหนดค่า ให้ดึง ID จาก path ของ URL แทน:
URL ทีม:
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... └────────────────────────────┘ Team conversation ID (URL-decode this)URL ช่อง:
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... └─────────────────────────┘ Channel ID (URL-decode this)สำหรับ config:
- คีย์ทีม = path segment หลัง
/team/(ถอดรหัส URL แล้ว เช่น19:[email protected]; tenant รุ่นเก่าอาจแสดง@thread.skypeซึ่งใช้ได้เช่นกัน) - คีย์ช่อง = path segment หลัง
/channel/(ถอดรหัส URL แล้ว) - ละเว้น พารามิเตอร์ query
groupIdสำหรับการกำหนดเส้นทางของ OpenClaw นั่นคือ Microsoft Entra group ID ไม่ใช่ Bot Framework conversation ID ที่ใช้ในกิจกรรม Teams ขาเข้า
ช่องส่วนตัว
บอทรองรับช่องส่วนตัวอย่างจำกัด:
| ฟีเจอร์ | ช่องมาตรฐาน | ช่องส่วนตัว |
|---|---|---|
| การติดตั้งบอท | ใช่ | จำกัด |
| ข้อความแบบเรียลไทม์ (webhook) | ใช่ | อาจไม่ทำงาน |
| สิทธิ์ RSC | ใช่ | อาจทำงานต่างออกไป |
| @mentions | ใช่ | หากเข้าถึงบอทได้ |
| ประวัติ Graph API | ใช่ | ใช่ (พร้อมสิทธิ์) |
วิธีเลี่ยงหากช่องส่วนตัวไม่ทำงาน:
- ใช้ช่องมาตรฐานสำหรับการโต้ตอบกับบอท
- ใช้ DM - ผู้ใช้สามารถส่งข้อความถึงบอทโดยตรงได้เสมอ
- ใช้ Graph API สำหรับการเข้าถึงข้อมูลย้อนหลัง (ต้องใช้
ChannelMessage.Read.All)
การแก้ไขปัญหา
ปัญหาทั่วไป
- รูปภาพไม่แสดงในช่อง: สิทธิ์ Graph หรือการยินยอมจากผู้ดูแลระบบหายไป ติดตั้งแอป Teams ใหม่ แล้วปิด Teams ทั้งหมดและเปิดใหม่
- ไม่มีการตอบกลับในช่อง: โดยค่าเริ่มต้นจำเป็นต้องมี mentions; ตั้งค่า
channels.msteams.requireMention=falseหรือกำหนดค่าแยกตามทีม/ช่อง - เวอร์ชันไม่ตรงกัน (Teams ยังแสดง manifest เก่า): ลบและเพิ่มแอปใหม่ แล้วปิด Teams ทั้งหมดเพื่อรีเฟรช
- 401 Unauthorized จาก webhook: เป็นสิ่งที่คาดไว้เมื่อทดสอบด้วยตนเองโดยไม่มี Azure JWT - หมายความว่า endpoint เข้าถึงได้ แต่การยืนยันตัวตนล้มเหลว ใช้ Azure Web Chat เพื่อทดสอบอย่างถูกต้อง
ข้อผิดพลาดการอัปโหลด manifest
- "Icon file cannot be empty": manifest อ้างอิงไฟล์ไอคอนที่มีขนาด 0 ไบต์ สร้างไอคอน PNG ที่ถูกต้อง (32x32 สำหรับ
outline.png, 192x192 สำหรับcolor.png) - "webApplicationInfo.Id already in use": แอปยังติดตั้งอยู่ในทีม/แชตอื่น ค้นหาและถอนการติดตั้งก่อน หรือรอ 5-10 นาทีให้การเปลี่ยนแปลงเผยแพร่
- "Something went wrong" ขณะอัปโหลด: ให้อัปโหลดผ่าน https://admin.teams.microsoft.com แทน เปิด DevTools ของเบราว์เซอร์ (F12) → แท็บ Network แล้วตรวจสอบเนื้อหา response เพื่อดูข้อผิดพลาดจริง
- Sideload ล้มเหลว: ลองใช้ "Upload an app to your org's app catalog" แทน "Upload a custom app" - วิธีนี้มักเลี่ยงข้อจำกัด sideload ได้
สิทธิ์ RSC ไม่ทำงาน
- ตรวจสอบว่า
webApplicationInfo.idตรงกับ App ID ของบอททุกตัวอักษร - อัปโหลดแอปอีกครั้งและติดตั้งใหม่ในทีม/แชต
- ตรวจสอบว่าผู้ดูแลระบบองค์กรของคุณบล็อกสิทธิ์ RSC หรือไม่
- ยืนยันว่าคุณใช้ scope ที่ถูกต้อง:
ChannelMessage.Read.Groupสำหรับทีม,ChatMessage.Read.Chatสำหรับแชตกลุ่ม
อ้างอิง
- สร้าง Azure Bot - คู่มือการตั้งค่า Azure Bot
- พอร์ทัลนักพัฒนา Teams - สร้าง/จัดการแอป Teams
- สคีมา manifest ของแอป Teams
- รับข้อความช่องด้วย RSC
- เอกสารอ้างอิงสิทธิ์ RSC
- การจัดการไฟล์ของบอท Teams (ช่อง/กลุ่มต้องใช้ Graph)
- การส่งข้อความเชิงรุก
- @microsoft/teams.cli - Teams CLI สำหรับการจัดการบอท
ที่เกี่ยวข้อง
- ภาพรวมช่อง - ช่องทั้งหมดที่รองรับ
- การจับคู่ - การยืนยันตัวตนและขั้นตอนการจับคู่ผ่าน DM
- กลุ่ม - พฤติกรรมแชตกลุ่มและการควบคุมด้วย mention
- การกำหนดเส้นทางช่อง - การกำหนดเส้นทาง session สำหรับข้อความ
- ความปลอดภัย - โมเดลการเข้าถึงและการเพิ่มความแข็งแกร่ง