{"id":11467,"date":"2022-09-27T10:36:10","date_gmt":"2022-09-27T17:36:10","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/?p=11467"},"modified":"2022-09-30T08:16:23","modified_gmt":"2022-09-30T15:16:23","slug":"build-a-stock-update-notification-bot-for-microsoft-teams-using-teams-toolkit-for-visual-studio-code","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/build-a-stock-update-notification-bot-for-microsoft-teams-using-teams-toolkit-for-visual-studio-code\/","title":{"rendered":"Build a stock update notification bot for Microsoft Teams using Teams Toolkit for Visual Studio Code"},"content":{"rendered":"<p>Microsoft Teams Toolkit for Visual Studio Code enables you to create, debug and deploy Microsoft Teams apps to a Microsoft 365 tenant fast using a no configuration approach.<\/p>\n<p>In the latest release of Teams Toolkit, a new project has been added to the Samples Gallery, Stocks Update Notification Bot, which this tutorial is based on.<\/p>\n<p>If you want to see the finished version, checkout the sample in the gallery or the <a href=\"https:\/\/github.com\/OfficeDev\/TeamsFx-Samples\/tree\/dev\/stocks-update-notification-bot\">final source code<\/a> on the TeamsFX GitHub repository.<\/p>\n<p>By following this tutorial, you will learn how Teams Toolkit helps simplify and accelerate your Microsoft Teams app development by building a bot that obtains data from an external API and sends a rich message using an Adaptive Card into Microsoft Teams on a pre-defined schedule.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/stocksupdatenotificationbot.png\"><img decoding=\"async\" class=\"wp-image-11551 size-full aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/stocksupdatenotificationbot.png\" alt=\"A Microsoft Teams chat window is shown with the latest stock price information for MSFT, formatted as an Adaptive Card. A toast notification is shown in the bottom right notifying the user that a card has been sent to them by the Stocks Update Notification Bot\" width=\"532\" height=\"274\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/stocksupdatenotificationbot.png 532w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/stocksupdatenotificationbot-300x155.png 300w\" sizes=\"(max-width: 532px) 100vw, 532px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>Table of Contents<\/h2>\n<ul>\n<li><a href=\"#_12_Pre-requisites\">Pre-requisites<\/a><\/li>\n<li><a href=\"#_12_Install_Teams\">Install Teams Toolkit for Visual Studio Code<\/a><\/li>\n<li><a href=\"#_12_Scaffold_the\">Scaffold the project<\/a><\/li>\n<li><a href=\"#_1212_Sign_into\">Sign into your Microsoft 365 tenant<\/a><\/li>\n<li><a href=\"#_\u2139\ufe0f_Update_the\">Update the adaptive card<\/a><\/li>\n<li><a href=\"#_12_Create_an\">Create an API connection<\/a><\/li>\n<li><a href=\"#_12_Update_the\">Update the bot logic<\/a><\/li>\n<li><a href=\"#_12_Test_your\">Test your bot!<\/a><\/li>\n<li><a href=\"#_\u23ed_What_next?\">What next?<\/a><\/li>\n<li><a href=\"#_12_Learn_more!\">Learn more!<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>\ud83c\udfc1 Pre-requisites<\/h2>\n<p>To follow this guide successfully, you will need an account that has access to a Microsoft 365 tenant,\u00a0which has been <a href=\"https:\/\/docs.microsoft.com\/microsoftteams\/platform\/m365-apps\/prerequisites\">configured for developing Microsoft Teams apps<\/a>, have <a href=\"https:\/\/code.visualstudio.com\/\">Visual Studio Code<\/a> and the current LTS version (v16) of <a href=\"https:\/\/nodejs.org\/\">nodejs<\/a> installed.<\/p>\n<hr \/>\n<p style=\"text-align: center;\">If you do not have a tenant, I highly recommend that you use a\nMicrosoft 365 Developer Tenant, which you can obtain for free by\n<a href=\"https:\/\/developer.microsoft.com\/microsoft-365\/dev-program\">joining the Microsoft 365 Developer Program<\/a>.<\/p>\n<hr \/>\n<p>With the pre-requisites done, let\u2019s begin!<\/p>\n<p>&nbsp;<\/p>\n<h2>\ud83d\udee0 Install Teams Toolkit for Visual Studio Code<\/h2>\n<p>Our first step is to install Teams Toolkit into Visual Studio Code, you can do this several ways, however the easiest is way is to search for and install the extension using the Extensions panel from the Side Bar within Visual Studio Code.<\/p>\n<ol>\n<li>Open Visual Studio Code<\/li>\n<li>Open the Extensions panel from the Side Bar<\/li>\n<li>Search for Teams Toolkit in the marketplace<\/li>\n<li>Select Teams Toolkit in the search results<\/li>\n<li>Click the install button to install the extension<\/li>\n<\/ol>\n<p>Once the extension has been successfully installed, you will see a Microsoft Teams icon in the Side Bar.<\/p>\n<p>Congratulations, you have successfully installed Teams Toolkit for Visual Studio Code and are now ready to create your first Microsoft Teams app! \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\ud83d\udc77 Scaffold the project<\/h2>\n<p>After installing the extension, let\u2019s create a new project.<\/p>\n<ol>\n<li>Open the Teams Toolkit extension by clicking on the Teams icon in the Side Bar<\/li>\n<li>Click the <em>Create a new Teams app<\/em> button to start the project scaffolding wizard<\/li>\n<li>Select <em>Create a new Teams app<\/em> in the first step<\/li>\n<li>Select <em>Notification Bot<\/em> in the Capabilities step<\/li>\n<li>Select <em>Timer Trigger<\/em> in the Choose triggers step<\/li>\n<li>Select <em>TypeScript<\/em> in the Programming Language step<\/li>\n<li>Select a folder of your choice in the Workspace folder step, this is the location of where the project will be created.<\/li>\n<li>Enter <em>StocksUpdateNotificationBot<\/em> in the Application name step. When you complete this step, Teams Toolkit will create a project in a folder with the same name in the Workspace folder location you selected in the previous step<\/li>\n<\/ol>\n<p>When the project scaffolding is complete, Teams Toolkit will re-open Visual Studio code in the project folder and open a Readme file for the project.<\/p>\n<p>Feel free to look through the Readme file to understand the base project functionality and structure.<\/p>\n<p>Congratulations, you have successfully created your first project with Teams Toolkit that contains a Notification Bot! \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\ud83e\uddd1\u200d\ud83d\udcbb Sign into your Microsoft 365 tenant<\/h2>\n<p>When creating a new project using Teams Toolkit, the next step is to ensure that you have authenticated Teams Toolkit to your Microsoft 365 tenant.<\/p>\n<ol>\n<li>Open the Teams Toolkit extension by clicking on the Teams icon in the Side Bar<\/li>\n<li>In the Accounts section, click <em>Sign in to M365<\/em><\/li>\n<li>Click the <em>Sign in<\/em> button in the prompt, this will open your primary browser and navigate to a Microsoft 365 login screen<\/li>\n<li>Enter your account details to complete the login flow<\/li>\n<li>Return to Visual Studio Code<\/li>\n<\/ol>\n<p>After successfully authenticating with your Microsoft 365 tenant, you will see that your account name is now showing in the Account section of the Teams Toolkit extension and an indication of the current Sideloading status in your tenant will also be displayed.<\/p>\n<p>Congratulations, you have successfully connected Teams Toolkit with your Microsoft 365 tenant! \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\u2139\ufe0f Update the Adaptive Card<\/h2>\n<p>As our notification bot will use Adaptive Cards to represent our stock price update, let\u2019s update the adaptive card design to fit our needs.<\/p>\n<p>Open the <em>notification-default.json<\/em> file in the <em>bot\/src\/adaptiveCards<\/em> directory and replace the contents with the following JSON.<\/p>\n<pre class=\"prettyprint\">{\r\n  \"$schema\": \"http:\/\/adaptivecards.io\/schemas\/adaptive-card.json\",\r\n  \"type\": \"AdaptiveCard\",\r\n  \"version\": \"1.4\",\r\n  \"body\": [\r\n    {\r\n      \"type\": \"Container\",\r\n      \"items\": [\r\n        {\r\n          \"type\": \"TextBlock\",\r\n          \"text\": \"${name}\",\r\n          \"size\": \"Medium\",\r\n          \"wrap\": true\r\n        },\r\n        {\r\n          \"type\": \"TextBlock\",\r\n          \"text\": \"${symbol}\",\r\n          \"isSubtle\": true,\r\n          \"spacing\": \"None\",\r\n          \"wrap\": true\r\n        },\r\n        {\r\n          \"type\": \"TextBlock\",\r\n          \"text\": \"{{DATE(${timestamp},SHORT)}} {{TIME(${timestamp})}}\",\r\n          \"wrap\": true\r\n        }\r\n      ]\r\n    },\r\n    {\r\n      \"type\": \"Container\",\r\n      \"spacing\": \"None\",\r\n      \"items\": [\r\n        {\r\n          \"type\": \"ColumnSet\",\r\n          \"columns\": [\r\n            {\r\n              \"type\": \"Column\",\r\n              \"width\": \"stretch\",\r\n              \"items\": [\r\n                {\r\n                  \"type\": \"TextBlock\",\r\n                  \"text\": \"${formatNumber(price,2)}\",\r\n                  \"size\": \"ExtraLarge\",\r\n                  \"wrap\": true\r\n                },\r\n                {\r\n                  \"type\": \"TextBlock\",\r\n                  \"text\": \"${if(change &gt;= 0, '\u25b2', '\u25bc')} ${formatNumber(change,2)} USD (${formatNumber(changePercent, 2)}%)\",\r\n                  \"spacing\": \"None\",\r\n                  \"wrap\": true\r\n                }\r\n              ]\r\n            },\r\n            {\r\n              \"type\": \"Column\",\r\n              \"width\": \"auto\",\r\n              \"items\": [\r\n                {\r\n                  \"type\": \"FactSet\",\r\n                  \"facts\": [\r\n                    {\r\n                      \"title\": \"Open\",\r\n                      \"value\": \"${formatNumber(open,2)}\"\r\n                    },\r\n                    {\r\n                      \"title\": \"High\",\r\n                      \"value\": \"${formatNumber(high,2)}\"\r\n                    },\r\n                    {\r\n                      \"title\": \"Low\",\r\n                      \"value\": \"${formatNumber(low,2)}\"\r\n                    }\r\n                  ]\r\n                }\r\n              ]\r\n            }\r\n          ]\r\n        }\r\n      ]\r\n    }\r\n  ]\r\n}<\/pre>\n<p>We are using Adaptive Cards Template Language here to provide separation between our data and layout.<\/p>\n<p>Using binding expressions i.e., ${name}, we can add placeholders to our layout in locations where we want our data to be rendered.<\/p>\n<p>Let\u2019s create an interface to represent the data object to use with the template.<\/p>\n<p>Open the <em>cardModels.ts<\/em> file in the <em>bot\/src<\/em> directory and replace the contents with the following.<\/p>\n<pre class=\"prettyprint\">export interface GlobalQuote {\r\n  symbol: string;\r\n  open: number;\r\n  high: number;\r\n  low: number;\r\n  price: number;\r\n  volume: number;\r\n  latestTradingDay: string;\r\n  previousClose: number;\r\n  change: number;\r\n  changePercent: number;\r\n  name?: string;\r\n  timestamp?: string;\r\n}\r\n<\/pre>\n<p>When we combine our template and data using the Adaptive Cards SDK, the notification will look something like this.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/teams-toolkit_test-bot.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-11472\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/teams-toolkit_test-bot.png\" alt=\"Image successfully creating an Adaptive Card which will represent our stock update notification!\" width=\"903\" height=\"480\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/teams-toolkit_test-bot.png 903w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/teams-toolkit_test-bot-300x159.png 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2022\/09\/teams-toolkit_test-bot-768x408.png 768w\" sizes=\"(max-width: 903px) 100vw, 903px\" \/><\/a><\/p>\n<p>Congratulations, you have successfully created an Adaptive Card which will represent our stock update notification! \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\ud83d\udce1 Create an API connection<\/h2>\n<p>When providing an update, our bot will need to obtain the latest stock price, to do this we will create an API connection to the Alpha Vantage API.<\/p>\n<ol>\n<li>Open the Teams Toolkit extension by clicking on the Teams icon in the Side Bar<\/li>\n<li>In the Development section, select <em>Add features<\/em> to start the wizard<\/li>\n<li>In the <em>Add features<\/em> step, select <em>API Connection<\/em><\/li>\n<li>In the <em>Enter an API endpoint for local debugging<\/em> step, enter the Alpha Vantage API endpoint, <a href=\"https:\/\/www.alphavantage.co\"><em>https:\/\/www.alphavantage.co<\/em><\/a><\/li>\n<li>In the <em>Select component(s) to invoke the API<\/em> step, select the <em>.\/bot<\/em> checkbox and select OK<\/li>\n<li>In the <em>Enter a friendly name for your API<\/em> step, enter a<em>lphaVantage<\/em><\/li>\n<li>In the <em>Select an API authentication type<\/em> step, select <em>API Key<\/em><\/li>\n<li>In the <em>Select the API position in request<\/em> step, select <em>Query parameter<\/em><\/li>\n<li>In the <em>Enter an API Key name<\/em> step, enter <em>apikey<\/em><\/li>\n<\/ol>\n<p>After completing the wizard, a new folder is created in the <em>bot\/src<\/em> directory called <em>apiConnections<\/em>, inside it a new file called <em>alphaVantage.ts<\/em> is generated which contains the logic to create an authenticated API client.<\/p>\n<p>To complete the setup, we must provide a value to pass in the <em>apikey<\/em> query string parameter.<\/p>\n<p>Open the <em>.env.teamsfx.local <\/em>file in the <em>bot<\/em> directory and update the environment variable <em>TEAMSFX_API_ALPHAVANTAGE_API_KEY<\/em> to <em>TEAMSFX_API_ALPHAVANTAGE_API_KEY=demo<\/em><\/p>\n<p>Congratulations, you have successfully created an API connection to an external API using API Key authentication! \ud83c\udf89<\/p>\n<p><em>\u00a0<\/em><\/p>\n<h2>\ud83e\udd16 Update the bot logic<\/h2>\n<p>Let\u2019s update the logic in the bot.<\/p>\n<p>Open the <em>timerTrigger.ts<\/em> file, this file contains the core logic of our bot that will be executed on a schedule. For now, remove the contents of the entire file, so we can work from a blank slate.<\/p>\n<p>First, let\u2019s include the imports for the dependant global modules.<\/p>\n<pre class=\"prettyprint\">import { AzureFunction, Context } from '@azure\/functions';\r\nimport { AdaptiveCards } from '@microsoft\/adaptivecards-tools';\r\nimport { AxiosInstance, ConversationBot, TeamsBotInstallation } from '@microsoft\/teamsfx';\r\n<\/pre>\n<p>Let\u2019s cover what these dependencies are.<\/p>\n<ul>\n<li><strong>@azure\/functions<\/strong>, a module which provides type definitions for working with Azure Functions.<\/li>\n<li><strong>@microsoft\/adaptivecards-tools<\/strong>, an SDK that makes it easier for developers to work with Adaptive Cards.<\/li>\n<li><strong>@microsoft\/teamsfx<\/strong>, an SDK that makes it easier for developers to complete common tasks such as implementing single sign on (SSO) and calling external APIs.<\/li>\n<\/ul>\n<p>Next, lets add our local dependencies.<\/p>\n<pre class=\"prettyprint\">import { bot } from '.\/internal\/initialize';\r\nimport { GlobalQuote } from '.\/cardModels';\r\nimport template from '.\/adaptiveCards\/notification-default.json'\r\nimport { alphaVantageClient } from '.\/apiConnections\/alphaVantage';\r\n<\/pre>\n<p>Let\u2019s cover what these dependencies are.<\/p>\n<ul>\n<li><strong>.\/internal\/initialize<\/strong>, is where our bot is initialised using the Bot Framework at run time.<\/li>\n<li><strong>.\/cardModels<\/strong>, is where we define our data models, in this case we use the <em>GlobalQuote<\/em> model which represents the quote data that we combine with our Adaptive Card.<\/li>\n<li><strong>.\/adaptiveCards\/notification-default.json<\/strong>, is our representation of our adaptive card.<\/li>\n<li><strong>.\/apiConnections\/alphaVantage<\/strong>, is where we initialise an API client to authenticate and request data from using the Alpha Vantage API.<\/li>\n<\/ul>\n<p>Now that we have our dependencies in place, lets implement the Azure Function trigger logic.<\/p>\n<pre class=\"prettyprint\">const timerTrigger: AzureFunction = async function (context: Context, myTimer: any): Promise&lt;void&gt; {\r\n  const timestamp = getTimestamp(new Date());\r\n\r\n  getQuoteBySymbol(alphaVantageClient)('MSFT')\r\n    .then(getResponseData)\r\n    .then(extractGlobalQuote)\r\n    .then(transformGlobalQuote)\r\n    .then(quote =&gt; addTimestamp(quote)(timestamp))\r\n    .then(quote =&gt; addCompanyName(quote)('Microsoft Corporation'))\r\n    .then(quote =&gt;\r\n      getInstallations(bot)\r\n        .then(targets =&gt; targets.map(target =&gt; sendCard(target)(AdaptiveCards)(template)(quote)))\r\n    )\r\n    .catch(err =&gt; handleError(err)(context));\r\n\r\n}\r\n\r\nexport default timerTrigger;\r\n<\/pre>\n<p>Whilst this is a lot to unpack, the general flow of the function is<\/p>\n<ol>\n<li>Get current timestamp<\/li>\n<li>Get quote data from Alpha Vantage API<\/li>\n<li>Transform quote data, adding timestamp and company name<\/li>\n<li>Get bot installations<\/li>\n<li>Iterate over bot installations and send an Adaptive Card<\/li>\n<\/ol>\n<p>Next, we will implement the functions that this handler uses.<\/p>\n<p>Add these after the <em>timerTrigger<\/em> function but before the <em>export<\/em> statement.<\/p>\n<p>The following functions, <em>getTimestamp<\/em> and <em>makeValidAdaptiveCardISOString,<\/em> are used to return a valid time stamp that can be used in an Adaptive Card.<\/p>\n<pre class=\"prettyprint\">const getTimestamp =\r\n  (date: Date): string =&gt;\r\n    makeValidAdaptiveCardISOString(date.toISOString());\r\n\r\nconst makeValidAdaptiveCardISOString =\r\n  (iso: string): string =&gt;\r\n    `${iso.split('.')[0]}Z`;\r\n<\/pre>\n<p>The following functions, <em>getQuoteBySymbol<\/em> and <em>getResponseData<\/em>, are used to send a request to the Alpha Vantage API and obtain the response data from the API client.<\/p>\n<pre class=\"prettyprint\">const getQuoteBySymbol =\r\n  (apiClient: AxiosInstance) =&gt;\r\n    (symbol: string) =&gt;\r\n      apiClient.get(`\/query?function=GLOBAL_QUOTE&amp;symbol=${symbol}`)\r\n\r\nconst getResponseData =\r\n  (res: any): object =&gt;\r\n    res.data;\r\n<\/pre>\n<p>The following functions, <em>extractGlobalQuote<\/em>, <em>transformGlobalQuote<\/em>, <em>removePercent, addTimestamp<\/em> and <em>addCompanyName<\/em>, are used to manipulate the API response data and convert it into a <em>GlobalQuote<\/em> object which we can use with our Adaptive Card.<\/p>\n<pre class=\"prettyprint\">const extractGlobalQuote =\r\n  (data: object): GlobalQuote =&gt;\r\n    data['Global Quote'];\r\n\r\nconst transformGlobalQuote =\r\n  (quote: GlobalQuote): GlobalQuote =&gt; ({\r\n    symbol: Object.values(quote)[0],\r\n    open: Number.parseFloat(Object.values(quote)[1]),\r\n    high: Number.parseFloat(Object.values(quote)[2]),\r\n    low: Number.parseFloat(Object.values(quote)[3]),\r\n    price: Number.parseFloat(Object.values(quote)[4]),\r\n    volume: Number.parseFloat(Object.values(quote)[5]),\r\n    latestTradingDay: Object.values(quote)[6],\r\n    previousClose: Number.parseFloat(Object.values(quote)[7]),\r\n    change: Number.parseFloat(Object.values(quote)[8]),\r\n    changePercent: Number.parseFloat(removePercent(Object.values(quote)[9]))\r\n  });\r\n\r\nconst removePercent =\r\n  (string: string): string =&gt;\r\n    string.replace('%', '');\r\n\r\nconst addTimestamp =\r\n  (quote: GlobalQuote) =&gt;\r\n    (timestamp: string): GlobalQuote =&gt; { return { ...quote, timestamp } }\r\n\r\nconst addCompanyName =\r\n  (quote: GlobalQuote) =&gt;\r\n    (name: string): GlobalQuote =&gt; { return { ...quote, name } }\r\n\r\n<\/pre>\n<p>The following function, <em>getInstallations<\/em>, is used to return an array of locations where the bot is installed.<\/p>\n<pre class=\"prettyprint\">const getInstallations =\r\n  (bot: ConversationBot): Promise&lt;TeamsBotInstallation[]&gt; =&gt;\r\n    bot.notification.installations();\r\n<\/pre>\n<p>The following function, <em>sendCard<\/em>, is used to send an Adaptive Card to a bot installation location, this is also where the card template and quote data is merged.<\/p>\n<pre class=\"prettyprint\">const sendCard =\r\n  &lt;T extends object&gt;(target: TeamsBotInstallation) =&gt;\r\n    (ac: typeof AdaptiveCards) =&gt;\r\n      (template: object) =&gt;\r\n        (quote: GlobalQuote): Promise&lt;any&gt; =&gt;\r\n          target.sendAdaptiveCard(ac.declare&lt;T&gt;(template).render(quote as T));\r\n<\/pre>\n<p>Finally, the following function, <em>handleError<\/em>, is used as a basic error handler, logging out any errors using the Azure Function context log.<\/p>\n<pre class=\"prettyprint\">const handleError =\r\n  (err: Error) =&gt;\r\n    (context: Context) =&gt;\r\n      context.log({ err });\r\n<\/pre>\n<p>Congratulations, you have successfully updated the bot to request data from an external API and render an Adaptive Card in the locations where the bot is installed! \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\ud83e\uddea Test your bot!<\/h2>\n<p>Now it is time to see our bot in action!<\/p>\n<p>Open the <em>Debug<\/em> panel from the Side Bar, select your browser of choice from the drop-down menu and start the bot by pressing the F5 key.<\/p>\n<p>Now, sit back, relax and watch the magic of Teams Toolkit in action \ud83e\ude84<\/p>\n<p>When you start the bot Teams Toolkit will perform several checks of your environment, ensuring that all the required dependencies are installed.<\/p>\n<ul>\n<li>Checks that a supported version of nodejs is installed.<\/li>\n<li>Checks Teams Toolkit has been authenticated with a Microsoft 365 tenant, that has Sideloading enabled.<\/li>\n<li>Checks that required npm packages are installed, if not these are installed automatically.<\/li>\n<li>Checks that Azure Functions Core Tools are installed, if not, this is installed automatically.<\/li>\n<li>Checks that ngrok is installed, if not, this is installed automatically.<\/li>\n<li>Checks that the required Ports are open.<\/li>\n<\/ul>\n<p>In addition to the above checks, Teams Toolkit is also registering a bot with Bot Framework and updating the app manifest for you.<\/p>\n<p>Once all the checks have been passed, Teams Toolkit will launch a browser session, you may be prompted to sign in with your account the first time you launch your bot, if so, complete the sign in flow.<\/p>\n<p>After signing into Microsoft 365, the web version of Microsoft Teams is opened and a prompt to sideload your app will be shown. Click the Add button to install the bot as a personal app.<\/p>\n<p>Wait a few seconds for your bot to be executed, sending a message into Microsoft Teams with the latest stock price.<\/p>\n<p>Congratulations, you have just created your first notification bot using Microsoft Teams Toolkit \ud83c\udf89<\/p>\n<p>&nbsp;<\/p>\n<h2>\u23ed What&#8217;s next?<\/h2>\n<p>Want to make more updates? Here are a few things you can try.<\/p>\n<ul>\n<li><strong>Use a different symbol<\/strong>, currently the stock price for MSFT is returned, pass a different symbol into the <em>getQuoteBySymbol<\/em> function and update the company name.<\/li>\n<li><strong>Change the timer frequency<\/strong>, currently the timer trigger is executed every 30 seconds, change this by updating the <em>schedule<\/em> property in the <em>json<\/em> file located in the <em>bot\/src\/notifyTimerTrigger<\/em> directory.<\/li>\n<li><strong>Update the app manifest<\/strong>, currently the app manifest contains mostly default values, use the <em>Edit manifest file<\/em> feature in Teams Toolkit to open the app manifest template and makes changes to it.<\/li>\n<li><strong>Update the app icons, <\/strong>currently the app uses stock icons, update the icons in the <em>templates\/appPackage\/resources<\/em> directory with new more relevant icons.<\/li>\n<li><strong>Disable the bot chat compose box<\/strong>, by default the bot chat compose box is enabled to disable this edit the app manifest and update the <em>isNotificationOnly<\/em> property to<\/li>\n<li><strong>Restrict supported Teams scopes<\/strong>, by default the bot can be installed into a personal, chat or channel context, to restrict where the bot can be used, update the scopes array in the app manifest.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>\ud83d\udcda Learn more!<\/h2>\n<ul>\n<li><a href=\"https:\/\/developer.microsoft.com\/microsoft-teams?WT.mc_id=m365-74415-garrytrinder\">Microsoft Teams Developer Center<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoftteams\/platform\/overview?WT.mc_id=m365-74415-garrytrinder\">Teams Toolkit Documentation<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/microsoftteams\/platform\/toolkit\/teams-toolkit-fundamentals?WT.mc_id=m365-74415-garrytrinder\">Teams Toolkit Overview<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/microsoftteams\/platform\/sbs-gs-notificationbot?WT.mc_id=m365-74415-garrytrinder\">Build notification bot with JavaScript<\/a><\/li>\n<\/ul>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how Microsoft Teams Toolkit helps simplify and accelerate your Microsoft Teams app development by building a bot that obtains data from an external API and sends a rich message using an Adaptive Card into Microsoft Teams.<\/p>\n","protected":false},"author":100385,"featured_media":11540,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[8,128],"tags":[23,55,44],"class_list":["post-11467","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-adaptive-cards","category-microsoft-teams","tag-teams-toolkit","tag-visual-studio","tag-visual-studio-code"],"acf":[],"blog_post_summary":"<p>Learn how Microsoft Teams Toolkit helps simplify and accelerate your Microsoft Teams app development by building a bot that obtains data from an external API and sends a rich message using an Adaptive Card into Microsoft Teams.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/11467","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/users\/100385"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/comments?post=11467"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/11467\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media\/11540"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media?parent=11467"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/categories?post=11467"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/tags?post=11467"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}