Поделиться через


Создание расширения сообщений на основе API

Примечание.

Расширения сообщений на основе API поддерживают только команды поиска.

Расширения сообщений на основе API — это возможность приложения Microsoft Teams, которая интегрирует внешние API непосредственно в Teams, повышая удобство использования приложения и обеспечивая простой пользовательский интерфейс. Расширения сообщений на основе API поддерживают команды поиска и могут использоваться для получения и отображения данных из внешних служб в Teams, упрощая рабочие процессы, уменьшая необходимость переключения между приложениями.

Прежде чем приступить к работе, убедитесь, что вы соответствуете следующим требованиям:


1. Описание OpenAPI (OAD)

Убедитесь, что вы придерживаетесь следующих рекомендаций для документа OpenAPI Description (OAD):

  • Поддерживаются версии OpenAPI 2.0 и 3.0.x.
  • Поддерживаемые форматы — JSON и YAML.
  • Текст запроса, если он присутствует, должен иметь значение application/Json.
  • Определите URL-адрес сервера протокола HTTPS для servers.url свойства.
  • Поддерживаются только методы HTTP POST и GET.
  • Документ Описание OpenAPI должен иметь operationId.
  • Допускается только один обязательный параметр без значения по умолчанию.
  • Обязательный параметр со значением по умолчанию считается необязательным.
  • Пользователи не должны вводить параметр для заголовка или файла cookie.
  • Операция не должна иметь обязательный заголовок или параметры файла cookie без значений по умолчанию.
  • Убедитесь, что в документе Описание OpenAPI отсутствуют удаленные ссылки.
  • Создание массивов для запроса не поддерживается; однако вложенные объекты в тексте запроса JSON поддерживаются.
  • Teams не поддерживает oneOfконструкции , anyOf, allOfи not (swagger.io).

Следующий код является примером документа OpenAPI Description:

openapi: 3.0.1
info:
title: OpenTools Plugin
description: A plugin that allows the user to find the most appropriate AI tools for their use cases, with their pricing information.
version: 'v1'
servers:
- url: https://gptplugin.opentools.ai
paths:
/tools:
 get:
   operationId: searchTools
   summary: Search for AI Tools
   parameters:
     - in: query
       name: search
       required: true
       schema:
         type: string
       description: Used to search for AI tools by their category based on the keywords. For example, ?search="tool to create music" will give tools that can create music.
   responses:
     "200":
       description: OK
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsResponse'
     "400":
       description: Search Error
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/searchToolsError'
components:
schemas:
 searchToolsResponse:
   required:
     - search
   type: object
   properties:
     tools:
       type: array
       items:
         type: object
         properties:
           name:
             type: string
             description: The name of the tool.
           opentools_url:
             type: string
             description: The URL to access the tool.
           main_summary:
             type: string
             description: A summary of what the tool is.
           pricing_summary:
             type: string
             description: A summary of the pricing of the tool.
           categories:
             type: array
             items:
               type: string
             description: The categories assigned to the tool.
           platforms:
             type: array
             items:
               type: string
             description: The platforms that this tool is available on.
       description: The list of AI tools.
 searchToolsError:
   type: object
   properties:
     message:
       type: string
       description: Message of the error.

Дополнительные сведения см. в разделе Структура OpenAPI.


2. Манифест приложения

