使用邮件扩展启动操作

重要

本部分中的文章基于 v3 Bot Framework SDK。 如果要查找当前文档 (4.6 或更高版本的 SDK) ,请参阅 与消息扩展的面向任务的交互 部分。

基于操作的消息扩展允许用户在 Teams 中触发外部服务中的操作。

屏幕截图是显示消息扩展卡的示例。

向应用添加消息扩展

消息扩展是一种云托管服务,用于侦听用户请求并使用结构化数据(如)进行响应。 可以通过 Bot Framework Activity 对象将服务与 Microsoft Teams 集成。 Bot Builder SDK 的 .NET 和 Node.js 扩展可以帮助你将消息扩展功能添加到应用。

显示 Teams 中基于操作的消息扩展的屏幕截图。

在 Bot Framework 中注册

如果尚未这样做,必须先向Microsoft Bot Framework注册机器人。 机器人的 Microsoft 应用 ID 和回调终结点(如其中定义)将在消息扩展中使用来接收和响应用户请求。 请记住为机器人启用 Microsoft Teams 频道。

记下机器人应用 ID 和应用密码,需要在应用清单中提供应用 ID。

更新应用清单

与机器人和选项卡一样,更新应用的 清单 以包含消息扩展属性。 这些属性控制消息扩展在 Microsoft Teams 客户端中的显示方式和行为方式。 从清单 v1.0 开始支持消息扩展。

声明消息扩展

若要添加消息扩展,请使用 属性在清单 composeExtensions 中包含新的顶级 JSON 结构。 目前,只能为应用创建单个消息扩展。

注意

清单将消息扩展称为 composeExtensions。 这是为了保持向后兼容性。

扩展定义是具有以下结构的对象:

属性名称 用途 是否必需?
botId 使用 Bot Framework 注册的自动程序的唯一 Microsoft 应用 ID。 这通常应与整个 Teams 应用的 ID 相同。
scopes 声明此扩展是否可以添加到 personalteam 范围 (或两者) 的数组。
canUpdateConfiguration 启用 “设置” 菜单项。
commands 此消息扩展支持的命令数组。 限制为 10 个命令。

定义命令

消息扩展应声明一个命令,当用户从撰写框中的 “更多选项 (” “) ”按钮中选择你的应用时,将显示该命令。

屏幕截图是一个示例,其中显示了 Teams 中的消息扩展列表。

在应用清单中,命令项是具有以下结构的对象:

属性名称 用途 是否必需? 最低清单版本
id 分配给此命令的唯一 ID。 用户请求将包含此 ID。 1.0
title 命令名称。 此值显示在 UI 中。 1.0
description 指示此命令执行的操作的帮助文本。 此值显示在 UI 中。 1.0
type 设置命令的类型。 可取值包括 queryaction。 如果不存在,则默认值设置为 query 1.4
initialRun 可选参数,与命令一 query 起使用。 如果设置为 true,则指示应在用户在 UI 中选择此命令后立即执行此命令。 1.0
fetchTask 可选参数,与命令一 action 起使用。 设置为 true 可提取要显示在任务模块中的自适应卡或 Web URL。 当命令的输入是动态的 action ,而不是一组静态参数时,会使用此方法。 请注意,如果设置为 true,将忽略命令的静态参数列表。 1.4
parameters 命令的参数的静态列表。 1.0
parameter.name 参数的名称。 这会在用户请求中发送到服务。 1.0
parameter.description 描述此参数的用途和应提供的值的示例。 此值显示在 UI 中。 1.0
parameter.title 简短的用户友好型参数标题或标签。 1.0
parameter.inputType 设置为所需的输入类型。 可能的值包括 text、、textareanumberdatetimetoggle。 默认值设置为 text 1.4
context 可选的值数组,用于定义消息操作可用的上下文。 可能的值为 messagecomposecommandBox。 默认值为“["compose", "commandBox"]”。 1.5

