Microsoft Teams (插件)
“进入此处者,皆放弃一切希望。”更新时间:2026-01-21 状态:支持文本和私聊附件;频道/群组文件发送需要
sharePointSiteId + Graph 权限(详见群聊中发送文件)。投票通过自适应卡(Adaptive Cards)发送。
需要插件
Microsoft Teams 作为插件提供,不包含于核心安装包中。 重大变更(2026.1.15): MS Teams 已从核心中拆分。若使用必须单独安装插件。 解释:此举让核心安装更轻量,且 MS Teams 依赖可独立更新。 使用 CLI 安装(npm 注册表):快速设置(初学者)
- 安装 Microsoft Teams 插件。
- 创建一个 Azure Bot(App ID + 客户端密钥 + 租户 ID)。
- 使用上述凭据配置 OpenClaw。
- 通过公网 URL 或隧道公开
/api/messages(默认端口 3978)。 - 安装 Teams 应用包并启动网关。
channels.msteams.groupPolicy: "allowlist")。若要允许群组回复,请设置 channels.msteams.groupAllowFrom,或使用 groupPolicy: "open" 允许所有成员(需 @提及)。
目标
- 通过 Teams 私聊、群组聊天或频道与 OpenClaw 对话。
- 保持路由确定性:回复总是返回到消息来源的频道。
- 默认采用安全的频道行为(需 @提及,除非另行配置)。
配置写入权限
默认情况下,Microsoft Teams 允许通过/config set|unset 命令触发的配置更新写入(需设置 commands.config: true)。
关闭方法:
访问控制(私聊 + 群组)
私聊访问- 默认:
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.teams列出团队和频道来限定群组/频道回复范围。 - 键可以是团队 ID 或名称;频道键能是会话 ID 或名称。
- 在
groupPolicy="allowlist"且存在 teams 白名单时,仅接受列表中的团队/频道(需要 @提及)。 - 配置向导可接受 Team/Channel 条目并为您存储。
- 启动时,OpenClaw 会尝试根据 Graph 权限将团队/频道及用户白名单的名称解析成 ID,并打印映射,未解析的条目保持原样。
工作原理
- 安装 Microsoft Teams 插件。
- 创建一个 Azure Bot(App ID + 密钥 + 租户 ID)。
- 构建一个 Teams 应用包,引用该 Bot 并包含以下的 RSC 权限。
- 将 Teams 应用上传/安装到团队(或个人范围用于私聊)。
- 在
~/.openclaw/openclaw.json(或环境变量)中配置msteams,启动网关。 - 网关默认监听 Bot Framework webhook 请求
/api/messages。
Azure Bot 设置(前提条件)
配置 OpenClaw 前,需要先创建 Azure Bot 资源。第 1 步:创建 Azure Bot
- 访问 创建 Azure Bot
-
填写 基础 页签内容:
字段 内容 Bot 句柄 机器人名称,例如 openclaw-msteams(必须唯一)订阅 选择您的 Azure 订阅 资源组 新建或复用已有资源组 定价层 选择 Free 适合开发/测试 应用类型 Single Tenant(推荐,详见说明) 创建类型 创建新的 Microsoft 应用 ID
弃用通知: 2025-07-31 后弃用新建多租户 Bot。新 Bot 请使用单租户。
- 点击 检查 + 创建 → 创建(等待约 1-2 分钟)
第 2 步:获取凭据
- 打开您的 Azure Bot 资源 → 配置
- 复制 Microsoft 应用 ID,即为
appId - 点击 管理密码 → 进入 App 注册
- 在 证书和机密 → 新客户端机密 处复制 值,即为
appPassword - 前往 概览 → 复制 目录 (租户) ID ,即为
tenantId
第 3 步:配置消息端点
- 在 Azure Bot → 配置
-
设置 消息端点 为您的 webhook URL:
- 生产环境:
https://your-domain.com/api/messages - 本地开发: 使用隧道(详见本地开发隧道)
- 生产环境:
第 4 步:启用 Teams 通道
- 在 Azure Bot → 通道
- 点击 Microsoft Teams → 配置 → 保存
- 同意服务条款
本地开发(隧道)
Teams 无法访问localhost,本地开发时需使用隧道:
方案 A:ngrok
Teams 开发者门户(替代方案)
无需手动制作 manifest ZIP,可通过 Teams Developer Portal:- 点击 + 新应用
- 填写基本信息(名称、描述、开发者信息)
- 转到 应用功能 → Bot
- 选择 手动输入 Bot ID 并粘贴 Azure Bot App ID
- 勾选权限范围:个人、团队、群组聊天
- 点击 分发 → 下载应用包
- 在 Teams 中:应用 → 管理你的应用 → 上传自定义应用 → 选择 ZIP 包
测试机器人
方案 A:Azure Web Chat(先验证 webhook)- 在 Azure 门户 → 您的 Azure Bot 资源 → Web Chat 测试
- 发送消息,确认收到响应
- 验证 webhook 是否正常,在 Teams 配置前先确认通畅
- 安装 Teams 应用(侧载或组织目录)
- 在 Teams 查找机器人并发送私聊消息
- 检查网关日志是否有收到请求
设置(最简文本消息)
-
安装 Microsoft Teams 插件
- 从 npm 安装:
openclaw plugins install @openclaw/msteams - 本地代码安装:
openclaw plugins install ./extensions/msteams
- 从 npm 安装:
-
机器人注册
- 创建一个 Azure Bot(见上文),并记录以下信息:
- 应用 ID
- 客户端密钥(App password)
- 租户 ID(单租户)
- 创建一个 Azure Bot(见上文),并记录以下信息:
-
Teams 应用 manifest
- 包含一个
bot条目,botId = <App ID> - 权限范围:
personal,team,groupChat supportsFiles: true(个人作用域文件处理必需)- 添加资源特定权限(RSC,见下文)
- 创建图标:
outline.png(32x32),color.png(192x192) - 将
manifest.json、outline.png、color.png打包为 ZIP
- 包含一个
-
配置 OpenClaw
也可以使用环境变量替代配置项:
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID
-
机器人端点
- 设置 Azure Bot 消息端点为:
https://<host>:3978/api/messages(或您指定的路径/端口)
- 设置 Azure Bot 消息端点为:
-
启动网关
- 安装插件且
msteams配置存在凭据时,Teams 频道自动启动
- 安装插件且
历史上下文
channels.msteams.historyLimit控制最近的频道/群组消息数量,用于上下文提示。默认 50,设为 0 表示禁用。- 回退至
messages.groupChat.historyLimit。 - 私聊历史消息长度限制支持
channels.msteams.dmHistoryLimit(用户换轮数),用户特定覆盖为channels.msteams.dms["<用户ID>"].historyLimit。
当前 Teams RSC 权限(Manifest)
以下是 Teams 应用 manifest 中的资源特定权限,仅适用于应用安装的团队或聊天内。 频道(团队作用域):ChannelMessage.Read.Group(应用权限)- 接收所有频道消息(无需 @提及)ChannelMessage.Send.Group(应用权限)Member.Read.Group(应用权限)Owner.Read.Group(应用权限)ChannelSettings.Read.Group(应用权限)TeamMember.Read.Group(应用权限)TeamSettings.Read.Group(应用权限)
ChatMessage.Read.Chat(应用权限)- 接收所有群聊消息(无需 @提及)
示例 Teams Manifest(隐去敏感信息)
最低有效示例,含必需字段,ID 和 URL 请替换。Manifest 注意事项(必填字段)
bots[].botId必须与 Azure Bot App ID 相符。webApplicationInfo.id必须与 Azure Bot App ID 相符。bots[].scopes必须包含计划使用的范围(personal,team,groupChat)。bots[].supportsFiles: true是个人作用域文件处理必需项。authorization.permissions.resourceSpecific必须包含频道读写权限,才能访问频道消息。
更新已安装应用
- 修改
manifest.json加入新设置 - 版本号递增,如
1.0.0→1.1.0 - 重新打包
manifest.json和图标 (outline.png,color.png) - 上传新版本:
- 方法 A(Teams 管理中心):Teams 管理中心 → Teams 应用 → 管理应用 → 找到您的应用 → 上传新版本
- 方法 B(侧载):Teams → 应用 → 管理你的应用 → 上传自定义应用
- 针对团队频道: 需在每个团队中重新安装应用以应用新权限
- 完全退出并重启 Teams(非仅关闭窗口)以清理缓存的应用元数据
功能对比:仅 RSC 与 Graph API
使用 仅 Teams RSC(安装应用,无 Graph API 权限)
支持:- 读取频道消息文本内容。
- 发送频道消息文本内容。
- 接收**私聊(DM)**中的文件附件。
- 频道/群组的图片或文件内容(载荷仅含 HTML 占位符)。
- 下载存储于 SharePoint/OneDrive 的附件。
- 阅读历史消息(仅限实时 webhook 事件)。
使用 Teams RSC + Microsoft Graph 应用权限
增加支持:- 下载托管内容(消息中粘贴的图片)。
- 下载存储在 SharePoint/OneDrive 的文件附件。
- 通过 Graph 阅读频道/聊天历史消息。
RSC 与 Graph API 对比
| 能力 | 仅 RSC 权限 | Graph API |
|---|---|---|
| 实时消息 | 支持(通过 webhook) | 不支持(仅轮询) |
| 历史消息 | 不支持 | 支持(可查询历史) |
| 配置复杂度 | 仅应用 manifest | 需管理员同意 + Token 流程 |
| 离线工作能力 | 不支持(必须在线) | 支持(可随时查询) |
ChannelMessage.Read.All。
启用 Graph 权限获取频道多媒体及历史消息(频道必需)
需启用 Microsoft Graph 权限并授予管理员同意,步骤:-
在 Entra ID(Azure AD)应用注册中添加 Microsoft Graph 应用权限:
ChannelMessage.Read.All(频道附件与历史消息)Chat.Read.All或ChatMessage.Read.All(群聊)
- 为租户授予管理员同意。
- 升级 Teams 应用 manifest 版本,重新上传,重新安装应用。
- 完全退出并重启 Teams,清除缓存的应用元数据。
User.Read.All(应用权限)并授予管理员同意。
已知限制
Webhook 超时
Teams 通过 HTTP webhook 投递消息。如处理时间过长(例如 LLM 响应慢):- 网关超时
- Teams 可能重复投递(导致重复消息)
- 回复丢失
格式支持
Teams Markdown 比 Slack 或 Discord 受限:- 支持基本格式:加粗、斜体、
代码、链接 - 复杂 Markdown(表格、嵌套列表)可能渲染异常
- 支持 Adaptive Cards 用于投票及自定义卡片(详见下文)
配置
关键配置项(详见/gateway/configuration 共享频道模式):
channels.msteams.enabled:启用/禁用频道。channels.msteams.appId、appPassword、tenantId:机器人凭据。channels.msteams.webhook.port(默认3978)channels.msteams.webhook.path(默认/api/messages)channels.msteams.dmPolicy:pairing | allowlist | open | disabled(默认:pairing)channels.msteams.allowFrom:私聊白名单(建议使用 AAD 对象 ID)。向导在有 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:频道/群组是否要求 @提及(默认 true)。channels.msteams.replyStyle:回复风格,thread | top-level(详见回复风格:线程 vs 帖子)。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:频道工具策略覆盖。channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender:频道按发送者的工具策略覆盖(支持"*"通配符)。toolsBySender键应使用前缀:
id:、e164:、username:、name:(旧版未带前缀的键仅映射到id:)。channels.msteams.sharePointSiteId:群聊/频道中发送文件所需的 SharePoint 站点 ID(详见群聊中发送文件)。
路由及会话
- 会话键符合标准代理格式(详见 /concepts/session):
- 私聊共用主会话:
agent:<agentId>:<mainKey> - 频道/群组消息按会话 ID 区分:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- 私聊共用主会话:
回复风格:线程 vs 帖子
Teams 最近推出两种频道 UI 样式,数据模型相同:| 样式 | 描述 | 推荐设置 replyStyle |
|---|---|---|
| 帖子(经典) | 消息以卡片形式出现,下方有线程回复 | thread(默认) |
| 线程(Slack 式) | 消息线性流动,更像 Slack | top-level |
replyStyle 会导致:
- 线程样式频道用
thread→ 回复会嵌套显示不协调 - 帖子样式频道用
top-level→ 回复变成独立顶层帖子,非线程内
replyStyle:
附件与图片
当前限制:- 私聊(DM): 支持通过 Teams 机器人文件 API 的图片和附件。
- 频道/群组: 附件存储于 M365(SharePoint/OneDrive),Webhook 载荷仅含 HTML 占位符,不含实际文件字节。需 Graph API 权限才能下载频道附件。
channels.msteams.mediaAllowHosts 覆盖(使用 ["*"] 允许任意域)。只有在
channels.msteams.mediaAuthAllowHosts 中的域才附加 Authorization 头(默认含 Graph + Bot Framework 域)。该列表应保持严格,避免多租户后缀。
在群聊中发送文件
机器人可以使用内置的 FileConsentCard 流程发送私聊文件。但群聊/频道发送文件需要额外配置:| 场景 | 发送方式 | 所需设置 |
|---|---|---|
| 私聊 | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 |
| 群聊/频道 | 上传到 SharePoint → 发送共享链接 | 需要 sharePointSiteId + Graph 权限 |
| 任意场景,图片 | Base64 编码内联 | 开箱即用 |
为什么群聊需 SharePoint
机器人没有个人 OneDrive 驱动(应用身份无/me/drive Graph API 端点),群聊/频道文件需上传到SharePoint 站点并创建共享链接。
设置步骤
-
在 Entra ID(Azure AD)应用注册中添加 Graph API 权限:
Sites.ReadWrite.All(应用) - 上传文件到 SharePointChat.Read.All(应用,选填) - 实现按用户共享链接
- 为租户授予管理员同意。
-
获取 SharePoint 站点 ID:
-
配置 OpenClaw:
共享权限行为
| 权限组合 | 共享行为 |
|---|---|
仅 Sites.ReadWrite.All | 组织内共享链接(组织中任何人均可访问) |
Sites.ReadWrite.All + Chat.Read.All | 按用户共享链接(仅聊天成员可访问) |
Chat.Read.All 权限时,回退为组织范围共享。
退回行为
| 场景 | 结果 |
|---|---|
群聊 + 文件 + 配置了 sharePointSiteId | 上传至 SharePoint,发送共享链接 |
群聊 + 文件 + 未配置 sharePointSiteId | 尝试 OneDrive 上传(可能失败),发送文本消息 |
| 个人聊天 + 文件 | 使用 FileConsentCard 流程(无须 SharePoint) |
| 任意场景 + 图片 | Base64 内联发送(无须 SharePoint) |
文件存储位置
上传文件存储于配置的 SharePoint 站点默认文档库中的/OpenClawShared/ 文件夹。
投票(自适应卡)
OpenClaw 使用 Adaptive Cards 发送 Teams 投票(Teams 无原生投票 API)。- CLI 示例:
openclaw message poll --channel msteams --target conversation:<id> ... - 投票数据保存在
~/.openclaw/msteams-polls.json。 - 网关需保持在线以记录投票。
- 目前不自动发布投票结果摘要(需自行查看存储文件)。
自适应卡(任意卡片)
使用message 工具或 CLI 向 Teams 用户或会话发送任意 Adaptive Card JSON。
card 参数接收 Adaptive Card JSON 对象,提供时消息文本可选。
Agent 工具示例:
目标格式
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:abc123...@thread.tacv2 |
| 群组/频道(原始) | <conversation-id> | 19:abc123...@thread.tacv2(包含 @thread 时可用) |
user: 前缀,名称默认为组/团队解析。按名称给个人发送时请始终使用 user: 前缀。
主动消息
- 仅在用户首次交互后可发送主动消息,因为需存储会话引用。
- 详见
/gateway/configuration的dmPolicy和允许列表设置。
团队和频道 ID(常见误区)
Teams URL 查询参数中的groupId 不是用于配置的团队 ID。请从 URL 路径提取:
团队 URL 示例:
- 团队 ID = 路径
/team/后的段(URL 解码) - 频道 ID = 路径
/channel/后的段(URL 解码) - 忽略
groupId查询参数
私有频道
机器人在私有频道支持有限:| 功能 | 标准频道支持 | 私有频道支持 |
|---|---|---|
| 机器人安装 | 是 | 有限制 |
| 实时消息(Webhook) | 支持 | 可能不支持 |
| RSC 权限 | 支持 | 可能表现不同 |
| @提及 | 支持 | 只要机器人可访问即可 |
| Graph API 历史访问 | 支持 | 仅限权限下支持 |
- 使用标准频道与机器人交互
- 使用私聊 - 用户始终能私聊机器人
- 使用 Graph API 访问历史消息(需
ChannelMessage.Read.All)
故障排查
常见问题
- 频道图片不显示: 缺少 Graph 权限或管理员同意。卸载重新安装 Teams 应用并完全退出重启 Teams。
- 频道无回复: 默认需 @提及。可设置
channels.msteams.requireMention=false或针对团队/频道配置。 - 版本不匹配(Teams 显示旧 manifest): 移除后重新添加应用,完全退出 Teams 刷新缓存。
- Webhook 401 未授权: 手动测试时常见,表示端点可达但身份认证失败。使用 Azure Web Chat 测试更合适。
Manifest 上传错误
- “图标文件不能为空”: Manifest 引用的图标文件大小为 0。请创建有效 PNG 图标(outline.png 32x32,color.png 192x192)。
- “webApplicationInfo.Id 已被使用”: 应用仍安装在其他团队/聊天中。先找到并卸载,或等待 5-10 分钟传播。
- 上传出错“出了点问题”: 使用 https://admin.teams.microsoft.com 上传,打开浏览器开发者工具(F12)→网络标签,查看响应体获得具体错误。
- 侧载失败: 尝试“上传到组织应用目录”替代“上传自定义应用”,常能绕过侧载限制。
RSC 权限无效
- 核实
webApplicationInfo.id与 Bot App ID 完全一致 - 重新上传应用并重装团队/聊天中应用
- 检查组织管理员是否禁用了 RSC 权限
- 确认所用权限范围正确:团队为
ChannelMessage.Read.Group,群聊为ChatMessage.Read.Chat
参考文档
- 创建 Azure Bot – Azure Bot 设置指南
- Teams Developer Portal – 创建/管理 Teams 应用
- Teams 应用 manifest schema
- 通过 RSC 接收频道消息
- RSC 权限参考
- Teams 机器人文件处理(频道/群组需 Graph 支持)
- 主动消息