Убедитесь, что вы придерживаетесь следующих рекомендаций для манифеста приложения:

  • Задайте для версии манифеста приложения значение 1.17.

  • Задайте значение composeExtensions.composeExtensionTypeapiBased.

  • Определите composeExtensions.apiSpecificationFile в качестве относительного пути к файлу описания OpenAPI в папке. Это связывает манифест приложения со спецификацией API.

  • Определите apiResponseRenderingTemplateFile в качестве относительного пути к шаблону отрисовки ответа. Это указывает расположение шаблона, используемого для отрисовки ответов API.

  • Каждая команда должна иметь ссылку на шаблон отрисовки ответа. При этом каждая команда подключается к соответствующему формату ответа.

  • Свойство Commands.id в манифесте приложения должно соответствовать свойству в описании operationId OpenAPI.

  • Если обязательный параметр не имеет значения по умолчанию, команда parameters.name в манифесте приложения должна соответствовать в документе parameters.name Описание OpenAPI.

  • Если обязательный параметр отсутствует, команда parameters.name в манифесте приложения должна соответствовать необязательным parameters.name в описании OpenAPI.

  • Убедитесь, что параметры каждой команды точно совпадают с именами параметров, определенных для операции в спецификации OpenAPI.

  • Шаблон отрисовки ответа должен быть определен для каждой команды, которая используется для преобразования ответов из API.

  • Полное описание не должно превышать 128 символов.

    {
    "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.17/MicrosoftTeams.schema.json",
    +  "manifestVersion": "1.17",
    "version": "1.0.0",
    "id": "04805b4b-xxxx-xxxx-xxxx-4dbc1cac8f89",
    "packageName": "com.microsoft.teams.extension",
    "developer": {
        "name": "Teams App, Inc.",
        "websiteUrl": "https://www.example.com",
        "privacyUrl": "https://www.example.com/termofuse",
        "termsOfUseUrl": "https://www.example.com/privacy"
    },
    "icons": {
        "color": "color.png",
        "outline": "outline.png"
    },
    "name": {
        "short": "AI tools",
        "full": "AI tools"
    },
    "description": {
        "short": "AI tools",
        "full": "AI tools"
    },
    "accentColor": "#FFFFFF",
    "composeExtensions": [
        {
    +      "composeExtensionType": "apiBased",
    +      "authorization": {
    +        "authType": "apiSecretServiceAuth ",
    +        "apiSecretServiceAuthConfiguration": {
    +            "apiSecretRegistrationId": "9xxxxxxx-7xxx-4xxx-bxxx-1xxxxxxxxxxx"
    +        }
    +      },
    +      "apiSpecificationFile": "aitools-openapi.yml",
           "commands": [
           {
              "id": "searchTools",
              "type": "query",
              "context": [
                 "compose",
                 "commandBox"
              ],
              "title": "search for AI tools",
              "description": "search for AI tools",
              "parameters": [
                 {
                 "name": "search",
                 "title": "search query",
                 "description": "e.g. search='tool to create music'"
                 }
              ],
    +          "apiResponseRenderingTemplateFile": "response-template.json"
           }
           ]
        }
    ],
    "validDomains": []
    }
    

Параметры

Имя Описание
composeExtensions.composeExtensionType Compose тип расширения. Обновите значение на apiBased.
composeExtensions.authorization Сведения об авторизации для расширения сообщений на основе API
composeExtensions.authorization.authType Перечисление возможных типов авторизации. Поддерживаемые значения: none, apiSecretServiceAuthи microsoftEntra.
composeExtensions.authorization.apiSecretServiceAuthConfiguration Объект, фиксирующий сведения, необходимые для проверки подлинности службы. Применимо, только если тип проверки подлинности имеет значение apiSecretServiceAuth.
composeExtensions.authorization.apiSecretServiceAuthConfiguration.apiSecretRegistrationId Идентификатор регистрации возвращается, когда разработчик отправляет ключ API через портал разработчика.
composeExtensions.apiSpecificationFile Ссылается на файл описания OpenAPI в пакете приложения. Включить, если тип имеет значение apiBased.
composeExtensions.commands.id Уникальный идентификатор, назначенный команде поиска. Запрос пользователя включает этот идентификатор. Идентификатор должен совпадать с OperationId доступным в описании OpenAPI.
composeExtensions.commands.context Массив, в котором определены точки входа для расширения сообщений. Значения по умолчанию: compose и commandBox.
composeExtensions.commands.parameters Определяет статический список параметров для команды . Имя должно сопоставляться с в parameters.name описании OpenAPI. Если вы ссылаетесь на свойство в схеме текста запроса, имя должно сопоставляться с properties.name параметрами запроса или .
composeExtensions.commands.apiResponseRenderingTemplateFile Шаблон, используемый для форматирования ответа JSON из API разработчика в ответ адаптивной карточки. [Обязательно]

Дополнительные сведения см. в разделе ComposeExtensions.


3. Шаблон отрисовки ответа

Примечание.