操作类型消息扩展

若要从消息扩展启动操作,请将 type 参数设置为 action。 单个消息扩展最多可以包含 10 个不同的命令,并包含多个基于搜索和基于操作的命令。

注意

justInTimeInstall 将应用上传到应用目录时,函数为 ,但在上传自定义应用时失败。

完整的应用清单示例

以下代码是具有搜索和 create 命令的清单示例:

{
  "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.8/MicrosoftTeams.schema.json",
  "manifestVersion": "1.5",
  "version": "1.0",
  "id": "57a3c29f-1fc5-4d97-a142-35bb662b7b23",
  "developer": {
    "name": "John Developer",
    "websiteUrl": "http://todobotservice.azurewebsites.net/",
    "privacyUrl": "http://todobotservice.azurewebsites.net/privacy",
    "termsOfUseUrl": "http://todobotservice.azurewebsites.net/termsofuse"
  },
  "name": {
    "short": "To Do",
    "full": "To Do"
  },
  "description": {
    "short": "Find or create a new task in To Do",
    "full": "Find or create a new task in To Do"
  },
  "icons": {
    "outline": "todo-outline.jpg",
    "color": "todo-color.jpg"
  },
  "accentColor": "#ff6a00",
  "composeExtensions": [
    {
      "botId": "57a3c29f-1fc5-4d97-a142-35bb662b7b23",
      "canUpdateConfiguration": true,
      "commands": [
        {
          "id": "searchCmd",
          "description": "Search you Todo's",
          "title": "Search",
          "initialRun": true,
          "context": ["commandBox", "compose"],
          "parameters": [
            {
              "name": "searchKeyword",
              "description": "Enter your search keywords",
              "title": "Keywords"
            }
          ]
        },
        {
          "id": "addTodo",
          "description": "Create a To Do item",
          "title": "Create To Do",
          "type": "action",
          "context": ["commandBox", "message", "compose"],
          "parameters": [
            {
              "name": "Name",
              "description": "To Do Title",
              "title": "Title",
              "inputType": "text"
            },
            {
              "name": "Description",
              "description": "Description of the task",
              "title": "Description",
              "inputType": "textarea"
            },
            {
              "name": "Date",
              "description": "Due date for the task",
              "title": "Date",
              "inputType": "date"
            }
          ]
        },
        {
          "id": "reassignTodo",
          "description": "Reassign a todo item",
          "title": "Reassign a todo item",
          "type": "action",
          "fetchTask": false,
          "parameters": [
            {
              "name": "Name",
              "title": "Title"
              "inputType": "text"
            }
          ]
        }
      ]
    }
  ],
  "permissions": [
    "identity",
    "messageTeamMembers"
  ],
  "validDomains": [
    "todobotservice.azurewebsites.net",
    "*.todobotservice.azurewebsites.net"
  ]
}

从消息启动操作

可以从撰写消息区域启动操作,也可以使用消息扩展从消息启动操作,以便将消息内容发送到机器人进行处理。 可以选择使用响应提交中所述的方法 响应该消息。 响应将作为邮件的回复包含在内,用户可以在提交之前对其进行编辑。

用户可以从溢出...菜单的“执行操作”选项访问消息扩展,如下图所示:

屏幕截图介绍了如何从消息启动操作。

若要使消息扩展能够处理消息,请将 参数添加到 context 应用清单中的消息扩展 commands 的对象,如以下示例所示。 数组的有效字符串 context"message""commandBox""compose"。 默认值为 ["compose", "commandBox"]。 有关参数的完整详细信息context,请参阅定义命令部分:

