Использование модулей задач из Microsoft Teams БотыUsing task modules from Microsoft Teams bots

Модули задач можно вызывать из Microsoft Teams боты с помощью кнопок на адаптивных карточках и карточек Bot-инфраструктуры (главный Имиджевый баннер, эскиз и Office 365 Connector).Task modules can be invoked from Microsoft Teams bots using buttons on Adaptive cards and Bot Framework cards (Hero, Thumbnail, and Office 365 Connector). Модули задач часто работают быстрее, чем несколько действий, которые разработчик должен отслеживать состояние Bot и позволять пользователю прерывать или отменять последовательность.Task modules are often a better user experience than multiple conversation steps where you as a developer have to keep track of bot state and allow the user to interrupt/cancel the sequence.

Существует два способа вызова модулей задач:There are two ways of invoking task modules:

  • Новый тип сообщения вызова task/fetch .A new kind of invoke message task/fetch. С помощью invoke действия карточки для карточек ленты (Bot) или Action.Submit действия карты для адаптивных карт с помощью task/fetch модуля задач (URL-адрес или Адаптивная карта) выполняется динамическое извлечение из ленты.Using the invoke card action for Bot Framework cards, or the Action.Submit card action for Adaptive cards, with task/fetch, the task module (either a URL or an Adaptive card) is fetched dynamically from your bot.
  • URL-адреса глубокой ссылки.Deep link URLs. С помощью синтаксиса глубокой ссылки для модулей задачможно использовать openUrl действия с картой для карточек ленты или Action.OpenUrl действия карты для адаптивных карт соответственно.Using the deep link syntax for task modules, you can use the openUrl card action for Bot Framework cards or the Action.OpenUrl card action for Adaptive cards, respectively. При использовании URL-адресов с глубокими ссылками URL-адрес модуля задачи или адаптивный текст карточки очевидно известен заранее, что позволяет избежать кругового пути к серверу task/fetch .With deep link URLs, the task module URL or Adaptive card body is obviously known in advance, avoiding a server round-trip relative to task/fetch.

Важно!

Для обеспечения безопасной связи каждый из url них fallbackUrl должен реализовать протокол шифрования HTTPS.To ensure secure communications, each url and fallbackUrl must implement the HTTPS encryption protocol.

Вызов модуля задачи с помощью задачи/извлеченияInvoking a task module via task/fetch

Когда value объект invoke действия карточки или Action.Submit инициализирован правильно (подробно описывается ниже), когда пользователь нажимает кнопку, invoke сообщение отправляется в Bot.When the value object of the invoke card action or Action.Submit is initialized in the proper way (explained in more detail below), when a user presses the button an invoke message is sent to the bot. В HTTP-ответе на invoke сообщение существует объект таскинфо , внедренный в объект-оболочку, который Teams использует для отображения модуля задачи.In the HTTP response to the invoke message, there's a TaskInfo object embedded in a wrapper object, which Teams uses to display the task module.

запрос/ответ на задачу/получение

Рассмотрим каждый шаг чуть подробнее:Let's look at each step in a bit more detail:

  1. В этом примере показана карточка главный Имиджевый баннер Framework с invoke действием"Купить" карточки.This example shows a Bot Framework Hero card with a "Buy" invoke card action. Значение type свойства — task/fetch остальная часть value объекта может быть любым.The value of the type property is task/fetch - the rest of the value object can be whatever you like.

  2. Bot получает invoke сообщение HTTP POST.The bot receives the invoke HTTP POST message.

  3. Bot создает объект Response и возвращает его в тексте ответа POST с кодом отклика HTTP 200.The bot creates a response object and returns it in the body of the POST response with an HTTP 200 response code. Схема для ответов описана ниже в разделе "задача/Передача", но важная вещь состоит в том, что текст HTTP-ответа содержит объект таскинфо , внедренный в объект-оболочку, например:The schema for responses is described below in the discussion on task/submit, but the important thing to remember now is that the body of the HTTP response contains a TaskInfo object embedded in a wrapper object, e.g.:

    {
      "task": {
        "type": "continue",
        "value": {
          "title": "Task module title",
          "height": 500,
          "width": "medium",
          "url": "https://contoso.com/msteams/taskmodules/newcustomer",
          "fallbackUrl": "https://contoso.com/msteams/taskmodules/newcustomer"
        }
      }
    }
    

    task/fetchСобытие и его ответ для Боты похожи, концептуально, в microsoftTeams.tasks.startTask() функцию в клиентской SDK.The task/fetch event and its response for bots is similar, conceptually, to the microsoftTeams.tasks.startTask() function in the client SDK.

  4. В Microsoft Teams отображается модуль задач.Microsoft Teams displays the task module.

