Microsoft Teams(外掛)

「入此門者,當捨棄一切希望。」

更新日期:2026-01-21

狀態:支援文字 + DM 附件;頻道/群組檔案傳送需要 sharePointSiteId + Graph 權限(參閱 在群組聊天中傳送檔案)。投票透過 Adaptive Cards 傳送。

需要外掛

Microsoft Teams 以外掛形式提供,不包含在核心安裝中。

重大變更(2026.1.15): MS Teams 移出核心。如果你在使用它,必須安裝外掛。

原因:保持核心安裝輕量,並讓 MS Teams 依賴項可以獨立更新。

透過 CLI 安裝(npm 登錄):

openclaw plugins install @openclaw/msteams

本地 checkout(從 git 儲存庫執行時):

openclaw plugins install ./extensions/msteams

如果你在設定/引導過程中選擇 Teams 且偵測到 git checkout,OpenClaw 會自動提供本地安裝路徑。

詳情:外掛

快速設定(初學者)

  1. 安裝 Microsoft Teams 外掛。
  2. 建立 Azure Bot(App ID + 用戶端密碼 + 租戶 ID)。
  3. 以這些憑證設定 OpenClaw。
  4. 透過公開 URL 或通道暴露 /api/messages(預設連接埠 3978)。
  5. 安裝 Teams 應用程式套件並啟動閘道。

最小設定:

{
  channels: {
    msteams: {
      enabled: true,
      appId: "<APP_ID>",
      appPassword: "<APP_PASSWORD>",
      tenantId: "<TENANT_ID>",
      webhook: { port: 3978, path: "/api/messages" },
    },
  },
}

注意:群組聊天預設被阻擋(channels.msteams.groupPolicy: "allowlist")。要允許群組回覆,請設定 channels.msteams.groupAllowFrom(或使用 groupPolicy: "open" 允許任何成員,需要提及)。

目標

  • 透過 Teams DM、群組聊天或頻道與 OpenClaw 對話。
  • 保持確定性路由:回覆總是回到它們到達的頻道。
  • 預設採用安全的頻道行為(除非另有設定,否則需要提及)。

設定寫入

預設情況下,Microsoft Teams 允許由 /config set|unset 觸發的設定更新寫入(需要 commands.config: true)。

停用:

{
  channels: { msteams: { configWrites: false } },
}

存取控制(DM + 群組)

DM 存取

  • 預設:channels.msteams.dmPolicy = "pairing"。未知發送者在核准前會被忽略。
  • channels.msteams.allowFrom 應使用穩定的 AAD 物件 ID。
  • UPN/顯示名稱是可變的;直接匹配預設停用,僅在 channels.msteams.dangerouslyAllowNameMatching: true 時啟用。
  • 精靈在憑證允許時可透過 Microsoft Graph 將名稱解析為 ID。

群組存取

  • 預設:channels.msteams.groupPolicy = "allowlist"(除非你新增 groupAllowFrom,否則被阻擋)。使用 channels.defaults.groupPolicy 覆寫未設定時的預設值。
  • channels.msteams.groupAllowFrom 控制哪些發送者可以在群組聊天/頻道中觸發(回退到 channels.msteams.allowFrom)。
  • 設定 groupPolicy: "open" 以允許任何成員(預設仍需提及)。
  • 禁止所有頻道,設定 channels.msteams.groupPolicy: "disabled"

範例:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      groupAllowFrom: ["[email protected]"],
    },
  },
}

團隊 + 頻道允許名單

  • 透過在 channels.msteams.teams 下列出團隊和頻道來限定群組/頻道回覆的範圍。
  • 鍵應使用穩定的團隊 ID 和頻道對話 ID。
  • groupPolicy="allowlist" 且有團隊允許名單時,僅接受列出的團隊/頻道(需要提及)。
  • 設定精靈接受 Team/Channel 項目並為你儲存。
  • 啟動時,OpenClaw 會將團隊/頻道和使用者允許名單名稱解析為 ID(當 Graph 權限允許時),並記錄對應關係;未解析的團隊/頻道名稱會保持原樣但預設不用於路由,除非啟用 channels.msteams.dangerouslyAllowNameMatching: true

