发送和接收消息
对话机器人通过消息传递与用户通信,实现无缝交互。 它可以通过文本或语音交互来模拟与用户的真实对话。 必须确保机器人对话具有交互性、动态性、自适应性和用户友好性。
邮件内容
机器人和用户之间的消息交互可以包括不同类型的消息内容,这些内容:
内容类型 | 从用户到机器人 | 从机器人到用户 |
---|---|---|
富文本和表情符号 | ✔️ | ✔️ |
图片 | ✔️ | ✔️ |
自适应卡 | ❌ | ✔️ |
使用富文本消息和表情符号
Teams 机器人可以发送富文本和表情符号。 Teams 通过 UTF-16 支持表情符号,例如用于笑脸的 U+1F600。
使用图片消息
若要使机器人消息弹出,用户可以添加图片作为附件:
图片最大可以为 1024 × 1024 像素,PNG、JPEG 或 GIF 格式为 1 MB。 不支持动画 GIF。
可以使用 XML 指定每个图像的高度和宽度。 在 Markdown 中,图像大小默认为 256×256。 例如:
- ✔️ :
<img src="http://aka.ms/Fo983c" alt="Duck on a rock" height="150" width="223"></img>
。 -
❌:
![Duck on a rock](http://aka.ms/Fo983c)
.
- ✔️ :
有关附件的详细信息,请参阅 向邮件添加媒体附件。
使用自适应卡片
聊天机器人可以包含可简化业务工作流的自适应卡片。 自适应卡片提供丰富的可自定义文本、语音、图像、按钮和输入字段。 可以在机器人中创作自适应卡片,并在多个应用(如 Teams、网站等)中显示。
有关更多信息,请参阅:
以下代码演示发送简单自适应卡片的示例:
示例:发送简单的自适应卡片
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"items": [
{
"size": "large",
"text": " Simple Adaptivecard Example with a Textbox",
"type": "TextBlock",
"weight": "bolder",
"wrap": true
},
],
"spacing": "extraLarge",
"type": "Container",
"verticalContentAlignment": "center"
}
]
}
发送和接收消息
发送和接收消息是机器人的核心功能。 它使机器人能够:
- 发送和接收邮件。
- 更新和删除机器人消息。
- 发送建议的操作。
- 在 Teams 频道数据中发送消息。
在聊天中,每条消息都是 Activity
类型的 messageType: message
对象。 当有人发送消息时,Microsoft Teams 将其发布到机器人。 Teams 将 JSON 对象发送到机器人的消息传送终结点,并且它只允许一个终结点进行消息传递。 然后,机器人会检查消息以确定其类型并做出相应的响应。
基本对话通过 Bot Framework 连接器进行管理,Bot Framework 连接器是单个 REST API。 此 API 使机器人能够与 Teams 和其他频道通信。 Bot Builder SDK 提供以下功能:
- 轻松访问 Bot Framework 连接器。
- 用于管理聊天流和状态的工具。
- 添加认知服务的简单方法,例如自然语言处理 (NLP) 。
机器人使用 属性从 Teams 获取消息, Text
并且可以向用户发送一个或多个响应。
有关详细信息,请参阅 机器人消息的用户属性。
下表列出了机器人可以接收和采取措施的活动:
消息类型 | 有效负载对象 | 范围 |
---|---|---|
接收消息活动 | 消息活动 | 全部 |
接收编辑消息活动 | 消息编辑活动 | 全部 |
接收取消删除消息活动 | 消息取消删除活动 | 全部 |
接收软删除消息活动 | 邮件软删除活动 | 全部 |
接收消息活动
若要接收文本消息,请使用 Text
对象的 属性 Activity
。 在机器人的活动处理程序中,使用圈上下文对象的 Activity
来读取单一消息请求。
以下代码显示了接收消息活动的示例:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
}
接收已读回执
Teams 中的 “阅读回执 ”设置允许当收件人在一对一聊天和群组聊天中阅读聊天邮件时通知聊天邮件的发件人。 收件人阅读邮件后, “已查看 ”显示在邮件旁边。 还可以选择将机器人配置为通过“已读回执”设置接收 已读回执 事件。 已读回执事件可通过以下方式帮助你增强用户体验:
如果你的应用用户未在个人聊天中阅读该消息,则可以将机器人配置为发送后续消息。
可以使用已读回执创建反馈循环来优化机器人的体验。
注意
- 仅用户到机器人聊天方案中支持已读回执。
- 机器人的已读回执不支持团队、频道和群聊范围。
- 如果管理员或用户禁用“ 已读回执 ”设置,机器人不会收到已读回执事件。
若要接收机器人的已读回执事件,请确保以下各项:
"webApplicationInfo": {
"id": "38f0ca43-1c38-4c39-8097e-47f62c686500",
"resource": ""
},
"authorization": {
"permissions": {
"orgwide": [],
"resourceSpecific": [
{
"name": "ChatMessageReadReceipt.Read.Chat",
"type": "Application"
}
]
}
}
还可以通过图形 API添加 RSC 权限。 有关详细信息,请参阅 consentedPermissionSet
。
使用
IsMessageRead
处理程序重写 方法OnTeamsReadReceiptAsync
。帮助
IsMessageRead
程序方法可用于确定邮件是否由收件人阅读。compareMessageId
如果 小于或等于LastReadMessageId
,则消息已读取。OnTeamsReadReceiptAsync
使用IsMessageRead
帮助程序方法重写 方法以接收已读回执:protected override async Task OnTeamsReadReceiptAsync(ReadReceiptInfo readReceiptInfo, ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken) { var lastReadMessageId = readReceiptInfo.LastReadMessageId; if (IsMessageRead("{id of the message that you care}", LastReadMessageId)) { await turnContext.SendActivityAsync(MessageFactory.Text("User read the bot's message"), cancellationToken); } }
以下示例显示了机器人收到的读取回执事件请求:
{ "name": "application/vnd.microsoft.readReceipt", "type": "event", "timestamp": "2023-08-16T17:23:11.1366686Z", "id": "f:b4783e72-9d7b-2ed9-ccef-ab446c873007", "channelId": "msteams", "serviceUrl": "https://smba.trafficmanager.net/amer/", "from": { "id": "29:1-8Iuh70W9pRqV8tQK8o2nVjxz33RRGDKLf4Bh7gKnrzN8s7e4vCyrFwjkPbTCX_Co8c4aXwWvq3RBLr-WkkVMw", "aadObjectId": "5b649834-7412-4cce-9e69-176e95a394f5" }, "conversation": { "conversationType": "personal", "tenantId": "6babcaad-604b-40ac-a9d7-9fd97c0b779f", "id": "a:1xlimp68NSUxEqK0ap2rXuwC9ITauHgV2M4RaDPkeRhV8qMaFn-RyilMZ62YiVdqs8pp43yQaRKvv_U2S2gOS5nM-y_pOxVe4BW1qMGPtqD0Bv3pw-nJXF0zhDlZHMZ1Z" }, "recipient": { "id": "28:9901a8b6-4fef-428b-80b1-ddb59361adeb", "name": "Test Bot" }, "channelData": { "tenant": { "id": "6babcaad-604b-40ac-a9d7-9fd97c0b779f" } }, "value": { "lastReadMessageId": "1692206589131" } }
已为机器人接收已读回执事件的租户启用“已读回执 管理员 设置”或“ 用户设置 ”。 管理员或用户必须启用或禁用已读回执设置。
在用户到机器人聊天方案中启用机器人后,当用户读取机器人的消息时,机器人将立即收到已读回执事件。 可以通过统计事件数来跟踪用户参与情况,还可以发送上下文感知消息。
接收编辑消息活动
编辑消息时,机器人会收到编辑消息活动的通知。
若要在机器人中获取编辑消息活动通知,可以重写 OnTeamsMessageEditAsync
处理程序。
下面是编辑已发送邮件时使用的 OnTeamsMessageEditAsync
编辑消息活动通知的示例:
protected override async Task OnTeamsMessageEditAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("message is updated");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
发送消息
若要发送文本消息,请指定要作为活动发送的字符串。 在机器人的活动处理程序中,使用轮次上下文对象的 SendActivityAsync
方法发送单个消息响应。 使用 对象的 SendActivitiesAsync
方法发送多个响应。
以下代码演示了在将用户添加到对话时发送消息的示例:
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Text($"Hello and welcome!"), cancellationToken);
}
注意
在用户和机器人之间发送的消息包括消息中的内部通道数据。 此数据允许机器人在该通道上正确通信。 Bot Builder SDK 允许修改消息结构。
接收取消删除消息活动
取消删除消息时,机器人会收到取消删除消息活动的通知。
若要在机器人中获取取消删除消息活动通知,可以重写 OnTeamsMessageUndeleteAsync
处理程序。
下面是还原已删除消息时使用的 OnTeamsMessageUndeleteAsync
取消删除消息活动通知的示例:
protected override async Task OnTeamsMessageUndeleteAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("message is undeleted");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
接收软删除消息活动
软删除消息时,机器人会收到软删除消息活动的通知。
若要在机器人中获取软删除消息活动通知,可以重写 OnTeamsMessageSoftDeleteAsync
处理程序。
以下示例显示软删除消息时使用的 OnTeamsMessageSoftDeleteAsync
软删除消息活动通知:
protected override async Task OnTeamsMessageSoftDeleteAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("message is soft deleted");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
更新和删除机器人发送的消息
重要
本部分中的代码示例基于 Bot Framework SDK 版本 4.6 和更高版本。 如果要查找早期版本的文档,请参阅文档的旧版 SDK 文件夹中的 机器人 - v3 SDK 部分。
机器人可以在发送消息后动态更新消息,而不是将其用作数据的静态快照。 也可以使用 Bot Framework 的 DeleteActivity
方法删除消息。
注意
机器人无法更新或删除用户在 Microsoft Teams 中发送的消息。
更新邮件
可以针对轮询更新、按下按钮后修改可用操作或任何其他异步状态更改等方案使用动态消息更新。
新消息不需要匹配类型中的原始消息。 例如,如果原始消息包含附件,则新消息可以是简单的文本消息。
若要更新现有消息,请将具有现有活动 ID 的新 Activity
对象传递给 TurnContext
类的方法 UpdateActivityAsync
。
// Send initial message
var response = await turnContext.SendActivityAsync(MessageFactory.Attachment(card.ToAttachment()), cancellationToken);
var activityId = response.Id; // Fetch activity id.
// MessageFactory.Text(): Specifies the type of text data in a message attachment.
var newActivity = MessageFactory.Text("The new text for the activity");
newActivity.Id = activityId;
// UpdateActivityAsync(): A method that can participate in update activity events for the current turn.
await turnContext.UpdateActivityAsync(newActivity, cancellationToken);
现在,你已更新消息,请为传入活动更新按钮选择的现有卡片。
更新卡片
若要更新按钮选择的现有卡片,可以使用传入活动的 ReplyToId
。
若要更新按钮选择的现有卡片,请将具有更新卡片和 ReplyToId
活动 ID 的新 Activity
对象传递给 TurnContext
类的方法 UpdateActivityAsync
。
// Returns a message activity that contains an attachment.
var activity = MessageFactory.Attachment(card.ToAttachment());
activity.Id = turnContext.Activity.ReplyToId;
// A method that can participate in update activity events for the current turn.
await turnContext.UpdateActivityAsync(activity, cancellationToken);
现在,你已更新卡片,可以使用 Bot Framework 删除消息。
删除邮件
在 Bot Framework 中,每个消息都有其唯一的活动标识符。 可以使用 Bot Framework 的 DeleteActivity
方法删除消息。
若要删除消息,请将该活动的 ID 传递给 TurnContext
类的方法 DeleteActivityAsync
。
foreach (var activityId in _list)
{
// When overridden in a derived class, deletes an existing activity in the conversation.
await turnContext.DeleteActivityAsync(activityId, cancellationToken);
}
发送建议的操作
建议的操作使机器人能够显示用户可以选择提供输入的按钮。 建议的操作可让用户通过选择按钮来回答问题或做出选择,而不是使用键盘键入响应,从而增强用户体验。 当用户选择按钮时,它在富卡中保持可见和可访问,但对于建议的操作则不可见。 这可以防止用户在对话中选择过时的按钮。
若要向消息添加建议的操作,请设置suggestedActions
活动对象的 属性,以指定表示要呈现给用户的按钮的卡操作对象的列表。 有关详细信息,请参阅 sugestedActions
。
下面是建议操作的实现和体验的示例:
"suggestedActions": {
"actions": [
{
"type": "imBack",
"title": "Action 1",
"value": "Action 1"
},
{
"type": "imBack",
"title": "Action 2",
"value": "Action 2"
}
],
"to": [<list of recepientIds>]
}
下面演示了建议的操作的示例:
注意
-
SuggestedActions
仅支持具有基于文本的消息和自适应卡片的一对一聊天机器人。 -
SuggestedActions
具有任何聊天类型的附件的聊天机器人不支持。 -
imBack
是唯一受支持的操作类型,Teams 最多显示六个建议的操作。
在 Teams 频道数据中发送消息
对象 channelData
包含特定于 Teams 的信息,是团队和频道 ID 的明确源。 (可选)可以缓存这些 ID 并将其用作本地存储的密钥。
TeamsActivityHandler
SDK 中的 从 channelData
对象中提取重要信息,使其可访问。 但是,始终可以从 对象访问原始数据 turnContext
。
对象 channelData
不包括在个人对话的消息中,因为这些发生在频道之外。
发送到机器人的活动中的典型 channelData
对象包含以下信息:
-
eventType
:Teams 事件类型仅在 Teams 机器人中的对话事件的情况下传递。 -
tenant.id
:Microsoft Entra在所有上下文中传递的租户 ID。 -
team
:仅在频道上下文中传递,不在个人聊天中传递。-
id
:通道的 GUID。 -
name
:仅在团队 重命名事件的情况下传递的团队名称。
-
-
channel
:仅在通道上下文中传递,当机器人被提及时或对于添加机器人的团队中的频道中的事件。-
id
:通道的 GUID。 -
name
:仅在通道 修改事件的情况下传递的通道名称。
-
-
channelData.teamsTeamId
:荒废的。 仅为了向后兼容而包含此属性。 -
channelData.teamsChannelId
:荒废的。 仅为了向后兼容而包含此属性。
以下代码显示了 channelData 对象 (channelCreated 事件) 的示例:
"channelData": {
"eventType": "channelCreated",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
},
"channel": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
"name": "My New Channel"
},
"team": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
}
}
Teams 频道数据
对象 channelData
包含特定于 Teams 的信息,是团队和频道 ID 的明确源。 (可选)可以缓存这些 ID 并将其用作本地存储的密钥。
TeamsActivityHandler
SDK 中的 从 channelData
对象中提取重要信息,使其可访问。 但是,始终可以从 对象访问原始数据 turnContext
。
对象 channelData
不包括在个人对话的消息中,因为这些发生在频道之外。
发送到机器人的活动中的典型 channelData
对象包含以下信息:
-
eventType
:仅在 通道修改事件的情况下传递的 Teams 事件类型。 -
tenant.id
:Microsoft Entra在所有上下文中传递的租户 ID。 -
team
:仅在频道上下文中传递,不在个人聊天中传递。-
id
:通道的 GUID。 -
name
:仅在 (how-to/conversations/subscribe-to-conversation-events.md#team-renamed) 的情况下传递的团队名称。
-
-
channel
:仅在通道上下文中传递,当机器人被提及时或对于添加机器人的团队中的频道中的事件。-
id
:通道的 GUID。 -
name
:仅在通道 修改事件的情况下传递的通道名称。
-
-
channelData.teamsTeamId
:荒废的。 仅为了向后兼容而包含此属性。 -
channelData.teamsChannelId
:荒废的。 仅为了向后兼容而包含此属性。
channelData 对象示例
以下代码显示了 channelData 对象 (channelCreated 事件) 的示例:
"channelData": {
"eventType": "channelCreated",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
},
"channel": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
"name": "My New Channel"
},
"team": {
"id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
}
}
机器人对话 API 中的状态代码
确保在 Teams 应用中正确处理这些错误。 下表列出了生成错误的错误代码和说明:
状态代码 | 错误代码和消息值 | 说明 | 重试请求 | 开发人员操作 |
---|---|---|---|---|
400 |
代码: Bad Argument 消息:*方案特定 |
机器人提供的请求有效负载无效。 有关具体的详细信息,请参阅错误消息。 | 否 | 重新计算错误的请求有效负载。 有关详细信息,请查看返回的错误消息。 |
401 |
代码: BotNotRegistered 消息:找不到此机器人的注册。 |
找不到此机器人的注册。 | 否 | 验证机器人 ID 和密码。 确保机器人 ID (Microsoft Entra ID) 已在 Teams 开发人员门户中注册,或者在启用了“Teams”通道的情况下通过 Azure 中的 Azure 机器人通道注册进行注册。 |
403 |
代码: BotDisabledByAdmin 消息:租户管理员已禁用此机器人 |
管理员阻止了用户与机器人应用之间的交互。 管理员需要允许应用策略中的用户应用。 有关详细信息,请参阅 应用策略。 | 否 | 停止发布到聊天,直到聊天中的用户显式启动与机器人的交互,指示机器人不再被阻止。 |
403 |
代码: BotNotInConversationRoster 消息:机器人不是对话名单的一部分。 |
机器人不是对话的一部分。 需要在对话中重新安装应用。 | 否 | 在尝试发送另一个会话请求之前,请等待事件 installationUpdate ,该事件指示再次添加机器人。 |
403 |
代码: ConversationBlockedByUser 消息:用户阻止了与机器人的对话。 |
用户通过审查设置在个人聊天或频道中阻止了机器人。 | 否 | 从缓存中删除对话。 停止尝试发布到聊天,直到聊天中的用户显式启动与机器人的交互,指示机器人不再被阻止。 |
403 |
代码: ForbiddenOperationException 消息:机器人未安装在用户的个人范围内 |
主动消息由机器人发送,该机器人未安装在个人范围内。 | 否 | 在尝试发送另一个对话请求之前,请在个人范围内安装应用。 |
403 |
代码: InvalidBotApiHost 消息:机器人 API 主机无效。 对于 GCC 租户,请调用 https://smba.infra.gcc.teams.microsoft.com 。 |
机器人为属于 GCC 租户的会话调用公共 API 终结点。 | 否 | 将会话的服务 URL 更新为 https://smba.infra.gcc.teams.microsoft.com 并重试请求。 |
403 |
代码: NotEnoughPermissions 消息:*方案特定 |
机器人没有执行请求的操作所需的权限。 | 否 | 从错误消息中确定所需的操作。 |
404 |
代码: ActivityNotFoundInConversation 消息:找不到对话。 |
在对话中找不到提供的消息 ID。 消息不存在或已删除。 | 否 | 检查发送的消息 ID 是否为预期值。 如果 ID 已缓存,请删除该 ID。 |
404 |
代码: ConversationNotFound 消息:找不到对话。 |
找不到对话,因为它不存在或删除。 | 否 | 检查发送的对话 ID 是否为预期值。 如果 ID 已缓存,请删除该 ID。 |
412 |
代码: PreconditionFailed 消息:前置条件失败,请重试。 |
由于同一会话上的多个并发操作,某个依赖项的前置条件失败。 | 是 | 使用指数退避重试。 |
413 |
代码: MessageSizeTooBig 消息:消息大小过大。 |
传入请求的大小太大。 有关详细信息,请参阅 设置机器人消息的格式。 | 否 | 减小有效负载大小。 |
429 |
代码: Throttled 消息:请求过多。 还返回稍后重试时间。 |
机器人发送的请求过多。 有关详细信息,请参阅 速率限制。 | 是 | 使用 Retry-After 标头重试以确定回退时间。 |
500 |
代码: ServiceError 消息:*各 |
内部服务器错误。 | 否 | 在 开发人员社区中报告问题。 |
502 |
代码: ServiceError 消息:*各 |
服务依赖项问题。 | 是 | 使用指数退避重试。 如果问题仍然存在,请在 开发人员社区中报告问题。 |
503 | 服务不可用。 | 是 | 使用指数退避重试。 如果问题仍然存在,请在 开发人员社区中报告问题。 | |
504 | 网关超时。 | 是 | 使用指数退避重试。 如果问题仍然存在,请在 开发人员社区中报告问题。 |
状态代码重试指南
下表列出了每个状态代码的常规重试指南,机器人必须避免重试未指定的状态代码:
状态代码 | 重试策略 |
---|---|
403 | 通过调用 的 GCC API https://smba.infra.gcc.teams.microsoft.com 进行 InvalidBotApiHost 重试。 |
412 | 使用指数退避重试。 |
429 | 使用 Retry-After 标头重试,以确定等待时间(以秒为单位),以及请求之间的等待时间(如果可用)。 否则,请尽可能使用带线程 ID 的指数回退重试。 |
502 | 使用指数退避重试。 |
503 | 使用指数退避重试。 |
504 | 使用指数退避重试。 |
请求自动程序的标题
当前对机器人的传出请求在标头或 URL 中不包含任何有助于机器人路由流量的信息,而无需解压缩整个有效负载。 活动通过类似于 https://< your_domain>/api/messages 的 URL 发送到机器人。 接收请求以在标头中显示对话 ID 和租户 ID。
请求标头字段
对于异步流和同步流,两个非标准请求标头字段将添加到向机器人发送的所有请求。 下表提供了请求标头字段及其值:
字段键 | 值 |
---|---|
x-ms-conversation-id | 与请求活动相对应的对话 ID(如果适用且已确认或已验证)。 |
x-ms-tenant-id | 与请求活动中的对话相对应的租户 ID。 |
如果租户或会话 ID 不在活动中或未在服务端进行验证,则值为空。
仅接收提及的消息
若要使机器人仅获取机器人所在的 @mentioned频道或聊天消息,必须筛选消息。 使用以下代码片段使机器人能够仅接收其所在 @mentioned位置的消息:
// When ChannelMessage.Read.Group or ChatMessage.Read.Chat RSC is in the app manifest, this method is called even when bot is not @mentioned.
// This code snippet allows the bot to ignore all messages that do not @mention the bot.
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// Ignore the message if bot was not mentioned.
// Remove this if block to process all messages received by the bot.
if (!turnContext.Activity.GetMentions().Any(mention => mention.Mentioned.Id.Equals(turnContext.Activity.Recipient.Id, StringComparison.OrdinalIgnoreCase)))
{
return;
}
// Sends an activity to the sender of the incoming activity.
await turnContext.SendActivityAsync(MessageFactory.Text("Using RSC the bot can receive messages across channels or chats in team without being @mentioned."));
}
如果希望机器人接收所有消息,则无需筛选 @mention 消息。
分步指南
按照 分步 指南创建 Teams 对话机器人。