Отправка результата модуля задачиSubmitting the result of a task module

Когда пользователь завершает работу с модулем задач, отправка результата обратно на сервер-Bot похожа на то, как он работает с вкладками, но существует несколько различий, поэтому он также описан здесь.When the user is finished with the task module, submitting the result back to the bot is similar to the way it works with tabs, but there are a few differences, so it's described here too.

  • HTML/JavaScript ( TaskInfo.url ).HTML/JavaScript (TaskInfo.url). Когда вы проверили введенные пользователем сведения, вы вызываете microsoftTeams.tasks.submitTask() функцию SDK (которая упоминается в дальнейшем submitTask() для удобства чтения).Once you've validated what the user has entered, you call the microsoftTeams.tasks.submitTask() SDK function (referred to hereafter as submitTask() for readability purposes). Вы можете вызвать submitTask() без параметров, если вы хотите, чтобы команда закрывала модуль задач, но в большинстве случаев требуется передать объект или строку в submitHandler .You can call submitTask() without any parameters if you just want Teams to close the task module, but most of the time you'll want to pass an object or a string to your submitHandler. Просто передавайте его в качестве первого параметра, result .Simply pass it as the first parameter, result. Teams вызовет submitHandler : err будет null и result будет объект или строка, которые вы передали submitTask() .Teams will invoke submitHandler: err will be null and result will be the object/string you passed to submitTask(). При вызове submitTask() с result параметром необходимо передать объект appId или массив appId строк: Это позволяет Teams проверить, что приложение отправляет результат, то же, что вызвало модуль задачи.If you do call submitTask() with a result parameter, you must pass an appId or an array of appId strings: this allows Teams to validate that the app sending the result is the same one which invoked the task module. Ваш робот получит task/submit сообщение result , в том числе, как описано ниже.Your bot will receive a task/submit message including result as described below.
  • Адаптивная карта ( TaskInfo.card ).Adaptive card (TaskInfo.card). Текст адаптивной карточки (заполняемой пользователем) будет отправляться в Bot через task/submit сообщение, когда пользователь нажимает любую Action.Submit кнопку.The Adaptive card body (as filled in by the user) will be sent to the bot via a task/submit message when the user presses any Action.Submit button.

Гибкость задачи и отсылкиThe flexibility of task/submit

В предыдущем разделе вы узнали, что когда пользователь завершается с помощью модуля задачи, вызванного из Bot, Bot всегда получает task/submit invoke сообщение.In the previous section, you learned that when the user finishes with a task module invoked from a bot, the bot always receives a task/submit invoke message. Как разработчик, у вас есть несколько вариантов ответа на task/submit сообщение:As a developer, you have several options when responding to the task/submit message:

Ответ основного текста HTTPHTTP Body Response СценарийScenario
Нет (пропускать task/submit сообщение)None (ignore the task/submit message) Самый простой ответ вообще не отвечает.The simplest response is no response at all. Ваш робот не должен отвечать, когда пользователь завершает работу с модулем задач.Your bot is not required to respond when the user is finished with the task module.
{
"task": {
"type": "message",
"value": "Message text"
}
}
Teams отобразит значение value всплывающего окна сообщения.Teams will display the value of value in a popup message box.
{
"task": {
"type": "continue",
"value": <TaskInfo object>
}
}
Позволяет "цепочку" цепочек адаптивных карточек в мастере и многоэтапной работе.Allows you to "chain" sequences of Adaptive cards together in a wizard/multi-step experience. Обратите внимание, что сцепление адаптивных карт в последовательность является расширенным сценарием и не задокументировано здесь. Однако в этом примере приложение Node.js поддерживает его, и как это работает в файле readme.md.Note that chaining Adaptive cards into a sequence is an advanced scenario and not documented here. The Node.js sample app supports it, however, and how it works is documented in its README.md file.

