-
Notifications
You must be signed in to change notification settings - Fork 196
ZZZ ‐ [Archived] ‐ Respond to chat commands in Teams
Important
Content in this document has been moved to Teams platform documentation. Please do not refer to or update this document.
We appreciate your feedback, please report any issues to us here.
Microsoft Teams allows you to automate simple and repetitive tasks right inside a conversation. You can build a Teams bot that can respond to simple commands sent in chats with adaptive cards.
In this tutorial, you will learn:
Get started with Teams Toolkit and TeamsFx SDK:
- How to create a command bot with Teams Toolkit
- How to understand the command bot project
- How to understand the command handler
Customize the scaffolded app template:
- How to customize the initialization
- How to customize the installation
- How to customize the adapter
- How to customize your app to add more command and response
- How to customize the trigger pattern
- How to build dynamic content in response with adaptive cards
Connect your app with Graph or other APIs:
- How to access Microsoft Graph from your workflow bot
- How to connect to existing APIs from your command bot
Extend command bot to other bot scenarios:
- From Teams Toolkit sidebar click
Create a new Teams app
or selectTeams: Create a new Teams app
from command palette.
- Select
Bot
.
- Select the
Chat Command
from Scenario-based Teams app section.
- Select programming language
- Enter an application name and then press enter.
-
Make sure you have installed ASP.NET workloads and "Microsoft Teams development tools".
-
Create a new project and select "Microsoft Teams App".
-
In next window enter your project name.
-
In next window select Command Bot.
-
If you prefer interactive mode, execute
teamsfx new
command, then use the keyboard to go through the same flow as in Visual Studio Code. -
If you prefer non-interactive mode, enter all required parameters in one command.
teamsfx new --interactive false --capabilities "command-bot" --programming-language "typescript" --folder "./" --app-name myAppName
After you successfully created the project, you can quickly start local debugging via F5
in VSCode. Select Debug (Edge)
or Debug (Chrome)
debug option of your preferred browser. You can send a helloWorld
command after running this template and get a response as below:
The created app is a normal TeamsFx project that will contain following folders:
Folder / File | Contents |
---|---|
teamsapp.yml |
Main project file describes your application configuration and defines the set of actions to run in each lifecycle stages |
teamsapp.local.yml |
This overrides teamsapp.yml with actions that enable local execution and debugging |
env/ |
Name / value pairs are stored in environment files and used by teamsapp.yml to customize the provisioning and deployment rules |
.vscode/ |
VSCode files for debugging |
appPackage/ |
Templates for the Teams application manifest |
infra/ |
Templates for provisioning Azure resources |
src/ |
The source code for the application |
The following files can be customized and demonstrate an example command app implementation to get you started:
File | Contents |
---|---|
src/index.js(ts) |
Application entry point and restify handlers for command and response |
src/teamsBot.js(ts) |
An empty teams activity handler for bot customization |
src/adaptiveCards/helloworldCommand.json |
A generated Adaptive Card that is sent to Teams |
src/helloworldCommandHandler.js(ts) |
The business logic to handle a command |
File name | Contents |
---|---|
teamsapp.yml |
Main project file describes your application configuration and defines the set of actions to run in each lifecycle stages |
teamsapp.local.yml |
This overrides teamsapp.yml with actions that enable local execution and debugging |
appPackage/ |
Templates for the Teams application manifest |
infra/ |
Templates for provisioning Azure resources |
Properties/ |
LaunchSetting file for local debug |
Controllers/ |
BotController and NotificationControllers to handle the conversation with user |
Commands/ |
Define the commands and how your Bot will react to these commands |
Models/ |
Adaptive card data models |
Resources/ |
Adaptive card templates |
appsettings.*.json |
The runtime settings |
GettingStarted.txt |
Instructions on minimal steps to wonderful |
Program.cs |
Create the Teams Bot instance |
TeamsBot.cs |
An empty Bot handler |
The TeamsFx Command-Response Bots are created using the Bot Framework SDK. The Bot Framework SDK provides built-in message handler to handle the incoming message activity, which requires learning curve to understand the concept of Bot Framework (e.g. the event-driven conversation model). To simplify the development, the TeamsFx SDK provides command-response abstraction layer to let developers only focus on the development of business logic to handle the command request without learning the Bot Framework SDK.
Behind the scenes, the TeamsFx SDK leverages Bot Framework Middleware to handle the integration with the underlying activity handlers. This middleware handles the incoming message activity and invokes the corresponding handlerCommandReceived
function if the received message text matches the command pattern provided in a TeamsFxBotCommandHandler
instance. After processing, the middleware will call context.sendActivity
to send the command response returned from the handlerCommandReceived
function to the user.
To respond to command in chat, you need to create ConversationBot
first. (Code already generated in project)
/** 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()],
},
});
/** .NET: Program.cs **/
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);
});
A Teams bot can be installed into a team, or a group chat, or as personal app, depending on difference scopes. You can choose the installation target when adding the App.
-
See Distribute your app for more install options.
-
See Remove an app from Teams for uninstallation.
/** Typescript **/
// Create your own adapter
const adapter = new CloudAdapter(...);
// Customize your adapter, e.g., error handling
adapter.onTurnError = ...
const bot = new ConversationBot({
// use your own adapter
adapter: adapter;
...
});
// Or, customize later
bot.adapter.onTurnError = ...
You can use the following 4 steps to add more command and response:
- Step 1: Add a command definition in manifest
- Step 2: Respond with an Adaptive Card
- Step 3: Handle the command
- Step 4: Register the new command
You can edit the manifest template file appPackage\manifest.json
to include definitions of a doSomething
command with its title and description in the commands
array:
"commandLists": [
{
"commands": [
{
"title": "helloWorld",
"description": "A helloworld command to send a welcome message"
},
{
"title": "doSomething",
"description": "A sample do something command"
}
]
}
]
To respond with an Adaptive Card, define your card in its JSON format:
- For JavaScript/TypeScript: create a new file
src/adaptiveCards/doSomethingCommandResponse.json
. - For .NET: create a new file
Resources/DoSomethingCommandResponse.json
.
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "Your doSomething Command is added!"
},
{
"type": "TextBlock",
"text": "Congratulations! Your hello world bot now includes a new DoSomething Command",
"wrap": true
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4"
}
You can use the Adaptive Card Designer to help visually design your Adaptive Card UI.
Please note:
- Respond with an Adaptive Card is optional, you can simply respond with plain texts.
- If you'd like to send adaptive card with dynamic data, please refer to this document.
The TeamsFx SDK provides a convenient class, TeamsFxBotCommandHandler
, to handle when an command is triggered from Teams conversation message. Create a new file, src/doSomethingCommandHandler.js(ts)
:
/** 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 **/
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));
}
}
The TeamsFx .NET SDK provides an interface ITeamsCommandHandler
for command handler. To handle when an command is triggered from Teams conversation message. Create a new file Commands/DoSomethingCommandHandler.cs
:
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);
}
}
}
Each new command needs to be configured in the ConversationBot
, which powers the conversational flow of the command bot template.
/** JavaScript / TypeScript **/
/** Update ConversationBot in src/internal/initialize.js(ts) **/
const commandApp = new ConversationBot({
//...
command: {
enabled: true,
commands: [
new HelloWorldCommandHandler(),
new DoSomethingCommandHandler()], // newly added command handler
},
});
/** .NET **/
/** 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);
});
Now, you are all done with the code development of adding a new command and response into your bot app. You can just press F5
to local debug with the command-response bot, or use provision and deploy command to deploy the change to Azure.
The default pattern to trigger a command is through a defined keyword. But often times you would want to collect and process additional information retrieved after the trigger keyword. In addition to keyword match, you could also define your trigger pattern with regular expressions and match against message.text
with more controls.
When using regular expressions, any capture group can be found in message.matches
. Below is an example that uses regular expression to capture strings after reboot
, for example if user inputs reboot myMachine
, message.matches[1]
will capture myMachine
:
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));
}
}
Adaptive card provides Template Language 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 (todo items, assigned bugs, etc) that could varies according to different user.
- Add your adaptive card template JSON file under
bot/adaptiveCards
folder - Import the card template to you code file where your command handler exists (e.g.
myCommandHandler.ts
) - Model your card data
- Use
MessageBuilder.attachAdaptiveCard
to render the template with dynamic card data
You can also add new cards if appropriate for your application. Please follow this sample to see how to build different types of adaptive cards with a list or a table of dynamic contents using ColumnSet
and FactSet
.
If you are responding to a command that needs access to Microsoft Graph, you can leverage single sign on to leverage the logged-in Teams user token to access their Microsoft Graph data. Read more about how Teams Toolkit can help you add SSO to your application.
If you want to invoke external APIs in your code but do not have the appropriate SDK, the "Teams: Connect to an API" command in Teams Toolkit VS Code extension or "teamsfx add api-connection" command in TeamsFx CLI would be helpful to bootstrap code to call target APIs. For more information, you can visit Connect existing API document.
-
Go to
bot\src\internal\initialize.ts(js)
, update yourconversationBot
initialization to enable notification feature: -
Follow this instruction to send notification to the bot installation target (channel/group chat/personal chat). To quickly add a sample notification triggered by a HTTP request, you can add the following sample code in
bot\src\index.ts(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({}); });
-
Uninstall your previous bot installation from Teams, and re-run local debug to test your bot notification. Then you can send a notification to the bot installation targets (channel/group chat/personal chat) by using a HTTP POST request with target URL
https://localhost:3978/api/notification
.
To explore more details of the notification feature (e.g. send notification with adaptive card, add more triggers), you can further refer to the notification document.
The Adaptive Card action handler feature enables the app to respond to adaptive card actions that triggered by end users to complete a sequential workflow. When user gets an Adaptive Card, it can provide one or more buttons in the card to ask for user's input, do something like calling some APIs, and then send another adaptive card in conversation to response to the card action.
To add adaptive card actions to command bot, you can follow the steps here.
Build Custom Engine Copilots
- Build a basic AI chatbot for Teams
- Build an AI agent chatbot for Teams
- Expand AI bot's knowledge with your content
Scenario-based Tutorials
- Send notifications to Teams
- Respond to chat commands in Teams
- Respond to card actions in Teams
- Embed a dashboard canvas in Teams
Extend your app across Microsoft 365
- Teams tabs in Microsoft 365 and Outlook
- Teams message extension for Outlook
- Add Outlook Add-in to a Teams app
App settings and Microsoft Entra Apps
- Manage Application settings with Teams Toolkit
- Manage Microsoft Entra Application Registration with Teams Toolkit
- Use an existing Microsoft Entra app
- Use a multi-tenant Microsoft Entra app
Configure multiple capabilities
- How to configure Tab capability within your Teams app
- How to configure Bot capability within your Teams app
- How to configure Message Extension capability within your Teams app
Add Authentication to your app
- How to add single sign on in Teams Toolkit for Visual Studio Code
- How to enable Single Sign-on in Teams Toolkit for Visual Studio
Connect to cloud resources
- How to integrate Azure Functions with your Teams app
- How to integrate Azure API Management
- Integrate with Azure SQL Database
- Integrate with Azure Key Vault
Deploy apps to production