Skip to content

Commit 6092f54

Browse files
Added article about how to extend an already existing Teams Bot (SharePoint#10051)
* Added article about how to extend an already existing Teams Bot * Update wording in TOC - remove unnecessary word from new entry - fix previously existing typo * Minor markdown & grammatica fixes - inline code references should be formatted as code, not bold - fix grammatical issues - add spacing between paragraphs * update title --------- Co-authored-by: Andrew Connell <[email protected]>
1 parent 53e6d68 commit 6092f54

File tree

3 files changed

+391
-1
lines changed

3 files changed

+391
-1
lines changed
Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
---
2+
title: Extending an existing Teams Bot to become a Bot Powered Adaptive Card Extension
3+
description: Learn how to extend a Teams Bot to become a Bot Powered Adaptive Card Extension for Microsoft Viva Connections.
4+
ms.date: 12/12/2024
5+
ms.localizationpriority: high
6+
---
7+
# Extending an existing Teams Bot to become a Bot Powered Adaptive Card Extension
8+
9+
The main purpose of having the new Bot Powered Adaptive Card Extensions (ACEs) for Microsoft Viva Connections is to make it possible to reuse already existing bots, enriching their user experience with the support for Microsoft Viva Connections Dashboard. In fact, the return on investment that you can achieve by using and extending an already existing effort is a fundamental benefit of the Bot Powered ACEs.
10+
11+
In this article, you'll learn how to upgrade an already existing Teams Bot into a Bot Powered Adaptive Card Extension (ACE) experience.
12+
13+
## Updating the code of an already existing bot
14+
15+
So, let's assume that you created a bot for Microsoft Teams, using the Microsoft Teams Toolkit, using the **Bot | Basic Bot** template. The first thing to do is to upgrade the **botbuilder** reference in **package.json** to version 4.23.1 or higher. You can use the following command.
16+
17+
```console
18+
npm i botbuilder --save
19+
```
20+
21+
You also need to import the **adaptivecards** package with version 1.2.3, executing the following command.
22+
23+
```console
24+
npm i [email protected] --save
25+
```
26+
27+
Then, open the **teamsBot.ts** file (or whatever name you gave to the main bot source code file) and add an import statement to import all the types needed by Bot Powered ACEs. The updated source code file should look like the following one.
28+
29+
```TypeScript
30+
import {
31+
AceData,
32+
AceRequest,
33+
ActivityHandler,
34+
CardViewResponse,
35+
PrimaryTextCardView,
36+
GetPropertyPaneConfigurationResponse,
37+
HandleActionResponse,
38+
InvokeResponse,
39+
QuickViewResponse,
40+
SetPropertyPaneConfigurationResponse,
41+
TeamsActivityHandler,
42+
TurnContext }
43+
from "botbuilder";
44+
import * as AdaptiveCards from 'adaptivecards';
45+
```
46+
47+
Then, you need to initialize some infrastructural data in the bot constructor, to support the rendering of the Bot Powered ACE. It's a common habit to define a set of Card Views and Quick Views in the constructor of the Bot Powered ACE. You can then reuse them while rendering the actual user experience of the ACE. You can learn more about implementing Bot Powered ACEs by reading the article [Building your first Bot Powered Adaptive Card Extension with Microsoft Teams Toolkit and TypeScript](./Building-Your-First-Bot-Powered-ACE-TTK-TS.md).
48+
49+
In the following code excerpt, you can see the updated constructor of the bot.
50+
51+
```TypeScript
52+
export class TeamsBot extends TeamsActivityHandler {
53+
54+
private readonly _botId: string = 'AceFromExistingBot';
55+
private _cardViews: { [key: string]: CardViewResponse } = {};
56+
57+
private WELCOME_CARD_VIEW_ID: string = 'WELCOME_CARD_VIEW';
58+
private WELCOME_QUICK_VIEW_ID: string = 'WELCOME_QUICK_VIEW';
59+
60+
constructor() {
61+
super();
62+
63+
// Prepare the ACE data for all the card views and quick views.
64+
const aceData: AceData = {
65+
id: this._botId,
66+
title: 'Your extended bot!',
67+
description: 'Welcome to your extended bot.',
68+
cardSize: 'Large',
69+
iconProperty: 'Robot',
70+
properties: {},
71+
dataVersion: '1.0',
72+
};
73+
74+
// Welcome Card View (Primary Text Card View)
75+
const welcomeCardViewResponse: CardViewResponse = {
76+
aceData: aceData,
77+
cardViewParameters: PrimaryTextCardView(
78+
{
79+
componentName: 'cardBar',
80+
title: 'Welcome!'
81+
},
82+
{
83+
componentName: 'text',
84+
text: 'Welcome!'
85+
},
86+
{
87+
componentName: 'text',
88+
text: 'Here is your Teams bot available also in Microsoft Viva Connections!'
89+
},
90+
[
91+
{
92+
componentName: 'cardButton',
93+
title: 'Show Quick View',
94+
id: 'ShowQuickView',
95+
action: {
96+
type: 'QuickView',
97+
parameters: {
98+
view: this.WELCOME_QUICK_VIEW_ID
99+
}
100+
}
101+
}
102+
]
103+
),
104+
viewId: this.WELCOME_CARD_VIEW_ID,
105+
onCardSelection: {
106+
type: 'QuickView',
107+
parameters: {
108+
view: this.WELCOME_QUICK_VIEW_ID
109+
}
110+
}
111+
};
112+
this._cardViews[this.WELCOME_CARD_VIEW_ID] = welcomeCardViewResponse;
113+
114+
this.onMessage(async (context, next) => {
115+
console.log("Running with Message Activity.");
116+
const removedMentionText = TurnContext.removeRecipientMention(context.activity);
117+
const txt = removedMentionText.toLowerCase().replace(/\n|\r/g, "").trim();
118+
await context.sendActivity(`Echo: ${txt}`);
119+
// By calling next() you ensure that the next BotHandler is run.
120+
await next();
121+
});
122+
123+
this.onMembersAdded(async (context, next) => {
124+
const membersAdded = context.activity.membersAdded;
125+
for (let cnt = 0; cnt < membersAdded.length; cnt++) {
126+
if (membersAdded[cnt].id) {
127+
await context.sendActivity(
128+
`Hi there! I'm a Teams bot that will echo what you said to me.`
129+
);
130+
break;
131+
}
132+
}
133+
await next();
134+
});
135+
}
136+
}
137+
```
138+
139+
Then, you need to add some new custom logic to the bot to support rendering as a Bot Powered ACE. Override the `onInvokeActivity` method of the base class and provide support for any of the following actions:
140+
141+
- **cardExtension/getCardView**: handles the rendering of a Card View.
142+
- **cardExtension/getQuickView**: handles the rendering of a Quick View.
143+
- **cardExtension/getPropertyPaneConfiguration**: allows rendering the Property Pane of the ACE.
144+
- **cardExtension/setPropertyPaneConfiguration**: allows saving the settings configured using the Property Pane of the ACE.
145+
- **cardExtension/handleAction**: handles a custom action in the ACE like the select on a button in a Card View or any custom action in the UI of a Quick View.
146+
147+
Here follows a sample implementation of the **onInvokeActivity** method.
148+
149+
```TypeScript
150+
/**
151+
* Invoked when an invoke activity is received from the connector.
152+
* Invoke activities can be used to communicate many different things.
153+
* * Invoke activities communicate programmatic commands from a client or channel to a bot.
154+
*
155+
* @param context A strongly-typed context object for this turn
156+
* @returns A task that represents the work queued to execute
157+
*/
158+
protected async onInvokeActivity(context: TurnContext): Promise<InvokeResponse> {
159+
try {
160+
switch (context.activity.name) {
161+
case 'cardExtension/getCardView':
162+
return ActivityHandler.createInvokeResponse(
163+
await this.onSharePointTaskGetCardViewAsync(context, context.activity.value as AceRequest)
164+
);
165+
case 'cardExtension/getQuickView':
166+
return ActivityHandler.createInvokeResponse(
167+
await this.onSharePointTaskGetQuickViewAsync(context, context.activity.value as AceRequest)
168+
);
169+
case 'cardExtension/getPropertyPaneConfiguration':
170+
return ActivityHandler.createInvokeResponse(
171+
await this.onSharePointTaskGetPropertyPaneConfigurationAsync(
172+
context,
173+
context.activity.value as AceRequest
174+
)
175+
);
176+
case 'cardExtension/setPropertyPaneConfiguration':
177+
return ActivityHandler.createInvokeResponse(
178+
await this.onSharePointTaskSetPropertyPaneConfigurationAsync(
179+
context,
180+
context.activity.value as AceRequest
181+
)
182+
);
183+
case 'cardExtension/handleAction':
184+
return ActivityHandler.createInvokeResponse(
185+
await this.onSharePointTaskHandleActionAsync(context, context.activity.value as AceRequest)
186+
);
187+
default:
188+
return super.onInvokeActivity(context);
189+
}
190+
} catch (err) {
191+
if (err.message === 'NotImplemented') {
192+
return { status: 501 };
193+
} else if (err.message === 'BadRequest') {
194+
return { status: 400 };
195+
}
196+
throw err;
197+
}
198+
}
199+
```
200+
201+
You can read the article [Overview of Bot Powered Adaptive Card Extensions](Overview-Bot-Powered-ACEs.md), to learn about the basic requirement for a Bot Powered ACE to provide at least one Card View. You can optionally provide more Card Views and one or more Quick Views, in case you want to have a better and more personalized user experience.
202+
203+
In this article, you'll add both a Card View and a Quick View. As such, you're going to implement the actions **cardExtension/getCardView** and **cardExtension/getQuickView**, respectively with methods `onSharePointTaskGetCardViewAsync` and `onSharePointTaskGetQuickViewAsync`. While you don't need to implement all the other methods.
204+
205+
```TypeScript
206+
/**
207+
* Override this in a derived class to provide logic for when a card view is fetched
208+
*
209+
* @param _context - A strongly-typed context object for this turn
210+
* @param _aceRequest - The Ace invoke request value payload
211+
* @returns A Card View Response for the request
212+
*/
213+
protected async onSharePointTaskGetCardViewAsync(
214+
_context: TurnContext,
215+
_aceRequest: AceRequest
216+
): Promise<CardViewResponse> {
217+
return this._cardViews[this.WELCOME_CARD_VIEW_ID];
218+
}
219+
220+
/**
221+
* Override this in a derived class to provide logic for when a quick view is fetched
222+
*
223+
* @param _context - A strongly-typed context object for this turn
224+
* @param _aceRequest - The Ace invoke request value payload
225+
* @returns A Quick View Response for the request
226+
*/
227+
protected async onSharePointTaskGetQuickViewAsync(_context: TurnContext, _aceRequest: AceRequest): Promise<QuickViewResponse> {
228+
229+
// Prepare the AdaptiveCard for the Quick View
230+
const card = new AdaptiveCards.AdaptiveCard();
231+
card.version = new AdaptiveCards.Version(1, 5);
232+
const cardPayload = {
233+
type: 'AdaptiveCard',
234+
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
235+
body: [
236+
{
237+
type: 'TextBlock',
238+
text: 'Welcome!',
239+
weight: 'Bolder',
240+
size: 'Large',
241+
wrap: true,
242+
maxLines: 1,
243+
spacing: 'None',
244+
color: 'Dark'
245+
},
246+
{
247+
type: 'TextBlock',
248+
text: 'We are happy to see that you are consuming your extended Teams Bot in Microsoft Viva Connections! Thanks!',
249+
weight: 'Normal',
250+
size: 'Medium',
251+
wrap: true,
252+
maxLines: 3,
253+
spacing: 'None',
254+
color: 'Dark'
255+
}
256+
]
257+
};
258+
card.parse(cardPayload);
259+
260+
// Add the Feedback QuickViews
261+
const welcomeQuickViewResponse: QuickViewResponse = {
262+
viewId: this.WELCOME_QUICK_VIEW_ID,
263+
title: 'Welcome!',
264+
template: card,
265+
data: {},
266+
externalLink: null,
267+
focusParameters: null
268+
};
269+
270+
return Promise.resolve(welcomeQuickViewResponse);
271+
}
272+
273+
/**
274+
* Override this in a derived class to provide logic for getting configuration pane properties.
275+
*
276+
* @param _context - A strongly-typed context object for this turn
277+
* @param _aceRequest - The Ace invoke request value payload
278+
* @returns A Property Pane Configuration Response for the request
279+
*/
280+
protected async onSharePointTaskGetPropertyPaneConfigurationAsync(
281+
_context: TurnContext,
282+
_aceRequest: AceRequest
283+
): Promise<GetPropertyPaneConfigurationResponse> {
284+
throw new Error('NotImplemented');
285+
}
286+
287+
/**
288+
* Override this in a derived class to provide logic for setting configuration pane properties.
289+
*
290+
* @param _context - A strongly-typed context object for this turn
291+
* @param _aceRequest - The Ace invoke request value payload
292+
* @returns A Card view or no-op action response
293+
*/
294+
protected async onSharePointTaskSetPropertyPaneConfigurationAsync(
295+
_context: TurnContext,
296+
_aceRequest: AceRequest
297+
): Promise<SetPropertyPaneConfigurationResponse> {
298+
throw new Error('NotImplemented');
299+
}
300+
301+
/**
302+
* Override this in a derived class to provide logic for setting configuration pane properties.
303+
*
304+
* @param _context - A strongly-typed context object for this turn
305+
* @param _aceRequest - The Ace invoke request value payload
306+
* @returns A handle action response
307+
*/
308+
protected async onSharePointTaskHandleActionAsync(
309+
_context: TurnContext,
310+
_aceRequest: AceRequest
311+
): Promise<HandleActionResponse> {
312+
throw new Error('NotImplemented');
313+
}
314+
```
315+
316+
The `onSharePointTaskGetCardViewAsync` method returns the Card View defined in the constructor as the only element in the dictionary of Card Views. The `onSharePointTaskGetQuickViewAsync` method creates an Adaptive Card using the **adaptivecards** package that you imported previously and then returns the card into a Quick View object.
317+
318+
## Updating the manifest of an already existing bot
319+
320+
Your bot is now ready to support rendering as a Bot Powered ACE in Microsoft Viva Connection. However, to make it available as a new ACE in the Viva Connections Dashboard, you need to update the **manifest.json** file of the solution to declare this new capability. Specifically, you need to add a section `dashboardCards`, for example right after the `bots` configured as follows. Here you can see an excerpt of the **manifest.json** file.
321+
322+
```JSON
323+
"dashboardCards": [
324+
{
325+
"id": "${{BOT_ID}}",
326+
"pickerGroupId": "8cd406cc-7a66-42b5-bda5-9576abe7a818",
327+
"displayName": "Teams Bot Extended",
328+
"description": "Bot Powered ACE created from an already existing Teams bot",
329+
"icon": {
330+
"officeUIFabricIconName": "Robot"
331+
},
332+
"contentSource": {
333+
"sourceType": "bot",
334+
"botConfiguration": {
335+
"botId": "${{BOT_ID}}"
336+
}
337+
},
338+
"defaultSize": "large"
339+
}
340+
]
341+
```
342+
343+
Then, update the `validDomains` section according to the following excerpt.
344+
345+
```JSON
346+
"validDomains": [
347+
"${{BOT_DOMAIN}}"
348+
]
349+
```
350+
351+
The setting includes the ___domain of the bot in the list of valid domains so that Microsoft 365 can trust any resource published by the Bot web application.
352+
353+
## Deploying the updated bot
354+
355+
You can now package and deploy the solution using the out-of-the-box capabilities of Microsoft Teams Toolkit. You simply need to trigger the actions to **Provision**, **Deploy**, and **Publish** the bot in Microsoft Teams. After no more than 24 hours, your bot becomes available in Microsoft Viva Connections as a new Bot Powered ACE.
356+
357+
## Configure the Bot in Azure
358+
359+
While waiting for the ACE to become available in the Dashboard, you need to slightly update the configuration of the Azure Bot. Open a web browser and navigate to the [Azure Management Portal](https://portal.azure.com/). From the Azure Management Portal home page, select "Azure Bot" and choose the Bot that you provisioned with the Microsoft Teams Toolkit.
360+
361+
Open the **Configuration** panel of the Bot. Notice that the Microsoft Teams Toolkit configured the Bot to run with a **User-Assigned Managed Identity**. Here you can see what the available options are:
362+
363+
- **User-Assigned Managed Identity**: if your Bot app doesn't need to access resources outside of its home tenant and if your Bot app is hosted on an Azure resource that supports Managed Identities.
364+
- **Single Tenant**: if your Bot app doesn't need to access resources outside of its home tenant, but your Bot app isn't hosted on an Azure resource that supports Managed Identities.
365+
- **Multi-Tenant**: if your Bot app needs to access resources outside its home tenant or serves multiple tenants.
366+
367+
Notice also that the "Messaging endpoint" URL for your Bot targets the URL of the Web App provisioned on Microsoft Azure by the Microsoft Teams Toolkit.
368+
369+
![The configuration panel for an Azure Bot. It includes settings about Messaging Endpoint URL, the Microsoft App ID, the Application Insights keys, the Schema Transformation Version, and the OAuth Connection settings.](./images/Azure-Portal-Create-Azure-Bot-TTK-01.png)
370+
371+
### Configuring the Microsoft 365 Channel
372+
373+
You can now select the panel **Channels** in the Azure Bot to configure a new channel for Microsoft 365. In the **Available Channels** section of the page, you need to select the channel with the name **Microsoft 365** to enable it.
374+
375+
![The panel to configure channels for the Azure Bot. There are two pre-selected channels: "Direct Line" and "Web Chat". There is a list of "Available Channels", including the "Microsoft 365" channel.](./images/Azure-Portal-Create-Azure-Bot-10-TTK.png)
376+
377+
A new page shows up, explaining the purpose of the **Microsoft 365** channel. Select the **Apply** button to enable the new channel and go back to the list of channels configured for your Azure Bot.
378+
379+
![The panel to configure the "Microsoft 365" channel in the list of channels supported by the Azure Bot. There is a description of the purpose of the channel and an "Apply" button to add the channel to the list of channels supported by the Azure Bot.](./images/Azure-Portal-Create-Azure-Bot-11.png)
380+
381+
Right after that, your Azure Bot is fully configured and ready to be used.
382+
383+
![The panel to configure channels for the Azure Bot. There are now three pre-selected channels: "Direct Line", "Web Chat", and "Microsoft 365.](./images/Azure-Portal-Create-Azure-Bot-12-TTK.png)
384+
385+
You're now ready to run and test your Bot Powered ACE built with Microsoft Teams Toolkit and TypeScript.
386+
In the following screenshot, you can see the output of the Bot Powered ACE in the Microsoft Viva Connections Dashboard.
387+
388+
![The interface Bot Powered ACE created starting from a Teams Bot. There is a Card View with a welcome message and a button to show a QuickView. There is a QuickView welcoming the user, too.](./images/Extend-Teams-Bot-01.png)
Loading

0 commit comments

Comments
 (0)