Teams поддерживает адаптивные карточки до версии 1.5, а адаптивные карточки Designer поддерживают до версии 1.6.

  • Определите URL-адрес ссылки на схему в свойстве $schema , чтобы установить структуру шаблона.
  • Поддерживаемые значения для responseLayoutgridи list , которые определяют способ визуального представления ответа.
  • Рекомендуется jsonPath для массивов или если данные для адаптивной карточки не являются корневым объектом. Например, если данные вложены в productDetails, путь JSON будет иметь значение productDetails.
  • Определите jsonPath в качестве пути к соответствующим данным или массиву в ответе API. Если путь указывает на массив, то каждая запись в массиве привязывается к шаблону адаптивной карточки и возвращается в виде отдельного результата. [Необязательно]
  • Получите пример ответа для проверки шаблона отрисовки ответа. Это служит в качестве теста, чтобы убедиться, что шаблон работает должным образом.
  • Используйте такие средства, как Fiddler или Postman , чтобы вызвать API и убедиться, что запрос и ответ действительны. Этот шаг имеет решающее значение для устранения неполадок и подтверждения правильности работы API.
  • С помощью Designer адаптивной карточки можно привязать ответ API к шаблону отрисовки ответа и просмотреть адаптивную карточку. Вставьте шаблон в РЕДАКТОР ПОЛЕЗНЫХ ДАННЫХ КАРТОЧКИ и вставьте пример записи ответа в РЕДАКТОР ОБРАЗЦОВ ДАННЫХ.

Следующий код является примером шаблона отрисовки ответа:

Пример шаблона отрисовки ответа
{
"version": "1.0",
"jsonPath": "repairs",
"responseLayout": "grid",
"responseCardTemplate": {
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.4",
  "body": [
    {
      "type": "Container",
      "items": [
        {
          "type": "ColumnSet",
          "columns": [
            {
              "type": "Column",
              "width": "stretch",
              "items": [
                {
                  "type": "TextBlock",
                  "text": "Title: ${if(title, title, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Description: ${if(description, description, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "TextBlock",
                  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
                  "wrap": true
                },
                {
                  "type": "Image",
                  "url": "${image}",
                  "size": "Medium",
                  "$when": "${image != null}"
                }
              ]
            },
            {
              "type": "Column",
              "width": "auto",
              "items": [
                {
                  "type": "Image",
                  "url": "${if(image, image, '')}",
                  "size": "Medium"
                }
              ]
            }
          ]
        },
        {
          "type": "FactSet",
          "facts": [
            {
              "title": "Repair ID:",
              "value": "${if(id, id, 'N/A')}"
            },
            {
              "title": "Date:",
              "value": "${if(date, date, 'N/A')}"
            }
          ]
        }
      ]
    }
  ]
  },
  "previewCardTemplate": {
  "title": "Title: ${if(title, title, 'N/A')}",
  "subtitle": "Description: ${if(description, description, 'N/A')}",
  "text": "Assigned To: ${if(assignedTo, assignedTo, 'N/A')}",
  "image": {
    "url": "${image}",
    "$when": "${image != null}"
    }
  }
 }

Карточка предварительного просмотра

Снимок экрана: пример расширения создания, отображающего массив карточек предварительного просмотра при поиске определенного слова. В этом случае при поиске

Расширенная адаптивная карточка

Пример того, как выглядит адаптивная карточка, развернутая после выбора пользователем предварительного карта. На адаптивной карточке отображаются значения Title, Full Description, AssignedTo, RepairId и Date.

Параметры

Свойство Тип Описание Обязательный
version string Версия схемы текущего шаблона отрисовки ответа. Да
jsonPath string Путь к соответствующему разделу в результатах, к которым должны применяться responseCardTemplate и previewCardTemplate. Если значение не задано, корневой объект обрабатывается как соответствующий раздел. Если соответствующий раздел является массивом, каждая запись сопоставляется с responseCardTemplate и previewCardTemplate. Нет
responseLayout responseLayoutType Указывает макет результатов во всплывающем элементе расширения сообщения. Поддерживаемые типы: list и grid. Да
responseCardTemplate adaptiveCardTemplate Шаблон для создания адаптивной карточки из результирующих записей. Да
previewCardTemplate previewCardTemplate Шаблон для создания предварительного карта из записи результата. Результирующий карта предварительного просмотра отображается во всплывающем меню расширения сообщения. Да

