Interactive notification bot in Teams

Microsoft Teams Toolkit enables you to build applications that capture events and send them as interactive notifications to a personal, group chat, or a channel in Microsoft 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.

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:

weather forecast sample notification scenario

You can send a bot notification in the following scenarios:

  • You want to notify everyone in a channel or chat about the same or related content​.

  • Highly customizable UI in a Card​

  • Need quick response, include media content, or action buttons​.

  • Send scheduled notifications​

  • Light up double badges on both Activity and Chat, Channel or App​

  • Add template in source code​.

  • Handling localization manually.

Advantages

  • Facilitates notifications to a personal, group chat, and in a channel, using APIs from TeamsFx SDK.

  • Enhances user experience by customizing notification with an Adaptive Card.

  • Provides multiple mechanisms to trigger notifications such as HTTP and schedule timer trigger with Azure Functions.

  • A notification card easily integrates with a bot and provides a consistent user experience within the Bot app.​

Note

Bot application needs to be installed with the corresponding scope before sending notification.

Back to top

Notification based on events

Bot Framework SDK provides the functionality to proactively message in Teams. TeamsFx SDK provides the functionality to manage bot's conversation references when a bot event is triggered. TeamsFx SDK recognizes the following bot events:

Event Behavior
The first time you install a bot to a person, group, or Team. Add the target conversation reference to the storage.
When the bot is uninstalled from a person, group, or Team. Remove the target conversation reference from the storage.
When the team installed by bot is deleted. Remove the target conversation reference from the storage.
When the team installed by bot is restored. Add the target conversation reference to the storage.
When the bot sends messages. When the target conversation reference doesn't exist, add it to the storage.

new notification event sample

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:

// 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...
        }
    );
}

Back to top

Notification bot installation

A notification bot needs to be installed into a team or a group chat or as a personal app, depending on the required scope. During installation, you can select the scope where you want to add and use the bot:

  • To open the bot in personal scope, select Open.

  • To open the bot in a shared scope, select the required channel, chat, or meeting from the list, and move through the dialog to select Go.

    Screenshot of the scope selection dialog to add installation scope.

For more install options, see configure default install options. For uninstalling, see remove an app from Teams.

Back to top

Customize notification

You can make the following customizations to extend the notification template to fit your business need:

Customize the trigger point from event source

You can customize the following triggers:

  • Express based notification:

    • When an 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, node-schedule, or from other packages.

      Note

      By default Teams Toolkit scaffolds a single express 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.

      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.

      sample of HTTP triggered notification

  • Azure Function triggers:

    • Event Hub trigger to send notifications when an event is pushed to Azure Event Hub.

    • 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.

Customize the notification content

The file src/adaptiveCards/notification-default.json defines the default Adaptive Card. You can use the Adaptive Card 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.

Customize where notifications are sent

You can customize sending the notification to the following targets:

  • Notifications to a personal chat:

    // 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(...);
        }
    }
    

  • Notifications to a group chat:

    // 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(...);
                }
            }
    
    }
    

  • Notifications to a channel:

    // 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(...);
            }
    
            // List all channels in the Team then notify each channel
            const channels = await target.channels();
            for (const channel of channels) {
                await channel.sendAdaptiveCard(...);
            }
        }
    }
    

  • Notifications to a specific channel:

    // 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:

    // 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(...);
    

    Note

    To prevent an undefined output and a missing notification, you need to include the specific person in notification installation scope.

Back to top

Customize initialization

You need to create ConversationBot to send notification.

Note

The code is generated in project.

/** 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: {
        MicrosoftAppId: config.botId,
        MicrosoftAppPassword: config.botPassword,
        MicrosoftAppType: "MultiTenant",
    },
    // Enable notification
    notification: {
        enabled: true,
    },
});

Back to top

Customize adapter

You can customize by creating your own adapter, or customize the adapter after initialization. Following is the code sample for creating your adapter:

// Create your own adapter
const adapter = new CloudAdapter(...);

// Customize your adapter, e.g., error handling
adapter.onTurnError = ...

const notificationApp = new ConversationBot({
    // use your own adapter
    adapter: adapter;
    ...
});

// Or, customize later
notificationApp.adapter.onTurnError = ...

Back to top

Add storage

Storage can be used to implement notification connections. You can add your own storage with the help of following code sample:

// implement your own storage
class MyStorage implements NotificationTargetStorage {...}
const myStorage = new MyStorage(...);

// initialize ConversationBot with notification enabled and customized storage
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: {
        MicrosoftAppId: config.botId,
        MicrosoftAppPassword: config.botPassword,
        MicrosoftAppType: "MultiTenant",
    },
    // Enable notification
    notification: {
        enabled: true,
        storage: myStorage,
    },
});

If storage isn't provided, you can use a default local file storage, which stores notification connections into:

  • .notification.localstore.json if running locally.
  • ${process.env.TEMP}/.notification.localstore.json, if process.env.RUNNING_ON_AZURE is set to 1.

If you're using the default local file storage, Azure web app and Azure Functions clean up the local file during a restart or redeploy. You can also uninstall the bot from Teams, then install it to again add connections to the storage.

The NotificationTargetStorage is different from Bot Framework SDK's 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 the notification storage implementation sample.

Note

  • 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

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 one of the following actions:

There can be more authentication or authorization solutions for an API, you can select as required.

Back to top

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 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.

Teams bot application or Teams Incoming Webhook

TeamsFx supports two ways to help you send notifications from your system to Teams:

  • Create a Teams bot app.
  • Create Teams Incoming Webhook.

In the following table, you can see the comparison of the two different ways:

  Teams bot app Teams Incoming Webhook
Message individual person ✔️
Message group chat ✔️
Message public channel ✔️ ✔️
Message private channel ✔️
Send card message ✔️ ✔️
Send welcome message ✔️
Retrieve Teams context ✔️
Require installation steps in Teams ✔️
Require Azure resource Azure Bot Service

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 that helps you to:

Back to top

Send activity feed notifications

If you want to send activity feed notifications for your app, you can use the activity feed notification APIs in Microsoft Graph. For more information, see Send activity feed notifications to users in Microsoft Teams.

FAQ


Why is the notification installations empty even though the bot app is installed in Teams?

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 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.
  • 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 .notification.localstore.json.

Note

For more information to add your own storage, see add storage.


Why Bad Request or Bad Argument error occurs when sending notification?

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.

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.


Why notification target is lost after restarting or redeploying the bot app?

Notification target connections are stored in the persistence storage. If you're using the default local file storage, Azure web app and Azure Functions clean up the local file during a restart or redeploy. You can also uninstall the bot from Teams, then install it to again add connections to the storage. It's recommended to use your own shared storage for production environment.


Why is undefined error returned when using the API `findChannel`()?

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.


Can I know all the targets where my bot is installed in and out of the notification project?

There are Microsoft Graph APIs 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.


How to customize the Azurite listening ports?

If Azurite exits due to port in use, you can specify another listening port and update the connection string of AzureWebJobsStorage in local.settings.json.


How to extend my notification bot to support command and response?
  1. Go to bot\src\internal\initialize.ts(js) and update your conversationBot initialization to enable the notification feature:

    Conversation bot initialization to enable notification feature.

  2. To add command to your bot, follow the instructions in Command bot in Teams.


How to extend my notification bot by adding workflow bot Adaptive 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.


Back to top

Step-by-step guide

Follow the step-by-step guide to build Teams notification bot.

See also