Service | Microsoft Docs article | Related commit history on GitHub | Change details |
---|---|---|---|
platform | Command Bot In Teams | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/bots/how-to/conversations/command-bot-in-teams.md | ms.localizationpriority: high Microsoft Teams allows you to automate simple and repetitive tasks in a conversation. You can build a command bot that can respond to simple commands sent in chats with Adaptive Cards. You can create a command bot template in Teams Toolkit that responds to chat commands by displaying UI using an Adaptive Card. This enables users to send messages in Teams and your app can provide a response as required. -The command bot template is built using the TeamsFx SDK, which provides a simple set of functions over the Microsoft Bot Framework to implement the scenario. Command bot can be used in different scenarios such as checking ticket status, and retrieve help information. +The command bot template is built using the TeamsFx SDK, which provides a simple set of functions over the Microsoft Bot Framework. Command bot can be used in different scenarios such as checking ticket status and retrieving help information. :::image type="content" source="../../../assets/images/command-bot-teams/commandbot-flowchart1.png" alt-text="Screenshot of creating command bot app with adaptive card flow chart." lightbox="../../../assets/images/command-bot-teams/commandbot-flowchart1.png"::: A command bot needs to be installed into a team, or a group chat, or as personal :::image type="content" source="../../../assets/images/command-bot-teams/commandbot-installation.png" alt-text="installation option selection"::: For more install options, see [configure default install options](../../../concepts/deploy-and-publish/apps-publish-overview.md#configure-default-install-options).+For uninstalling, see [remove an app from Teams](https://support.microsoft.com/en-us/office/remove-an-app-from-teams-0bc48d54-e572-463c-a7b7-71bfdc0e4a9d). ## Command and response -The TeamsFx command and response bots are built using the Bot Framework SDK. The Bot Framework SDK provides built-in message handler to handle the incoming message activity, which requires you to understand the concept of Bot Framework such as the event-driven conversation model. TeamsFx SDK provides command-response abstraction layer to let the users focus on handling the command request according to the business need, without learning the Bot Framework SDK. +The TeamsFx command and response bots are built using the [Bot Framework SDK](/azure/bot-service/bot-builder-basics). The Bot Framework SDK provides [built-in message handler](../../bot-basics.md#teams-activity-handlers) to handle the incoming message activity, which requires you to understand the concept of Bot Framework such as the [event-driven conversation model](/azure/bot-service/bot-activity-handler-concept). TeamsFx SDK provides command-response abstraction layer to let the users focus on handling the command request according to the business need, without learning the Bot Framework SDK. -TeamsFx SDK pulls Bot Framework middleware to handle the integration with the underlying activity handlers. If the received message text matches the command pattern provided in a `TeamsFxBotCommandHandler` instance, the middleware handles the incoming message activity and invokes the corresponding `handlerCommandReceived` function. After this process, the middleware calls `context.sendActivity` to send the command response returned from the `handlerCommandReceived` function to the user. +TeamsFx SDK pulls [Bot Framework middleware](/azure/bot-service/bot-builder-concept-middleware) to handle the integration with the underlying activity handlers. If the received message text matches the command pattern provided in a `TeamsFxBotCommandHandler` instance, the middleware handles the incoming message activity and invokes the corresponding `handlerCommandReceived` function. The middleware calls `context.sendActivity` to send the command response returned from the `handlerCommandReceived` function to the user. ## Customize initialization -You can initialize with your own adapter or customize after initialization. +You need to create `ConversationBot` to respond to the command in a chat. You can initialize the `ConversationBot` with your adapter or customize after initialization. ++# [JavaScript/TypeScript](#tab/jsts1) ```js(ts)+/** JavaScript/TypeScript: src/internal/initialize.js(ts) **/ +const commandApp = new ConversationBot({ + // The bot id and password to create CloudAdapter. + // See https://aka.ms/about-bot-adapter to learn more about adapters. + adapterConfig: { + MicrosoftAppId: config.botId, + MicrosoftAppPassword: config.botPassword, + MicrosoftAppType: "MultiTenant", + }, + command: { + enabled: true, + commands: [new HelloWorldCommandHandler()], + }, +}); +``` ++# [C#](#tab/csharp1) ++```csharp +builder.Services.AddSingleton<HelloWorldCommandHandler>(); +builder.Services.AddSingleton(sp => +{ + var options = new ConversationOptions() + { + Adapter = sp.GetService<CloudAdapter>(), + Command = new CommandOptions() + { + Commands = new List<ITeamsCommandHandler> { sp.GetService<HelloWorldCommandHandler>() } + } + }; ++ return new ConversationBot(options); +}); +``` ++ - // Create your own adapter - const adapter = new BotFrameworkAdapter(...); +## Customize adapter - // Customize your adapter, e.g., error handling - adapter.onTurnError = ... +```Typescript +// Create your own adapter +const adapter = new CloudAdapter(...); - const bot = new ConversationBot({ - // use your own adapter - adapter: adapter; - ... - }); +// Customize your adapter, e.g., error handling +adapter.onTurnError = ... - // Or, customize later - bot.adapter.onTurnError = ... +const bot = new ConversationBot({ + // use your own adapter + adapter: adapter; + ... +}); +// Or, customize later +bot.adapter.onTurnError = ... ``` ## Add command and response You can perform the following steps to add command and responses: <summary><b>1. Add a command definition in manifest</b></summary> -You can edit the manifest template file `templates\appPackage\manifest.template.json` to include the `doSomething` command with its title and description in the `commands` array: +You can edit the manifest template file `appPackage\manifest.json` to update the `title` and `description` properties for `doSomething` command in the `commands` array as follows: ```JSON- "commandLists": [ - { - "scopes": [ - "team", - "groupchat" - ], - "commands": [ - { - "title": "helloWorld", - "description": "A helloworld command to send a welcome message" - }, - { - "title": "doSomething", - "description": "A sample do something command" - } - ] - } +"commandLists": [ + { + "commands": [ + { + "title": "helloWorld", + "description": "A helloworld command to send a welcome message" + }, + { + "title": "doSomething", + "description": "A sample do something command" + } ]+ } +] ``` <br> You can edit the manifest template file `templates\appPackage\manifest.template. <summary><b>2. Respond with an Adaptive Card</b></summary> -You can define your card in its JSON format to respond with an Adaptive Card. Following is a code sample to create a new file `src/adaptiveCards/doSomethingCommandResponse.json`: +You can define your card in the JSON format to respond with an Adaptive Card. Create a new file in the following path for JavaScript or TypeScript and .NET as follows: ++* For JavaScript or TypeScript: `src/adaptiveCards/doSomethingCommandResponse.json` +* For .NET: `Resources/DoSomethingCommandResponse.json` ++Add the following JSON code to `doSomethingCommandResponse.json` and `DoSomethingCommandResponse`: ```JSON { You can define your card in its JSON format to respond with an Adaptive Card. Fo } ``` -Respond with plain text, or with an Adaptive Card. You can use the [Adaptive Card Designer](https://adaptivecards.io/designer/) to help visually design your Adaptive Card UI. How to send an Adaptive card with dynamic data, see this [section](#how-to-build-command-and-response-using-adaptive-card-with-dynamic-content). +Respond with plain text, or with an Adaptive Card. You can use the [Adaptive Card Designer](https://adaptivecards.io/designer/) to help visually design your Adaptive Card UI. For more information on how to send an Adaptive card with dynamic data, see [build command and response using Adaptive card](https://github.com/OfficeDev/TeamsFx/wiki/Respond-to-chat-commands-in-Teams#how-to-build-command-response-using-adaptive-card-with-dynamic-content). <br> Respond with plain text, or with an Adaptive Card. You can use the [Adaptive Car <summary><b>3. Handle the command</b></summary> -TeamsFx SDK provides a convenient class `TeamsFxBotCommandHandler`, to handle when a command is triggered from Teams conversation message. Create a new file, `bot/src/doSomethingCommandHandler.ts`: -- ```TypeScript - import { Activity, CardFactory, MessageFactory, TurnContext } from "botbuilder"; - import { CommandMessage, TeamsFxBotCommandHandler, TriggerPatterns, MessageBuilder, } from "@microsoft/teamsfx"; - import doSomethingCard from "./adaptiveCards/doSomethingCommandResponse.json"; - import { AdaptiveCards } from "@microsoft/adaptivecards-tools"; - import { CardData } from "./cardModels"; -- export class DoSomethingCommandHandler implements TeamsFxBotCommandHandler { - triggerPatterns: TriggerPatterns = "doSomething"; -- async handleCommandReceived( - context: TurnContext, - message: CommandMessage - ): Promise<string | Partial<Activity>> { - // verify the command arguments which are received from the client if needed. - console.log(`Bot received message: ${message.text}`); -- const cardData: CardData = { - Title: "doSomething command is added", - body: "Congratulations! You have responded to doSomething command", - }; -- const cardJson = AdaptiveCards.declare(doSomethingCard).render(cardData); - return MessageFactory.attachment(CardFactory.adaptiveCard(cardJson)); - } - } +Following are the JavaScript, TypeScript, and C# command handlers to handle the command: ++# [JavaScript](#tab/js) ++TeamsFx SDK provides a convenient class `TeamsFxBotCommandHandler`, to handle when a command is triggered from Teams conversation message. Create a new file in the path `src/doSomethingCommandHandler.js`. ++Add the following code to the `doSomethingCommandHandler.js` file: ++```javascript +const doSomethingCard = require("./adaptiveCards/doSomethingCommandResponse.json"); +const { AdaptiveCards } = require("@microsoft/adaptivecards-tools"); +const { CardFactory, MessageFactory } = require("botbuilder"); ++class DoSomethingCommandHandler { + triggerPatterns = "doSomething"; - ``` + async handleCommandReceived(context, message) { + // verify the command arguments which are received from the client if needed. + console.log(`App received message: ${message.text}`); + const cardData = { + Title: "doSomething command is added", + body: "Congratulations! You have responded to doSomething command", + }; ++ const cardJson = AdaptiveCards.declare(doSomethingCard).render(cardData); + return MessageFactory.attachment(CardFactory.adaptiveCard(cardJson)); + } +} ++module.exports = { + DoSomethingCommandHandler, +}; ++``` ++# [TypeScript](#tab/ts) ++TeamsFx SDK provides a convenient class `TeamsFxBotCommandHandler`, to handle when a command is triggered from Teams conversation message. Create a new file in the path `src/doSomethingCommandHandler.ts`. ++Add the following code to the `doSomethingCommandHandler.ts` file: ++ ```TypeScript +/** TypeScript **/ +import { Activity, CardFactory, MessageFactory, TurnContext } from "botbuilder"; +import { + CommandMessage, + TeamsFxBotCommandHandler, + TriggerPatterns, + MessageBuilder, +} from "@microsoft/teamsfx"; +import doSomethingCard from "./adaptiveCards/doSomethingCommandResponse.json"; +import { AdaptiveCards } from "@microsoft/adaptivecards-tools"; +import { CardData } from "./cardModels"; ++export class DoSomethingCommandHandler implements TeamsFxBotCommandHandler { + triggerPatterns: TriggerPatterns = "doSomething"; ++ async handleCommandReceived( + context: TurnContext, + message: CommandMessage + ): Promise<string | Partial<Activity>> { + // verify the command arguments which are received from the client if needed. + console.log(`App received message: ${message.text}`); ++ const cardData: CardData = { + Title: "doSomething command is added", + body: "Congratulations! You have responded to doSomething command", + }; ++ const cardJson = AdaptiveCards.declare(doSomethingCard).render(cardData); + return MessageFactory.attachment(CardFactory.adaptiveCard(cardJson)); + } +} +``` ++# [C#](#tab/csharp) ++TeamsFx .NET SDK provides an interface `ITeamsCommandHandler` for command handler to handle when a command is triggered from Teams conversation message. Create a new file in the path `Commands/DoSomethingCommandHandler.cs`. ++Add the following code to the `DoSomethingCommandHandler.cs`: ++```csharp +using MyCommandApp.Models; +using AdaptiveCards.Templating; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Schema; +using Microsoft.TeamsFx.Conversation; +using Newtonsoft.Json; ++namespace MyCommandApp.Commands +{ + public class DoSomethingCommandHandler : ITeamsCommandHandler + { + private readonly ILogger<HelloWorldCommandHandler> _logger; + private readonly string _adaptiveCardFilePath = Path.Combine(".", "Resources", "DoSomethingCommandResponse.json"); ++ public IEnumerable<ITriggerPattern> TriggerPatterns => new List<ITriggerPattern> + { + new RegExpTrigger("doSomething") + }; ++ public HelloWorldCommandHandler(ILogger<HelloWorldCommandHandler> logger) + { + _logger = logger; + } ++ public async Task<ICommandResponse> HandleCommandAsync(ITurnContext turnContext, CommandMessage message, CancellationToken cancellationToken = default) + { + _logger?.LogInformation($"App received message: {message.Text}"); ++ // Read adaptive card template + var cardTemplate = await File.ReadAllTextAsync(_adaptiveCardFilePath, cancellationToken); ++ // Render adaptive card content + var cardContent = new AdaptiveCardTemplate(cardTemplate).Expand + ( + new HelloWorldModel + { + Title: "doSomething command is added", + body: "Congratulations! You have responded to doSomething command", + } + ); ++ // Build attachment + var activity = MessageFactory.Attachment + ( + new Attachment + { + ContentType = "application/vnd.microsoft.card.adaptive", + Content = JsonConvert.DeserializeObject(cardContent), + } + ); ++ // send response + return new ActivityCommandResponse(activity); + } + } +} +``` ++ You can customize the command, including calling an API, processing data, or any other command . <br> You can customize the command, including calling an API, processing data, or any <summary><b>4. Register the new command</b></summary> -Each new command needs to be configured in the `ConversationBot`, which initiates the conversational flow of the command bot template. In the `bot/src/internal/initialize.ts` file, update the commands array of the command property: +Each new command needs to be configured in the `ConversationBot`, which initiates the conversational flow of the command bot template. ++# [JavaScript/TypeScript](#tab/jsts2) ```TypeScript- import { HelloWorldCommandHandler } from "../helloworldCommandHandler"; - import { DoSomethingCommandHandler } from "../doSomethingCommandHandler"; - import { ConversationBot } from "@microsoft/teamsfx"; -- const commandBot = new ConversationBot({ - //... - command: { - enabled: true, - commands: [ - new HelloWorldCommandHandler(), - new DoSomethingCommandHandler() ], - }, - }); +/** Update ConversationBot in src/internal/initialize.js(ts) **/ +const commandApp = new ConversationBot({ + //... + command: { + enabled: true, + commands: [ + new HelloWorldCommandHandler(), + new DoSomethingCommandHandler()], // newly added command handler + }, +}); +``` ++# [C#](#tab/csharp3) ++```csharp +/** Update ConversationBot in Program.cs **/ +builder.Services.AddSingleton<HelloWorldCommandHandler>(); +builder.Services.AddSingleton<DoSomethingCommandHandler>(); // Add doSomething command handler to serrvice container +builder.Services.AddSingleton(sp => +{ + var options = new ConversationOptions() + { + Adapter = sp.GetService<CloudAdapter>(), + Command = new CommandOptions() + { + Commands = new List<ITeamsCommandHandler> + { + sp.GetService<HelloWorldCommandHandler>(), + sp.GetService<DoSomethingCommandHandler>(), // Register doSomething command handler to ConversationBot + } + } + }; ++ return new ConversationBot(options); +}); ``` -By completing the steps of adding a new command and response into your bot app, you can press F5 to debug locally with the command-response bot. Otherwise you can provision and deploy commands to deploy the change to Azure. +++Press **F5** to debug locally or provision and deploy commands to deploy the change to Azure. <br> </details> The default pattern to trigger a command is through a defined keyword. You can a You can find any capture group in `message.matches`, when using regular expressions. For example if user inputs `reboot myMachine`, `message.matches[1]`, it captures `myMachine`. The following example uses regular expression to capture strings after `reboot`: ```-- class HelloWorldCommandHandler { - triggerPatterns = /^reboot (.*?)$/i; //"helloWorld"; - async handleCommandReceived(context, message) { - console.log(`Bot received message: ${message.text}`); - const machineName = message.matches[1]; - console.log(machineName); - // Render your adaptive card for reply message - const cardData = { - Title: "Your Hello World Bot is Running", - body: "Congratulations! Your hello world bot is running. Click the button below to trigger an action.", - }; - const cardJson = AdaptiveCards.declare(helloWorldCard).render(cardData); - return MessageFactory.attachment(CardFactory.adaptiveCard(cardJson)); - } - } -- module.exports = { - HelloWorldCommandHandler, - } -+class HelloWorldCommandHandler { + triggerPatterns = /^reboot (.*?)$/i; //"reboot myDevMachine"; + async handleCommandReceived(context, message) { + console.log(`Bot received message: ${message.text}`); + const machineName = message.matches[1]; + console.log(machineName); + // Render your adaptive card for reply message + const cardData = { + Title: "Your Hello World Bot is Running", + body: "Congratulations! Your hello world bot is running. Click the button below to trigger an action.", + }; + const cardJson = AdaptiveCards.declare(helloWorldCard).render(cardData); + return MessageFactory.attachment(CardFactory.adaptiveCard(cardJson)); + } +} ``` -### How to build command and response using Adaptive card with dynamic content? +### Build command and response using Adaptive Card with dynamic content ++Adaptive Card provides [template language](/adaptive-cards/templating/) to allow users to render dynamic content with the same layout (the template). For example, use the adaptive card to render a list of items, such as to-do items or assign bugs that varies between different users. -Adaptive Card provides [template language](/adaptive-cards/templating/) to allow users to use dynamic content with the same template. For example, you can use the Adaptive Card to provide a list of items such as to-do list, assigned bugs and other similar lists that can vary according to users. You can perform the following steps to build command and response using Adaptive Card with dynamic content: +You can perform the following steps to build command and response using Adaptive Card with dynamic content: 1. Add your Adaptive Card template JSON file under `bot/adaptiveCards` folder.-1. Import the card template in your code file, where the command handler exists such as `myCommandHandler.ts`. +1. In the code file where the commnad handler exists, for example `myCommandHandler.ts`. Import the Adaptive Card template JSON file. 1. Model your card data. 1. Use `MessageBuilder.attachAdaptiveCard` in the template with dynamic card data. -You can also add new cards, if required for your application. For more information on how to build different types of Adaptive Cards with a list, or a table of dynamic contents using `ColumnSet`, and `FactSet`, see [sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/ga/adaptive-card-notification). +If needed, you can add new cards for your application. For more information on how to build different types of Adaptive Cards with a list, or a table of dynamic contents using `ColumnSet` and `FactSet`, see [sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/ga/adaptive-card-notification). ### Access Microsoft Graph If you're responding to a command that needs to access Microsoft Graph data of a ### Connect to existing APIs -If you don't have the required SDK, and need to invoke external APIs in your code. The `Teams: Connect to an API` command in Microsoft Visual Studio Code Teams Toolkit extension, or `teamsfx add api-connection` command in TeamsFx CLI can be used to bootstrap code to call target APIs. For more information, see [configure API connection](../../../toolkit/add-API-connection.md#). +If you don't have the required SDK and need to invoke external APIs in your code, the **Teams: Connect to an API** command in Microsoft Visual Studio Code (VS Code) Teams Toolkit extension, or the **teamsfx add api-connection** command in TeamsFx CLI can be used to bootstrap code to call target APIs. For more information, see [configure API connection](../../../toolkit/add-API-connection.md#). ## FAQ If you don't have the required SDK, and need to invoke external APIs in your cod <summary><b>How to extend my command and response to support notifications?</b></summary> -Add Adaptive Cards in response to external events to extend your command and response to support notifications. See the steps to [customize notifications](notification-bot-in-teams.md#customize-notification). +1. Go to `bot\src\internal\initialize.ts(js)` and update your `conversationBot` initialization to enable notification feature. ++ :::image type="content" source="../../../assets/images/command-bot-teams/notification-enable.png" alt-text="Conversation bot initialization to enable notification feature." lightbox="../../../assets/images/command-bot-teams/notification-enable.png"::: ++1. To customize sending the notification, see [Send notification to the bot installation target](notification-bot-in-teams.md#customize-where-notifications-are-sent). ++ 1. If you want to quickly add a sample notification triggered by an HTTP request, add the following sample code in `bot\src\index.ts(js)`: ++ ```js + server.post("/api/notification", async (req, res) => { + for (const target of await commandBot.notification.installations()) { + await target.sendMessage("This is a sample notification message"); + } ++ res.json({}); + }); + ``` ++1. Uninstall your previous bot installation from Teams, and run local debug to test your bot notification. +1. Send a notification to the bot installation targets (channel, group chat, or personal chat) by using an HTTP POST request with target URL `https://localhost:3978/api/notification`. ++To send a notification with Adaptive Card and add more triggers, see [Notification bot in Teams](notification-bot-in-teams.md). <br> Add Adaptive Cards in response to external events to extend your command and res <summary><b>How to extend my command bot by adding workflow bot Adaptive Card actions?</b></summary> -For more information on how to add workflow bot Adaptive Card actions to command bot, see the steps to [add card actions](workflow-bot-in-teams.md#add-card-actions). +The Adaptive Card action handler feature enables the app to respond to Adaptive Card actions triggered by users to complete a sequential workflow. An Adaptive Card provides one or more buttons in the card to ask for user's input such as calling some APIs. The Adaptive Card then sends another Adaptive Card in the conversation to respond to the card action. ++For more information on how to add Adaptive Card actions to command bot, see [Workflow bot in Teams](workflow-bot-in-teams.md). <br> |
platform | Notification Bot In Teams | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/bots/how-to/conversations/notification-bot-in-teams.md | ms.localizationpriority: high # Notification bot in Teams -Microsoft Teams Toolkit enables you to build applications that capture events and send them as notifications to a personal, group chat, or a channel in Teams. You can send notifications as plain text or Adaptive Cards. The notification bot template creates an app that sends a message to Teams with Adaptive Cards triggered by HTTP post request. +Microsoft Teams Toolkit enables you to build applications that capture events and send them as notifications to a personal, group chat, or a channel in Teams. You can send notifications as plain text or [Adaptive Cards](../../../task-modules-and-cards/cards/cards-reference.md). The notification bot template creates an app that sends a message to Teams with Adaptive Cards triggered by HTTP post request. -The app template is built using the TeamsFx SDK, which provides a simple set of functions over Microsoft Bot Framework to implement your requirement. For example, in a scenario where a travel agency builds an app in Teams for their customers to keep them up-to-date with the weather forecast. In the following diagram you can see a Teams app that sends notification to the travelers about the destination weather forecast: +The app template is built using the TeamsFx SDK, which provides a simple set of functions over Microsoft Bot Framework to implement your requirement. For example, a travel agency builds an app in Teams for their users to keep them up-to-date with the weather forecast. In the following flowchart, a Teams app notifies about the weather forecast to the users using an Adaptive Card: :::image type="content" source="../../../assets/images/notification-bot/notification-new-scenario-diagram.png" alt-text="weather forecast sample notification scenario" lightbox="../../../assets/images/notification-bot/notification-new-scenario-diagram.png"::: Bot Framework SDK provides the functionality to proactively message in Teams. Te When you send notifications, TeamsFx SDK creates a new conversation from the selected conversation reference, and then sends a message. For advanced usage, you can directly access the conversation reference to execute your own bot logic: -# [TypeScript](#tab/ts) +# [TypeScript](#tab/ts1) ```TypeScript-- // list all installation targets -for (const target of await bot.notification.installations()) { - // call Bot Framework's adapter.continueConversation() - await target.adapter.continueConversation(target.conversationReference, async (context) => { - // your own bot logic - await context... - }); +// list all installation targets +for (const target of await notificationApp.notification.installations()) { + // call Bot Framework's adapter.continueConversationAsync() + await target.adapter.continueConversationAsync( + target.botAppId, + target.conversationReference, + async (context) => { + // your own bot logic + await context... + } + ); } ``` -# [C#](#tab/csharp) +# [C#](#tab/csharp1) ```C# // list all installation targets-foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { + foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { // call Bot Framework's adapter.ContinueConversationAsync() await target.Adapter.ContinueConversationAsync( target.BotAppId, A notification bot needs to be installed into a team, or a group chat, or as per :::image type="content" source="../../../assets/images/notification-bot/notification-installation-scope.png" alt-text="add installation scope"::: For more install options, see [configure default install options](../../../concepts/deploy-and-publish/apps-publish-overview.md#configure-default-install-options).+For uninstalling, see [remove an app from Teams](https://support.microsoft.com/en-us/office/remove-an-app-from-teams-0bc48d54-e572-463c-a7b7-71bfdc0e4a9d). [Back to top](#notification-bot-in-teams) You can make the following customizations to extend the notification template to You can customize the following triggers: -* `Restify` based notification +* `Restify` based notification: - When HTTP request is sent to `src/index.js` entry point, the default implementation sends an Adaptive Card to Teams. You can customize this event by modifying `src/index.js`. A typical implementation can call an API to retrieve events, data, or both that can send an Adaptive Card as required. You can perform the following to add more triggers: + * When a HTTP request is sent to `src/index.js` entry point, the default implementation sends an Adaptive Card to Teams. You can customize this event by modifying `src/index.js`. A typical implementation can call an API to retrieve events, data, or both that can send an Adaptive Card as required. You can perform the following to add more triggers: - * Create a new routing: `server.post("/api/new-trigger", ...)`. - * Add timer trigger(s) from widely used npm packages, such as [cron](https://www.npmjs.com/package/cron), [node-schedule](https://www.npmjs.com/package/node-schedule), or from other packages. + * Create a new routing: `server.post("/api/new-trigger", ...)`. + * Add timer trigger(s) from widely used npm packages, such as [cron](https://www.npmjs.com/package/cron), [node-schedule](https://www.npmjs.com/package/node-schedule), or from other packages. > [!NOTE] > By default Teams Toolkit scaffolds a single `restify` entry point in `src/index.js`. * Azure Functions based notification: - * When you select timer trigger, the default implemented Azure Function timer trigger `src/timerTrigger.ts` sends an Adaptive Card every 30 seconds. You can edit the file `*Trigger/function.json` to customize the `schedule` property. For more information, see [Azure Function documentation](/azure/azure-functions/functions-bindings-timer?tabs=in-process&pivots=programming-language-javascript). + * When you select `timer` trigger, the default implemented Azure Function timer trigger `src/timerTrigger.ts` sends an Adaptive Card every 30 seconds. You can edit the file `*Trigger/function.json` to customize the `schedule` property. For more information, see [Azure Function documentation](/azure/azure-functions/functions-bindings-timer?tabs=python-v2%2Cin-process&pivots=programming-language-javascript#ncrontab-expressions). :::image type="content" source="../../../assets/images/notification-bot/notification-timer-triggered.png" alt-text="sample of timer triggered notification"::: - * When you select `http` trigger, the HTTP request triggers the notification, and the default implementation sends an Adaptive Card to Teams. You can change this event by customizing `src/*Trigger.ts`. This implementation can call an API to retrieve events, data, or both, which can send an Adaptive Card as required. + * When you select `http` trigger, the HTTP request triggers the notification, and the default implementation sends an Adaptive Card to Teams. You can change this event by customizing `src/*Trigger.ts`. This implementation can call an API to retrieve events, data, or both, which can send an Adaptive Card as required. :::image type="content" source="../../../assets/images/notification-bot/notification-http-triggered.png" alt-text="sample of HTTP triggered notification"::: You can customize the following triggers: * `Cosmos DB` trigger to send notifications when a Cosmos document is created or updated. -For more information on support triggers, see [Azure Functions support triggers](/azure/azure-functions/functions-triggers-bindings?tabs=javascript). +For more information on support triggers, see [Azure Functions support triggers](/azure/azure-functions/functions-triggers-bindings?tabs=javascript#supported-bindings). ### Customize the notification content The file `src/adaptiveCards/notification-default.json` defines the default Adaptive Card. You can use the [Adaptive Card designer](https://adaptivecards.io/designer/) to help visually design your Adaptive Card UI. The `src/cardModels.ts` defines a data structure that is used to load data for the Adaptive Card. The binding between the card model and the Adaptive Card is done by matching name such as `CardData.title` maps to `${title}` in the Adaptive Card. You can add, edit, or remove properties and their bindings to customize the Adaptive Card as required. -You can also add new cards if needed. For more information on how to build different types of Adaptive Cards with a list or table of dynamic contents using `ColumnSet` and `FactSet`, see [Adaptive Card notification sample](<https://github.com/OfficeDev/TeamsFx-Samples/tree/ga/adaptive-card-notification>). +You can also add new cards if needed. For more information on how to build different types of Adaptive Cards with a list or table of dynamic contents using `ColumnSet` and `FactSet`, see [Adaptive Card notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/v3/adaptive-card-notification). ### Customize where notifications are sent You can customize sending the notification to the following targets: * Notifications to a personal chat: - ```TypeScript - // list all installation targets - for (const target of await bot.notification.installations()) { - // "Person" means this bot is installed as Personal app - if (target.type === "Person") { - // Directly notify the individual person - await target.sendAdaptiveCard(...); - } - } - ``` -- ```C# - // list all installation targets - foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { - // "Person" means this bot is installed as Personal app - if (target.Type == NotificationTargetType.Person) - { - // Directly notify the individual person - await target.SendAdaptiveCard(...); - } - } - ``` + # [TypeScript](#tab/ts2) ++ ```TypeScript + // list all installation targets + for (const target of await notificationApp.notification.installations()) { + // "Person" means this bot is installed as Personal app + if (target.type === "Person") { + // Directly notify the individual person + await target.sendAdaptiveCard(...); + } + } + ``` ++ # [C#](#tab/csharp2) ++ ```C# + // list all installation targets + foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { + // "Person" means this bot is installed as Personal app + if (target.Type == NotificationTargetType.Person) + { + // Directly notify the individual person + await target.SendAdaptiveCard(...); + } + } + ``` ++ * Notifications to a group chat: - ```TypeScript - // list all installation targets - for (const target of await bot.notification.installations()) { - // "Group" means this bot is installed to a Group Chat - if (target.type === "Group") { + # [TypeScript](#tab/ts3) ++ ```TypeScript + // list all installation targets + for (const target of await notificationApp.notification.installations()) { + // "Group" means this bot is installed to a Group Chat + if (target.type === "Group") { // Directly notify the Group Chat await target.sendAdaptiveCard(...); - // List all members in the Group Chat then notify each member - const members = await target.members(); - for (const member of members) { - await member.sendAdaptiveCard(...); - + // List all members in the Group Chat then notify each member + const members = await target.members(); + for (const member of members) { + await member.sendAdaptiveCard(...); + } }- } - } -- ``` + + } + ``` - ```C# - // list all installation targets - foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { - // "Group" means this bot is installed to a Group Chat - if (target.Type == NotificationTargetType.Group) - { - // Directly notify the Group Chat - await target.SendAdaptiveCard(...); + # [C#](#tab/csharp3) - // List all members in the Group Chat then notify each member - var members = await target.GetMembersAsync(); - foreach (var member in members) { + ```C# + // list all installation targets + foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { + // "Group" means this bot is installed to a Group Chat + if (target.Type == NotificationTargetType.Group) + { + // Directly notify the Group Chat + await target.SendAdaptiveCard(...); + // List all members in the Group Chat then notify each member + var members = await target.GetMembersAsync(); + foreach (var member in members) { await member.SendAdaptiveCard(...);- } - } - } - ``` + } + } + } + ``` ++ * Notifications to a channel: - ```TypeScript - // list all installation targets - for (const target of await bot.notification. installations()) { - // "Channel" means this bot is installed to a Team (default to notify General channel) - if (target.type === "Channel") { + # [TypeScript](#tab/ts4) ++ ```TypeScript + // list all installation targets + for (const target of await notificationApp.notification.installations()) { + // "Channel" means this bot is installed to a Team (default to notify General channel) + if (target.type === "Channel") { // Directly notify the Team (to the default General channel) await target.sendAdaptiveCard(...); // List all members in the Team then notify each member const members = await target.members(); for (const member of members) {- await member.sendAdaptiveCard(...); - } + await member.sendAdaptiveCard(...); + } - // List all channels in the Team then notify each channel - const channels = await target.channels(); - for (const channel of channels) { + // List all channels in the Team then notify each channel + const channels = await target.channels(); + for (const channel of channels) { await channel.sendAdaptiveCard(...);- } - } - } - ``` -- ```C# - // list all installation targets - foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { + } + } + } + ``` ++ # [C#](#tab/csharp4) ++ ```C# + // list all installation targets + foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { // "Channel" means this bot is installed to a Team (default to notify General channel) if (target.Type == NotificationTargetType.Channel) {- // Directly notify the Team (to the default General channel) - await target.SendAdaptiveCard(...); + // Directly notify the Team (to the default General channel) + await target.SendAdaptiveCard(...); // List all members in the Team then notify each member var members = await target.GetMembersAsync(); foreach (var member in members) {- await member.SendAdaptiveCard(...); - } + await member.SendAdaptiveCard(...); + } - // List all channels in the Team then notify each channel - var channels = await target.GetChannelsAsync(); - foreach (var channel in channels) { - await channel.SendAdaptiveCard(...); + // List all channels in the Team then notify each channel + var channels = await target.GetChannelsAsync(); + foreach (var channel in channels) { + await channel.SendAdaptiveCard(...); + } }- } }+ ``` - ``` + * Notifications to a specific channel: - ```TypeScript - // find the first channel when the predicate is true. - const channel = await bot.notification.findChannel(c => Promise.resolve(c.info.name === "MyChannelName")); -- // send adaptive card to the specific channel. - await channel?.sendAdaptiveCard(...); - ``` + ```TypeScript + // find the first channel when the predicate is true. + const channel = await notificationApp.notification.findChannel(c => Promise.resolve(c.info.name === "MyChannelName")); + + // send adaptive card to the specific channel. + await channel?.sendAdaptiveCard(...); + ``` > [!NOTE] > To prevent an undefined output, ensure that you install the bot app in the **General** channel of a Team. * Notifications to a specific person: - ```TypeScript + ```TypeScript + // find the first person when the predicate is true. + const member = await notificationApp.notification.findMember(m => Promise.resolve(m.account.name === "Bob")); + + // send adaptive card to the specific person. + await member?.sendAdaptiveCard(...); + ``` - // find the first person when the predicate is true. - const member = await bot.notification.findMember(m => Promise.resolve(m.account.name === "Bob")); -- // send adaptive card to the specific person. - await member?.sendAdaptiveCard(...); - ``` -- > [!NOTE] - > To prevent an undefined output and a missing notification, you need to include the specific person in notification installation scope. + > [!NOTE] + > To prevent an undefined output and a missing notification, you need to include the specific person in notification installation scope. [Back to top](#notification-bot-in-teams) You need to create `ConversationBot` to send notification. # [JavaScript/TypeScript](#tab/jsts) ```JS/TS-/** Javascript/Typescript: bot/src/internal/initialize.*s **/ -const bot = new ConversationBot({ - // The bot id and password to create BotFrameworkAdapter. - // See https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0 to learn more about adapters. +/** Javascript/Typescript: src/internal/initialize.*s **/ +const notificationApp = new ConversationBot({ + // The bot id and password to create CloudAdapter. + // See https://aka.ms/about-bot-adapter to learn more about adapters. adapterConfig: {- appId: process.env.BOT_ID, - appPassword: process.env.BOT_PASSWORD, + MicrosoftAppId: config.botId, + MicrosoftAppPassword: config.botPassword, + MicrosoftAppType: "MultiTenant", }, // Enable notification notification: { const bot = new ConversationBot({ }); ``` -# [C#](#tab/csharp1) +# [C#](#tab/csharp5) ```C# /** .NET: Program.cs or Startup.cs **/-// list all installation targets -foreach (var target in await _conversation.Notification.GetInstallationsAsync()) { - // "Person" means this bot is installed as Personal app - if (target.Type == NotificationTargetType.Person) +// Create the Conversation with notification feature enabled. +builder.Services.AddSingleton(sp => +{ + var options = new ConversationOptions() {- // Directly notify the individual person - await target.SendAdaptiveCard(...); - } -} + // To use your own CloudAdapter + Adapter = sp.GetService<CloudAdapter>(), + Notification = new NotificationOptions + { + BotAppId = builder.Configuration["MicrosoftAppId"], + }, + }; ++ return new ConversationBot(options); +}); ``` foreach (var target in await _conversation.Notification.GetInstallationsAsync()) You can customize by creating your own adapter, or customize the adapter after initialization. Following is the code sample for creating your adapter: ```Typescript-- // Create your own adapter -const adapter = new BotFrameworkAdapter(...); +// Create your own adapter +const adapter = new CloudAdapter(...); // Customize your adapter, e.g., error handling adapter.onTurnError = ... -const bot = new ConversationBot({ +const notificationApp = new ConversationBot({ // use your own adapter adapter: adapter; ... }); // Or, customize later-bot.adapter.onTurnError = ... -+notificationApp.adapter.onTurnError = ... ``` [Back to top](#notification-bot-in-teams) bot.adapter.onTurnError = ... Storage can be used to implement notification connections. You can add your own storage with the help of following code sample: -# [TypeScript](#tab/ts4) +# [TypeScript](#tab/ts5) ```Typescript- // implement your own storage +// implement your own storage class MyStorage implements NotificationTargetStorage {...} const myStorage = new MyStorage(...); // initialize ConversationBot with notification enabled and customized storage-const bot = new ConversationBot({ - // The bot id and password to create BotFrameworkAdapter. - // See https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0 to learn more about adapters. +const notificationApp = new ConversationBot({ + // The bot id and password to create CloudAdapter. + // See https://aka.ms/about-bot-adapter to learn more about adapters. adapterConfig: {- appId: process.env.BOT_ID, - appPassword: process.env.BOT_PASSWORD, + MicrosoftAppId: config.botId, + MicrosoftAppPassword: config.botPassword, + MicrosoftAppType: "MultiTenant", }, // Enable notification notification: { const bot = new ConversationBot({ storage: myStorage, }, });-- ``` -# [C#](#tab/csharp2) +# [C#](#tab/csharp6) ```C#- // implement your own storage public class MyStorage : INotificationTargetStorage {...} builder.Services.AddSingleton(sp => return new ConversationBot(options); });- ``` If storage isn't provided, you can use a default local file storage, which store * `.notification.localstore.json` if running locally. * `${process.env.TEMP}/.notification.localstore.json`, if `process.env.RUNNING_ON_AZURE` is set to 1. -For sample implementation to use Azure blob storage, see [add notification storage implementation sample](https://github.com/OfficeDev/TeamsFx-Samples/blob/ga/adaptive-card-notification/bot/src/storage/blobsStorage.ts). +The `NotificationTargetStorage` is different from Bot Framework SDK's [custom storage](/azure/bot-service/bot-builder-custom-storage). The notification storage requires `read`, `write`, `delete`, and `list` functionalities but Bot Framework SDK's storage has `read`, `write`, and `delete` functionalities and doesnΓÇÖt have the `list` functionality. ++For more information about Azure blob storage, see [add notification storage implementation sample](https://github.com/OfficeDev/TeamsFx-Samples/blob/v3/adaptive-card-notification/src/storage/blobsStorage.ts). > [!NOTE]-> It's recommended to use your own shared storage for production environment. +> +> * It's recommended to use your own shared storage for production environment. +> * If you implement your own Bot Framework SDK's storage, for example, `botbuilder-azure-blobs.BlobsStorage`, you need to implement another storage for notification. You can share the same Blob Connection String with different containers. [Back to top](#notification-bot-in-teams) ## Add authentication for notification API -If you select HTTP trigger, the scaffolded notification API doesn't have authentication or authorization enabled. Ensure that you add authentication or authorization for the API before using it for production. You can perform either of the following: +If you select HTTP trigger, the scaffolded notification API doesn't have authentication or authorization enabled. Ensure that you add authentication or authorization for the API before using it for production. You can perform one of the following actions: * Use an API key. You can use [function access keys](/azure/azure-functions/security-concepts?tabs=v4#function-access-keys), if you select Azure Functions to host your notification bot. There can be more authentication or authorization solutions for an API, you can ## Connect to existing APIs -If you don't have the required SDK, and want to invoke external APIs in your code. The `Teams: Connect to an API` command in Microsoft Visual Studio Code Teams Toolkit extension, or `teamsfx add api-connection` command in TeamsFx CLI can be used to bootstrap code to call target APIs. For more information, see [Integrate existing third-party APIs](../../../toolkit/add-API-connection.md). +If you don't have the required SDK and want to invoke external APIs in your code, the **Teams: Connect to an API** command in Microsoft Visual Studio Code Teams Toolkit extension, or the **teamsfx add api-connection** command in TeamsFx CLI can be used to bootstrap code to call target APIs. For more information, see [Integrate existing third-party APIs](../../../toolkit/add-API-connection.md). ### Teams bot application or Teams Incoming Webhook In the following table, you can see the comparison of the two different ways: ### Incoming Webhook notification Incoming Webhooks help in posting messages from apps to Teams. If Incoming Webhooks are enabled for a Team in any channel, it exposes the HTTPS endpoint, which accepts correctly formatted JSON and inserts the messages into that channel. For example, you can create an Incoming Webhook in your DevOps channel, configure your build, and simultaneously deploy and monitor services to send alerts.-TeamsFx provides you with an [Incoming Webhook notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/ga/incoming-webhook-notification#getting-started-with-incoming-webhook-notification-sample) that helps you: +TeamsFx provides you with an [Incoming Webhook notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/v3/incoming-webhook-notification) that helps you to: * [Create an Incoming Webhook](../../../webhooks-and-connectors/how-to/add-incoming-webhook.md) in Teams. * Send notifications using Incoming Webhooks with Adaptive Cards. TeamsFx provides you with an [Incoming Webhook notification sample](https://gith <summary><b>Why is the notification installations empty even though the bot app is installed in Teams?</b></summary> -Teams sends an event only at the first installation. If the bot app is already installed before your notification bot service is launched, the installation event, either didn't reach the bot service, or is omitted. +Teams sends an event only at the first installation. If the bot app is already installed before your notification bot service is launched, either the installation event didn't reach the bot service or is omitted. -You can resolve this in the following ways: +You can resolve this issue in the following ways: -* Send a message to your personal bot or mention your bot in group chat, or channel, which helps you to reach the bot service again with correct installation information. +* Send a message to your personal bot or mention your bot in group chat or channel, which helps you to reach the bot service again with correct installation information. * Uninstall the bot app from Teams then redebug or relaunch it. You can resend the installation event to bot service. -Notification target connections are stored in the persistence storage. If you're using the default local file storage, all installations are stored under `bot/.notification.localstore.json`. +Notification target connections are stored in the persistence storage. If you're using the default local file storage, all installations are stored under `.notification.localstore.json`. > [!NOTE] > For more information to add your own storage, see [add storage](#add-storage). Notification target connections are stored in the persistence storage. If you're <summary><b>Why Bad Request or Bad Argument error occurs when sending notification?</b></summary> -If the notification installation doesn't match the bot ID or password, you can get a **Failed to decrypt conversation ID** error. One possible cause for this error is that the bot ID or password is changed due to cleaning local state or reprovisioning. +If the notification installation doesn't match the bot ID or password, you can get a **Failed to decrypt conversation ID** error. One possible cause for this error is that the bot ID or password is changed due to cleaning local state or re-provisioning. -You can resolve this by cleaning your notification storage. After cleaning, notify in Teams to reinstall your bot, and ensure that the new installation is up-to-date. Each stored notification installation is bound with one bot. If you're able to check your notification storage, its bot field should match the bot you're running such as the bot ID with the same GUID. +You can resolve this issue by cleaning your notification storage. After cleaning, notify in Teams to reinstall your bot, and ensure that the new installation is up to date. Each stored notification installation is bound with one bot. If you're able to check your notification storage, its bot field should match the bot you're running such as the bot ID with the same GUID. > [!NOTE] > In case of local storage the default location is `.notification.localstore.json`. Notification target connections are stored in the persistence storage. If you're <details> -<summary><b>Why is undefined error returned when using the API findChannel()?</b></summary> +<summary><b>Why is undefined error returned when using the API `findChannel`()?</b></summary> -You can encounter an undefined error, when the bot app is installed into other channels instead of the **General** channel. To fix this error, you can uninstall the bot app from Teams and redebug and relaunch it. After you've redebug and relaunched, ensure that the bot app is installed into the **General** channel. +You can encounter an undefined error, when the bot app is installed into other channels instead of the `General` channel. To fix this error, you can uninstall the bot app from Teams and redebug and relaunch it. After you've redebug and relaunched, ensure that the bot app is installed into the `General` channel. <br> You can encounter an undefined error, when the bot app is installed into other c <summary><b>Can I know all the targets where my bot is installed in and out of the notification project?</b></summary> -There are Microsoft Graph APIs to list apps installed in a team, group, or chat. If required you need to iterate your team, group, or chat into an installed app to be targeted. In the notification project, it uses persistence storage to store installation targets. For more information, see [notification based on events](#notification-based-on-events). +There are [Microsoft Graph APIs](/graph/api/team-list-installedapps) to list apps installed in a team, group, or chat. If necessary, iterate your team, group, or chat into an installed app to be targeted. In the notification project, it uses persistence storage to store installation targets. For more information, see [notification based on events](#notification-based-on-events). <br> There are Microsoft Graph APIs to list apps installed in a team, group, or chat. <summary><b>How to customize the Azurite listening ports?</b></summary> -If Azurite exits due to port in use, you can specify another listening port and update the connection string of `AzureWebJobsStorage` in `bot/local.settings.json`. +If Azurite exits due to port in use, you can [specify another listening port](/azure/storage/common/storage-use-azurite?tabs=visual-studio#blob-listening-port-configuration) and update the [connection string](/azure/storage/common/storage-use-azurite?tabs=visual-studio#http-connection-strings) of `AzureWebJobsStorage` in `local.settings.json`. <br> If Azurite exits due to port in use, you can specify another listening port and <summary><b>How to extend my notification bot to support command and response?</b></summary> - The command and response adds the ability for your bot to capture the commands sent in a Teams message and respond with Adaptive Cards. See the steps to [add command and response](command-bot-in-teams.md#add-command-and-response). +1. Go to `bot\src\internal\initialize.ts(js)` and update your `conversationBot` initialization to enable the notification feature: ++ :::image type="content" source="../../../assets/images/notification-bot/notification-bot-enable.png" alt-text="Conversation bot initialization to enable notification feature." lightbox="../../../assets/images/notification-bot/notification-bot-enable.png"::: ++2. To add command to your bot, follow the instructions in [Command bot in Teams](command-bot-in-teams.md). <br> If Azurite exits due to port in use, you can specify another listening port and <summary><b>How to extend my notification bot by adding workflow bot Adaptive Card actions?</b></summary> -You can extend your notification bot by defining actions and use workflow bot to return an Adaptive Card in response to the action. To add an Adaptive Card in notification bot, see the steps to [add card action](workflow-bot-in-teams.md#add-card-actions). +The Adaptive Card action handler feature enables the app to respond to Adaptive Card actions that are triggered by end users to complete a sequential workflow. An Adaptive Card provides one or more buttons in the card to ask for user's input such as calling some APIs. The Adaptive Card then sends another Adaptive Card in the conversation to respond to the card action. ++For more information on how to add adaptive card actions to command bot, see [Workflow bot in Teams](workflow-bot-in-teams.md). <br> |
platform | Workflow Bot In Teams | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/bots/how-to/conversations/workflow-bot-in-teams.md | Title: Workflow bot in Teams description: Learn how to send a response to card action in Teams workflow bot, add more card actions and customize action responses. -+ ms.localizationpriority: high A workflow bot can be installed into a team, group chat, or as personal app, dep > [!NOTE] > You can select the capability that you want to install, when adding the app. For more information, see [configure default install options](../../../concepts/deploy-and-publish/apps-publish-overview.md#configure-default-install-options). -You can create a workflow bot to respond to the Adaptive Card triggered by users. Adaptive Card action handler powered by TeamsFx SDK can execute the Adaptive Card universal action `Action.Execute` triggered by users. In response to this respective card action in the conversation another Adaptive Card is sent by the Adaptive card action handler. +You can create a workflow bot to respond to the Adaptive Card triggered by users. Adaptive Card action handler powered by TeamsFx SDK can execute the Adaptive Card universal action `Action.Execute` triggered by users. In response to this respective card action in the conversation, the Adaptive Card action handler sends another Adaptive card. :::image type="content" source="../../../assets/images/sbs-workflow-bot/sbs-workflow-bot-final-output.png" alt-text="Workflow bot final output with a button" lightbox="../../../assets/images/sbs-workflow-bot/sbs-workflow-bot-final-output.png" ::: The following diagram illustrates how to respond to an Adaptive Card action with 1. **Card action handler**: Triggered when users invoke the corresponding card action, its `triggerVerb` is same as the `verb` property in Adaptive Card action. It can send a response card to respond to the action. 1. **Response card**: The card that responds to the action when user invokes it from the action card. -To handle card actions with TeamsFx SDK, each card action handler must implement the `TeamsFxAdaptiveCardActionHandler` interface. This is the interface definition for `TeamsFxAdaptiveCardActionHandler`: +To handle card actions with TeamsFx SDK, each card action handler must implement the `TeamsFxAdaptiveCardActionHandler` interface: ``` Export interface-+- TeamsFxAdaptiveCardActionHandler { /** For more information on initialization customization, see [additional initializa ## Add card actions -To add card actions with JavaScript and TypeScript, you can perform the following: +To add card actions with JavaScript and TypeScript, follow these steps: <br> To add card actions with JavaScript and TypeScript, you can perform the followin <summary><b>1. Add an action to your Adaptive Card</b></summary> -You can add a new action (button) to an Adaptive Card by defining it in the JSON file, such as add a new `DoSomething` action to the `src/adaptiveCards/helloworldCommandResponse.json` file. This is a sample action type `Action.Execute`: +You can add a new action (button) to an Adaptive Card by defining it in the JSON file, such as add a new `DoSomething` action to the `src/adaptiveCards/helloworldCommandResponse.json` file. ++The following code is an example of the action type `Action.Execute`: ```helloworldCommandResponse.json { You can create a new file `bot/src/cardActions/doSomethingActionHandler.ts`: -The following is an example of action handler: --* `triggerVerb` is the verb property of your action. -* `actionData` is the data associated with the action, which may include dynamic user input, or some contextual data provided in the data property of your action. -* If an Adaptive Card is returned, the existing card is replaced with it by default. +> [!NOTE] +> +> * `triggerVerb` is the verb property of your action. +> * `actionData` is the data associated with the action, which may include dynamic user input, or some contextual data provided in the data property of your action. +> * If an Adaptive Card is returned, the existing card is replaced with it by default. <br> You need to configure each new card action in the `conversationBot` that enables The following steps help you to register the action handler: 1. You can open file `bot/src/internal/initialize.js(ts)`.-1. You need to update your `conversationBot` initialization, to enable `cardAction` feature and add the handler to actions array: +1. You need to update your `conversationBot` initialization, to enable `cardAction` feature. Add the handler to `actions` array using the following code: ```initialize.js(ts) const conversationBot = new ConversationBot({ The following steps help you to register the action handler: You can use the `adaptiveCardResponse` property in handler to customize how the bot sends the Adaptive Card to users. Following are the three options to customize: -* The response card is replaced by the current card where the button is defined for the interactor that triggers the action. The users in the conversation can still view the original action card `AdaptiveCardResponse.ReplaceForInteractor`. This is the default behavior. +* The response card is replaced by the current card where the button is defined for the interactor that triggers the action. The users in the conversation can still view the original action card `AdaptiveCardResponse.ReplaceForInteractor` by default. :::image type="content" source="../../../assets/images/sbs-workflow-bot/replace-for-interactor.png" alt-text="Customize how the bot sends adaptive card" lightbox="../../../assets/images/sbs-workflow-bot/replace-for-interactor.png"::: The following steps help you to add user-specific view with TeamsFx SDK: <summary><b>1. Enable refresh in base Adaptive Card</b></summary> - The user-specific views are refreshed from a base card, when response card is refreshed from the base card, as illustrated in the [auto-refresh user-specific view](#auto-refresh-to-user-specific-view). You need to enable auto-refresh on the base card. There are two options to achieve this: + The user-specific views are refreshed from a base card, when response card is refreshed from the base card, as illustrated in the [auto-refresh user-specific view](#auto-refresh-to-user-specific-view). You can enable auto-refresh on the base card as follows: -* First option enables user-specific view refresh with SDK. The base card can be sent as a command response or a card action response. You can enable user-specific view refresh in `handleCommandReceived` of a command handler, or in `handleActionInvoked` of card action handler where the base card is returned. You can use `refresh(refreshVerb, userIds, data)` method from the `@microsoft/adaptivecards-tools` library to inject a refresh section into your base card. To define the refresh section, ensure that you provide the following: +* First option enables user-specific view refresh with SDK. The base card can be sent as a command response or a card action response. You can enable user-specific view refresh in `handleCommandReceived` of a command handler, or in `handleActionInvoked` of card action handler where the base card is returned. You can use `refresh(refreshVerb, userIds, data)` method from the `@microsoft/adaptivecards-tools` library to inject a refresh section into your base card. To define the refresh section, ensure that you provide the following properties: 1. `userIds`: A set of user MRIs for those who can trigger auto-refresh. For more information on how to add in `userIds` list in refresh section of Adaptive Card, see [fetch the roster or user profile](../get-teams-context.md#fetch-the-roster-or-user-profile). 1. `verb`: A string to identify the refresh action. The following steps help you to add user-specific view with TeamsFx SDK: * Second option enables user-specific view to refresh your Adaptive Card. This is a sample refresh action defined in `baseCard.json`: ```baseCard.json- { "type": "AdaptiveCard", "refresh": { The following steps help you to add user-specific view with TeamsFx SDK: ], ... }+ ``` You need to replace `${userID}` with user MRI in code, while rendering your card content. The following steps help you to add user-specific view with TeamsFx SDK: You need to design the user-specific Adaptive Card to refresh a specific response card such as `responseCard.json` for `userA` shown in the diagram for [refresh behavior](#auto-refresh-to-user-specific-view). To get started, you can create a `responseCard.json` with the following content, and save it in `bot/src/adaptiveCards` folder: ```responseCard.json-+- { "type": "AdaptiveCard", "body": [ |
platform | Overview | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/concepts/deploy-and-publish/appsource/post-publish/overview.md | With your app listed on the Microsoft Teams store, start thinking about how you' ## Analyze app usage -You can track your app usage in the [Teams app usage report](/office/dev/store/teams-apps-usage) in Partner Center. Metrics include Monthly, Daily, and Weekly active users, and retention and intensity charts enabling you to track churn and frequency of usage. +You can track your app usage in the [Teams app usage report](/office/dev/store/teams-apps-usage) in Partner Center. Metrics include Monthly, Daily, and Weekly active users, and retention and intensity charts. The metrics help you to track churn and frequency of usage. Data for newly published apps takes about a week to appear in the report. ## Publish updates to your app -You can submit changes to your app (such as new features or even metadata) in Partner Center. These changes require a new review process. +Submit changes to your app (such as new features or metadata) in Partner Center. These changes require a new review process. -Ensure to check the following when you're publishing updates: +Ensure that you check the following when you're publishing updates: * Don't change your app ID. * Increment your app's version number. If you make any of the following changes to your app, however, your existing use * Add a personal tab. * Add a channel and group tab. * Add a connector.-* Modify configurations related to your Microsoft Azure Active Directory (Azure AD) app registration. For more information, see [`webApplicationInfo`](~/resources/schem#webapplicationinfo). --## Fix issues with your published app --Microsoft runs daily automation tests on apps listed on the Teams store. If issues with your app are identified, we contact you with a detailed report on how to reproduce the issues and recommendations to resolve them. If you can't fix the problems within a stated timeline, your app listing may be removed from the store. +* Modify configurations related to your Microsoft Azure Active Directory (Azure AD) app registration. For more information, see [webApplicationInfo](~/resources/schem#webapplicationinfo). ## Promote your app on another site -When your app is listed in the Teams store, you can create a link that launches Teams and displays a dialog to install your app. You could include this link, for example, with a download button on your product's marketing page. +When your app is listed in the Teams store, create a link that launches Teams and displays a dialog to install your app. For example, you can include this link as a download button on your product's marketing page. Create the link using the following URL appended with your app ID: `https://teams.microsoft.com/l/app/<your-app-id>`. Create the link using the following URL appended with your app ID: `https://team [Microsoft 365 Certification](/microsoft-365-app-certification/docs/certification) offers assurances that data and privacy are adequately secured and protected when a third-party Microsoft 365 app or an add-in is installed in your Microsoft 365 ecosystem. The certification confirms that your app is compatible with Microsoft technologies, is compliant with cloud app security best practices, and is supported by Microsoft. -## Stop app distribution +## Keep your app details updated ++You must keep the following app details updated: ++| App details | Description | +| | | +| Your app's listing must be kept updated. | Any changes to functionality, pricing, visual appearance, or any other updates must be accurately reflected in your app's listing.| +| You must regularly update your app to ensure that it remains compliant with the commercial marketplace policies.| Stay up to date with policy changes by subscribing to the changelog.| +| Your contact details must be kept up to date in your Partner Center account. |Microsoft will contact you occasionally to resolve any bugs or commercial marketplace policy violations in your app. If your contact details aren't updated, you might miss important notices or updates from Microsoft.| +| Maintain your app's functionality and user experience. | Your app's functionality and user experience must match or exceed the quality of experience at submission. You must maintain your app's performance.| ++## Fix issues with your published app ++Microsoft runs daily automation tests on apps listed on the Teams store. If issues with your app are identified, Microsoft contacts you with a detailed report on how to reproduce the issues and then provide recommendations to resolve them. Your app listing might be removed from the store if you can't fix the problems within a stated timeline. ++## Possible enforcement actions ++Microsoft runs automated and manual continuous health checks for all the published apps. It's intended to maintain the health and user experience of the Microsoft commercial marketplace and the Teams App store. In certain situations, Microsoft might contact you and remove your app from the commercial marketplace and the Teams App store, temporarily halt new user acquisition for your app, or take further action on your app as deemed appropriate. ++**Microsoft might contact you for a resolution when:** ++* Microsoft is unable to run the continuous health evaluation tests on your app as the test credentials or test environment you provided have expired. ++* Microsoft sees or is made aware of critical security vulnerabilities in your app, which might endanger your users or the Microsoft commercial marketplace. ++* Microsoft is made aware of issues with your app by your users through any of MicrosoftΓÇÖs support channels. The issues include, but aren't limited to, spammy behavior, broken functionality, or unexpected user experience bugs and user interface bugs. ++* Microsoft might unilaterally take cognizance of issues highlighted by users for your app with rating and reviews. ++* Microsoft has identified commercial marketplace policy failures in your app as part of the continuous health evaluation of your app post publish. ++If Microsoft doesn't receive a suitable response from you, it reaches out to you again, and might simultaneously remove your app to protect users. If Microsoft receives a response that your issues are resolved and you've submitted an updated app for review, Microsoft will re-list your app when the app passes review. ++**Microsoft might remove your app without prior notice (other than to inform you of that action) when:** ++* Microsoft receives a takedown notice for your app alleging copyright or trademark infringement. +* Your app appears to be unmaintained or abandoned and unused. +* There's no response from you on Microsoft's reach outs. ++## Discontinuing your published app ++You must maintain your app's user experience as at the initial app review. If you don't maintain the app actively, or no longer wish to support the app, ensure that you discontinue your published app from your Partner Center account. -You can remove an app from the [Microsoft commercial marketplace](/azure/marketplace/overview) and the Microsoft Teams store to prevent its discovery and use. +To discontinue your app: -To stop distribution of an app after you've published, follow the steps: +* To remove your app from Microsoft AppSource, in your Microsoft Partner Center account, go the **Product Overview** page and select **Stop Selling**. -1. In **Partner Center**, on the **Product overview** page, select **Stop selling**. It removes the app from the Microsoft AppSource. -1. To initiate de-listing of the app from the Microsoft Teams store, write to [apphealthevaluation](mailto:apphealthevaluation@microsoft.com). +* Contact the Microsoft Teams App Health evaluation team at [apphealthevaluation@microsoft.com](mailto:apphealthevaluation@microsoft.com) or [teams-sas@microsoft.com](mailto:teams-sas@microsoft.com) to remove your app from the Microsoft Teams store. -After you stop the distribution of an app, you can still see it in Partner Center with a **Not available** status. If you decide to list the app again, follow the instructions to [Publish your app to the Microsoft Teams store](../publish.md). +* Contact your customers where appropriate. Delete or revoke any security or authorization tokens generated for your app. ## See also -[Monetize your app through Microsoft Commercial Marketplace](/office/dev/store/monetize-addins-through-microsoft-commercial-marketplace) +[Monetize your app through Microsoft commercial marketplace](/office/dev/store/monetize-addins-through-microsoft-commercial-marketplace) |
platform | Debug Local | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/toolkit/debug-local.md | The following image displays task names in the **OUTPUT** and **TERMINAL** tabs :::image type="content" source="../assets/images/teams-toolkit-v2/debug/Terminal1.png" alt-text="Screenshot shows the Start app services." lightbox="../assets/images/teams-toolkit-v2/debug/Terminal1.png"::: +### Start local tunnel ++Use dev tunnel as a local tunnel service to make your local bot message endpoint public. ++#### Dev tunnel ++To manually migrate your local tunnel task from a v4 project, update the following code in the `.vscode/tasks.json` file: ++```json +{ + "label": "Start local tunnel", + "type": "teamsfx", + "command": "debug-start-local-tunnel", + "args": { + "type": "dev-tunnel", + "ports": [ + { + "portNumber": 3978, + "protocol": "http", + "access": "public", + "writeToEnvironmentFile": { + "endpoint": "BOT_ENDPOINT", + "domain": "BOT_DOMAIN" + } + } + ], + "env": "local" + }, + "isBackground": true, + "problemMatcher": "$teamsfx-local-tunnel-watch" + }, +``` ++To use another port for local bot service, change the `portNumber` in the `.vscode/tasks.json` file and also change the `portNumber` in the `index.js` or `index.ts` file. ++The following table lists the required arguments: ++| **Arguments** | **Type** | **Required** | **Description** | +| | | |--| +| `type` | string | required | The type of tunnel service to use. This argument must be set to `dev-tunnel`. | +| `env` | string | optional | The environment name. Teams Toolkit writes the environment variables defined in `output` to `.env.<env>` file. | +| `ports` | array | required | An array of port configurations, each specifying the local port number, protocol, and access control settings. | ++The `ports` argument must be an array of objects, with each object specifying the configuration for a particular port. Each object must contain the following fields: ++| **Port** | **Type** | **Required** | **Description**| +||||| +| `portNumber` | number | required | The local port number of the tunnel. | +| `protocol` | string | required | The protocol of the tunnel. | +| `access` | string | optional | The access control setting for the tunnel. This value can be set to `private` or `public`. If not specified, the default value is `private`.| + | `writeToEnvironmentFile` | object | optional | The key of tunnel endpoint and tunnel domain environment variables that are written to `.env` file.| ++The `writeToEnvironmentFile` object contains two fields: ++| **WriteToEnvironmentFile** | **Type** | **Required** | **Description** | +|--|||| +| `endpoint` | string | optional | The key of tunnel endpoint environment variable.| +| `domain` | string | optional | The key of tunnel domain environment variable.| ++When `writeToEnvironmentFile` is included, the specified environment variables are written to the `.env` file. When the field is omitted, no environment variables are written to the file. + ### Launches debug configurations Launches the debug configurations as defined in `.vscode/launch.json`. |
platform | Install Teams Toolkit | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/toolkit/install-Teams-Toolkit.md | You can install Teams Toolkit using **Extensions** in Visual Studio Code, or ins 1. Enter **Teams Toolkit** in the search box. - :::image type="content" source="../assets/images/teams-toolkit-v2/teams toolkit fundamentals/install-toolkit-2_2_1.png" alt-text="Screenshot show the Teams Toolkit listed in the search result."::: + :::image type="content" source="../assets/images/teams-toolkit-v2/teams toolkit fundamentals/teams-toolkit-search.PNG" alt-text="Screenshot show the Teams Toolkit listed in the search result."::: Teams Toolkit appears in the search result. 1. Select **Teams Toolkit**, and then from the Teams Toolkit extension page that appears in the right pane, select **Install**. - :::image type="content" source="../assets/images/teams-toolkit-v2/teams toolkit fundamentals/select-install-ttk_2.png" alt-text="Screenshot shows the Install option to install toolkit v5."::: + :::image type="content" source="../assets/images/teams-toolkit-v2/teams toolkit fundamentals/teams-toolkit-ga.PNG" alt-text="Screenshot show the Teams Toolkit."::: After successful installation of Teams Toolkit in Visual Studio Code, the Teams Toolkit icon appears in the Visual Studio Code activity bar. |
platform | What Are Webhooks And Connectors | https://github.com/MicrosoftDocs/msteams-docs/commits/main/msteams-platform/webhooks-and-connectors/what-are-webhooks-and-connectors.md | Learn more about the differences between a notification bot and Incoming Webhook | Send Adaptive Card | Yes | Yes | | Send a welcome message | Yes | No | | Trigger supported | All triggers are supported. If you use Teams Toolkit, you can quickly get a template project with the following triggers: <br> ΓÇó Time trigger hosted on Azure functions. <br> ΓÇó Restify HTTP trigger hosted on Azure app service. <br> ΓÇó HTTP trigger hosted on Azure Functions. | All triggers are supported. |-| Building Tools | ΓÇó [Teams Toolkit Overview for Visual Studio Code](../toolkit/teams-toolkit-fundamentals.md) <br> ΓÇó [Teams Toolkit overview for Visual Studio](/microsoftteams/platform/toolkit/teams-toolkit-fundamentals?pivots=visual-studio) <br> ΓÇó [TeamsFx Library](../toolkit/TeamsFx-CLI.md) <br> ΓÇó [TeamsFx SDK](../toolkit/TeamsFx-SDK.md) | No tools are required. | +| Building Tools | ΓÇó [Teams Toolkit Overview for Visual Studio Code](../toolkit/teams-toolkit-fundamentals.md) <br> ΓÇó[Teams Toolkit v4 Overview for Visual Studio](../toolkit/toolkit-v4/teams-toolkit-fundamentals-v4.md#available-for-visual-studio) <br> ΓÇó [TeamsFx Library](../toolkit/TeamsFx-CLI.md) <br> ΓÇó [TeamsFx SDK](../toolkit/TeamsFx-SDK.md) | No tools are required. | | Cloud resource required | Azure Bot Framework | No resources are required. | | Tutorial | [Build notification bot with JavaScript](../sbs-gs-notificationbot.yml) | [Incoming Webhook notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/dev/incoming-webhook-notification) | |