Путь JSON

Путь JSON необязателен, но его следует использовать для массивов или где объект, используемый в качестве данных для адаптивного карта, не является корневым объектом. Путь JSON должен соответствовать формату, определенному Newtonsoft. Если путь JSON указывает на массив, то каждая запись в этом массиве привязана к шаблону адаптивного карта и возвращается в виде отдельных результатов.

Пример Предположим, что у вас есть приведенный ниже код JSON для списка продуктов и вы хотите создать карта результат для каждой записи.

{
   "version": "1.0",
   "title": "All Products",
   "warehouse": {
      "products": [
        ...
      ]
   }
}

Как видите, массив результатов находится в разделе "products", который вложен в "warehouse", поэтому путь JSON будет "warehouse.products".

Используйте https://adaptivecards.io/designer/ для предварительного просмотра адаптивной карта, вставив шаблон в полезные данные карточки Редактор, а затем возьмите пример записи ответа из массива или для объекта и вставьте его в редактор данных справа. Убедитесь, что карта правильно отрисовывается и соответствует вашему вкусу. Обратите внимание, что Teams поддерживает карточки до версии 1.5, а конструктор поддерживает 1.6.

Сопоставление схемы

Свойства в документе Описание OpenAPI сопоставляются с шаблоном адаптивной карточки следующим образом:

  • string, number, integerboolean типы преобразуются в TextBlock.

    Пример
    • Исходная схема: string, number, integerи boolean

       name:
         type: string
         example: doggie
      
    • Целевая схема: Textblock

      {
      "type": "TextBlock",
      "text": "name: ${if(name, name, 'N/A')}",
      "wrap": true
      }
      
  • array: массив преобразуется в контейнер в адаптивной карточке.

    Пример
    • Исходная схема: array

          type: array
                    items:
                    required:
                      - name
                    type: object
                      properties:
                      id:
                        type: integer
                      category:
                        type: object
                        properties:
                        name:
                          type: string
      
    • Целевая схема: Container

          {
                    "type": "Container",
                    "$data": "${$root}",
                    "items": [
                      {
                        "type": "TextBlock",
                        "text": "id: ${if(id, id, 'N/A')}",
                        "wrap": true
                      },
                      {
                        "type": "TextBlock",
                        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
                        "wrap": true
                      }
                    ]
                  }
      
      
  • object: объект преобразуется во вложенное свойство в адаптивной карточке.

    Пример
    • Исходная схема: object

      components:
        schemas:
          Pet:
              category:
                type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
      
      
    • Целевая схема: вложенное свойство в адаптивной карточке

      {
        "type": "TextBlock",
        "text": "category.id: ${if(category.id, category.id, 'N/A')}",
        "wrap": true
      },
      {
        "type": "TextBlock",
        "text": "category.name: ${if(category.name, category.name, 'N/A')}",
        "wrap": true
      }
      
      
  • image: если свойство является URL-адресом изображения, оно преобразуется в элемент Image в адаптивной карточке.

    Пример
    • Исходная схема: image

          image:
            type: string
            format: uri
            description: The URL of the image of the item to be repaired
      
      
    • Целевая схема: "Image"

      {
            "type": "Image",
            "url": "${image}",
            "$when": "${image != null}"
          }
      
      

Расширение сообщений на основе API можно создать с помощью портала разработчика для Teams, набора средств Teams для Visual Studio Code, интерфейса командной строки (CLI) или Visual Studio.

