page_type | products | languages | description | urlFragment | extensions | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
sample |
|
|
A task module allows you to create modal popup experiences in your Teams application. |
teams-module-node |
|
A task module allows you to create modal popup experiences in your Teams application. Inside the popup, you can run your own custom HTML/JavaScript code, show an <iframe>
-based widget such as a YouTube or Microsoft Stream video, or display an Adaptive card.
Task modules build on the foundation of Microsoft Teams tabs: a task module is essentially a tab in a popup window. It uses the same SDK, so if you've built a tab you are already 90% of the way to being able to create a task module.
This sample is deployed on Microsoft Azure and you can try it yourself by uploading TaskModule.zip to one of your teams and/or as a personal app. (Sideloading must be enabled for your tenant; see step 6 here.) The app is running on the free Azure tier, so it may take a while to load if you haven't used it recently and it goes back to sleep quickly if it's not being used, but once it's loaded it's pretty snappy.
This sample app was developed in conjunction with the task module feature itself to exercise as much of it as possible. Here's what's included:
- A channel tab. Add the app to a team, then add a Task Module tab and choose "Task Module Demo" from the dropdown list.
- A bot. Add the app to a team, then chat with it (@Task Module)
- A personal app. When you upload the TaskModule.zip file, choose "Add for you" and "Task Module" will appear in the "..." menu in the Teams app bar. The personal app has both a tab and a bot.
The tab shows how to invoke the task module using the Teams SDK. Source code for the tab is found in TaskModuleTab.ts; the view definition is in taskmodule.pug. This sample app uses Pug (formerly Jade) for HTML rendering.
The following task modules are supported:
- YouTube, which is comprised of a generic template for embedded
<iframe>
experiences (also used for the PowerApp task module below) plus a four-line stub containing the YouTube embed URL
- PowerApp — unfortunately it doesn't work out of the box; click the button or see the source code for details on how you can customize it for your tenant
- There are two Adaptive card examples:
- Showing the results of an
Action.Submit
button returned to the tab - Showing the results returned to the bot as a message
- Showing the results of an
The sample app also contains a bot with cards allowing you to invoke these task modules.
You can invoke them from an Adaptive card (using the tasks command) or from a Bot Framework thumbnail card (using the bfcard command).
RootDialog.ts contains the code for the tasks and bfcard commands, and TeamsBot.ts contains the code for responding to task/fetch
and task/submit
messages. The task modules when invoked from a bot are the same as for the tab, except for the Adaptive card examples:
- Adaptive Card - Single returns the results to the conversation as a message.
- Adaptive Card - Sequence shows how adaptive cards can be chained together: instead of returning the result to the chat, the result is shown in another Adaptive card.
Note these instructions are for running the sample on your local machine, the tunnelling solution is required because the Teams service needs to call into the bot.
In Azure portal, create a Azure Bot resource.
- Ensure that you've enabled the Teams Channel
-
Clone the repository
git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git
-
In a terminal, navigate to
samples/app-task-module/nodejs
-
Install modules
npm install
-
Build application:
npm run build
-
Run ngrok - point to port 3978
ngrok http -host-header=rewrite 3978
This will be used as baseURI.
-
Update the
.env
configuration for the bot to use theMICROSOFT_APP_ID
andMICROSOFT_APP_PASSWORD
from the Azure Bot resource. (Note that the MicrosoftAppId is the AppId created in step 1, the MicrosoftAppPassword is referred to as the "client secret" in step 1 and you can always create a new client secret anytime.). ForBASE_URI
provide the application base url, you get by running ngrok it should look something likehttps://abc21-hun-12ef.ngrok.io
-
Run your bot at the command line:
npm start
- This sample is data-driven as much as possible and shares as much code as possible between the bot and the tabs forms of task module:
- Metadata used to generate TaskInfo objects is in contants.ts.
- All cards are generated via JSON templates in the cardTemplates collection in CardTemplates.ts and rendered using the ST-JS (Select Transform) JavaScript library. The values in
cardTemplates
have data placeholders, e.g.{{title}}
; JavaScript objects are created in TaskModuleTab.ts and RootDialog.ts, and helper functions in CardUtils.ts are used to render the cards. - Deep links are generated in the
taskModuleLink()
function in DeepLinks.ts. - Except for the JavaScript in the task modules themselves, all code is in TypeScript. Three source files, contants.ts, DeepLinks.ts, and CardTemplates.ts are used on both the client (tab) and server (bot). Browserify is used in the gulpfile.js build script to bundle the JavaScript generated by TypeScript; this script file is referenced by taskmodule.pug.
- The logic for handling
task/fetch
messages is also data-driven, shared across card types (Adaptive and Bot Framework), and supports chaining of Adaptive card task modules. The data-driven approach for card generation combined with the fact that the Adaptive and Bot Framework card schemas are quite different unfortunately means that the way it works is not obvious. Here's how it works:- All the
task/fetch
andtask/submit
requests and responses are JavaScript objects defined in thefetchTemplates[]
collection in CardTemplates.ts and generated in TeamsBot.ts in theonInvoke()
function. - The custom properties
data.taskModule
(for Adaptive cardAction.Submit
buttons) andvalue.taskModule
(for Bot Framework card actions) are used as lookup values to select a specific response from thefetchTemplates[]
collection when atask/fetch
message is received. - Adaptive card chaining — showing multiple adaptive cards in a sequence — is implemented using the
data.taskResponse
property on an Adaptive cardAction.Submit
button. With Adaptive Card - Single, the card is rendered withdata.taskResponse
set to "message" (show a message to the user); for Adaptive Card - Sequence, the same Adaptive card is rendered withdata.taskResponse
set to "continue" (show another Adaptive card). This is the only difference betweenfetchTemplates.adaptivecard1
andfetchTemplates.adaptivecard2
. The second Adaptive card (cardTemplates.acSubmitResponse
in CardTemplates.ts) in the sequence just shows the JSON submitted in the first; itsdata.taskResponse
is set to "final". The logic for handling the various values ofdata.taskResponse
is in TeamsBot.ts. ThetaskResponse
values form a simple "protocol" for responding to the different ways to respond totask/submit
messages: do nothing (complete), show a message to the user, load a subsequent adaptive card in a sequence, or load the final adaptive card in a sequence.
- All the
- To avoid hardcoding an
appId
in customform.pug, theappId
is passed in at Pug template rendering time by tabs.ts, the TypeScript which implements the tab routes. - Theming and simple keyboard handling for the Esc key are supported.
There's also an actester bot command. It's a handy way of seeing what an Adaptive card looks like in Teams - simply copy/paste the JSON of Adaptive cards, e.g. the Samples page on the Adaptive card site, and the bot will render it as a reply.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.