範例:

{
  channels: {
    msteams: {
      groupPolicy: "allowlist",
      teams: {
        "My Team": {
          channels: {
            General: { requireMention: true },
          },
        },
      },
    },
  },
}

運作方式

  1. 安裝 Microsoft Teams 外掛。
  2. 建立 Azure Bot(App ID + 密碼 + 租戶 ID)。
  3. 建立一個Teams 應用程式套件,參考機器人並包含下方的 RSC 權限。
  4. 將 Teams 應用程式上傳/安裝到團隊(或用於 DM 的個人範圍)。
  5. ~/.openclaw/openclaw.json(或環境變數)中設定 msteams 並啟動閘道。
  6. 閘道預設在 /api/messages 上監聽 Bot Framework webhook 流量。

Azure Bot 設定(先決條件)

在設定 OpenClaw 之前,你需要建立 Azure Bot 資源。

步驟 1:建立 Azure Bot

  1. 前往 建立 Azure Bot

  2. 填寫基本標籤頁:

    欄位
    Bot handle你的機器人名稱,例如 openclaw-msteams(必須唯一)
    訂用帳戶選擇你的 Azure 訂用帳戶
    資源群組建立新的或使用現有的
    定價層開發/測試用 Free
    應用程式類型Single Tenant(建議 — 見下方注意事項)
    建立類型建立新的 Microsoft App ID

棄用通知: 2025-07-31 之後已棄用建立新的多租戶機器人。新機器人請使用 Single Tenant

  1. 點擊檢閱 + 建立建立(等待約 1-2 分鐘)

步驟 2:取得憑證

  1. 前往你的 Azure Bot 資源 → 設定
  2. 複製 Microsoft App ID → 這是你的 appId
  3. 點擊管理密碼 → 前往 App Registration
  4. 憑證與密碼新增用戶端密碼 → 複製 → 這是你的 appPassword
  5. 前往概觀 → 複製目錄(租戶)ID → 這是你的 tenantId