Полезные данные о задачах, получении и задачах и отправляя сообщенияPayload of task/fetch and task/submit messages

В этом разделе определяется схема того, что Bot получает, когда он получает task/fetch task/submit объект или объект Bot Framework Activity .This section defines the schema of what your bot receives when it receives a task/fetch or task/submit Bot Framework Activity object. Ниже приведен самый важный уровень верхнего уровня:The important top-level appear below:

СвойствоProperty ОписаниеDescription
type Всегда будет invokeWill always be invoke
name Один task/fetch или task/submitEither task/fetch or task/submit
value Полезные данные, определенные разработчиком.The developer-defined payload. Как правило, структура value объекта отражается на том, что было отправлено из Teams.Normally the structure of the value object mirrors what was sent from Teams. В этом случае, однако, это отличается, так как мы хотим поддерживать динамическую выборку ( task/fetch ) из обеих действий: Bot Framework ( value ) и адаптивных карт Action.Submit ( data ), и нам нужен способ для обмена командами context с Bot в дополнение к тому, что было включено в value / data .In this case, however, it's different because we want to support dynamic fetch (task/fetch) from both Bot Framework (value) and Adaptive card Action.Submit actions (data), and we need a way to communicate Teams context to the bot in addition to what was included in value/data.

Для этого необходимо объединить два объекта в родительский.We do this by combining the two into a parent object:

{
"context": {
"theme": "default" | "dark" | "contrast",
},
"data": [value field from Bot Framework card] | [data field from Adaptive Card]
}

Пример: получение и ответ на сообщения о задачах, выдачах и задачах и вызываемых запросах — Node.jsExample: Receiving and responding to task/fetch and task/submit invoke messages - Node.js

Примечание

Приведенный ниже пример кода был изменен между техническим и окончательным выпуском этой функции: схема task/fetch запроса изменилась на следующий пример documented in the previous section.The sample code below was modified between Technical Preview and final release of this feature: the schema of the task/fetch request changed to follow what was documented in the previous section. Это значит, что документация была правильной, но не была реализована.That is, the documentation was correct but the implementation was not. Ознакомьтесь с // for Technical Preview [...] комментариями, которые вы изменили.See the // for Technical Preview [...] comments below for what changed.

 handleTeamsTaskModuleFetch(context, taskModuleRequest) {
        // Called when the user selects an options from the displayed HeroCard or
        // AdaptiveCard.  The result is the action to perform.

        const cardTaskFetchValue = taskModuleRequest.data.data;
        var taskInfo = {}; // TaskModuleTaskInfo

        if (cardTaskFetchValue === TaskModuleIds.YouTube) {
            // Display the YouTube.html page
            taskInfo.url = taskInfo.fallbackUrl = this.baseUrl + '/' + TaskModuleIds.YouTube + '.html';
            this.setTaskInfo(taskInfo, TaskModuleUIConstants.YouTube);
        } else if (cardTaskFetchValue === TaskModuleIds.CustomForm) {
            // Display the CustomForm.html page, and post the form data back via
            // handleTeamsTaskModuleSubmit.
            taskInfo.url = taskInfo.fallbackUrl = this.baseUrl + '/' + TaskModuleIds.CustomForm + '.html';
            this.setTaskInfo(taskInfo, TaskModuleUIConstants.CustomForm);
        } else if (cardTaskFetchValue === TaskModuleIds.AdaptiveCard) {
            // Display an AdaptiveCard to prompt user for text, and post it back via
            // handleTeamsTaskModuleSubmit.
            taskInfo.card = this.createAdaptiveCardAttachment();
            this.setTaskInfo(taskInfo, TaskModuleUIConstants.AdaptiveCard);
        }

        return TaskModuleResponseFactory.toTaskModuleResponse(taskInfo);
    }

    async handleTeamsTaskModuleSubmit(context, taskModuleRequest) {
        // Called when data is being returned from the selected option (see `handleTeamsTaskModuleFetch').

        // Echo the users input back.  In a production bot, this is where you'd add behavior in
        // response to the input.
        await context.sendActivity(MessageFactory.text('handleTeamsTaskModuleSubmit: ' + JSON.stringify(taskModuleRequest.data)));

        // Return TaskModuleResponse
        return {
            // TaskModuleMessageResponse
            task: {
                type: 'message',
                value: 'Thanks!'
            }
        };
    }

