diff --git a/templates/ts/notification-express/README.md.tpl b/templates/ts/notification-express/README.md.tpl index f420062a06..91a4df26ca 100644 --- a/templates/ts/notification-express/README.md.tpl +++ b/templates/ts/notification-express/README.md.tpl @@ -1,6 +1,6 @@ # Overview of the Notification bot template -This template showcases an app that send a message to Teams with Adaptive Cards triggered by a HTTP post request. You can further extend the template to consume, transform and post events to individual, chat or channel in Teams. +This template showcases an app that sends a message to Teams with Adaptive Cards triggered by an HTTP post request. You can further extend the template to consume, transform, and post events to an individual, chat, or channel in Teams. The app template is built using the TeamsFx SDK, which provides a simple set of functions over the Microsoft Bot Framework to implement this scenario. @@ -9,51 +9,33 @@ The app template is built using the TeamsFx SDK, which provides a simple set of > > **Prerequisites** > -> To run the notification bot template in your local dev machine, you will need: +> To run the notification bot template on your local dev machine, you will need: > > - [Node.js](https://nodejs.org/), supported versions: 18, 20 -{{^enableTestToolByDefault}} -> - An [Microsoft 365 account for development](https://docs.microsoft.com/microsoftteams/platform/toolkit/accounts) -{{/enableTestToolByDefault}} > - [Teams Toolkit Visual Studio Code Extension](https://aka.ms/teams-toolkit) version 5.0.0 and higher or [Teams Toolkit CLI](https://aka.ms/teamsfx-toolkit-cli) > > **Note** > -> Your app can be installed into a team, or a group chat, or as personal app. See [Installation and Uninstallation](https://aka.ms/teamsfx-notification-new#customize-installation). -> For local debugging using Teams Toolkit CLI, you need to do some extra steps described in [Set up your Teams Toolkit CLI for local debugging](https://aka.ms/teamsfx-cli-debugging). +> Your app can be installed into a team, a group chat, or as a personal app. See [Installation and Uninstallation](https://aka.ms/teamsfx-notification-new#customize-installation). +> For local debugging using Teams Toolkit CLI, you need to follow the steps described in [Set up your Teams Toolkit CLI for local debugging](https://aka.ms/teamsfx-cli-debugging). 1. First, select the Teams Toolkit icon on the left in the VS Code toolbar. -{{#enableTestToolByDefault}} -2. Press F5 to start debugging which launches your app in Teams App Test Tool using a web browser. Select `Debug in Test Tool`. +2. Press F5 to start debugging, which launches your app in Teams App Test Tool using a web browser. Select `Debug in Test Tool`. 3. The browser will pop up to open Teams App Test Tool. 4. Send a POST request to `http:///api/notification` with your favorite tool (like `Postman`) -{{/enableTestToolByDefault}} -{{^enableTestToolByDefault}} -2. In the Account section, sign in with your [Microsoft 365 account](https://docs.microsoft.com/microsoftteams/platform/toolkit/accounts) if you haven't already. -3. Press F5 to start debugging which launches your app in Teams using a web browser. Select `Debug in Teams (Edge)` or `Debug in Teams (Chrome)`. -4. When Teams launches in the browser, select the Add button in the dialog to install your app to Teams. -5. Send a POST request to `http:///api/notification` with your favorite tool (like `Postman`) -{{/enableTestToolByDefault}} - When your project is running locally, replace `` with `localhost:3978` - - When your project is deployed to Azure App Service, replace `` with the url from Azure App Service + - When your project is deployed to Azure App Service, replace `` with the URL from Azure App Service -{{#enableTestToolByDefault}} The bot will send an Adaptive Card to Teams App Test Tool: ![Notification Message in Test Tool](https://github.com/OfficeDev/TeamsFx/assets/9698542/52aa4826-e8b3-4341-b9e1-bcba50470861) -{{/enableTestToolByDefault}} -{{^enableTestToolByDefault}} -The bot will send an Adaptive Card to Teams: - -![Notification Message in Teams](https://user-images.githubusercontent.com/7642967/223006044-5003574e-2aee-4a41-9b71-c103d0439012.png) -{{/enableTestToolByDefault}} ## What's included in the template | 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.yml` | Main project file describes your application configuration and defines the set of actions to run in each lifecycle stage | | `teamsapp.local.yml`| This overrides `teamsapp.yml` with actions that enable local execution and debugging in Microsoft Teams | | `teamsapp.testtool.yml`| This overrides `teamsapp.yml` with actions that enable local execution and debugging in Teams App Test Tool | | `.vscode/` | VSCode files for local debug | @@ -66,33 +48,31 @@ The following files can be customized and demonstrate an example implementation | File | Contents | | - | - | | `src/index.ts` | Application entry point and `express` handlers for notifications | -| `src/teamsBot.ts`| An empty teams activity handler for bot customization | +| `src/teamsBot.ts`| An empty Teams activity handler for bot customization | | `src/adaptiveCards/notification-default.json` | A generated Adaptive Card that is sent to Teams | | `src/cardModels.ts` | The default Adaptive Card data model | ## Extend the notification bot template -There are few customizations you can make to extend the template to fit your business requirements. +There are a few customizations you can make to extend the template to fit your business requirements. -1. [Step 1: Customize the trigger point from event source](#step-1-customize-the-trigger-point-from-event-source) +1. [Step 1: Customize the trigger point from the event source](#step-1-customize-the-trigger-point-from-event-source) 2. [Step 2: Customize the notification content](#step-2-customize-the-notification-content) 3. [Step 3: Customize where notifications are sent](#step-3-customize-where-notifications-are-sent) -### Step 1: Customize the trigger point from event source +### Step 1: Customize the trigger point from the event source -By default Teams Toolkit scaffolds a single `express` entry point in `src/index.ts`. When a HTTP request is sent to this entry point, the default implementation sends a hard-coded Adaptive Card to Teams. You can customize this behavior by customizing `src/index.ts`. A typical implementation might make an API call to retrieve some events and/or data, and then send an Adaptive Card as appropriate. +By default, Teams Toolkit scaffolds a single `express` entry point in `src/index.ts`. When an HTTP request is sent to this entry point, the default implementation sends a hard-coded Adaptive Card to Teams. You can customize this behavior by modifying `src/index.ts`. A typical implementation might make an API call to retrieve some events and/or data, and then send an Adaptive Card as appropriate. You can also add additional triggers by: - Creating new routing: `server.post("/api/new-trigger", ...);` -- Add Timer trigger(s) via widely-used npm packages such as [cron](https://www.npmjs.com/package/cron), [node-schedule](https://www.npmjs.com/package/node-schedule), etc. Or add other trigger(s) via other packages. +- Adding Timer triggers via widely-used npm packages such as [cron](https://www.npmjs.com/package/cron), [node-schedule](https://www.npmjs.com/package/node-schedule), etc., or adding other triggers via other packages. ### Step 2: Customize the notification content `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. -`src/cardModels.ts` defines a data structure that is used to fill data for the Adaptive Card. The binding between the model and the Adaptive Card is done by name matching (for example,`CardData.title` maps to `${title}` in the Adaptive Card). You can add, edit, or remove properties and their bindings to customize the Adaptive Card to your needs. - You can also add new cards if needed. Follow this [sample](https://aka.ms/teamsfx-adaptive-card-sample-new) to see how to build different types of adaptive cards with a list or a table of dynamic contents using `ColumnSet` and `FactSet`. ### Step 3: Customize where notifications are sent @@ -108,7 +88,7 @@ You can also send the notifications to a specific receiver: - [Send notifications to a specific channel](https://aka.ms/teamsfx-notification-new#send-notifications-to-a-specific-channel) - [Send notifications to a specific person](https://aka.ms/teamsfx-notification-new#send-notifications-to-a-specific-person) -Congratulations, you've just created your own notification! To learn more about extending the notification bot template, [visit the documentation on Github](https://aka.ms/teamsfx-notification-new). You can find more scenarios like: +Congratulations, you've just created your own notification! To learn more about extending the notification bot template, [visit the documentation on GitHub](https://aka.ms/teamsfx-notification-new). You can find more scenarios like: - [Customize storage](https://aka.ms/teamsfx-notification-new#customize-storage) - [Customize adapter](https://aka.ms/teamsfx-notification-new#customize-adapter) @@ -119,21 +99,21 @@ Congratulations, you've just created your own notification! To learn more about ## Extend notification bot with other bot scenarios -Notification bot is compatible with other bot scenarios like command bot and workflow bot. +The notification bot is compatible with other bot scenarios like command bot and workflow bot. ### Add command to your application -The command and response feature adds the ability for your application to "listen" to commands sent to it via a Teams message and respond to commands with Adaptive Cards. Follow the [steps here](https://aka.ms/teamsfx-command-new#How-to-add-more-command-and-response) to add the command response feature to your workflow bot. Refer [the command bot document](https://aka.ms/teamsfx-command-new) for more information. +The command and response feature adds the ability for your application to "listen" to commands sent to it via a Teams message and respond to commands with Adaptive Cards. Follow the [steps here](https://aka.ms/teamsfx-command-new#How-to-add-more-command-and-response) to add the command response feature to your workflow bot. Refer to [the command bot document](https://aka.ms/teamsfx-command-new) for more information. ### Add workflow to your notification bot -Adaptive cards can be updated on user action to allow user progress through a series of cards that require user input. Developers can define actions and use a bot to return an Adaptive Cards in response to user action. This can be chained into sequential workflows. Follow the [steps here](https://aka.ms/teamsfx-workflow-new#add-more-card-actions) to add workflow feature to your command bot. Refer [the workflow document](https://aka.ms/teamsfx-workflow-new) for more information. +Adaptive cards can be updated on user action to allow users to progress through a series of cards that require user input. Developers can define actions and use a bot to return Adaptive Cards in response to user actions. This can be chained into sequential workflows. Follow the [steps here](https://aka.ms/teamsfx-workflow-new#add-more-card-actions) to add the workflow feature to your command bot. Refer to [the workflow document](https://aka.ms/teamsfx-workflow-new) for more information. ## Additional information and references - [Manage multiple environments](https://docs.microsoft.com/microsoftteams/platform/toolkit/teamsfx-multi-env) - [Collaborate with others](https://docs.microsoft.com/microsoftteams/platform/toolkit/teamsfx-collaboration) -- [Teams Toolkit Documentations](https://docs.microsoft.com/microsoftteams/platform/toolkit/teams-toolkit-fundamentals) +- [Teams Toolkit Documentation](https://docs.microsoft.com/microsoftteams/platform/toolkit/teams-toolkit-fundamentals) - [Teams Toolkit CLI](https://aka.ms/teamsfx-toolkit-cli) - [TeamsFx SDK](https://docs.microsoft.com/microsoftteams/platform/toolkit/teamsfx-sdk) - [Teams Toolkit Samples](https://github.com/OfficeDev/TeamsFx-Samples) diff --git a/templates/ts/notification-express/package.json.tpl b/templates/ts/notification-express/package.json.tpl index b52d2fc385..f36874f839 100644 --- a/templates/ts/notification-express/package.json.tpl +++ b/templates/ts/notification-express/package.json.tpl @@ -23,9 +23,9 @@ "url": "https://github.com" }, "dependencies": { + "@microsoft/teams-ai": "^1.7.0", "adaptivecards-templating": "^2.3.1", "adaptive-expressions": "^4.23.1", - "@microsoft/teamsfx": "^3.0.0", "botbuilder": "^4.23.1", "express": "^5.0.1" }, diff --git a/templates/ts/notification-express/src/index.ts b/templates/ts/notification-express/src/index.ts index 12117ab0c5..3fe23db3c3 100644 --- a/templates/ts/notification-express/src/index.ts +++ b/templates/ts/notification-express/src/index.ts @@ -1,8 +1,11 @@ import * as ACData from "adaptivecards-templating"; +import { CardFactory, ConversationReference, TurnContext } from "botbuilder"; import express from "express"; -import notificationTemplate from "./adaptiveCards/notification-default.json"; -import { notificationApp } from "./internal/initialize"; -import { TeamsBot } from "./teamsBot"; +import notificationCard from "./adaptiveCards/notification-default.json"; +import { adapter } from "./internal/initialize"; +import { ApplicationTurnState } from "./internal/interface"; +import conversationReferenceStore from "./store/storage"; +import { app } from "./teamsBot"; // Create express application. const expressApp = express(); @@ -12,6 +15,20 @@ const server = expressApp.listen(process.env.port || process.env.PORT || 3978, ( console.log(`\nBot Started, ${expressApp.name} listening to`, server.address()); }); +app.conversationUpdate( + "membersAdded", + async (context: TurnContext, state: ApplicationTurnState) => { + if (!state.conversation.greeted) { + const reference = TurnContext.getConversationReference(context.activity); + await conversationReferenceStore.add(getKey(reference), reference, { + overwrite: true, + }); + state.conversation.greeted = true; + await context.sendActivity("Hello and welcome to the team!"); + } + } +); + // Register an API endpoint with `express`. // // This endpoint is provided by your application to listen to events. You can configure @@ -29,103 +46,59 @@ expressApp.post("/api/notification", async (req, res) => { const pageSize = 100; let continuationToken: string | undefined = undefined; do { - const pagedData = await notificationApp.notification.getPagedInstallations( - pageSize, - continuationToken - ); - const installations = pagedData.data; - continuationToken = pagedData.continuationToken; + if (conversationReferenceStore === undefined || adapter === undefined) { + throw new Error("NotificationBot has not been initialized."); + } - for (const target of installations) { - await target.sendAdaptiveCard( - new ACData.Template(notificationTemplate).expand({ - $root: { - title: "New Event Occurred!", - appName: "Contoso App Notification", - description: `This is a sample http-triggered notification to ${target.type}`, - notificationUrl: "https://aka.ms/teamsfx-notification-new", - }, - }) - ); + const references = await conversationReferenceStore.list(pageSize, continuationToken); + + const installations = references.data; + continuationToken = references.continuationToken; + + for (const reference of installations) { + const cardJson = new ACData.Template(notificationCard).expand({ + $root: { + title: "New Event Occurred!", + appName: "Contoso App Notification", + description: `This is a sample http-triggered notification to ${reference.conversation?.conversationType}`, + notificationUrl: "https://aka.ms/teamsfx-notification-new", + }, + }); + await app.sendProactiveActivity(reference, { + attachments: [CardFactory.adaptiveCard(cardJson)], + }); // Note - you can filter the installations if you don't want to send the event to every installation. /** For example, if the current target is a "Group" this means that the notification application is - * installed in a Group Chat. - if (target.type === NotificationTargetType.Group) { - // You can send the Adaptive Card to the Group Chat - await target.sendAdaptiveCard(...); - - // Or you can list all members in the Group Chat and send the Adaptive Card to each Team member - const pageSize = 100; - let continuationToken: string | undefined = undefined; - do { - const pagedData = await target.getPagedMembers(pageSize, continuationToken); - const members = pagedData.data; - continuationToken = pagedData.continuationToken; - - for (const member of members) { - // You can even filter the members and only send the Adaptive Card to members that fit a criteria - await member.sendAdaptiveCard(...); - } - } while (continuationToken); - } - **/ + * installed in a Group Chat. + if (reference.conversation?.conversationType === "groupChat") { + // You can send the Adaptive Card to the Group Chat + await app.sendProactiveActivity(reference, { + attachments: [CardFactory.adaptiveCard(cardJson)], + }); + } + **/ /** If the current target is "Channel" this means that the notification application is installed - * in a Team. - if (target.type === NotificationTargetType.Channel) { - // If you send an Adaptive Card to the Team (the target), it sends it to the `General` channel of the Team - await target.sendAdaptiveCard(...); - - // Alternatively, you can list all channels in the Team and send the Adaptive Card to each channel - const channels = await target.channels(); - for (const channel of channels) { - await channel.sendAdaptiveCard(...); - } - - // Or, you can list all members in the Team and send the Adaptive Card to each Team member - const pageSize = 100; - let continuationToken: string | undefined = undefined; - do { - const pagedData = await target.getPagedMembers(pageSize, continuationToken); - const members = pagedData.data; - continuationToken = pagedData.continuationToken; - - for (const member of members) { - // You can even filter the members and only send the Adaptive Card to members that fit a criteria - await member.sendAdaptiveCard(...); - } - } while (continuationToken); - } - **/ + * in a Team. + if (reference.conversation?.conversationType === "channel") { + const details = await app.getTeamDetails(reference); + const teamMembers = await app.getPagedMembers(reference); + console.log(details, teamMembers.members.length); + } + **/ /** If the current target is "Person" this means that the notification application is installed in a - * personal chat. - if (target.type === NotificationTargetType.Person) { - // Directly notify the individual person - await target.sendAdaptiveCard(...); - } - **/ + * personal chat. + if (reference.conversation?.conversationType === "personal") { + // Directly notify the individual person + await reference.sendAdaptiveCard(...); + } + **/ } } while (continuationToken); - /** You can also find someone and notify the individual person - const member = await notificationApp.notification.findMember( - async (m) => m.account.email === "someone@contoso.com" - ); - await member?.sendAdaptiveCard(...); - **/ - - /** Or find multiple people and notify them - const members = await notificationApp.notification.findAllMembers( - async (m) => m.account.email?.startsWith("test") - ); - for (const member of members) { - await member.sendAdaptiveCard(...); - } - **/ - res.json({}); }); @@ -134,10 +107,13 @@ expressApp.post("/api/notification", async (req, res) => { // // The Teams Toolkit bot registration configures the bot with `/api/messages` as the // Bot Framework endpoint. If you customize this route, update the Bot registration -// in `/templates/provision/bot.bicep`. -const teamsBot = new TeamsBot(); +// in `infra/botRegistration/azurebot.bicep`. expressApp.post("/api/messages", async (req, res) => { - await notificationApp.requestHandler(req, res, async (context) => { - await teamsBot.run(context); + await adapter.process(req, res, async (context) => { + await app.run(context); }); }); + +function getKey(reference: Partial): string { + return `_${reference.conversation?.tenantId}_${reference.conversation?.id}`; +} diff --git a/templates/ts/notification-express/src/internal/initialize.ts b/templates/ts/notification-express/src/internal/initialize.ts index fcef09ea0f..7a1eb9ee3e 100644 --- a/templates/ts/notification-express/src/internal/initialize.ts +++ b/templates/ts/notification-express/src/internal/initialize.ts @@ -1,14 +1,34 @@ -import { BotBuilderCloudAdapter } from "@microsoft/teamsfx"; -import ConversationBot = BotBuilderCloudAdapter.ConversationBot; +import { TeamsAdapter } from "@microsoft/teams-ai"; +import { ConfigurationServiceClientCredentialFactory, TurnContext } from "botbuilder"; import config from "./config"; -// Create bot. -export 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: config, - // Enable notification - notification: { - enabled: true, - }, -}); +// Create adapter. +// See https://aka.ms/about-bot-adapter to learn more about how bots work. +export const adapter = new TeamsAdapter( + {}, + new ConfigurationServiceClientCredentialFactory(config) +); + +// Catch-all for errors. +const onTurnErrorHandler = async (context: TurnContext, error: any) => { + // This check writes out errors to console log .vs. app insights. + // NOTE: In production environment, you should consider logging this to Azure + // application insights. + console.error(`\n [onTurnError] unhandled error: ${error}`); + console.log(error); + + // Send a trace activity, which will be displayed in Bot Framework Emulator + await context.sendTraceActivity( + "OnTurnError Trace", + `${error}`, + "https://www.botframework.com/schemas/error", + "TurnError" + ); + + // Send a message to the user + await context.sendActivity("The bot encountered an error or bug."); + await context.sendActivity("To continue to run this bot, please fix the bot source code."); +}; + +// Set the onTurnError for the singleton CloudAdapter. +adapter.onTurnError = onTurnErrorHandler; diff --git a/templates/ts/notification-express/src/internal/interface.ts b/templates/ts/notification-express/src/internal/interface.ts new file mode 100644 index 0000000000..ab5e80762b --- /dev/null +++ b/templates/ts/notification-express/src/internal/interface.ts @@ -0,0 +1,12 @@ +import { TurnState } from "@microsoft/teams-ai"; + +export interface ConversationState { + greeted: boolean; +} + +export type ApplicationTurnState = TurnState; + +export interface PagedData { + data: T[]; + continuationToken?: string; +} diff --git a/templates/ts/notification-express/src/store/storage.ts b/templates/ts/notification-express/src/store/storage.ts new file mode 100644 index 0000000000..8b12ffcfe3 --- /dev/null +++ b/templates/ts/notification-express/src/store/storage.ts @@ -0,0 +1,167 @@ +import { ConversationReference } from "botbuilder"; +import * as fs from "fs"; +import * as path from "path"; +import { PagedData } from "../internal/interface"; + +export class LocalFileStorage { + private readonly localFileName = + process.env.TEAMSFX_NOTIFICATION_STORE_FILENAME ?? ".notification.localstore.json"; + private readonly filePath: string; + + constructor(fileDir: string) { + this.filePath = path.resolve(fileDir, this.localFileName); + } + + async read(key: string): Promise<{ [key: string]: unknown } | undefined> { + if (!(await this.storeFileExists())) { + return undefined; + } + + const data = await this.readFromFile(); + + return data[key]; + } + + async list(): Promise<{ [key: string]: unknown }[]> { + if (!(await this.storeFileExists())) { + return []; + } + + const data = await this.readFromFile(); + + return Object.entries(data).map((entry) => entry[1] as { [key: string]: unknown }); + } + + async write(key: string, object: { [key: string]: unknown }): Promise { + if (!(await this.storeFileExists())) { + await this.writeToFile({ [key]: object }); + return; + } + + const data = await this.readFromFile(); + await this.writeToFile(Object.assign(data, { [key]: object })); + } + + async delete(key: string): Promise { + if (await this.storeFileExists()) { + const data = await this.readFromFile(); + if (data[key] !== undefined) { + delete data[key]; + await this.writeToFile(data); + } + } + } + + private storeFileExists(): Promise { + return new Promise((resolve) => { + try { + fs.access(this.filePath, (err) => { + if (err) { + resolve(false); + } else { + resolve(true); + } + }); + } catch (error: unknown) { + resolve(false); + } + }); + } + + private readFromFile(): Promise { + return new Promise((resolve, reject) => { + try { + fs.readFile(this.filePath, { encoding: "utf-8" }, (err, rawData) => { + if (err) { + reject(err); + } else { + resolve(JSON.parse(rawData)); + } + }); + } catch (error: unknown) { + reject(error); + } + }); + } + + private async writeToFile(data: unknown): Promise { + return new Promise((resolve, reject) => { + try { + const rawData = JSON.stringify(data, undefined, 2); + fs.writeFile(this.filePath, rawData, { encoding: "utf-8" }, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + } catch (error: unknown) { + reject(error); + } + }); + } +} + +export class DefaultConversationReferenceStore { + private readonly storage: LocalFileStorage; + private static instance: DefaultConversationReferenceStore; + + private constructor(storage: LocalFileStorage) { + this.storage = storage; + } + + public static getInstance(): DefaultConversationReferenceStore { + if (!DefaultConversationReferenceStore.instance) { + DefaultConversationReferenceStore.instance = new DefaultConversationReferenceStore( + new LocalFileStorage( + path.resolve(process.env.RUNNING_ON_AZURE === "1" ? process.env.TEMP ?? "./" : "./") + ) + ); + } + return DefaultConversationReferenceStore.instance; + } + + async add( + key: string, + reference: Partial, + options: { + overwrite?: boolean; + } + ): Promise { + if (options.overwrite) { + await this.storage.write(key, reference); + return true; + } + + const ref = await this.storage.read(key); + if (ref === undefined) { + await this.storage.write(key, reference); + return true; + } + + return false; + } + + async remove(key: string, reference: Partial): Promise { + const ref = await this.storage.read(key); + if (ref === undefined) { + return false; + } + + await this.storage.delete(key); + return true; + } + + async list( + pageSize?: number, + continuationToken?: string + ): Promise>> { + const data = await this.storage.list(); + return { + data, + continuationToken: "", + }; + } +} + +export default DefaultConversationReferenceStore.getInstance(); diff --git a/templates/ts/notification-express/src/teamsBot.ts b/templates/ts/notification-express/src/teamsBot.ts index 6cae0dc829..53a7ce08c3 100644 --- a/templates/ts/notification-express/src/teamsBot.ts +++ b/templates/ts/notification-express/src/teamsBot.ts @@ -1,24 +1,14 @@ -import { TeamsActivityHandler } from "botbuilder"; +import { Application } from "@microsoft/teams-ai"; +import { MemoryStorage } from "botbuilder"; +import config from "./internal/config"; +import { adapter } from "./internal/initialize"; +import { ApplicationTurnState } from "./internal/interface"; -// Teams activity handler. -// You can add your customization code here to extend your bot logic if needed. -export class TeamsBot extends TeamsActivityHandler { - constructor() { - super(); - - // Listen to MembersAdded event, view https://docs.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bots-notifications for more events - this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; - for (let cnt = 0; cnt < membersAdded.length; cnt++) { - if (membersAdded[cnt].id) { - await context.sendActivity( - "Welcome to the Notification Bot! I am designed to send you updates and alerts using Adaptive Cards triggered by HTTP post requests. " + - "Please note that I am a notification-only bot and you can't interact with me. Follow the README in the project and stay tuned for notifications!" - ); - break; - } - } - await next(); - }); - } -} +// Define storage and application +const storage = new MemoryStorage(); +export const app = new Application({ + // Adapter and botAppId are required for the Application to send proactive messages. + adapter: adapter, + botAppId: config.MicrosoftAppId, + storage: storage, +});