步驟 3:設定訊息端點

  1. 在 Azure Bot → 設定
  2. 訊息端點設定為你的 webhook URL:
    • 正式環境:https://your-domain.com/api/messages
    • 本地開發:使用通道(參閱下方本地開發

步驟 4:啟用 Teams 頻道

  1. 在 Azure Bot → 頻道
  2. 點擊 Microsoft Teams → 設定 → 儲存
  3. 接受服務條款

本地開發(通道)

Teams 無法存取 localhost。本地開發請使用通道:

方式 A:ngrok

ngrok http 3978
# Copy the https URL, e.g., https://abc123.ngrok.io
# Set messaging endpoint to: https://abc123.ngrok.io/api/messages

方式 B:Tailscale Funnel

tailscale funnel 3978
# Use your Tailscale funnel URL as the messaging endpoint

Teams Developer Portal(替代方案)

除了手動建立 manifest ZIP,你也可以使用 Teams Developer Portal

  1. 點擊 + New app
  2. 填寫基本資訊(名稱、描述、開發者資訊)
  3. 前往 App featuresBot
  4. 選擇 Enter a bot ID manually 並貼上你的 Azure Bot App ID
  5. 勾選範圍:PersonalTeamGroup Chat
  6. 點擊 DistributeDownload app package
  7. 在 Teams 中:AppsManage your appsUpload a custom app → 選擇 ZIP

這通常比手動編輯 JSON manifest 更容易。

測試機器人

方式 A:Azure Web Chat(先驗證 webhook)

  1. 在 Azure Portal → 你的 Azure Bot 資源 → Test in Web Chat
  2. 傳送訊息 — 你應該會看到回應
  3. 這確認你的 webhook 端點在 Teams 設定前正常運作

方式 B:Teams(應用程式安裝後)

  1. 安裝 Teams 應用程式(側載或組織目錄)
  2. 在 Teams 中找到機器人並傳送 DM
  3. 檢查閘道記錄中的傳入活動

設定(最小純文字)

  1. 安裝 Microsoft Teams 外掛

    • 從 npm:openclaw plugins install @openclaw/msteams
    • 從本地 checkout:openclaw plugins install ./extensions/msteams
  2. 機器人註冊

    • 建立 Azure Bot(見上方)並記下:
      • App ID
      • 用戶端密碼(App password)
      • 租戶 ID(single-tenant)
  3. Teams 應用程式 manifest

    • 包含 bot 項目,botId = <App ID>
    • 範圍:personalteamgroupChat
    • supportsFiles: true(個人範圍的檔案處理需要此項)。
    • 新增 RSC 權限(下方)。
    • 建立圖示:outline.png(32x32)和 color.png(192x192)。
    • 將三個檔案一起壓縮:manifest.jsonoutline.pngcolor.png
  4. 設定 OpenClaw

    {
      "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
  5. 機器人端點

    • 將 Azure Bot 訊息端點設為:
      • https://<host>:3978/api/messages(或你選擇的路徑/連接埠)。
  6. 執行閘道

    • 當外掛已安裝且 msteams 設定含有憑證時,Teams 頻道會自動啟動。

歷史記錄上下文

  • channels.msteams.historyLimit 控制有多少近期的頻道/群組訊息會包含在提示中。
  • 回退到 messages.groupChat.historyLimit。設定 0 停用(預設 50)。
  • DM 歷史記錄可用 channels.msteams.dmHistoryLimit(使用者回合)限制。每使用者覆寫:channels.msteams.dms["<user_id>"].historyLimit

目前的 Teams RSC 權限(Manifest)

這些是 Teams 應用程式 manifest 中現有的 resourceSpecific 權限。它們僅適用於已安裝應用程式的團隊/聊天內部。

頻道(team 範圍):

  • ChannelMessage.Read.Group(Application)- 不需要 @提及即可接收所有頻道訊息
  • 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)- 不需要 @提及即可接收所有群組聊天訊息

Teams Manifest 範例(已遮蔽)

最小、有效的範例,包含必要欄位。請替換 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 必須包含你計畫使用的介面(personalteamgroupChat)。
  • bots[].supportsFiles: true 是個人範圍檔案處理所必需的。
  • authorization.permissions.resourceSpecific 必須包含頻道讀取/傳送權限(如果你需要頻道流量)。

更新現有應用程式

要更新已安裝的 Teams 應用程式(例如新增 RSC 權限):

  1. 以新設定更新 manifest.json
  2. 遞增 version 欄位(例如 1.0.01.1.0
  3. 重新壓縮 manifest 和圖示(manifest.jsonoutline.pngcolor.png
  4. 上傳新的 zip:
    • 方式 A(Teams Admin Center): Teams Admin Center → Teams apps → Manage apps → 找到你的應用程式 → Upload new version
    • 方式 B(側載): 在 Teams 中 → Apps → Manage your apps → Upload a custom app
  5. 對於團隊頻道: 在每個團隊中重新安裝應用程式以使新權限生效
  6. 完全退出並重新啟動 Teams(不僅是關閉視窗)以清除快取的應用程式中繼資料

功能:僅 RSC vs Graph

僅使用 Teams RSC(已安裝應用程式,無 Graph API 權限)

可運作:

  • 讀取頻道訊息文字內容。
  • 傳送頻道訊息文字內容。
  • 接收**個人(DM)**檔案附件。

無法運作:

  • 頻道/群組圖片或檔案內容(payload 僅包含 HTML 存根)。
  • 下載儲存在 SharePoint/OneDrive 中的附件。
  • 讀取訊息歷史記錄(超出即時 webhook 事件)。

使用 Teams RSC + Microsoft Graph Application 權限

新增:

  • 下載托管內容(貼到訊息中的圖片)。
  • 下載儲存在 SharePoint/OneDrive 中的檔案附件。
  • 透過 Graph 讀取頻道/聊天訊息歷史記錄。

RSC vs Graph API

功能RSC 權限Graph API
即時訊息是(透過 webhook)否(僅輪詢)
歷史訊息是(可查詢歷史記錄)
設定複雜度僅應用程式 manifest需要管理員同意 + token 流程
離線運作否(必須執行中)是(隨時可查詢)

結論: RSC 用於即時監聽;Graph API 用於歷史存取。要在離線期間補上遺漏的訊息,你需要具有 ChannelMessage.Read.All 的 Graph API(需要管理員同意)。

啟用 Graph 的媒體 + 歷史記錄(頻道必需)

如果你需要頻道中的圖片/檔案或想擷取訊息歷史記錄,必須啟用 Microsoft Graph 權限並授予管理員同意。

  1. 在 Entra ID(Azure AD)App Registration 中,新增 Microsoft Graph Application 權限
    • ChannelMessage.Read.All(頻道附件 + 歷史記錄)
    • Chat.Read.AllChatMessage.Read.All(群組聊天)
  2. 為租戶授予管理員同意
  3. 遞增 Teams 應用程式 manifest 版本,重新上傳,並在 Teams 中重新安裝應用程式
  4. 完全退出並重新啟動 Teams 以清除快取的應用程式中繼資料。

使用者提及的額外權限: 使用者 @提及對對話中的使用者可直接使用。但如果你想動態搜尋和提及不在當前對話中的使用者,請新增 User.Read.All(Application)權限並授予管理員同意。

已知限制

Webhook 逾時

Teams 透過 HTTP webhook 送達訊息。如果處理時間過長(例如 LLM 回應緩慢),你可能會看到:

  • 閘道逾時
  • Teams 重試訊息(導致重複)
  • 遺失的回覆

OpenClaw 透過快速回傳並主動傳送回覆來處理這個問題,但非常慢的回應仍可能造成問題。

格式

Teams 的 markdown 比 Slack 或 Discord 更受限:

  • 基本格式可運作:粗體斜體程式碼、連結
  • 複雜 markdown(表格、巢狀清單)可能無法正確顯示
  • 支援 Adaptive Cards 用於投票和任意卡片傳送(見下方)

設定

重要設定(參閱 /gateway/configuration 了解共享頻道模式):

  • channels.msteams.enabled:啟用/停用頻道。
  • channels.msteams.appIdchannels.msteams.appPasswordchannels.msteams.tenantId:機器人憑證。
  • channels.msteams.webhook.port(預設 3978
  • channels.msteams.webhook.path(預設 /api/messages
  • channels.msteams.dmPolicypairing | allowlist | open | disabled(預設:pairing)
  • channels.msteams.allowFrom:DM 允許名單(建議使用 AAD 物件 ID)。精靈在可存取 Graph 時會在設定期間將名稱解析為 ID。
  • channels.msteams.dangerouslyAllowNameMatching:緊急開關,重新啟用可變的 UPN/顯示名稱匹配和直接團隊/頻道名稱路由。
  • channels.msteams.textChunkLimit:傳出文字分段大小。
  • channels.msteams.chunkModelength(預設)或 newline(在長度分段前先依空行分割)。
  • channels.msteams.mediaAllowHosts:收到附件主機的允許名單(預設為 Microsoft/Teams 網域)。
  • channels.msteams.mediaAuthAllowHosts:媒體重試時附加 Authorization 標頭的允許名單(預設為 Graph + Bot Framework 主機)。
  • channels.msteams.requireMention:頻道/群組中需要 @提及(預設 true)。
  • channels.msteams.replyStylethread | 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 的鍵應使用明確前綴: id:e164:username:name:(舊版無前綴鍵仍僅對應到 id:)。
  • channels.msteams.sharePointSiteId:群組聊天/頻道中檔案上傳的 SharePoint 網站 ID(參閱 在群組聊天中傳送檔案)。

路由與工作階段

  • Session key 遵循標準代理格式(參閱 /concepts/session):
    • 私訊共享主工作階段(agent:<agentId>:<mainKey>)。
    • 頻道/群組訊息使用對話 ID:
      • agent:<agentId>:msteams:channel:<conversationId>
      • agent:<agentId>:msteams:group:<conversationId>

回覆風格:討論串 vs 貼文

Teams 最近在相同的底層資料模型上引入了兩種頻道 UI 風格:

風格說明建議的 replyStyle
Posts(經典)訊息以卡片形式出現,下方有討論串回覆thread(預設)
Threads(類 Slack)訊息線性流動,更像 Slacktop-level

問題: Teams API 不會公開頻道使用哪種 UI 風格。如果你使用了錯誤的 replyStyle

  • 在 Threads 風格的頻道中使用 thread → 回覆會以不自然的方式巢狀顯示
  • 在 Posts 風格的頻道中使用 top-level → 回覆會作為獨立的最上層貼文出現,而非在討論串中

解決方案: 根據頻道的設定方式逐頻道設定 replyStyle

{
  "msteams": {
    "replyStyle": "thread",
    "teams": {
      "19:[email protected]": {
        "channels": {
          "19:[email protected]": {
            "replyStyle": "top-level"
          }
        }
      }
    }
  }
}

附件與圖片

目前限制:

  • DM: 圖片和檔案附件透過 Teams bot 檔案 API 運作。
  • 頻道/群組: 附件儲存在 M365 儲存空間(SharePoint/OneDrive)中。Webhook payload 僅包含 HTML 存根,不包含實際檔案位元組。需要 Graph API 權限才能下載頻道附件。

沒有 Graph 權限時,含有圖片的頻道訊息將只接收為純文字(機器人無法存取圖片內容)。 預設情況下,OpenClaw 僅從 Microsoft/Teams 主機名下載媒體。使用 channels.msteams.mediaAllowHosts 覆寫(使用 ["*"] 允許任何主機)。 Authorization 標頭僅附加在 channels.msteams.mediaAuthAllowHosts 中的主機上(預設為 Graph + Bot Framework 主機)。保持此清單嚴格(避免多租戶後綴)。

在群組聊天中傳送檔案

機器人可以使用 FileConsentCard 流程在 DM 中傳送檔案(內建)。但在群組聊天/頻道中傳送檔案需要額外設定:

情境檔案傳送方式需要的設定
DMFileConsentCard → 使用者接受 → 機器人上傳直接可用
群組聊天/頻道上傳到 SharePoint → 分享連結需要 sharePointSiteId + Graph 權限
圖片(任何情境)Base64 編碼內嵌直接可用

為什麼群組聊天需要 SharePoint

機器人沒有個人 OneDrive 磁碟(/me/drive Graph API 端點對應用程式身份不起作用)。要在群組聊天/頻道中傳送檔案,機器人會上傳到 SharePoint 網站並建立分享連結。

設定

  1. 在 Entra ID(Azure AD)→ App Registration 中新增 Graph API 權限

    • Sites.ReadWrite.All(Application)- 上傳檔案到 SharePoint
    • Chat.Read.All(Application)- 選填,啟用每使用者分享連結
  2. 為租戶授予管理員同意

  3. 取得你的 SharePoint 網站 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"
  4. 設定 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)

檔案儲存位置

上傳的檔案儲存在設定的 SharePoint 網站預設文件庫中的 /OpenClawShared/ 資料夾中。

投票(Adaptive Cards)

OpenClaw 以 Adaptive Cards 傳送 Teams 投票(沒有原生的 Teams 投票 API)。

  • CLI:openclaw message poll --channel msteams --target conversation:<id> ...
  • 投票記錄由閘道儲存在 ~/.openclaw/msteams-polls.json 中。
  • 閘道必須保持線上以記錄投票。
  • 投票尚未自動發布結果摘要(如需查看,請檢查儲存檔案)。

Adaptive Cards(任意)

使用 message 工具或 CLI 將任何 Adaptive Card JSON 傳送給 Teams 使用者或對話。

card 參數接受 Adaptive Card JSON 物件。提供 card 時,訊息文字是選填的。

代理工具:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:<id>",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello!" }]
  }
}