*See also*, [Microsoft Teams task module sample code — nodejs](https://github.com/OfficeDev/microsoft-teams-sample-task-module-nodejs/blob/master/src/TeamsBot.ts) and  [Bot Framework samples](https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md).

Пример: получение и ответ на сообщения о задачах, выдачах и задачах и запросах Invoke-C #Example: Receiving and responding to task/fetch and task/submit invoke messages - C#

В C# Боты invoke сообщения обрабатываются с помощью HttpResponseMessage() обработки сообщения контроллера Activity .In C# bots, invoke messages are processed by an HttpResponseMessage() controller processing an Activity message. task/fetchЗапросы и task/submit ответы — JSON.The task/fetch and task/submit requests and responses are JSON. В C# неудобно работать с необработанным JSON, так как он находится в Node.js, поэтому для обработки сериализации и последующего использования JSON необходимы классы-оболочки.In C#, it's not as convenient to deal with raw JSON as it is in Node.js, so you need wrapper classes to handle the serialization to and from JSON. В Microsoft Teams SDK нет прямой поддержки, но вы можете увидеть пример того, как будут выглядеть эти простые классы оберток в образце приложения на языке C#.There's no direct support for this in the Microsoft Teams C# SDK yet, but you can see an example of what these simple wrapper classes would look like in the C# sample app.

Ниже приведен пример кода на языке C# для обработки task/fetch и task/submit сообщений, использующих эти классы оболочки ( TaskInfo , TaskEnvelope ), взятые из примера:Below is example code in C# for handling task/fetch and task/submit messages using these wrapper classes (TaskInfo, TaskEnvelope), excerpted from the sample:

protected override Task<TaskModuleResponse> OnTeamsTaskModuleFetchAsync(ITurnContext<IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken)
        {
            var asJobject = JObject.FromObject(taskModuleRequest.Data);
            var value = asJobject.ToObject<CardTaskFetchValue<string>>()?.Data;

            var taskInfo = new TaskModuleTaskInfo();
            switch (value)
            {
                case TaskModuleIds.YouTube:
                    taskInfo.Url = taskInfo.FallbackUrl = _baseUrl + "/" + TaskModuleIds.YouTube;
                    SetTaskInfo(taskInfo, TaskModuleUIConstants.YouTube);
                    break;
                case TaskModuleIds.CustomForm:
                    taskInfo.Url = taskInfo.FallbackUrl = _baseUrl + "/" + TaskModuleIds.CustomForm;
                    SetTaskInfo(taskInfo, TaskModuleUIConstants.CustomForm);
                    break;
                case TaskModuleIds.AdaptiveCard:
                    taskInfo.Card = CreateAdaptiveCardAttachment();
                    SetTaskInfo(taskInfo, TaskModuleUIConstants.AdaptiveCard);
                    break;
                default:
                    break;
            }

            return Task.FromResult(taskInfo.ToTaskModuleResponse());
        }

        protected override async Task<TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnContext<IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken)
        {
            var reply = MessageFactory.Text("OnTeamsTaskModuleSubmitAsync Value: " + JsonConvert.SerializeObject(taskModuleRequest));
            await turnContext.SendActivityAsync(reply, cancellationToken);

            return TaskModuleResponseFactory.CreateResponse("Thanks!");
        }