Чтобы создать расширение сообщений на основе API с помощью портала разработчика, выполните следующие действия.

  1. Перейдите на портал разработчика.

  2. Перейдите в раздел Приложения.

  3. Выберите + Создать приложение.

  4. Введите имя приложения и выберите версию манифеста в качестве общедоступной предварительной версии разработчика (devPreview).

  5. Нажмите Добавить.

    Снимок экрана: имя приложения и версия манифеста, выбранные как последняя предварительная версия (devPreview) на портале разработчика.

  6. В левой области в разделе Настройка обновите следующие основные сведения:

    1. Полное имя
    2. Краткое описание
    3. Длинное описание
    4. Название разработчика или компании
    5. Веб-сайт (должен быть допустимым URL-адресом HTTPS)
    6. Политика конфиденциальности
    7. Условия использования
  7. Выберите Сохранить.

  8. Выберите Функции приложений.

  9. Выберите Расширение сообщений.

    Снимок экрана: параметр расширения сообщений на портале разработчика.

  10. В разделе Тип расширения сообщений выберите API.

    1. Если вы получили заявление об отказе от ответственности, которое считывает расширение сообщений Bot, уже используется пользователями. Вы хотите изменить тип расширения сообщений на API?, выберите Да, изменить.
  11. В разделе Спецификация OpenAPI выберите Отправить сейчас.

    Снимок экрана: параметр

  12. Выберите документ Описание OpenAPI в формате JSON или YAML и нажмите кнопку Открыть.

  13. Выберите Сохранить. Появится всплывающее окно со спецификацией API сообщения, успешно сохраненной.

  14. Выберите Пункт Получил.

    Снимок экрана: пример успешно сохраненного сообщения спецификации API и кнопки

Добавление команд

Примечание.

Расширения сообщений, созданные на основе API, поддерживают только один параметр.

Вы можете добавить команды и параметры в расширение сообщений, чтобы добавить команды:

  1. В разделе Тип расширения сообщений выберите Добавить.

    Снимок экрана: параметр добавления команд на портале разработчика.

    Откроется всплывающее окно Добавить команду со списком всех доступных API из документа Описание OpenAPI.

  2. Выберите API из списка и нажмите кнопку Далее.

    Снимок экрана: список API из документа описание OpenAPI во всплывающем окне Добавление команды.

  3. В разделе Шаблон ответа выберите Отправить сейчас.

    Снимок экрана: параметр Отправить сейчас для добавления шаблона адаптивной карточки для команды.

    Примечание.

    Если у вас несколько API, обязательно отправьте шаблон ответа адаптивной карточки для каждого API.

  4. Выберите файл шаблона ответа адаптивной карточки в формате JSON и нажмите кнопку Открыть.

    Следующие атрибуты автоматически обновляются из шаблона адаптивной карточки:

    • Тип команды
    • Идентификатор команды
    • Заголовок команды
    • Имя параметра
    • Описание параметра
  5. В разделе Сведения обновите описание команды.

  6. Если вы хотите запустить команду с помощью триггера в Microsoft 365 Copilot, включите переключатель Автоматически выполнять эту команду, когда пользователь открывает расширение.

  7. Нажмите Добавить. Команда успешно добавлена.

    Снимок экрана: поля, доступные на странице сведений о команде.

  8. Выберите Сохранить.

  9. В разделе Проверка подлинности и авторизация выберите любой из следующих параметров:

    • Нет проверки подлинности (не рекомендуется)
    • Ключ API
    • OAuth

Создается расширение сообщений на основе API.

Снимок экрана: подключаемый модуль для Microsoft 365 Copilot, созданный на странице функций приложения на портале разработчика.

Чтобы протестировать расширение сообщений на основе API, созданное на портале разработчика, можно использовать следующие методы:

  • Предварительный просмотр в Teams. Откройте расширение для сообщений и выберите Предварительный просмотр в Teams в правом верхнем углу. Вы будете перенаправлены в Teams, где вы можете добавить приложение в Teams для предварительного просмотра приложения.

  • Скачать пакет приложения. На странице расширения сообщения выберите Пакет приложения в левой области, а затем в левом верхнем углу окна выберите Скачать пакет приложения. Пакет приложения загружается на локальный компьютер в файле .zip. Вы можете отправить пакет приложения в команды и протестировать расширение сообщений.

Несколько параметров

Несколько параметров позволяют расширениям сообщений на основе API иметь несколько типов входных данных для команд запроса. Например, можно искать аниме по жанру, рейтингу, состоянию и дате.

Можно указать типы входных данных, заголовки, описания и обязательные поля для параметров манифеста.

  • Свойство isRequired в поле параметра указывает, является ли параметр обязательным для команды запроса.
  • Свойство name поля в манифесте parameters приложения должно соответствовать полю id в документе Описание OpenAPI для соответствующего параметра.

Пример

