Documentation Index
Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt
Use this file to discover all available pages before exploring further.
สถานะ: รองรับข้อความ + ไฟล์แนบ 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@preview
teams login
teams status # verify you're logged in and see your tenant info
Teams CLI ขณะนี้ยังอยู่ในสถานะ preview คำสั่งและแฟล็กอาจเปลี่ยนแปลงระหว่างรุ่นได้
2. เริ่ม tunnel (Teams เข้าถึง localhost ไม่ได้)
ติดตั้งและยืนยันตัวตนกับ devtunnel CLI หากคุณยังไม่ได้ทำ (คู่มือเริ่มต้นใช้งาน)
# One-time setup (persistent URL across sessions):
devtunnel create my-openclaw-bot --allow-anonymous
devtunnel 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
ต้องใช้ --allow-anonymous เพราะ Teams ไม่สามารถยืนยันตัวตนกับ devtunnels ได้ คำขอบอทขาเข้าทุกรายการยังคงถูกตรวจสอบโดย Teams SDK โดยอัตโนมัติ
ทางเลือกอื่น: 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-link
6. ตรวจสอบว่าทุกอย่างทำงานได้
teams app doctor <teamsAppId>
คำสั่งนี้รันการวินิจฉัยครอบคลุมการลงทะเบียนบอท การกำหนดค่าแอป AAD ความถูกต้องของ manifest และการตั้งค่า SSO
สำหรับการปรับใช้ใน production ให้พิจารณาใช้ การยืนยันตัวตนแบบ federated (ใบรับรองหรือ managed identity) แทน client secret
แชทกลุ่มถูกบล็อกตามค่าเริ่มต้น (channels.msteams.groupPolicy: "allowlist") หากต้องการอนุญาตการตอบกลับในกลุ่ม ให้ตั้งค่า channels.msteams.groupAllowFrom หรือใช้ groupPolicy: "open" เพื่ออนุญาตสมาชิกใดก็ได้ (มีการกั้นด้วยการกล่าวถึง)
เป้าหมาย
- คุยกับ 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 },
},
},
},
},
},
}
การยืนยันตัวตนแบบ 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=federated
MSTEAMS_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=federated
MSTEAMS_USE_MANAGED_IDENTITY=true
MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id> (เฉพาะแบบที่ผู้ใช้กำหนด)
การตั้งค่า AKS Workload Identity
สำหรับการปรับใช้ AKS ที่ใช้ workload identity:
-
เปิดใช้งาน workload identity บนคลัสเตอร์ AKS ของคุณ
-
สร้างข้อมูลประจำตัวแบบสหพันธรัฐ บนการลงทะเบียนแอป Entra ID:
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 ด้วยรหัสไคลเอนต์ของแอป:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-bot-sa
annotations:
azure.workload.identity/client-id: "<APP_CLIENT_ID>"
-
ติดป้ายกำกับให้พ็อด สำหรับการฉีด workload identity:
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-anonymous
devtunnel 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_ID
MSTEAMS_APP_PASSWORD
MSTEAMS_TENANT_ID
MSTEAMS_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) - รับข้อความช่องทั้งหมดโดยไม่ต้อง @mention
ChannelMessage.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 ID
webApplicationInfo.id ต้อง ตรงกับ Azure Bot App ID
bots[].scopes ต้องรวมพื้นผิวที่คุณวางแผนจะใช้ (personal, team, groupChat)
- ต้องมี
bots[].supportsFiles: true สำหรับการจัดการไฟล์ในขอบเขตส่วนบุคคล
authorization.permissions.resourceSpecific ต้องรวมสิทธิ์อ่าน/ส่งของช่องหากคุณต้องการทราฟฟิกช่อง
การอัปเดตแอปที่มีอยู่
หากต้องการอัปเดตแอป Teams ที่ติดตั้งอยู่แล้ว (เช่น เพื่อเพิ่มสิทธิ์ RSC):
# Download, edit, and re-upload the manifest
teams 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 ทั้งหมดแล้วเปิดใหม่ (ไม่ใช่แค่ปิดหน้าต่าง) เพื่อล้างเมทาดาทาแอปที่แคชไว้
ความสามารถ: 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 ระหว่างการตั้งค่าเมื่อมีสิทธิ์เข้าถึง Graph
channels.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:abc...@thread.tacv2": {
channels: {
"19:xyz...@thread.tacv2": {
replyStyle: "top-level",
},
},
},
},
},
},
}
ลำดับความสำคัญในการแก้ค่า
เมื่อบอตส่งคำตอบเข้าไปในช่องทาง ระบบจะแก้ค่า replyStyle จากการแทนที่ที่เฉพาะเจาะจงที่สุดลงไปถึงค่าเริ่มต้น ค่าแรกที่ไม่ใช่ undefined จะชนะ:
- ต่อช่องทาง —
channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle
- ต่อทีม —
channels.msteams.teams.<teamId>.replyStyle
- ส่วนกลาง —
channels.msteams.replyStyle
- ค่าเริ่มต้นโดยนัย — ได้มาจาก
requireMention:
requireMention: true → thread
requireMention: 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) - อัปโหลดไฟล์ไปยัง SharePoint
Chat.Read.All (Application) - ไม่บังคับ, เปิดใช้งานลิงก์แชร์ต่อผู้ใช้
-
ให้ความยินยอมของผู้ดูแลระบบ สำหรับผู้เช่า
-
รับ SharePoint site ID ของคุณ:
# 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:
{
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:abc...@thread.tacv2" \
--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:abc123...@thread.tacv2 |
| กลุ่ม/ช่อง (ดิบ) | <conversation-id> | 19:abc123...@thread.tacv2 (หากมี @thread) |
ตัวอย่าง CLI:
# Send to a user by ID
openclaw 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 channel
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
# Send a presentation card to a conversation
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \
--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:abc...@thread.tacv2",
presentation: {
title: "Hello",
blocks: [{ type: "text", text: "Hello" }],
},
}
หากไม่มีคำนำหน้า user: ชื่อจะใช้ค่าเริ่มต้นเป็นการค้นหากลุ่มหรือทีม ใช้ user: เสมอเมื่อกำหนดเป้าหมายเป็นบุคคลด้วยชื่อที่แสดง
การส่งข้อความเชิงรุก
- ข้อความเชิงรุกทำได้เฉพาะ หลังจาก ผู้ใช้มีการโต้ตอบแล้วเท่านั้น เพราะเราจะเก็บการอ้างอิงการสนทนา ณ จุดนั้น
- ดู
/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:Bk4j...@thread.tacv2; 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 สำหรับแชตกลุ่ม
อ้างอิง
ที่เกี่ยวข้อง