"composeExtensions": [
  {
    "botId": "57a3c29f-1fc5-4d97-a142-35bb662b7b23",
    "canUpdateConfiguration": true,
    "commands": [
      {
        "id": "reassignTodo",
        "description": "Reassign a todo item",
        "title": "Create To Do",
        "type": "Action",
        "context": ["message"],
        "fetchTask": true
    }]
    ...

以下代码是 对象的一个示例, value 其中包含作为请求的 composeExtensions 一部分发送到机器人的消息详细信息:

{
  "name": "composeExtension/submitAction",
  "type": "invoke",
...
  "value": {
    "commandId": "setReminder",
    "commandContext": "message",
    "messagePayload": {
      "id": "1111111111",
      "replyToId": null,
      "createdDateTime": "2019-02-25T21:29:36.065Z",
      "lastModifiedDateTime": null,
      "deleted": false,
      "subject": "Message subject",
      "summary": null,
      "importance": "normal",
      "locale": "en-us",
      "body": {
        "contentType": "html",
        "content": "this is the message"
    },
      "from": {
        "device": null,
        "conversation": null,
        "user": {
          "userIdentityType": "aadUser",
          "id": "wxyz12ab8-ab12-cd34-ef56-098abc123876",
          "displayName": "Jamie Smythe"
        },
        "application": null
      },
      "reactions": [
        {
          "reactionType": "like",
          "createdDateTime": "2019-02-25T22:40:40.806Z",
          "user": {
            "device": null,
            "conversation": null,
            "user": {
              "userIdentityType": "aadUser",
              "id": "qrst12346-ab12-cd34-ef56-098abc123876",
              "displayName": "Jim Brown"
            },
            "application": null
          }
        }
      ],
      "mentions": [
        {
          "id": 0,
          "mentionText": "Sarah",
          "mentioned": {
            "device": null,
            "conversation": null,
            "user": {
              "userIdentityType": "aadUser",
              "id": "ab12345678-ab12-cd34-ef56-098abc123876",
              "displayName": "Sarah"
            },
            "application": null
          }
        }
      ]
    }
  ...

通过上传进行测试

可以通过上传应用来测试消息扩展。 有关详细信息,请参阅 在团队中上传应用

若要打开消息扩展,请转到任何聊天或频道。 选择撰写框中的“ 更多选项 () ”按钮,然后选择邮件扩展。

收集用户的输入

有三种方法可以从 Teams 中的用户收集信息。

静态参数列表

在此方法中,只需在清单中定义参数的静态列表,如“创建要执行”命令中所示。 若要使用此方法,请确保 fetchTask 将 设置为 false 并在清单中定义参数。

当用户选择具有静态参数的命令时,Teams 会在任务模块中生成一个窗体,其中包含清单中定义的参数。 点击“提交”时 composeExtensions/submitAction ,会将 发送到机器人。 有关预期响应集的详细信息,请参阅 响应提交

使用自适应卡片的动态输入

在此方法中,服务可以定义自定义自适应卡来收集用户输入。 对于此方法,请在 fetchTask 清单中将 参数 true 设置为 。 如果设置为 fetchTasktrue,则忽略为命令定义的任何静态参数。

在此方法中,服务接收事件 composeExtensions/fetchTask 并使用基于自适应卡片 的任务模块响应进行响应。 下面是使用自适应卡片的示例响应:

{
    "task": {
        "type": "continue",
        "value": {
            "card": {
                "contentType": "application/vnd.microsoft.card.adaptive",
                "content": {
                    "body": [
                        {
                            "type": "TextBlock",
                            "text": "Please enter the following information:"
                        },
                        {
                            "type": "TextBlock",
                            "text": "Name"
                        },
                        {
                            "type": "Input.Text",
                            "spacing": "None",
                            "title": "New Input.Toggle",
                            "placeholder": "Placeholder text"
                        },
                        {
                            "type": "TextBlock",
                            "text": "Date of birth"
                        },
                        {
                            "type": "Input.Date",
                            "spacing": "None",
                            "title": "New Input.Toggle"
                        }
                    ],
                    "type": "AdaptiveCard",
                    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                    "version": "1.0"
                }
            }
        }
    }
}

如果用户需要在获取用户输入之前进行身份验证或配置扩展,机器人还可以使用身份验证/配置响应进行响应。

使用 Web 视图的动态输入

在此方法中,服务可以显示基于的小 <iframe> 组件,以显示任何自定义 UI 并收集用户输入。 对于此方法,请在 fetchTask 清单中将 参数 true 设置为 。

就像在自适应卡片流中一 fetchTask 样,服务发送事件并使用基于 URL 的任务模块响应进行响应。 下面是具有自适应卡的示例响应:

{
    "task": {
        "value": {
            "url": "http://mywebapp.com/input"
        },
        "type": "continue"
    }
}

请求安装对话机器人

如果应用包含聊天机器人,请确保在加载任务模块之前将其安装在对话中,以获取任务模块的更多上下文。 例如,可能需要提取名单以填充人员选取器控件或团队中的频道列表。

为了促进此流,当消息扩展首次收到composeExtensions/fetchTask调用时,检查查看机器人是否已安装在当前上下文中。 可以通过尝试获取名册调用来获取此消息。 例如,如果未安装机器人,则返回自适应卡片,其中包含请求用户安装机器人的操作。 用户需要具有在该位置安装应用的权限。 如果无法安装,消息会提示与管理员联系。

下面是响应的示例:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "text": "Looks like you haven't used Disco in this team/chat"
    }
  ],
  "actions": [
    {
      "type": "Action.Submit",
      "title": "Continue",
      "data": {
        "msteams": {
          "justInTimeInstall": true
        }
      }
    }
  ],
  "version": "1.0"
}

用户完成安装后,机器人会收到另一条带有 和 value.data.msteams.justInTimeInstall = truename = composeExtensions/submitAction调用消息。

下面是调用的示例:

{
  "value": {
    "commandId": "giveKudos",
    "commandContext": "compose",
    "context": {
      "theme": "default"
    },
    "data": {
      "msteams": {
        "justInTimeInstall": true
      }
    }
  },
  "conversation": {
    "id": "19:7705841b240044b297123ad7f9c99217@thread.skype"
  },
  "name": "composeExtension/submitAction",
  "imdisplayname": "Bob Smith"
}

如果安装了机器人,则使用响应时使用的相同任务响应来响应调用。

响应提交

用户完成输入后,机器人会收到一个 composeExtensions/submitAction 设置了命令 ID 和参数值的事件。

这些是对 submitAction的不同预期响应。

任务模块响应

当扩展需要将对话链接在一起以获取详细信息时,将使用任务模块响应。 响应与 fetchTask 前面提到的相同。

撰写扩展身份验证/配置响应

当扩展需要进行身份验证或配置以继续时,将使用撰写扩展身份验证/配置响应。 有关详细信息,请参阅搜索 部分中的身份验证 部分。

撰写扩展结果响应

撰写扩展结果响应用于将卡插入到撰写框中作为命令的结果。 它与搜索命令中使用的响应相同,但仅限于数组中的一个卡或一个结果。

{
  "composeExtension": {
    "type": "result",
    "attachmentLayout": "list",
    "preview": {
          "contentType": "application/vnd.microsoft.card.thumbnail",
          "content": {
            "title": "85069: Create a cool app",
            "images": [
              {
                "url": "https://placekitten.com/200/200"
              }
            ]
          }
        },
    "attachments": [
      {  
        "contentType": "application/vnd.microsoft.teams.card.o365connector",
        "content": {
          "sections": [
            {
              "activityTitle": "[85069]: Create a cool app",
              "activityImage": "https://placekitten.com/200/200"
            },
            {
              "title": "Details",
              "facts": [
                {
                  "name": "Assigned to:",
                  "value": "[Larry Brown](mailto:larryb@example.com)"
                },
                {
                  "name": "State:",
                  "value": "Active"
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

使用从机器人发送的自适应卡片消息进行响应

使用机器人将带有自适应卡片的消息插入到通道中,从而响应提交操作。 用户可在提交邮件之前预览邮件,并可能对其进行编辑/交互。 在创建自适应卡片响应之前需要从用户那里收集信息的情况下,这非常有用。 以下方案演示如何使用此流配置轮询,而无需在通道消息中包含配置步骤。

  1. 用户选择消息扩展以触发任务模块。
  2. 用户使用任务模块来配置投票。
  3. 提交配置任务模块后,应用将使用任务模块中提供的信息来制作自适应卡片,并将其作为 botMessagePreview 响应发送到客户端。
  4. 然后,用户可以在机器人将自适应卡片消息插入频道之前预览它。 如果机器人尚不是通道的成员,请单击添加 Send 机器人。
  5. 在发送消息之前,与自适应卡片交互会更改消息。
  6. 用户选择 Send后,机器人会将消息发布到频道。

注意

  • activityPreview 必须包含仅具有一个自适应卡片附件的 message 活动。
  • Outlook 不支持使用从机器人发送的自适应卡片消息进行响应。

若要启用此流,任务模块应响应,如以下示例中所示,该示例向用户显示预览消息:

{
  "composeExtension": {
    "type": "botMessagePreview",
    "activityPreview": {
      "type": "message",
      "attachments":  [
        {
          "contentType": "application/vnd.microsoft.card.adaptive",
          "content": << Card Payload >>
        }
      ]
    }
  }
}

消息扩展需要响应两种新类型的交互, value.botMessagePreviewAction = "send"value.botMessagePreviewAction = "edit"。 以下代码是需要处理的 对象的一个示例 value

{
  "name": "composeExtension/submitAction",
  "type": "invoke",
  "conversation": { "id": "19:c366b75791784100b6e8b515fd55b063@thread.skype" },
  "imdisplayname": "Pranav Smith",
  ...
  "value": {
    "botMessagePreviewAction": "send" | "edit",
    "botActivityPreview": [
      {
        "type": "message/card",
        "attachments": [
          {
            "content":
              {
                "type": "AdaptiveCard",
                "body": [{<<card payload>>}]
              },
            "contentType" : "application/vnd.microsoft.card.adaptive"
          }
        ],
        "context": { "theme": "default" }
      }
    ],
  }
}

响应 edit 请求时,必须使用 task 使用用户提交的信息填充的值进行响应。 响应 send 请求时,应向包含最终自适应卡的通道发送消息。

teamChatConnector.onComposeExtensionSubmitAction((
    event: builder.IEvent,
    request: teamBuilder.IComposeExtensionActionCommandRequest,
    callback: (err: Error, result: any, statusCode: number) => void) => {
        let invokeValue = (<any> event).value;

        if (invokeValue.botMessagePreviewAction ) {
            let attachment = invokeValue.botActivityPreview[0].attachments[0];

            if (invokeValue.botMessagePreviewAction === 'send') {
                let msg = new builder.Message()
                    .address(event.address)
                    .addAttachment(attachment);
                teamChatConnector.send([msg.toMessage()],
                    (error) => {
                        if(error){
                            // TODO: Handle error and callback.
                        }
                        else {
                            callback(null, null, 200);
                        }
                    }
                );
            }

            else if (invokeValue.botMessagePreviewAction === 'edit') {
              // Create the card and populate with user-inputted information.
              let card = { ... }

              let taskResponse = {
                task: {
                  type: "continue",
                  value: {
                    title: "Card Preview",
                    card: {
                      contentType: 'application/vnd.microsoft.card.adaptive',
                      content: card
                    }
                  }
                }
              }
              callback(null, taskResponse, 200);
            }

        else {
            let attachment = {
                  // Create Adaptive Card.
                };
            let activity = new builder.Message().addAttachment(attachment).toMessage();
            let response = teamBuilder.ComposeExtensionResponse.messagePreview()
                .preview(activity)
                .toResponse();
            callback(null, response, 200);
        }
    });

另请参阅

Bot Framework 示例