"composeExtensions": [
        {
            "composeExtensionType": "apiBased",
            "apiSpecificationFile": "apiSpecificationFiles/openapi.json",
            "commands": [
                {
                    "context": [
                        "compose"
                    ],
                    "type": "query",
                    "title": "Search Animes",
                    "id": "getAnimeSearch",
                    "parameters": [
                        {
                            "name": "q",
                            "title": "Search Query",
                            "description": "The search query",
                            "isRequired": true
                        },
                        {
                            "name": "type",
                            "inputType": "choiceset",
                            "title": "Type",
                            "description": "Available anime types",
                            "choices": [
                                {
                                    "title": "TV",
                                    "value": "tv"
                                },
                                {
                                    "title": "OVA",
                                    "value": "ova"
                                },
                                {
                                    "title": "Movie",
                                    "value": "movie"
                                },
                                {
                                    "title": "Special",
                                    "value": "special"
                                },
                                {
                                    "title": "ONA",
                                    "value": "ona"
                                },
                                {
                                    "title": "Music",
                                    "value": "music"
                                }
                            ]
                        },
                        {
                            "name": "status",
                            "inputType": "choiceset",
                            "title": "Status",
                            "description": "Available airing statuses",
                            "choices": [
                                {
                                    "title": "Airing",
                                    "value": "airing"
                                },
                                {
                                    "title": "Completed",
                                    "value": "complete"
                                },
                                {
                                    "title": "Upcoming",
                                    "value": "upcoming"
                                }
                            ]
                        },
                        {
                            "name": "rating",
                            "inputType": "choiceset",
                            "title": "Rating",
                            "description": "Available ratings",
                            "choices": [
                                {
                                    "title": "G",
                                    "value": "g"
                                },
                                {
                                    "title": "PG",
                                    "value": "pg"
                                },
                                {
                                    "title": "PG-13",
                                    "value": "pg13"
                                },
                                {
                                    "title": "R",
                                    "value": "r17"
                                },
                                {
                                    "title": "R+",
                                    "value": "r"
                                },
                                {
                                    "title": "Rx",
                                    "value": "rx"
                                }
                            ]
                        }
                    ],
                    "description": "Search animes",
                    "apiResponseRenderingTemplateFile": "response_json/getAnimeSearch.json"
                },
                {
                    "context": [
                        "compose"
                    ],
                    "type": "query",
                    "title": "Search mangas",
                    "id": "getMangaSearch",
                    "parameters": [
                        {
                            "name": "q",
                            "title": "Search Query",
                            "description": "The search query",
                            "isRequired": true
                        },
                        {
                            "name": "type",
                            "inputType": "choiceset",
                            "title": "Type",
                            "description": "Available manga types",
                            "choices": [
                                {
                                    "title": "Manga",
                                    "value": "manga"
                                },
                                {
                                    "title": "Novel",
                                    "value": "novel"
                                },
                                {
                                    "title": "Light Novel",
                                    "value": "lightnovel"
                                },
                                {
                                    "title": "One Shot",
                                    "value": "oneshot"
                                },
                                {
                                    "title": "Doujin",
                                    "value": "doujin"
                                },
                                {
                                    "title": "Manhwa",
                                    "value": "manhwa"
                                },
                                {
                                    "title": "Manhua",
                                    "value": "manhua"
                                }
                            ]
                        },
                        {
                            "name": "status",
                            "inputType": "choiceset",
                            "title": "Status",
                            "description": "Available manga statuses",
                            "choices": [
                                {
                                    "title": "Publishing",
                                    "value": "publishing"
                                },
                                {
                                    "title": "Complete",
                                    "value": "complete"
                                },
                                {
                                    "title": "Hiatus",
                                    "value": "hiatus"
                                },
                                {
                                    "title": "Discontinued",
                                    "value": "discontinued"
                                },
                                {
                                    "title": "Upcoming",
                                    "value": "upcoming"
                                }
                            ]
                        },
                        {
                            "name": "start_date",
                            "title": "Start Date",
                            "description": "Start date of the manga",
                            "inputType": "date"
                        },
                        {
                            "name": "end_date",
                            "title": "End Date",
                            "description": "End date of the manga",
                            "inputType": "date"
                        }
                    ],

Пошаговые руководства

Чтобы создать расширение сообщений на основе API, следуйте этим пошаговым руководствам.

См. также

Проверка подлинности для расширений сообщений на основе API