CLI:

openclaw message send --channel msteams \
  --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'

參閱 Adaptive Cards 文件 了解卡片結構和範例。目標格式詳情參閱下方目標格式

目標格式

MSTeams 目標使用前綴來區分使用者和對話:

目標類型格式範例
使用者(依 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 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:[email protected]" --message "Hello"

# Send an Adaptive Card to a conversation
openclaw message send --channel msteams --target "conversation:19:[email protected]" \
  --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'

代理工具範例:

{
  "action": "send",
  "channel": "msteams",
  "target": "user:John Smith",
  "message": "Hello!"
}
{
  "action": "send",
  "channel": "msteams",
  "target": "conversation:19:[email protected]",
  "card": {
    "type": "AdaptiveCard",
    "version": "1.5",
    "body": [{ "type": "TextBlock", "text": "Hello" }]
  }
}

注意:沒有 user: 前綴時,名稱會預設解析為群組/團隊。以顯示名稱定位人員時,務必使用 user:

主動訊息

  • 主動訊息僅在使用者互動過之後才可能,因為我們會在那時儲存對話參考。
  • 參閱 /gateway/configuration 了解 dmPolicy 和允許名單控制。

團隊和頻道 ID(常見陷阱)

Teams URL 中的 groupId 查詢參數不是用於設定的團隊 ID。請從 URL 路徑中擷取 ID:

團隊 URL:

https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
                                    └────────────────────────────┘
                                    Team ID (URL-decode this)

頻道 URL:

https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
                                      └─────────────────────────┘
                                      Channel ID (URL-decode this)

設定用:

  • Team ID = /team/ 後的路徑段落(URL 解碼後,例如 19:[email protected]
  • Channel ID = /channel/ 後的路徑段落(URL 解碼後)
  • 忽略 groupId 查詢參數

私人頻道

機器人在私人頻道中的支援有限:

功能標準頻道私人頻道
機器人安裝有限
即時訊息(webhook)可能無法運作
RSC 權限行為可能不同
@提及如果機器人可存取
Graph API 歷史記錄是(需要權限)

如果私人頻道不起作用的解決方法:

  1. 使用標準頻道進行機器人互動
  2. 使用 DM — 使用者可以隨時直接向機器人傳訊
  3. 使用 Graph API 取得歷史存取(需要 ChannelMessage.Read.All

疑難排解

常見問題

  • 頻道中圖片不顯示: 缺少 Graph 權限或管理員同意。重新安裝 Teams 應用程式並完全退出/重開 Teams。
  • 頻道中沒有回應: 預設需要提及;設定 channels.msteams.requireMention=false 或按團隊/頻道設定。
  • 版本不匹配(Teams 仍顯示舊 manifest): 移除 + 重新新增應用程式並完全退出 Teams 以重新整理。
  • Webhook 回傳 401 Unauthorized: 在沒有 Azure JWT 的情況下手動測試時是預期行為 — 表示端點可達但驗證失敗。使用 Azure Web Chat 進行正確測試。

Manifest 上傳錯誤

  • 「Icon file cannot be empty」: Manifest 參考了 0 位元組的圖示檔案。建立有效的 PNG 圖示(outline.png 32x32、color.png 192x192)。
  • 「webApplicationInfo.Id already in use」: 應用程式仍安裝在另一個團隊/聊天中。先找到並解除安裝,或等待 5-10 分鐘讓傳播完成。
  • 上傳時「Something went wrong」: 改用 https://admin.teams.microsoft.com 上傳,開啟瀏覽器 DevTools(F12)→ Network 標籤,檢查回應主體以取得實際錯誤。
  • 側載失敗: 嘗試「Upload an app to your org’s app catalog」而非「Upload a custom app」— 這通常可以繞過側載限制。

RSC 權限不起作用

  1. 確認 webApplicationInfo.id 與機器人的 App ID 完全相符
  2. 重新上傳應用程式並在團隊/聊天中重新安裝
  3. 檢查你的組織管理員是否封鎖了 RSC 權限
  4. 確認你使用了正確的範圍:團隊用 ChannelMessage.Read.Group,群組聊天用 ChatMessage.Read.Chat

參考資料