Пример: получение и ответ на сообщения о задачах, получении и задачах и вызываемых вызовах — PythonExample: Receiving and responding to task/fetch and task/submit invoke messages - Python

async def on_teams_task_module_fetch(
        self, turn_context: TurnContext, task_module_request: TaskModuleRequest
    ) -> TaskModuleResponse:
        """
        Called when the user selects an options from the displayed HeroCard or
        AdaptiveCard.  The result is the action to perform.
        """

        card_task_fetch_value = task_module_request.data["data"]

        task_info = TaskModuleTaskInfo()
        if card_task_fetch_value == TaskModuleIds.YOUTUBE:
            # Display the YouTube.html page
            task_info.url = task_info.fallback_url = (
                self.__base_url + "/" + TaskModuleIds.YOUTUBE + ".html"
            )
            TeamsTaskModuleBot.__set_task_info(task_info, TaskModuleUIConstants.YOUTUBE)
        elif card_task_fetch_value == TaskModuleIds.CUSTOM_FORM:
            # Display the CustomForm.html page, and post the form data back via
            # on_teams_task_module_submit.
            task_info.url = task_info.fallback_url = (
                self.__base_url + "/" + TaskModuleIds.CUSTOM_FORM + ".html"
            )
            TeamsTaskModuleBot.__set_task_info(
                task_info, TaskModuleUIConstants.CUSTOM_FORM
            )
        elif card_task_fetch_value == TaskModuleIds.ADAPTIVE_CARD:
            # Display an AdaptiveCard to prompt user for text, and post it back via
            # on_teams_task_module_submit.
            task_info.card = TeamsTaskModuleBot.__create_adaptive_card_attachment()
            TeamsTaskModuleBot.__set_task_info(
                task_info, TaskModuleUIConstants.ADAPTIVE_CARD
            )

        return TaskModuleResponseFactory.to_task_module_response(task_info)

    async def on_teams_task_module_submit(
        self, turn_context: TurnContext, task_module_request: TaskModuleRequest
    ) -> TaskModuleResponse:
        """
        Called when data is being returned from the selected option (see `on_teams_task_module_fetch').
        """

        # Echo the users input back.  In a production bot, this is where you'd add behavior in
        # response to the input.
        await turn_context.send_activity(
            MessageFactory.text(
                f"on_teams_task_module_submit: {json.dumps(task_module_request.data)}"
            )
        )

        message_response = TaskModuleMessageResponse(value="Thanks!")
        return TaskModuleResponse(task=message_response)

Not shown in the above example is the `SetTaskInfo()` function, which sets the `height`, `width`, and `title` properties of the `TaskInfo` object for each case. Here's the [source code for SetTaskInfo()](https://github.com/OfficeDev/microsoft-teams-sample-task-module-csharp/blob/master/Microsoft.Teams.Samples.TaskModule.Web/Controllers/MessagesController.cs).

Действия с карточками Bot и адаптивной карты. действия по отсылкеBot Framework card actions vs. Adaptive card Action.Submit actions

Схема действий для действий с карточной картой несколько отличается от действий адаптивной карточки Action.Submit .The schema for Bot Framework card actions is slightly different from Adaptive card Action.Submit actions. В результате, способ вызова модулей задач немного отличается: data объект Action.Submit содержит msteams объект, поэтому он не будет влиять на другие свойства карточки.As a result, the way to invoke task modules is slightly different too: the data object in Action.Submit contains an msteams object so it won't interfere with other properties in the card. В следующей таблице приведен пример каждого из них:The following table shows an example of each:

Действие с картой инфраструктуры BotBot Framework card action Действие адаптивной карточки. действие отсылкиAdaptive card Action.Submit action
{
"type": "invoke",
"title": "Buy",
"value": {
"type": "task/fetch",
<...>
}
}
{
"type": "Action.Submit",
"id": "btnBuy",
"title": "Buy",
"data": {
<...>,
"msteams": {
"type": "task/fetch"
}
}
}