From da9adeffaec88473e22a23e189ce61f6172e7be3 Mon Sep 17 00:00:00 2001 From: "Yugal Pradhan (Tata Consultancy Services Limited)" Date: Mon, 22 Dec 2025 15:40:31 +0530 Subject: [PATCH 1/2] updated bot-suggested-actions csharp --- .github/workflows/build-complete-samples.yml | 4 +- .../csharp/M365Agent/.gitignore | 10 + .../csharp/M365Agent/M365Agent.atkproj | 9 + .../csharp/M365Agent/M365Agent.ttkproj | 14 - .../csharp/M365Agent/aad.manifest.json | 4 +- .../csharp/M365Agent/appPackage/color.png | Bin 3415 -> 5117 bytes .../csharp/M365Agent/appPackage/manifest.json | 10 +- .../csharp/M365Agent/appPackage/outline.png | Bin 407 -> 492 bytes .../csharp/M365Agent/env/.env.dev | 15 + .../csharp/M365Agent/env/.env.local | 14 +- .../csharp/M365Agent/infra/azure.bicep | 96 ++-- .../M365Agent/infra/azure.parameters.json | 35 +- .../infra/botRegistration/azurebot.bicep | 42 ++ .../M365Agent/infra/botRegistration/readme.md | 1 + .../csharp/M365Agent/launchSettings.json | 36 +- .../csharp/M365Agent/m365agents.local.yml | 86 ++-- .../csharp/M365Agent/m365agents.yml | 89 +++- .../bot-suggested-actions/csharp/README.md | 27 +- .../csharp/SuggestedActions.sln | 37 -- .../csharp/SuggestedActions.slnLaunch.user | 43 +- .../csharp/SuggestedActions.slnx | 6 + .../csharp/SuggestedActions/.gitignore | 6 +- .../AdapterWithErrorHandler.cs | 50 --- .../Bots/SuggestedActionsBot.cs | 174 -------- .../csharp/SuggestedActions/Config.cs | 14 + .../Controllers/BotController.cs | 35 -- .../Controllers/Controller.cs | 101 +++++ .../csharp/SuggestedActions/Program.cs | 61 +-- .../Properties/launchSettings.json | 19 +- .../csharp/SuggestedActions/Startup.cs | 57 --- .../SuggestedActions/SuggestedActions.csproj | 30 ++ .../SuggestedActionsBot.csproj | 23 - .../appsettings.Development.json | 25 +- .../appsettings.Playground.json | 18 + .../csharp/SuggestedActions/appsettings.json | 20 +- .../SuggestedActions/wwwroot/default.htm | 417 ------------------ 36 files changed, 613 insertions(+), 1015 deletions(-) create mode 100644 samples/bot-suggested-actions/csharp/M365Agent/.gitignore create mode 100644 samples/bot-suggested-actions/csharp/M365Agent/M365Agent.atkproj delete mode 100644 samples/bot-suggested-actions/csharp/M365Agent/M365Agent.ttkproj create mode 100644 samples/bot-suggested-actions/csharp/M365Agent/env/.env.dev create mode 100644 samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep create mode 100644 samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/readme.md delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions.sln create mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions.slnx delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/AdapterWithErrorHandler.cs delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/Bots/SuggestedActionsBot.cs create mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/Config.cs delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/BotController.cs create mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/Controller.cs delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/Startup.cs create mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActions.csproj delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActionsBot.csproj create mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Playground.json delete mode 100644 samples/bot-suggested-actions/csharp/SuggestedActions/wwwroot/default.htm diff --git a/.github/workflows/build-complete-samples.yml b/.github/workflows/build-complete-samples.yml index 9e357bec34..2357387e30 100644 --- a/.github/workflows/build-complete-samples.yml +++ b/.github/workflows/build-complete-samples.yml @@ -280,9 +280,9 @@ jobs: name: 'bot-message-reaction' version: '8.0.x' - - project_path: 'samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActionsBot.csproj' + - project_path: 'samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActions.csproj' name: 'bot-suggested-actions' - version: '6.0.x' + version: '10.0.x' - project_path: 'samples/bot-task-module/csharp/TeamsTaskModule.csproj' name: 'bot-task-module' diff --git a/samples/bot-suggested-actions/csharp/M365Agent/.gitignore b/samples/bot-suggested-actions/csharp/M365Agent/.gitignore new file mode 100644 index 0000000000..c5cae9258c --- /dev/null +++ b/samples/bot-suggested-actions/csharp/M365Agent/.gitignore @@ -0,0 +1,10 @@ +# TeamsFx files +build +appPackage/build +env/.env.*.user +env/.env.local +appsettings.Development.json +.deployment + +# User-specific files +*.user diff --git a/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.atkproj b/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.atkproj new file mode 100644 index 0000000000..124eb75046 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.atkproj @@ -0,0 +1,9 @@ + + + + b069b3bd-f6bc-cc40-82ab-3fcc2ea50fdf + + + + + \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.ttkproj b/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.ttkproj deleted file mode 100644 index 5a6f0c5d4b..0000000000 --- a/samples/bot-suggested-actions/csharp/M365Agent/M365Agent.ttkproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - 45c5b04b-e16f-4eeb-b567-8de2a5fef1ed - - - - - - - - - - \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/aad.manifest.json b/samples/bot-suggested-actions/csharp/M365Agent/aad.manifest.json index 6f1efa8862..030cd33b06 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/aad.manifest.json +++ b/samples/bot-suggested-actions/csharp/M365Agent/aad.manifest.json @@ -1,6 +1,6 @@ { - "id": "${{AAD_APP_OBJECT_ID}}", - "appId": "${{AAD_APP_CLIENT_ID}}", + "id": "${{BOT_OBJECT_ID}}", + "appId": "${{BOT_ID}}", "name": "bot-suggested-actions-aad", "accessTokenAcceptedVersion": 2, "signInAudience": "AzureADMyOrg", diff --git a/samples/bot-suggested-actions/csharp/M365Agent/appPackage/color.png b/samples/bot-suggested-actions/csharp/M365Agent/appPackage/color.png index b8cf81afbe2f5bafd8563920edfadb78b7b71be6..01aa37e347d0841d18728d51ee7519106f0ed81e 100644 GIT binary patch literal 5117 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 KMNUMnLSTZJKEgBr delta 330 zcmV-Q0k!_@1D6AkR(~u>L_t(|0qvDdQUWm$g&*odcOD@}z!QikP){J9K)pdcfqDY* z7_KOH?#8VvV8;$_0Iw?nil0dusws-9d{tj%7H=lW^h^gR6bc8Xp~yVZJLq_!f7k-e z0w!^Mr2Ry$MQ`a9jFAO++Erw`F>Ag6p`y>3tfM2^#2yyVjDHVznY=8rf)edF@Il82 zHJ0%vbB~nb2a0l>BF})H$LoKDH*rCK6ZgpEpY)p!Va#4iu`M2c5 zG!lN%OBmy551~7zN-@hh7}G)czyy!_1|^d}z!-=1U2SSm?U*xX)2>$k3Kw&x-B4(w zepeogQoLbNB11DA^1G_yz c3WZ~03R8+kzeh6v6951J07*qoM6N<$g2(8RBLDyZ diff --git a/samples/bot-suggested-actions/csharp/M365Agent/env/.env.dev b/samples/bot-suggested-actions/csharp/M365Agent/env/.env.dev new file mode 100644 index 0000000000..df4f9da508 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/M365Agent/env/.env.dev @@ -0,0 +1,15 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev + +# Updating AZURE_SUBSCRIPTION_ID or AZURE_RESOURCE_GROUP_NAME after provision may also require an update to RESOURCE_SUFFIX, because some services require a globally unique name across subscriptions/resource groups. +AZURE_SUBSCRIPTION_ID= +AZURE_RESOURCE_GROUP_NAME= +RESOURCE_SUFFIX= + +# Generated during provision, you can also add your own variables. +BOT_ID= +TEAMS_APP_ID= +BOT_AZURE_APP_SERVICE_RESOURCE_ID= \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/env/.env.local b/samples/bot-suggested-actions/csharp/M365Agent/env/.env.local index 76ee8a0ba6..5216a6cbb0 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/env/.env.local +++ b/samples/bot-suggested-actions/csharp/M365Agent/env/.env.local @@ -5,19 +5,11 @@ TEAMSFX_ENV=local APP_NAME_SUFFIX=local # Generated during provision, you can also add your own variables. -BOT_ID= + TEAMS_APP_ID= -RESOURCE_SUFFIX= -AZURE_SUBSCRIPTION_ID= -AZURE_RESOURCE_GROUP_NAME= -AAD_APP_CLIENT_ID= -AAD_APP_OBJECT_ID= -AAD_APP_TENANT_ID= -AAD_APP_OAUTH_AUTHORITY= -AAD_APP_OAUTH_AUTHORITY_HOST= TEAMS_APP_TENANT_ID= -MICROSOFT_APP_TYPE= -MICROSOFT_APP_TENANT_ID= +BOT_ID= +BOT_OBJECT_ID= TEAMSFX_M365_USER_NAME= BOT_ENDPOINT= diff --git a/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.bicep b/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.bicep index c3ce051b3d..658e412a21 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.bicep +++ b/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.bicep @@ -3,42 +3,84 @@ @description('Used to generate names for all resources in this file') param resourceBaseName string -@description('Required when create Azure Bot service') -param botAadAppClientId string - -param botAppDomain string +param webAppSKU string @maxLength(42) param botDisplayName string -param botServiceName string = resourceBaseName -param botServiceSku string = 'F0' -param microsoftAppType string -param microsoftAppTenantId string +param serverfarmsName string = resourceBaseName +param webAppName string = resourceBaseName +param identityName string = resourceBaseName +param location string = resourceGroup().location -// Register your web service as a bot with the Bot Framework -resource botService 'Microsoft.BotService/botServices@2021-03-01' = { - kind: 'azurebot' - location: 'global' - name: botServiceName - properties: { - displayName: botDisplayName - endpoint: 'https://${botAppDomain}/api/messages' - msaAppId: botAadAppClientId - msaAppType: microsoftAppType - msaAppTenantId: microsoftAppType == 'SingleTenant' ? microsoftAppTenantId : '' - } +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + location: location + name: identityName +} + +// Compute resources for your Web App +resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = { + kind: 'app' + location: location + name: serverfarmsName sku: { - name: botServiceSku + name: webAppSKU } } -// Connect the bot service to Microsoft Teams -resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { - parent: botService - location: 'global' - name: 'MsTeamsChannel' +// Web App that hosts your bot +resource webApp 'Microsoft.Web/sites@2021-02-01' = { + kind: 'app' + location: location + name: webAppName properties: { - channelName: 'MsTeamsChannel' + serverFarmId: serverfarm.id + httpsOnly: true + siteConfig: { + appSettings: [ + { + name: 'WEBSITE_RUN_FROM_PACKAGE' + value: '1' + } + { + name: 'Teams__ClientId' + value: identity.properties.clientId + } + { + name: 'Teams__TenantId' + value: identity.properties.tenantId + } + { + name: 'Teams__BotType' + value: 'UserAssignedMsi' + } + ] + ftpsState: 'FtpsOnly' + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${identity.id}': {} + } + } +} + +// Register your web service as a bot with the Bot Framework +module azureBotRegistration './botRegistration/azurebot.bicep' = { + name: 'Azure-Bot-registration' + params: { + resourceBaseName: resourceBaseName + identityClientId: identity.properties.clientId + identityResourceId: identity.id + identityTenantId: identity.properties.tenantId + botAppDomain: webApp.properties.defaultHostName + botDisplayName: botDisplayName } } + +// The output will be persisted in .env.{envName}. Visit https://aka.ms/teamsfx-actions/arm-deploy for more details. +output BOT_AZURE_APP_SERVICE_RESOURCE_ID string = webApp.id +output BOT_DOMAIN string = webApp.properties.defaultHostName +output BOT_ID string = identity.properties.clientId +output BOT_TENANT_ID string = identity.properties.tenantId diff --git a/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.parameters.json b/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.parameters.json index 0bf44f8d07..f78b989f5f 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.parameters.json +++ b/samples/bot-suggested-actions/csharp/M365Agent/infra/azure.parameters.json @@ -1,24 +1,15 @@ { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "resourceBaseName": { - "value": "bot${{RESOURCE_SUFFIX}}" - }, - "botAadAppClientId": { - "value": "${{AAD_APP_CLIENT_ID}}" - }, - "botAppDomain": { - "value": "${{BOT_DOMAIN}}" - }, - "botDisplayName": { - "value": "bot-suggested-actions" - }, - "microsoftAppType": { - "value": "${{MICROSOFT_APP_TYPE}}" - }, - "microsoftAppTenantId": { - "value": "${{MICROSOFT_APP_TENANT_ID}}" + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "bot${{RESOURCE_SUFFIX}}" + }, + "webAppSKU": { + "value": "B1" + }, + "botDisplayName": { + "value": "SuggestedActions" + } } - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep b/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep new file mode 100644 index 0000000000..a5a27b8fe4 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/azurebot.bicep @@ -0,0 +1,42 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@maxLength(42) +param botDisplayName string + +param botServiceName string = resourceBaseName +param botServiceSku string = 'F0' +param identityResourceId string +param identityClientId string +param identityTenantId string +param botAppDomain string + +// Register your web service as a bot with the Bot Framework +resource botService 'Microsoft.BotService/botServices@2021-03-01' = { + kind: 'azurebot' + location: 'global' + name: botServiceName + properties: { + displayName: botDisplayName + endpoint: 'https://${botAppDomain}/api/messages' + msaAppId: identityClientId + msaAppMSIResourceId: identityResourceId + msaAppTenantId:identityTenantId + msaAppType:'UserAssignedMSI' + } + sku: { + name: botServiceSku + } +} + +// Connect the bot service to Microsoft Teams +resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { + parent: botService + location: 'global' + name: 'MsTeamsChannel' + properties: { + channelName: 'MsTeamsChannel' + } +} diff --git a/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/readme.md b/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/readme.md new file mode 100644 index 0000000000..d5416243cd --- /dev/null +++ b/samples/bot-suggested-actions/csharp/M365Agent/infra/botRegistration/readme.md @@ -0,0 +1 @@ +The `azurebot.bicep` module is provided to help you create Azure Bot service when you don't use Azure to host your app. If you use Azure as infrastrcture for your app, `azure.bicep` under infra folder already leverages this module to create Azure Bot service for you. You don't need to deploy `azurebot.bicep` again. \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/launchSettings.json b/samples/bot-suggested-actions/csharp/M365Agent/launchSettings.json index d6491ef52c..2af8ce7a8a 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/launchSettings.json +++ b/samples/bot-suggested-actions/csharp/M365Agent/launchSettings.json @@ -1,15 +1,25 @@ { - "profiles": { - // Debug project within Teams - "Microsoft Teams (browser)": { - "commandName": "Project", - "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" - }, - // Launch project within Teams without prepare app dependencies - "Microsoft Teams (browser) (skip update app)": { - "commandName": "Project", - "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, - "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" - } - } + "profiles": { + // Launch project within Microsoft 365 Agents Playground + "Microsoft 365 Agents Playground (browser)": { + "commandName": "Project", + "environmentVariables": { + "UPDATE_TEAMS_APP": "false", + "M365_AGENTS_PLAYGROUND_TARGET_SDK": "teams-ai-v2-dotnet" + }, + "launchTestTool": true, + "launchUrl": "http://localhost:56150", + }, + // Launch project within Teams + "Microsoft Teams (browser)": { + "commandName": "Project", + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}", + }, + // Launch project within Teams without prepare app dependencies + "Microsoft Teams (browser) (skip update app)": { + "commandName": "Project", + "environmentVariables": { "UPDATE_TEAMS_APP": "false" }, + "launchUrl": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TEAMS_APP_TENANT_ID}}&login_hint=${{TEAMSFX_M365_USER_NAME}}" + }, + } } \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/M365Agent/m365agents.local.yml b/samples/bot-suggested-actions/csharp/M365Agent/m365agents.local.yml index d9a9a96afa..8b6ab2261a 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/m365agents.local.yml +++ b/samples/bot-suggested-actions/csharp/M365Agent/m365agents.local.yml @@ -1,79 +1,75 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.2 - -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-suggested-actions-csharp +version: v1.11 provision: - - uses: aadApp/create # Creates a new Azure Active Directory (AAD) app to authenticate users if the environment variable that stores clientId is empty - with: - name: bot-suggested-actions-aad # Note: when you run aadApp/update, the AAD app name will be updated based on the definition in manifest. If you don't want to change the name, make sure the name in AAD manifest is the same with the name defined here. - generateClientSecret: true # If the value is false, the action will not generate client secret for you - signInAudience: "AzureADMultipleOrgs" # Multitenant - writeToEnvironmentFile: # Write the information of created resources into environment file for the specified environment variable(s). - clientId: AAD_APP_CLIENT_ID - clientSecret: SECRET_AAD_APP_CLIENT_SECRET # Environment variable that starts with `SECRET_` will be stored to the .env.{envName}.user environment file - objectId: AAD_APP_OBJECT_ID - tenantId: AAD_APP_TENANT_ID - authority: AAD_APP_OAUTH_AUTHORITY - authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST - # Creates a Teams app - uses: teamsApp/create with: # Teams app name - name: bot-suggested-actions${{APP_NAME_SUFFIX}} + name: SuggestedActions${{APP_NAME_SUFFIX}} # Write the information of created resources into environment file for # the specified environment variable(s). - writeToEnvironmentFile: + writeToEnvironmentFile: teamsAppId: TEAMS_APP_ID - - uses: script + # Create or reuse an existing Microsoft Entra application for bot. + - uses: aadApp/create + with: + # The Microsoft Entra application's display name + name: SuggestedActions${{APP_NAME_SUFFIX}} + generateClientSecret: true + generateServicePrincipal: true + signInAudience: AzureADMultipleOrgs + writeToEnvironmentFile: + # The Microsoft Entra application's client id created for bot. + clientId: BOT_ID + # The Microsoft Entra application's client secret created for bot. + clientSecret: SECRET_BOT_PASSWORD + # The Microsoft Entra application's object id created for bot. + objectId: BOT_OBJECT_ID + + # Update the AAD app using the manifest file + - uses: aadApp/update with: - run: - echo "::set-teamsfx-env MICROSOFT_APP_TYPE=SingleTenant"; - echo "::set-teamsfx-env MICROSOFT_APP_TENANT_ID=${{AAD_APP_TENANT_ID}}"; + # Relative path to the AAD app manifest file + manifestPath: ./aad.manifest.json + # Output the final AAD manifest to env folder + outputFilePath: ./env/aad.manifest.${{TEAMSFX_ENV}}.json # Generate runtime appsettings to JSON file - uses: file/createOrUpdateJsonFile with: - target: ../SuggestedActions/appsettings.json + target: ../SuggestedActions/appsettings.Development.json content: - MicrosoftAppId: ${{AAD_APP_CLIENT_ID}} - MicrosoftAppPassword: ${{SECRET_AAD_APP_CLIENT_SECRET}} - MicrosoftAppType: ${{MICROSOFT_APP_TYPE}} - MicrosoftAppTenantId: ${{MICROSOFT_APP_TENANT_ID}} + Teams: + ClientId: ${{BOT_ID}} + ClientSecret: ${{SECRET_BOT_PASSWORD}} + TenantId: ${{TEAMS_APP_TENANT_ID}} - - uses: arm/deploy # Deploy given ARM templates parallelly. + # Create or update the bot registration on dev.botframework.com + - uses: botFramework/create with: - subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} # The AZURE_SUBSCRIPTION_ID is a built-in environment variable. TeamsFx will ask you select one subscription if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select subscription if it's empty in this case. - resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} # The AZURE_RESOURCE_GROUP_NAME is a built-in environment variable. TeamsFx will ask you to select or create one resource group if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select or create resource grouop if it's empty in this case. - templates: - - path: ./infra/azure.bicep - parameters: ./infra/azure.parameters.json - deploymentName: Create-resources-for-bot - bicepCliVersion: v0.9.1 # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, will use bicep CLI in PATH if you remove this config. - - - uses: aadApp/update # Apply the AAD manifest to an existing AAD app. Will use the object id in manifest file to determine which AAD app to update. - with: - manifestPath: ./aad.manifest.json # Relative path to teamsfx folder. Environment variables in manifest will be replaced before apply to AAD app - outputFilePath: ./build/aad.manifest.${{TEAMSFX_ENV}}.json + botId: ${{BOT_ID}} + name: SuggestedActions + messagingEndpoint: ${{BOT_ENDPOINT}}/api/messages + description: "" + channels: + - name: msteams # Validate using manifest schema - uses: teamsApp/validateManifest with: # Path to manifest template manifestPath: ./appPackage/manifest.json - # Build Teams app package with latest env value - uses: teamsApp/zipAppPackage with: # Path to manifest template manifestPath: ./appPackage/manifest.json outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip - outputJsonPath: ./appPackage/build/manifest.${{TEAMSFX_ENV}}.json + outputFolder: ./appPackage/build # Validate app package using validation rules - uses: teamsApp/validateAppPackage with: @@ -86,4 +82,4 @@ provision: - uses: teamsApp/update with: # Relative path to this file. This is the path for built zip file. - appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip \ No newline at end of file + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip diff --git a/samples/bot-suggested-actions/csharp/M365Agent/m365agents.yml b/samples/bot-suggested-actions/csharp/M365Agent/m365agents.yml index ffd6fb1753..62e1a8cc1d 100644 --- a/samples/bot-suggested-actions/csharp/M365Agent/m365agents.yml +++ b/samples/bot-suggested-actions/csharp/M365Agent/m365agents.yml @@ -1,9 +1,88 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.9/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.2 +version: v1.9 -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-suggested-actions-csharp +environmentFolderPath: ./env -environmentFolderPath: ./env \ No newline at end of file +# Triggered when 'teamsapp provision' is executed +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: SuggestedActions${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + # AZURE_SUBSCRIPTION_ID is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select a subscription. + # Referencing other environment variables with empty values + # will skip the subscription selection prompt. + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} + # AZURE_RESOURCE_GROUP_NAME is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select or create one + # resource group. + # Referencing other environment variables with empty values + # will skip the resource group selection prompt. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} + templates: + - path: ./infra/azure.bicep # Relative path to this file + # Relative path to this yaml file. + # Placeholders will be replaced with corresponding environment + # variable before ARM deployment. + parameters: ./infra/azure.parameters.json + # Required when deploying ARM template + deploymentName: Create-resources-for-bot + # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, + # will use bicep CLI in PATH if you remove this config. + bicepCliVersion: v0.9.1 + + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + # Build Teams app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the Teams app manifest to an existing Teams app in + # Developer Portal. + # Will use the app id in manifest file to determine which Teams app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + +# Triggered when 'teamsapp deploy' is executed +deploy: + - uses: cli/runDotnetCommand + with: + args: publish --configuration Release --runtime win-x86 --self-contained + SuggestedActions.csproj + workingDirectory: ../SuggestedActions + # Deploy your application to Azure App Service using the zip deploy feature. + # For additional details, refer to https://aka.ms/zip-deploy-to-app-services. + - uses: azureAppService/zipDeploy + with: + # Deploy base folder + artifactFolder: bin/Release/net10.0/win-x86/publish + # The resource id of the cloud resource to be deployed to. + # This key will be generated by arm/deploy action automatically. + # You can replace it with your existing Azure Resource id + # or add it to your environment variable file. + resourceId: ${{BOT_AZURE_APP_SERVICE_RESOURCE_ID}} + workingDirectory: ../SuggestedActions diff --git a/samples/bot-suggested-actions/csharp/README.md b/samples/bot-suggested-actions/csharp/README.md index b527232c0a..c9a6d9f9e6 100644 --- a/samples/bot-suggested-actions/csharp/README.md +++ b/samples/bot-suggested-actions/csharp/README.md @@ -62,7 +62,7 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool * Choose the **supported account types** (any account type will work) * Leave **Redirect URI** empty. * Choose **Register**. - 3. On the overview page, copy and save the **Application (client) ID, Directory (tenant) ID**. You'll need those later when updating your Teams application manifest and in the appsettings.json. + 3. On the overview page, copy and save the **Application (client) ID, Directory (tenant) ID**. You'll need those later when updating your Teams application manifest and in the appsettings.Development.json. 4. Navigate to **API Permissions**, and make sure to add the follow permissions: Select Add a permission * Select Add a permission @@ -78,17 +78,18 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool > NOTE: When you create your app registration, you will create an App ID and App password - make sure you keep these for later. -3. Setup NGROK - - Run ngrok - point to port 3978 +3. Setup dev tunnel - point to port 3978 + + Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: ```bash - ngrok http 3978 --host-header="localhost:3978" - ``` + devtunnel host -p 3978 --allow-anonymous + ``` - Alternatively, you can also use the `dev tunnels`. Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below: + Alternatively, you can also use ngrok: ```bash - devtunnel host -p 3978 --allow-anonymous + ngrok http 3978 --host-header="localhost:3978" ``` 4. Setup for code @@ -97,9 +98,10 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool ```bash git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git ``` - - Modify the `/appsettings.json` and fill in the following details: - - `{{MicrosoftAppId}}` - Generated from Step 1 is the application app id - - `{{MicrosoftAppPassword}}` - Generated from Step 1, also referred to as Client secret + - Modify the `SuggestedActions/appsettings.Development.json` and fill in the following details: + - `{{Teams:ClientId}}` - Generated from Step 1.3, the Application (client) ID + - `{{Teams:ClientSecret}}` - Generated from Step 2, also referred to as Client secret/App password + - `{{Teams:TenantId}}` - Generated from Step 1.3, the Directory (tenant) ID - In a terminal, navigate to `samples/bot-suggested-actions/csharp` @@ -122,7 +124,7 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool 5. Setup Manifest for Teams - __*This step is specific to Teams.*__ - - **Edit** the `manifest.json` contained in the ./appPackage folder to replace your Microsoft App Id (that was created when you registered your app registration earlier) *everywhere* you see the place holder string `{{Microsoft-App-Id}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Edit** the `manifest.json` contained in the ./appPackage folder to replace your Microsoft App Id (that was created when you registered your app registration earlier) *everywhere* you see the place holder string `{{BOT_ID}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) - **Edit** the `manifest.json` for `validDomains` and replace `{{domain-name}}` with base Url of your domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your domain-name will be `1234.ngrok-free.app` and if you are using dev tunnels then your domain will be like: `12345.devtunnels.ms`. - **Zip** up the contents of the `appPackage` folder to create a `manifest.zip` (Make sure that zip file does not contains any subfolder otherwise you will get error while uploading your .zip package) @@ -132,7 +134,6 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool - Go to your project directory, the ./appPackage folder, select the zip folder, and choose Open. - Select Add in the pop-up dialog box. Your app is uploaded to Teams. -**Note**: If you are facing any issue in your app, please uncomment [this](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-suggested-actions/csharp/SuggestedActions/AdapterWithErrorHandler.cs#L28) line and put your debugger for local debug. ## Running the sample @@ -164,7 +165,7 @@ To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](htt ## Further reading - [Send suggested actions](https://learn.microsoft.com/microsoftteams/platform/bots/how-to/conversations/conversation-messages?tabs=dotnet#send-suggested-actions) -- [Bot Framework Documentation](https://docs.botframework.com) +- [Teams Bot Service Documentation](https://docs.botframework.com) - [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) - [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) - [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions.sln b/samples/bot-suggested-actions/csharp/SuggestedActions.sln deleted file mode 100644 index 8442b7cb6f..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions.sln +++ /dev/null @@ -1,37 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.34814.14 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuggestedActionsBot", "SuggestedActions\SuggestedActionsBot.csproj", "{BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}" -EndProject -Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "M365Agent", "M365Agent\M365Agent.ttkproj", "{45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9A2118D2-216C-448F-AC6C-62C33F0537AB}" - ProjectSection(SolutionItems) = preProject - SuggestedActions.slnLaunch.user = SuggestedActions.slnLaunch.user - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BFDF6B3D-BB62-42DD-88EF-528FFE9FCBAD}.Release|Any CPU.Build.0 = Release|Any CPU - {45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {45C5B04B-E16F-4EEB-B567-8DE2A5FEF1ED}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {8F7F570D-74C3-4CF3-AACD-361A3D3F8E37} - EndGlobalSection -EndGlobal diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions.slnLaunch.user b/samples/bot-suggested-actions/csharp/SuggestedActions.slnLaunch.user index 9ac6c6961a..f52c3c0861 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions.slnLaunch.user +++ b/samples/bot-suggested-actions/csharp/SuggestedActions.slnLaunch.user @@ -1,31 +1,52 @@ [ { - "Name": "Microsoft Teams (browser)", + "Name": "Microsoft 365 Agents Playground (browser)", "Projects": [ { - "Path": "SuggestedActions\\SuggestedActionsBot.csproj", - "Action": "Start", - "DebugTarget": "Start Project" + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft 365 Agents Playground (browser)" }, { - "Path": "M365Agent\\M365Agent.ttkproj", - "Action": "StartWithoutDebugging", - "DebugTarget": "Microsoft Teams (browser)" + "Path": "SuggestedActions\\SuggestedActions.csproj", + "Name": "SuggestedActions\\SuggestedActions.csproj", + "Action": "Start", + "DebugTarget": "Microsoft 365 Agents Playground" } ] }, { - "Name": "Microsoft Teams (browser) (skip update app)", + "Name": "Microsoft Teams (browser)", "Projects": [ { - "Path": "SuggestedActions\\SuggestedActionsBot.csproj", + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", + "Action": "StartWithoutDebugging", + "DebugTarget": "Microsoft Teams (browser)" + }, + { + "Path": "SuggestedActions\\SuggestedActions.csproj", + "Name": "SuggestedActions\\SuggestedActions.csproj", "Action": "Start", "DebugTarget": "Start Project" - }, + } + ] + }, + { + "Name": "Microsoft Teams (browser) (skip update app)", + "Projects": [ { - "Path": "M365Agent\\M365Agent.ttkproj", + "Path": "M365Agent\\M365Agent.atkproj", + "Name": "M365Agent\\M365Agent.atkproj", "Action": "StartWithoutDebugging", "DebugTarget": "Microsoft Teams (browser) (skip update app)" + }, + { + "Path": "SuggestedActions\\SuggestedActions.csproj", + "Name": "SuggestedActions\\SuggestedActions.csproj", + "Action": "Start", + "DebugTarget": "Start Project" } ] } diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions.slnx b/samples/bot-suggested-actions/csharp/SuggestedActions.slnx new file mode 100644 index 0000000000..157c82c46c --- /dev/null +++ b/samples/bot-suggested-actions/csharp/SuggestedActions.slnx @@ -0,0 +1,6 @@ + + + + + + diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/.gitignore b/samples/bot-suggested-actions/csharp/SuggestedActions/.gitignore index 7466c01f9c..89b80db704 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/.gitignore +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/.gitignore @@ -22,4 +22,8 @@ bld/ [Ll]og/ # Notification local store -.notification.localstore.json \ No newline at end of file +.notification.localstore.json +.notification.playgroundstore.json + +# devTools +devTools/ \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/AdapterWithErrorHandler.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/AdapterWithErrorHandler.cs deleted file mode 100644 index 95bb27d622..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/AdapterWithErrorHandler.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Bot.Builder.TraceExtensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Microsoft.BotBuilderSamples -{ - public class AdapterWithErrorHandler : CloudAdapter - { - public AdapterWithErrorHandler(IConfiguration configuration, IHttpClientFactory httpClientFactory, ILogger logger, ConversationState conversationState = default) - : base(configuration, httpClientFactory, logger) - { - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - // NOTE: In production environment, you should consider logging this to - // Azure Application Insights. Visit https://aka.ms/bottelemetry to see how - // to add telemetry capture to your bot. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); - - // Uncomment below commented line for local debugging. - // await turnContext.SendActivityAsync($"Sorry, it looks like something went wrong. Exception Caught: {exception.Message}"); - - if (conversationState != null) - { - try - { - // Delete the conversationState for the current conversation to prevent the - // bot from getting stuck in a error-loop caused by being in a bad state. - // ConversationState should be thought of as similar to "cookie-state" in a Web pages. - await conversationState.DeleteAsync(turnContext); - } - catch (Exception e) - { - logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}"); - } - } - - // Send a trace activity, which will be displayed in the Bot Framework Emulator - await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); - }; - } - } -} diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Bots/SuggestedActionsBot.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Bots/SuggestedActionsBot.cs deleted file mode 100644 index 5c3564e772..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/Bots/SuggestedActionsBot.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.BotBuilderSamples -{ - /// - /// This bot will respond to the user's input with suggested actions. - /// Suggested actions enable your bot to present buttons that the user - /// can tap to provide input. - /// - public class SuggestedActionsBot : ActivityHandler - { - private readonly IConfiguration _configuration; - - public SuggestedActionsBot(IConfiguration config) - { - _configuration = config; - } - - /// - /// Invoked when members are added to the conversation. - /// Sends a welcome message to the user and tells them what actions they may perform to use this bot. - /// - /// List of members added to the conversation. - /// Context object containing information for a single turn of conversation with a user. - /// Cancellation token. - protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) - { - await SendWelcomeMessageAsync(turnContext, cancellationToken); - } - - /// - /// Invoked when a message activity is received from the user. - /// Processes the user's input and responds with the appropriate message and suggested actions. - /// - /// Context object containing information for a single turn of conversation with a user. - /// Cancellation token. - protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - var text = turnContext.Activity.Text.ToLowerInvariant(); - var responseText = ProcessInput(text); - - await turnContext.SendActivityAsync(responseText, cancellationToken: cancellationToken); - await SendSuggestedActionsAsync(turnContext, cancellationToken); - } - - /// - /// Sends a welcome message to the user and tells them what actions they may perform to use this bot. - /// - /// Context object containing information for a single turn of conversation with a user. - /// Cancellation token. - private async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - foreach (var member in turnContext.Activity.MembersAdded) - { - if (member.Id != turnContext.Activity.Recipient.Id) - { - await turnContext.SendActivityAsync( - "Welcome to the suggested actions bot. This bot will introduce you to suggested actions. Please answer the question:", - cancellationToken: cancellationToken); - await SendSuggestedActionsAsync(turnContext, cancellationToken); - } - } - } - - /// - /// Processes the user's input and returns the appropriate response text. - /// - /// The user's input text. - /// The response text based on the user's input. - private static string ProcessInput(string text) - { - const string colorText = "How can I assist you today?"; - return text switch - { - "hello" => $"Hello, {colorText}", - "welcome" => $"Welcome, {colorText}", - _ => "Please select one action", - }; - } - - /// - /// Creates and sends an activity with suggested actions to the user. - /// When the user clicks one of the buttons, the text value from the "CardAction" will be - /// displayed in the channel just as if the user entered the text. - /// - /// Context object containing information for a single turn of conversation with a user. - /// Cancellation token. - private async Task SendSuggestedActionsAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - var reply = MessageFactory.Text("Choose one of the action from the suggested action?"); - - var payload = new - { - type = "Teams.chatMessage", - data = new - { - body = new - { - additionalData = new { }, - backingStore = new - { - returnOnlyChangedValues = false, - initializationCompleted = true - }, - content = "SuggestedActionsBot" - }, - mentions = new[] - { - new - { - additionalData = new { }, - backingStore = new - { - returnOnlyChangedValues = false, - initializationCompleted = false - }, - id = 0, - mentioned = new - { - additionalData = new { }, - backingStore = new - { - returnOnlyChangedValues = false, - initializationCompleted = false - }, - odataType = "#microsoft.graph.chatMessageMentionedIdentitySet", - user = new - { - additionalData = new { }, - backingStore = new - { - returnOnlyChangedValues = false, - initializationCompleted = false - }, - displayName = "Suggested Actions Bot", - id = "28:" + _configuration["MicrosoftAppId"], - } - }, - mentionText = "Suggested Actions Bot" - } - }, - additionalData = new { }, - backingStore = new - { - returnOnlyChangedValues = false, - initializationCompleted = true - } - } - }; - - reply.SuggestedActions = new SuggestedActions() - { - Actions = new List() - { - new CardAction() { Title = "Hello", Type = ActionTypes.ImBack, Value = "Hello" }, - new CardAction() { Title = "Welcome", Type = ActionTypes.ImBack, Value = "Welcome" }, - new CardAction() { Title = "@SuggestedActionsBot", Type = "Action.Compose", Value = payload } - } - }; - - await turnContext.SendActivityAsync(reply, cancellationToken); - } - } -} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Config.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Config.cs new file mode 100644 index 0000000000..93dd16a38f --- /dev/null +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/Config.cs @@ -0,0 +1,14 @@ +namespace SuggestedActions +{ + public class ConfigOptions + { + public TeamsConfigOptions Teams { get; set; } + } + + public class TeamsConfigOptions + { + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public string TenantId { get; set; } + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/BotController.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/BotController.cs deleted file mode 100644 index fa4ea81324..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/BotController.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; - -namespace Microsoft.BotBuilderSamples -{ - // This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot - // implementation at runtime. Multiple different IBot implementations running at different endpoints can be - // achieved by specifying a more specific type for the bot constructor argument. - [Route("api/messages")] - [ApiController] - public class BotController : ControllerBase - { - private readonly IBotFrameworkHttpAdapter _adapter; - private readonly IBot _bot; - - public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } - - [HttpPost] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _adapter.ProcessAsync(Request, Response, _bot); - } - } -} diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/Controller.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/Controller.cs new file mode 100644 index 0000000000..6305552205 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/Controllers/Controller.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Teams.Api.Activities; +using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Activities; +using Microsoft.Teams.Apps.Annotations; +using Microsoft.Extensions.Configuration; +using Microsoft.Teams.Api; +using Microsoft.Teams.Api.Cards; + +namespace SuggestedActions.Controllers +{ + [TeamsController] + public class Controller + { + private readonly IConfiguration _configuration; + + public Controller(IConfiguration configuration) + { + _configuration = configuration; + } + + [Message] + public async Task OnMessage([Context] MessageActivity activity, [Context] IContext.Client client, [Context] Microsoft.Teams.Common.Logging.ILogger log) + { + log.Info("Message received"); + + var text = activity.Text?.ToLowerInvariant() ?? string.Empty; + var responseText = ProcessInput(text); + + await client.Typing(); + await client.Send(responseText); + await SendSuggestedActionsCardAsync(client, log); + } + + [Microsoft.Teams.Apps.Activities.Conversation.MembersAdded] + public async Task OnMembersAdded([Context] ConversationUpdateActivity activity, [Context] IContext.Client client, [Context] Microsoft.Teams.Common.Logging.ILogger log) + { + foreach (var member in activity.MembersAdded) + { + if (member.Id != activity.Recipient.Id) + { + await client.Send("Welcome to the suggested actions bot. This bot will introduce you to suggested actions. Please answer the question:"); + await SendSuggestedActionsCardAsync(client, log); + } + } + } + + /// + /// Processes the user's input and returns the appropriate response text. + /// + /// The user's input text. + /// The response text based on the user's input. + private static string ProcessInput(string text) + { + const string colorText = "How can I assist you today?"; + return text switch + { + "hello" => $"Hello, {colorText}", + "welcome" => $"Welcome, {colorText}", + _ => "Please select one action", + }; + } + + /// + /// Creates and sends a message with suggested actions to the user. + /// Uses native Teams SDK SuggestedActions with IMBack action type. + /// When the user clicks a button, the text value will be displayed in the channel as if the user typed it, + /// and will automatically trigger the OnMessage handler. + /// + private async Task SendSuggestedActionsCardAsync(IContext.Client client, Microsoft.Teams.Common.Logging.ILogger? log) + { + log?.Info("Sending suggested actions"); + + // Create MessageActivity with SuggestedActions using native Teams SDK + var message = new MessageActivity() + { + Text = "Choose one of the action from the suggested action?", + SuggestedActions = new Microsoft.Teams.Api.SuggestedActions() + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "Hello", + Value = "Hello" + }) + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "Welcome", + Value = "Welcome" + }) + .AddAction(new Microsoft.Teams.Api.Cards.Action(ActionType.IMBack) + { + Title = "@SuggestedActionsBot", + Value = "@SuggestedActionsBot" + }) + }; + + await client.Send(message); + } + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Program.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Program.cs index 8c73eec6fc..e87b4bf433 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/Program.cs +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/Program.cs @@ -1,41 +1,28 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using SuggestedActions; +using SuggestedActions.Controllers; +using Azure.Core; +using Azure.Identity; +using Microsoft.Teams.Api.Auth; +using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Extensions; +using Microsoft.Teams.Common.Http; +using Microsoft.Teams.Plugins.AspNetCore.Extensions; -namespace Microsoft.BotBuilderSamples -{ - /// - /// The main entry point for the application. - /// - public class Program - { - /// - /// The main method which is the entry point of the application. - /// - /// The command-line arguments. - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +// Create web application builder and load configuration +var builder = WebApplication.CreateBuilder(args); +var config = builder.Configuration.Get(); - /// - /// Creates and configures the host builder. - /// - /// The command-line arguments. - /// An initialized instance. - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureLogging(logging => - { - logging.AddDebug(); - logging.AddConsole(); - }); - webBuilder.UseStartup(); - }); - } -} +// Create Teams app builder +var appBuilder = App.Builder(); + +// Register controller and configure Teams services +builder.Services.AddSingleton(); +builder.AddTeams(appBuilder); + +// Build and run the application +var app = builder.Build(); +app.UseTeams(); +app.Run(); \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Properties/launchSettings.json b/samples/bot-suggested-actions/csharp/SuggestedActions/Properties/launchSettings.json index ff9d8fe153..921e10d3e8 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/Properties/launchSettings.json +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/Properties/launchSettings.json @@ -1,13 +1,26 @@ -{ +{ "profiles": { + // Debug project within Microsoft 365 Agents Playground + "Microsoft 365 Agents Playground": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:3978", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Playground", + "TEAMSFX_NOTIFICATION_STORE_FILENAME": ".notification.playgroundstore.json", + "UPDATE_TEAMS_APP": "false" + }, + "hotReloadProfile": "aspnetcore" + }, + // Debug project within Teams "Start Project": { "commandName": "Project", "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7130;http://localhost:5130", + "applicationUrl": "http://localhost:3978", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "hotReloadProfile": "aspnetcore" - } + }, } } \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/Startup.cs b/samples/bot-suggested-actions/csharp/SuggestedActions/Startup.cs deleted file mode 100644 index d2da0c62b3..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/Startup.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.BotBuilderSamples -{ - /// - /// Configures services and the app's request pipeline. - /// - public class Startup - { - /// - /// Adds services to the container. - /// - /// The service collection to add services to. - public void ConfigureServices(IServiceCollection services) - { - services.AddHttpClient() - .AddControllers() - .AddNewtonsoftJson(); - - // Create the Bot Framework Adapter with error handling enabled. - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); - } - - /// - /// Configures the HTTP request pipeline. - /// - /// The application builder. - /// The web host environment. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseDefaultFiles() - .UseStaticFiles() - .UseRouting() - .UseAuthorization() - .UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActions.csproj b/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActions.csproj new file mode 100644 index 0000000000..81958d0257 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActions.csproj @@ -0,0 +1,30 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + + + PreserveNewest + None + + + + PreserveNewest + None + + + \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActionsBot.csproj b/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActionsBot.csproj deleted file mode 100644 index 0e4ef5d761..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/SuggestedActionsBot.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net6.0 - latest - - - - - - - - - - Always - - - - - - - - diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Development.json b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Development.json index e203e9407e..8dadbe2c24 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Development.json +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Development.json @@ -1,9 +1,18 @@ { - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "", + "TenantId": "" + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Playground.json b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Playground.json new file mode 100644 index 0000000000..5981a18df5 --- /dev/null +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.Playground.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "", + "TenantId": "" + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.json b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.json index 97dff81a1b..ed78418678 100644 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.json +++ b/samples/bot-suggested-actions/csharp/SuggestedActions/appsettings.json @@ -1,4 +1,18 @@ { - "MicrosoftAppId": "", - "MicrosoftAppPassword": "" -} + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "Microsoft.Teams": { + "Enable": "*", + "Level": "debug" + } + }, + "AllowedHosts": "*", + "Teams": { + "ClientId": "", + "ClientSecret": "", + "TenantId": "" + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/csharp/SuggestedActions/wwwroot/default.htm b/samples/bot-suggested-actions/csharp/SuggestedActions/wwwroot/default.htm deleted file mode 100644 index 88c7538a0b..0000000000 --- a/samples/bot-suggested-actions/csharp/SuggestedActions/wwwroot/default.htm +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - - Suggested Actions Sample - - - - - -
-
-
-
Suggested Actions Sample
-
-
-
-
-
Your bot is ready!
-
You can test your bot in the Bot Framework Emulator
- by connecting to http://localhost:3978/api/messages.
- -
Visit Azure - Bot Service to register your bot and add it to
- various channels. The bot's endpoint URL typically looks - like this:
-
https://your_bots_hostname/api/messages
-
-
-
-
- -
- - From c11bf67826a3f02c7c9de6fbbb4aaed411201674 Mon Sep 17 00:00:00 2001 From: "Yugal Pradhan (Tata Consultancy Services Limited)" Date: Mon, 29 Dec 2025 18:10:18 +0530 Subject: [PATCH 2/2] updated bot-suggested-actions python --- .../bot-suggested-actions/python/.gitignore | 13 +- .../python/.vscode/extensions.json | 8 +- .../python/.vscode/launch.json | 196 ++++++++++++------ .../python/.vscode/settings.json | 12 +- .../python/.vscode/tasks.json | 144 ++++++++++++- .../python/.webappignore | 10 + .../bot-suggested-actions/python/README.md | 20 +- samples/bot-suggested-actions/python/app.py | 72 ------- .../python/appManifest/color.png | Bin 3415 -> 0 bytes .../python/appManifest/outline.png | Bin 407 -> 0 bytes .../python/appPackage/color.png | Bin 0 -> 5117 bytes .../{appManifest => appPackage}/manifest.json | 8 +- .../python/appPackage/outline.png | Bin 0 -> 492 bytes .../python/bots/Suggested_Actions_Bot.py | 117 ----------- .../python/bots/__init__.py | 3 - .../bot-suggested-actions/python/config.py | 17 -- .../bot-suggested-actions/python/env/.env.dev | 17 ++ .../python/env/.env.local | 20 +- .../python/env/.env.playground | 8 + .../python/infra/azure.bicep | 107 +++++++--- .../python/infra/azure.parameters.json | 35 ++-- .../infra/botRegistration/azurebot.bicep | 42 ++++ .../python/infra/botRegistration/readme.md | 1 + .../python/m365agents.local.yml | 100 +++++---- .../python/m365agents.playground.yml | 24 +++ .../python/m365agents.yml | 129 +++++++++++- .../python/requirements.txt | 2 - .../bot-suggested-actions/python/src/app.py | 94 +++++++++ .../python/src/config.py | 18 ++ .../python/src/requirements.txt | 3 + 30 files changed, 799 insertions(+), 421 deletions(-) create mode 100644 samples/bot-suggested-actions/python/.webappignore delete mode 100644 samples/bot-suggested-actions/python/app.py delete mode 100644 samples/bot-suggested-actions/python/appManifest/color.png delete mode 100644 samples/bot-suggested-actions/python/appManifest/outline.png create mode 100644 samples/bot-suggested-actions/python/appPackage/color.png rename samples/bot-suggested-actions/python/{appManifest => appPackage}/manifest.json (87%) create mode 100644 samples/bot-suggested-actions/python/appPackage/outline.png delete mode 100644 samples/bot-suggested-actions/python/bots/Suggested_Actions_Bot.py delete mode 100644 samples/bot-suggested-actions/python/bots/__init__.py delete mode 100644 samples/bot-suggested-actions/python/config.py create mode 100644 samples/bot-suggested-actions/python/env/.env.dev create mode 100644 samples/bot-suggested-actions/python/env/.env.playground create mode 100644 samples/bot-suggested-actions/python/infra/botRegistration/azurebot.bicep create mode 100644 samples/bot-suggested-actions/python/infra/botRegistration/readme.md create mode 100644 samples/bot-suggested-actions/python/m365agents.playground.yml delete mode 100644 samples/bot-suggested-actions/python/requirements.txt create mode 100644 samples/bot-suggested-actions/python/src/app.py create mode 100644 samples/bot-suggested-actions/python/src/config.py create mode 100644 samples/bot-suggested-actions/python/src/requirements.txt diff --git a/samples/bot-suggested-actions/python/.gitignore b/samples/bot-suggested-actions/python/.gitignore index e8442994dd..513a8cc211 100644 --- a/samples/bot-suggested-actions/python/.gitignore +++ b/samples/bot-suggested-actions/python/.gitignore @@ -1,14 +1,17 @@ # TeamsFx files env/.env.*.user env/.env.local -appManifest/build/ +.env +appPackage/build # python virtual environment .venv/ +__pycache__/ -# misc -.env +# others .deployment/ +node_modules/ +devTools/*.log -# tmp files -__pycache__/ \ No newline at end of file +# Dev tool directories +/devTools/ \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/.vscode/extensions.json b/samples/bot-suggested-actions/python/.vscode/extensions.json index bf8c33db9c..760a0b1d8f 100644 --- a/samples/bot-suggested-actions/python/.vscode/extensions.json +++ b/samples/bot-suggested-actions/python/.vscode/extensions.json @@ -1,6 +1,6 @@ { - "recommendations": [ - "TeamsDevApp.ms-teams-vscode-extension", - "ms-python.python", - ] + "recommendations": [ + "TeamsDevApp.ms-teams-vscode-extension", + "ms-python.python" + ] } \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/.vscode/launch.json b/samples/bot-suggested-actions/python/.vscode/launch.json index 6d66d8beb8..acebe10dcf 100644 --- a/samples/bot-suggested-actions/python/.vscode/launch.json +++ b/samples/bot-suggested-actions/python/.vscode/launch.json @@ -1,69 +1,133 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Launch App (Edge)", - "type": "msedge", - "request": "launch", - "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", - "cascadeTerminateToConfigurations": [ - "Python: Run App Locally" - ], - "presentation": { - "group": "all", - "hidden": true - }, - "internalConsoleOptions": "neverOpen" + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Remote (Edge)", + "type": "msedge", + "request": "launch", + "url": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", + "presentation": { + "group": "1-Teams", + "order": 4 + }, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Launch Remote (Chrome)", + "type": "chrome", + "request": "launch", + "url": "https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", + "presentation": { + "group": "1-Teams", + "order": 5 + }, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Launch Remote (Desktop)", + "type": "node", + "request": "launch", + "preLaunchTask": "Start App in Desktop Client (Remote)", + "presentation": { + "group": "1-Teams", + "order": 6 + }, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Start App (Edge)", + "type": "msedge", + "request": "launch", + "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", + "presentation": { + "group": "all", + "hidden": true + }, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Start App (Chrome)", + "type": "chrome", + "request": "launch", + "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", + "presentation": { + "group": "all", + "hidden": true + }, + "internalConsoleOptions": "neverOpen" + }, + { + "name": "Start Python", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/src/app.py", + "cwd": "${workspaceFolder}/src", + "console": "integratedTerminal" + }, + { + "name": "Start Microsoft 365 Agents Playground", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/devTools/playground/node_modules/@microsoft/m365agentsplayground/cli.js", + "args": [ + "start" + ], + "env": { + "PATH": "${workspaceFolder}/devTools/nodejs;${env:PATH}" }, - { - "name": "Launch App (Chrome)", - "type": "chrome", - "request": "launch", - "url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}", - "cascadeTerminateToConfigurations": [ - "Python: Run App Locally" - ], - "presentation": { - "group": "all", - "hidden": true - }, - "internalConsoleOptions": "neverOpen" + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ], + "compounds": [ + { + "name": "Debug in Teams (Edge)", + "configurations": ["Start App (Edge)", "Start Python"], + "cascadeTerminateToConfigurations": ["Start Python"], + "preLaunchTask": "Start App Locally", + "presentation": { + "group": "1-Teams", + "order": 1 + }, + "stopAll": true + }, + { + "name": "Debug in Teams (Chrome)", + "configurations": ["Start App (Chrome)", "Start Python"], + "cascadeTerminateToConfigurations": ["Start Python"], + "preLaunchTask": "Start App Locally", + "presentation": { + "group": "1-Teams", + "order": 2 + }, + "stopAll": true + }, + { + "name": "Debug in Teams (Desktop)", + "configurations": ["Start Python"], + "preLaunchTask": "Start App in Desktop Client", + "presentation": { + "group": "1-Teams", + "order": 3 + }, + "stopAll": true + }, + { + "name": "Debug in Microsoft 365 Agents Playground", + "configurations": [ + "Start Python", + "Start Microsoft 365 Agents Playground" + ], + "cascadeTerminateToConfigurations": [ + "Start Microsoft 365 Agents Playground" + ], + "preLaunchTask": "Deploy (Microsoft 365 Agents Playground)", + "presentation": { + "group": "0-TestTool", + "order": 1 }, - { - "name": "Python: Run App Locally", - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/app.py", - "cwd": "${workspaceFolder}", - "console": "integratedTerminal" - } - ], - "compounds": [ - { - "name": "Debug (Edge)", - "configurations": [ - "Launch App (Edge)", - "Python: Run App Locally" - ], - "preLaunchTask": "Prepare Teams App Resources", - "presentation": { - "group": "all", - "order": 1 - }, - "stopAll": true - }, - { - "name": "Debug (Chrome)", - "configurations": [ - "Launch App (Chrome)", - "Python: Run App Locally" - ], - "preLaunchTask": "Prepare Teams App Resources", - "presentation": { - "group": "all", - "order": 2 - }, - "stopAll": true - } - ] -} \ No newline at end of file + "stopAll": true + } + ] +} diff --git a/samples/bot-suggested-actions/python/.vscode/settings.json b/samples/bot-suggested-actions/python/.vscode/settings.json index 3014fd9cf0..0d3ba10b02 100644 --- a/samples/bot-suggested-actions/python/.vscode/settings.json +++ b/samples/bot-suggested-actions/python/.vscode/settings.json @@ -1,3 +1,11 @@ { - "debug.onTaskErrors": "abort" -} + "debug.onTaskErrors": "abort", + "json.schemas": [ + { + "fileMatch": [ + "/aad.*.json" + ], + "schema": {} + } + ] +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/.vscode/tasks.json b/samples/bot-suggested-actions/python/.vscode/tasks.json index 253c1ead76..778f7beb7d 100644 --- a/samples/bot-suggested-actions/python/.vscode/tasks.json +++ b/samples/bot-suggested-actions/python/.vscode/tasks.json @@ -5,7 +5,118 @@ "version": "2.0.0", "tasks": [ { - "label": "Prepare Teams App Resources", + "label": "Start App (Sandbox)", + "dependsOn": [ + "Validate prerequisites (Sandbox)", + "Start local tunnel (Sandbox)", + "Provision (Sandbox)", + "Deploy (Sandbox)", + "Sideload App to channel" + ], + "dependsOrder": "sequence" + }, + { + // Check all required prerequisites. + // See https://aka.ms/teamsfx-tasks/check-prerequisites to know the details and how to customize the args. + "label": "Validate prerequisites (Sandbox)", + "type": "teamsfx", + "command": "debug-check-prerequisites", + "args": { + "prerequisites": [ + "portOccupancy", + "sandbox", + "nodejs" + ], + "portOccupancy": [ + 3978 + ] + } + }, + { + // Start the local tunnel service to forward public URL to local port and inspect traffic. + // See https://aka.ms/teamsfx-tasks/local-tunnel for the detailed args definitions. + "label": "Start local tunnel (Sandbox)", + "type": "teamsfx", + "command": "debug-start-local-tunnel", + "args": { + "type": "dev-tunnel", + "ports": [ + { + "portNumber": 3978, + "protocol": "http", + "access": "public", + "writeToEnvironmentFile": { + "endpoint": "BOT_ENDPOINT", // output tunnel endpoint as BOT_ENDPOINT + "domain": "BOT_DOMAIN" // output tunnel domain as BOT_DOMAIN + } + } + ], + "env": "sandbox" + }, + "isBackground": true, + "problemMatcher": "$teamsfx-local-tunnel-watch" + }, + { + // Create the debug resources. + // See https://aka.ms/teamsfx-tasks/provision to know the details and how to customize the args. + "label": "Provision (Sandbox)", + "type": "teamsfx", + "command": "provision", + "args": { + "env": "sandbox" + } + }, + { + // Build the project. + // See https://aka.ms/teamsfx-tasks/deploy to know the details and how to customize the args. + "label": "Deploy (Sandbox)", + "type": "teamsfx", + "command": "deploy", + "args": { + "env": "sandbox" + } + }, + { + "label": "Sideload App to channel", + "type": "teamsfx", + "command": "install-app", + "args": { + "env": "sandbox", + "appPackagePath": "${workspaceFolder}/appPackage/build/appPackage.sandbox.zip" + } + }, + { + // Check all required prerequisites. + // See https://aka.ms/teamsfx-tasks/check-prerequisites to know the details and how to customize the args. + "label": "Validate prerequisites (Microsoft 365 Agents Playground)", + "type": "teamsfx", + "command": "debug-check-prerequisites", + "args": { + "prerequisites": [ + "nodejs", // Check if Node.js is installed and the version is >= 12. + "portOccupancy" // Validate available ports to ensure those debug ones are not occupied. + ], + "portOccupancy": [ + 3978, // app service port + 56150, // Microsoft 365 Agents Playground port + ] + } + }, + { + // Build project. + // See https://aka.ms/teamsfx-tasks/deploy to know the details and how to customize the args. + "label": "Deploy (Microsoft 365 Agents Playground)", + "dependsOn": [ + "Validate prerequisites (Microsoft 365 Agents Playground)" + ], + "type": "teamsfx", + "command": "deploy", + "args": { + "env": "playground", + } + }, + { + "label": "Start App Locally", "dependsOn": [ "Validate prerequisites", "Start local tunnel", @@ -22,11 +133,11 @@ "command": "debug-check-prerequisites", "args": { "prerequisites": [ - "m365Account", // Sign-in prompt for Microsoft 365 account, then validate if the account enables the uploading permission. + "m365Account", // Sign-in prompt for Microsoft 365 account, then validate if the account enables the sideloading permission. "portOccupancy" // Validate available ports to ensure those debug ones are not occupied. ], "portOccupancy": [ - 3978, // app service port + 3978 // app service port ] } }, @@ -73,6 +184,33 @@ "args": { "env": "local" } + }, + { + "label": "Start App in Desktop Client", + "dependsOn": [ + "Validate prerequisites", + "Start local tunnel", + "Provision", + "Deploy", + "Start desktop client" + ], + "dependsOrder": "sequence" + }, + { + "label": "Start desktop client", + "type": "teamsfx", + "command": "launch-desktop-client", + "args": { + "url": "teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true" + } + }, + { + "label": "Start App in Desktop Client (Remote)", + "type": "teamsfx", + "command": "launch-desktop-client", + "args": { + "url": "teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true" + } } ] } \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/.webappignore b/samples/bot-suggested-actions/python/.webappignore new file mode 100644 index 0000000000..a93f2c5f65 --- /dev/null +++ b/samples/bot-suggested-actions/python/.webappignore @@ -0,0 +1,10 @@ +.venv/ +.vscode/ +.env +env/ +__pycache__/ +README.md +m365agents.yml +m365agents.local.yml +m365agents.playground.yml +/devTools/ \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/README.md b/samples/bot-suggested-actions/python/README.md index 599f32985b..6d4ff2b8f0 100644 --- a/samples/bot-suggested-actions/python/README.md +++ b/samples/bot-suggested-actions/python/README.md @@ -46,7 +46,7 @@ The simplest way to run this sample in Teams is to use Microsoft 365 Agents Tool 1. Ensure you have downloaded and installed [Visual Studio Code](https://code.visualstudio.com/docs/setup/setup-overview) 1. Install the [Microsoft 365 Agents Toolkit extension](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) and [Python Extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) 1. Select **File > Open Folder** in VS Code and choose this samples directory from the repo -1. Press **CTRL+Shift+P** to open the command box and enter **Python: Create Environment** to create and activate your desired virtual environment. Remember to select `requirements.txt` as dependencies to install when creating the virtual environment. +1. Press **CTRL+Shift+P** to open the command box and enter **Python: Create Environment** to create and activate your desired virtual environment. Remember to select `src/requirements.txt` as dependencies to install when creating the virtual environment. 1. Using the extension, sign in with your Microsoft 365 account where you have permissions to upload custom apps 1. Select **Debug > Start Debugging** or **F5** to run the app in a Teams web client. 1. In the browser that launches, select the **Add** button to install the app to Teams. @@ -101,16 +101,22 @@ the Teams service needs to call into the bot. 5) Activate your desired virtual environment -6) Install dependencies by running ```pip install -r requirements.txt``` in the project folder. +6) Install dependencies by running ```pip install -r src/requirements.txt``` in the project folder. -7) Update the `config.py` configuration for the bot to use the Microsoft App Id and App Password from the Bot Framework registration. (Note the App Password is referred to as the "client secret" in the azure portal and you can always create a new client secret anytime.) +7) Create a `.env` file in the project root and add the following variables: + ``` + CLIENT_ID= + CLIENT_SECRET= + TENANT_ID= + ``` + Fill in the values from your Azure Bot registration (CLIENT_ID is the Application ID, CLIENT_SECRET is the client secret from the azure portal, and TENANT_ID is the Directory/tenant ID). 8) __*This step is specific to Teams.*__ - - **Edit** the `manifest.json` contained in the `appManifest` folder to replace your Microsoft App Id (that was created when you registered your bot earlier) *everywhere* you see the place holder string `${{AAD_APP_CLIENT_ID}}` and `${{TEAMS_APP_ID}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) - - **Zip** up the contents of the `appManifest` folder to create a `manifest.zip` + - **Edit** the `manifest.json` contained in the `appPackage` folder to replace your Microsoft App Id (that was created when you registered your bot earlier) *everywhere* you see the place holder string `${{BOT_ID}}` and `${{TEAMS_APP_ID}}` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Zip** up the contents of the `appPackage` folder to create a `manifest.zip` - **Upload** the `manifest.zip` to Teams (in the Apps view click "Upload a custom app") -9) Run your bot with `python app.py` +9) Run your bot with `python src/app.py` ## Running the sample @@ -143,7 +149,7 @@ the Teams service needs to call into the bot. ## Further reading - [Send suggested actions](https://learn.microsoft.com/microsoftteams/platform/bots/how-to/conversations/conversation-messages?tabs=dotnet#send-suggested-actions) -- [Bot Framework Documentation](https://docs.botframework.com) +- [Teams SDK Documentation](https://learn.microsoft.com/en-us/microsoftteams/platform/teams-ai-library/welcome) - [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) - [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) - [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) diff --git a/samples/bot-suggested-actions/python/app.py b/samples/bot-suggested-actions/python/app.py deleted file mode 100644 index ebca539075..0000000000 --- a/samples/bot-suggested-actions/python/app.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import sys -import traceback -import uuid -from datetime import datetime -from http import HTTPStatus - -from aiohttp import web -from aiohttp.web import Request, Response, json_response -from botbuilder.core import ( - TurnContext, -) -from botbuilder.integration.aiohttp import CloudAdapter, ConfigurationBotFrameworkAuthentication -from botbuilder.core.integration import aiohttp_error_middleware -from botbuilder.schema import Activity, ActivityTypes - -from bots import SuggestedActionsBot -from config import DefaultConfig - -CONFIG = DefaultConfig() - -# Create adapter. -# See https://aka.ms/about-bot-adapter to learn more about how bots work. -ADAPTER = CloudAdapter(ConfigurationBotFrameworkAuthentication(CONFIG)) - - -# Catch-all for errors. -async def on_error(context: TurnContext, error: Exception): - # This check writes out errors to console log .vs. app insights. - # NOTE: In production environment, you should consider logging this to Azure - # application insights. - print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) - traceback.print_exc() - - # Send a message to the user - await context.send_activity("The bot encountered an error or bug.") - await context.send_activity( - "To continue to run this bot, please fix the bot source code." - ) - # Send a trace activity if we're talking to the Bot Framework Emulator - if context.activity.channel_id == "emulator": - # Create a trace activity that contains the error object - trace_activity = Activity( - label="TurnError", - name="on_turn_error Trace", - timestamp=datetime.utcnow(), - type=ActivityTypes.trace, - value=f"{error}", - value_type="https://www.botframework.com/schemas/error", - ) - # Send a trace activity, which will be displayed in Bot Framework Emulator - await context.send_activity(trace_activity) - -ADAPTER.on_turn_error = on_error - -# Create the Bot -BOT = SuggestedActionsBot() - -# Listen for incoming requests on /api/messages. -async def messages(req: Request) -> Response: - return await ADAPTER.process(req, BOT) - -APP = web.Application(middlewares=[aiohttp_error_middleware]) -APP.router.add_post("/api/messages", messages) - -if __name__ == "__main__": - try: - web.run_app(APP, host="localhost", port=CONFIG.PORT) - except Exception as error: - raise error diff --git a/samples/bot-suggested-actions/python/appManifest/color.png b/samples/bot-suggested-actions/python/appManifest/color.png deleted file mode 100644 index b8cf81afbe2f5bafd8563920edfadb78b7b71be6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3415 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 Bp2+|J diff --git a/samples/bot-suggested-actions/python/appPackage/color.png b/samples/bot-suggested-actions/python/appPackage/color.png new file mode 100644 index 0000000000000000000000000000000000000000..01aa37e347d0841d18728d51ee7519106f0ed81e GIT binary patch literal 5117 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" - }, - "mentions": [ - { - "additionalData": {}, - "backingStore": { - "returnOnlyChangedValues": False, - "initializationCompleted": False - }, - "id": 0, - "mentioned": { - "additionalData": {}, - "backingStore": { - "returnOnlyChangedValues": False, - "initializationCompleted": False - }, - "odataType": "#microsoft.graph.chatMessageMentionedIdentitySet", - "user": { - "additionalData": {}, - "backingStore": { - "returnOnlyChangedValues": False, - "initializationCompleted": False - }, - "displayName": "Suggested Actions Bot", - "id": "28:" + CONFIG.APP_ID, - } - }, - "mentionText": "Suggested Actions Bot" - } - ], - "additionalData": {}, - "backingStore": { - "returnOnlyChangedValues": False, - "initializationCompleted": True - } - } - } - ) - ] - ) - reply = MessageFactory.text("Choose one of the action from the suggested actions") - reply.suggested_actions = actions - await turn_context.send_activity(reply) \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/bots/__init__.py b/samples/bot-suggested-actions/python/bots/__init__.py deleted file mode 100644 index 0d5f44b366..0000000000 --- a/samples/bot-suggested-actions/python/bots/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .Suggested_Actions_Bot import SuggestedActionsBot - -__all__ = ["SuggestedActionsBot"] \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/config.py b/samples/bot-suggested-actions/python/config.py deleted file mode 100644 index 6aa78c8ba5..0000000000 --- a/samples/bot-suggested-actions/python/config.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import os - -""" Bot Configuration """ - - -class DefaultConfig: - """ Bot Configuration """ - - PORT = 3978 - APP_TYPE = os.environ.get("MicrosoftAppType", "") - APP_ID = os.environ.get("MicrosoftAppId", "") - APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "") - APP_TENANTID = os.environ.get("MicrosoftAppTenantId", "") diff --git a/samples/bot-suggested-actions/python/env/.env.dev b/samples/bot-suggested-actions/python/env/.env.dev new file mode 100644 index 0000000000..8172044cec --- /dev/null +++ b/samples/bot-suggested-actions/python/env/.env.dev @@ -0,0 +1,17 @@ +# This file includes environment variables that will be committed to git by default. + +# Built-in environment variables +TEAMSFX_ENV=dev +APP_NAME_SUFFIX=dev + +# Updating AZURE_SUBSCRIPTION_ID or AZURE_RESOURCE_GROUP_NAME after provision may also require an update to RESOURCE_SUFFIX, because some services require a globally unique name across subscriptions/resource groups. +AZURE_SUBSCRIPTION_ID= +AZURE_RESOURCE_GROUP_NAME= +RESOURCE_SUFFIX= + +# Generated during provision, you can also add your own variables. +BOT_ID= +TEAMS_APP_ID= +TEAMS_APP_TENANT_ID= +BOT_AZURE_APP_SERVICE_RESOURCE_ID= +BOT_DOMAIN= \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/env/.env.local b/samples/bot-suggested-actions/python/env/.env.local index 553698b86a..dd248d208d 100644 --- a/samples/bot-suggested-actions/python/env/.env.local +++ b/samples/bot-suggested-actions/python/env/.env.local @@ -2,20 +2,12 @@ # Built-in environment variables TEAMSFX_ENV=local +APP_NAME_SUFFIX=local -# Generated during provision, you can also add your own variables. If you're adding a secret value, add SECRET_ prefix to the name so Teams Toolkit can handle them properly -BOT_ENDPOINT= -BOT_DOMAIN= -AAD_APP_CLIENT_ID= -AAD_APP_OBJECT_ID= -AAD_APP_TENANT_ID= -AAD_APP_OAUTH_AUTHORITY= -AAD_APP_OAUTH_AUTHORITY_HOST= +# Generated during provision, you can also add your own variables. +BOT_ID= TEAMS_APP_ID= TEAMS_APP_TENANT_ID= -MICROSOFT_APP_TYPE= -MICROSOFT_APP_TENANT_ID= -RESOURCE_SUFFIX= -AZURE_SUBSCRIPTION_ID= -AZURE_RESOURCE_GROUP_NAME= -APP_NAME_SUFFIX=local \ No newline at end of file +BOT_DOMAIN= +BOT_ENDPOINT= +BOT_OBJECT_ID= \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/env/.env.playground b/samples/bot-suggested-actions/python/env/.env.playground new file mode 100644 index 0000000000..52e3286033 --- /dev/null +++ b/samples/bot-suggested-actions/python/env/.env.playground @@ -0,0 +1,8 @@ +# This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment. + +# Built-in environment variables +TEAMSFX_ENV=playground + +# Environment variables used by Microsoft 365 Agents Playground +TEAMSAPPTESTER_PORT= +TEAMSFX_NOTIFICATION_STORE_FILENAME= \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/infra/azure.bicep b/samples/bot-suggested-actions/python/infra/azure.bicep index 98e1027752..0b515af787 100644 --- a/samples/bot-suggested-actions/python/infra/azure.bicep +++ b/samples/bot-suggested-actions/python/infra/azure.bicep @@ -3,42 +3,95 @@ @description('Used to generate names for all resources in this file') param resourceBaseName string -@description('Required when create Azure Bot service') -param botAadAppClientId string - -param botAadAppTenantId string - -param botAppDomain string +param webAppSKU string +param linuxFxVersion string @maxLength(42) param botDisplayName string -param botServiceName string = resourceBaseName -param botServiceSku string = 'F0' +param serverfarmsName string = resourceBaseName +param webAppName string = resourceBaseName +param identityName string = resourceBaseName +param location string = resourceGroup().location +param pythonVersion string = linuxFxVersion -// Register your web service as a bot with the Bot Framework -resource botService 'Microsoft.BotService/botServices@2021-03-01' = { - kind: 'azurebot' - location: 'global' - name: botServiceName - properties: { - displayName: botDisplayName - endpoint: 'https://${botAppDomain}/api/messages' - msaAppId: botAadAppClientId - msaAppType: 'SingleTenant' - msaAppTenantId: botAadAppTenantId - } +resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + location: location + name: identityName +} + +// Compute resources for your Web App +resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = { + kind: 'app,linux' + location: location + name: serverfarmsName sku: { - name: botServiceSku + name: webAppSKU + } + properties:{ + reserved: true } } -// Connect the bot service to Microsoft Teams -resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { - parent: botService - location: 'global' - name: 'MsTeamsChannel' +// Web App that hosts your agent +resource webApp 'Microsoft.Web/sites@2021-02-01' = { + kind: 'app,linux' + location: location + name: webAppName properties: { - channelName: 'MsTeamsChannel' + serverFarmId: serverfarm.id + siteConfig: { + alwaysOn: true + appCommandLine: 'python app.py' + linuxFxVersion: pythonVersion + appSettings: [ + { + name: 'WEBSITES_CONTAINER_START_TIME_LIMIT' + value: '900' + } + { + name: 'SCM_DO_BUILD_DURING_DEPLOYMENT' + value: 'true' + } + { + name: 'CLIENT_ID' + value: identity.properties.clientId + } + { + name: 'TENANT_ID' + value: identity.properties.tenantId + } + { + name: 'BOT_TYPE' + value: 'UserAssignedMsi' + } + ] + ftpsState: 'FtpsOnly' + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${identity.id}': {} + } } } + +// Register your web service as a bot with the Bot Framework +module azureBotRegistration './botRegistration/azurebot.bicep' = { + name: 'Azure-Bot-registration' + params: { + resourceBaseName: resourceBaseName + identityClientId: identity.properties.clientId + identityResourceId: identity.id + identityTenantId: identity.properties.tenantId + botAppDomain: webApp.properties.defaultHostName + botDisplayName: botDisplayName + } +} + +// The output will be persisted in .env.{envName}. Visit https://aka.ms/teamsfx-actions/arm-deploy for more details. +output BOT_AZURE_APP_SERVICE_RESOURCE_ID string = webApp.id +output BOT_DOMAIN string = webApp.properties.defaultHostName +output BOT_ID string = identity.properties.clientId +output BOT_TENANT_ID string = identity.properties.tenantId diff --git a/samples/bot-suggested-actions/python/infra/azure.parameters.json b/samples/bot-suggested-actions/python/infra/azure.parameters.json index afc0eb822f..5a08029064 100644 --- a/samples/bot-suggested-actions/python/infra/azure.parameters.json +++ b/samples/bot-suggested-actions/python/infra/azure.parameters.json @@ -1,21 +1,18 @@ { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "resourceBaseName": { - "value": "bot${{RESOURCE_SUFFIX}}" - }, - "botAadAppClientId": { - "value": "${{AAD_APP_CLIENT_ID}}" - }, - "botAadAppTenantId": { - "value": "${{AAD_APP_TENANT_ID}}" - }, - "botAppDomain": { - "value": "${{BOT_DOMAIN}}" - }, - "botDisplayName": { - "value": "TeamsConversationBot" - } + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceBaseName": { + "value": "bot${{RESOURCE_SUFFIX}}" + }, + "webAppSKU": { + "value": "B3" + }, + "botDisplayName": { + "value": "bot-suggested-actions" + }, + "linuxFxVersion": { + "value": "PYTHON|3.12" } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/infra/botRegistration/azurebot.bicep b/samples/bot-suggested-actions/python/infra/botRegistration/azurebot.bicep new file mode 100644 index 0000000000..a5a27b8fe4 --- /dev/null +++ b/samples/bot-suggested-actions/python/infra/botRegistration/azurebot.bicep @@ -0,0 +1,42 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@maxLength(42) +param botDisplayName string + +param botServiceName string = resourceBaseName +param botServiceSku string = 'F0' +param identityResourceId string +param identityClientId string +param identityTenantId string +param botAppDomain string + +// Register your web service as a bot with the Bot Framework +resource botService 'Microsoft.BotService/botServices@2021-03-01' = { + kind: 'azurebot' + location: 'global' + name: botServiceName + properties: { + displayName: botDisplayName + endpoint: 'https://${botAppDomain}/api/messages' + msaAppId: identityClientId + msaAppMSIResourceId: identityResourceId + msaAppTenantId:identityTenantId + msaAppType:'UserAssignedMSI' + } + sku: { + name: botServiceSku + } +} + +// Connect the bot service to Microsoft Teams +resource botServiceMsTeamsChannel 'Microsoft.BotService/botServices/channels@2021-03-01' = { + parent: botService + location: 'global' + name: 'MsTeamsChannel' + properties: { + channelName: 'MsTeamsChannel' + } +} diff --git a/samples/bot-suggested-actions/python/infra/botRegistration/readme.md b/samples/bot-suggested-actions/python/infra/botRegistration/readme.md new file mode 100644 index 0000000000..d5416243cd --- /dev/null +++ b/samples/bot-suggested-actions/python/infra/botRegistration/readme.md @@ -0,0 +1 @@ +The `azurebot.bicep` module is provided to help you create Azure Bot service when you don't use Azure to host your app. If you use Azure as infrastrcture for your app, `azure.bicep` under infra folder already leverages this module to create Azure Bot service for you. You don't need to deploy `azurebot.bicep` again. \ No newline at end of file diff --git a/samples/bot-suggested-actions/python/m365agents.local.yml b/samples/bot-suggested-actions/python/m365agents.local.yml index c6126f1ef3..fca9724d2b 100644 --- a/samples/bot-suggested-actions/python/m365agents.local.yml +++ b/samples/bot-suggested-actions/python/m365agents.local.yml @@ -1,45 +1,52 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.2 - -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-suggested-actions-python +version: v1.11 provision: - # Creates a new Azure Active Directory (AAD) app to authenticate users if the environment variable that stores clientId is empty - - uses: aadApp/create - with: - name: teamsConversationBot-aad # Note: when you run aadApp/update, the AAD app name will be updated based on the definition in manifest. If you don't want to change the name, make sure the name in AAD manifest is the same with the name defined here. - generateClientSecret: true # If the value is false, the action will not generate client secret for you - signInAudience: "AzureADMultipleOrgs" # Multitenant - writeToEnvironmentFile: # Write the information of created resources into environment file for the specified environment variable(s). - clientId: AAD_APP_CLIENT_ID - clientSecret: SECRET_AAD_APP_CLIENT_SECRET # Environment variable that starts with `SECRET_` will be stored to the .env.{envName}.user environment file - objectId: AAD_APP_OBJECT_ID - tenantId: AAD_APP_TENANT_ID - authority: AAD_APP_OAUTH_AUTHORITY - authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST - - # Creates a Teams app + # Creates an app - uses: teamsApp/create with: - # Teams app name - name: teamsConversationBot${{APP_NAME_SUFFIX}} + # app name + name: bot-suggested-actions${{APP_NAME_SUFFIX}} # Write the information of created resources into environment file for # the specified environment variable(s). writeToEnvironmentFile: teamsAppId: TEAMS_APP_ID - - uses: arm/deploy # Deploy given ARM templates parallelly. + # Create or reuse an existing Microsoft Entra application for bot. + - uses: aadApp/create + with: + # The Microsoft Entra application's display name + name: bot-suggested-actions${{APP_NAME_SUFFIX}} + generateClientSecret: true + generateServicePrincipal: true + signInAudience: AzureADMultipleOrgs + writeToEnvironmentFile: + # The Microsoft Entra application's client id created for bot. + clientId: BOT_ID + # The Microsoft Entra application's client secret created for bot. + clientSecret: SECRET_BOT_PASSWORD + # The Microsoft Entra application's object id created for bot. + objectId: BOT_OBJECT_ID + + # Create or update the bot registration on dev.botframework.com + - uses: botFramework/create with: - subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} # The AZURE_SUBSCRIPTION_ID is a built-in environment variable. TeamsFx will ask you select one subscription if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select subscription if it's empty in this case. - resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} # The AZURE_RESOURCE_GROUP_NAME is a built-in environment variable. TeamsFx will ask you to select or create one resource group if its value is empty. You're free to reference other environment varialbe here, but TeamsFx will not ask you to select or create resource grouop if it's empty in this case. - templates: - - path: ./infra/azure.bicep - parameters: ./infra/azure.parameters.json - deploymentName: Create-resources-for-bot - bicepCliVersion: v0.9.1 # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, will use bicep CLI in PATH if you remove this config. + botId: ${{BOT_ID}} + name: bot-suggested-actions + messagingEndpoint: ${{BOT_ENDPOINT}}/api/messages + description: "" + channels: + - name: msteams + + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build # manifest file to determine which AAD app to update. - uses: aadApp/update @@ -49,40 +56,27 @@ provision: manifestPath: ./aad.manifest.json outputFilePath: ./build/aad.manifest.${{TEAMSFX_ENV}}.json - # Validate using manifest schema - - uses: teamsApp/validateManifest - with: - # Path to manifest template - manifestPath: ./appManifest/manifest.json - - # Build Teams app package with latest env value - - uses: teamsApp/zipAppPackage - with: - # Path to manifest template - manifestPath: ./appManifest/manifest.json - outputZipPath: ./appManifest/build/appManifest.${{TEAMSFX_ENV}}.zip - outputJsonPath: ./appManifest/build/manifest.${{TEAMSFX_ENV}}.json # Validate app package using validation rules - uses: teamsApp/validateAppPackage with: # Relative path to this file. This is the path for built zip file. - appPackagePath: ./appManifest/build/appManifest.${{TEAMSFX_ENV}}.zip + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip - # Apply the Teams app manifest to an existing Teams app in + # Apply the app manifest to an existing app in # Developer Portal. - # Will use the app id in manifest file to determine which Teams app to update. + # Will use the app id in manifest file to determine which app to update. - uses: teamsApp/update with: # Relative path to this file. This is the path for built zip file. - appPackagePath: ./appManifest/build/appManifest.${{TEAMSFX_ENV}}.zip + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip deploy: # Generate runtime environment variables - uses: file/createOrUpdateEnvironmentFile with: - target: ./.env - envs: - MicrosoftAppType: "SingleTenant" - MicrosoftAppId: ${{AAD_APP_CLIENT_ID}} - MicrosoftAppPassword: ${{SECRET_AAD_APP_CLIENT_SECRET}} - MicrosoftAppTenantId: ${{AAD_APP_TENANT_ID}} \ No newline at end of file + target: ./.env + envs: + PORT: 3978 + CLIENT_ID: ${{BOT_ID}} + CLIENT_SECRET: ${{SECRET_BOT_PASSWORD}} + TENANT_ID: ${{TEAMS_APP_TENANT_ID}} diff --git a/samples/bot-suggested-actions/python/m365agents.playground.yml b/samples/bot-suggested-actions/python/m365agents.playground.yml new file mode 100644 index 0000000000..66b018629e --- /dev/null +++ b/samples/bot-suggested-actions/python/m365agents.playground.yml @@ -0,0 +1,24 @@ +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.9/yaml.schema.json +# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file +# Visit https://aka.ms/teamsfx-actions for details on actions +version: v1.9 + +deploy: + # Install development tool(s) + - uses: devTool/install + with: + testTool: + version: ~0.2.1 + symlinkDir: ./devTools/playground + nodejs: + symlinkDir: ./devTools/nodejs + + # Generate runtime environment variables + - uses: file/createOrUpdateEnvironmentFile + with: + target: ./.env + envs: + PORT: 3978 + TEAMSFX_NOTIFICATION_STORE_FILENAME: ${{TEAMSFX_NOTIFICATION_STORE_FILENAME}} + CLIENT_ID: "" + CLIENT_SECRET: "" diff --git a/samples/bot-suggested-actions/python/m365agents.yml b/samples/bot-suggested-actions/python/m365agents.yml index 51db2d89bb..37e299453e 100644 --- a/samples/bot-suggested-actions/python/m365agents.yml +++ b/samples/bot-suggested-actions/python/m365agents.yml @@ -1,10 +1,127 @@ -# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.2/yaml.schema.json +# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.9/yaml.schema.json # Visit https://aka.ms/teamsfx-v5.0-guide for details on this file # Visit https://aka.ms/teamsfx-actions for details on actions -version: v1.2 - -additionalMetadata: - sampleTag: Microsoft-Teams-Samples:bot-suggested-actions-python +version: v1.9 environmentFolderPath: ./env -projectId: 83682327-2c9c-4add-987b-13f44acd9ba8 + +# Triggered when 'teamsfx provision' is executed +provision: + # Creates an app + - uses: teamsApp/create + with: + # app name + name: bot-suggested-actions${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + - uses: arm/deploy # Deploy given ARM templates parallelly. + with: + # AZURE_SUBSCRIPTION_ID is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select a subscription. + # Referencing other environment variables with empty values + # will skip the subscription selection prompt. + subscriptionId: ${{AZURE_SUBSCRIPTION_ID}} + # AZURE_RESOURCE_GROUP_NAME is a built-in environment variable, + # if its value is empty, TeamsFx will prompt you to select or create one + # resource group. + # Referencing other environment variables with empty values + # will skip the resource group selection prompt. + resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}} + templates: + - path: ./infra/azure.bicep # Relative path to this file + # Relative path to this yaml file. + # Placeholders will be replaced with corresponding environment + # variable before ARM deployment. + parameters: ./infra/azure.parameters.json + # Required when deploying ARM template + deploymentName: Create-resources-for-tab + # Microsoft 365 Agents Toolkit will download this bicep CLI version from github for you, + # will use bicep CLI in PATH if you remove this config. + bicepCliVersion: v0.9.1 + + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + +# Triggered when 'teamsfx deploy' is executed +deploy: + # Deploy your application to Azure App Service using the zip deploy feature. + # For additional details, refer to https://aka.ms/zip-deploy-to-app-services. + - uses: azureAppService/zipDeploy + with: + # Deploy base folder + artifactFolder: src + # Ignore file location, leave blank will ignore nothing + ignoreFile: .webappignore + # The resource id of the cloud resource to be deployed to. + # This key will be generated by arm/deploy action automatically. + # You can replace it with your existing Azure Resource id + # or add it to your environment variable file. + resourceId: ${{BOT_AZURE_APP_SERVICE_RESOURCE_ID}} + +# Triggered when 'teamsapp publish' is executed +publish: + # Validate using manifest schema + - uses: teamsApp/validateManifest + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + + # Build app package with latest env value + - uses: teamsApp/zipAppPackage + with: + # Path to manifest template + manifestPath: ./appPackage/manifest.json + outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + outputFolder: ./appPackage/build + + # Validate app package using validation rules + - uses: teamsApp/validateAppPackage + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + + # Apply the app manifest to an existing app in + # Developer Portal. + # Will use the app id in manifest file to determine which app to update. + - uses: teamsApp/update + with: + # Relative path to this file. This is the path for built zip file. + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Publish the app to + # Teams Admin Center (https://admin.teams.microsoft.com/policies/manage-apps) + # for review and approval + - uses: teamsApp/publishAppPackage + with: + appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + publishedAppId: TEAMS_APP_PUBLISHED_APP_ID diff --git a/samples/bot-suggested-actions/python/requirements.txt b/samples/bot-suggested-actions/python/requirements.txt deleted file mode 100644 index c6424216e6..0000000000 --- a/samples/bot-suggested-actions/python/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests==2.31.0 -botbuilder-integration-aiohttp>=4.14.5 diff --git a/samples/bot-suggested-actions/python/src/app.py b/samples/bot-suggested-actions/python/src/app.py new file mode 100644 index 0000000000..0db42982b4 --- /dev/null +++ b/samples/bot-suggested-actions/python/src/app.py @@ -0,0 +1,94 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import asyncio +from microsoft_teams.apps import App, ActivityContext +from microsoft_teams.api import MessageActivity, MessageActivityInput, ConversationUpdateActivity +from microsoft_teams.api.models import SuggestedActions, CardAction, CardActionType +from config import Config + +config = Config() + +# Create the Teams App +app = App( + client_id=config.APP_ID, + client_secret=config.APP_PASSWORD, + tenant_id=config.APP_TENANTID +) + +# Welcome new members with suggested actions +@app.on_conversation_update +async def on_members_added(ctx: ActivityContext[ConversationUpdateActivity]): + """Handle new members added to conversation.""" + if ctx.activity.members_added: + for member in ctx.activity.members_added: + if member.id != ctx.activity.recipient.id: + welcome_message = "Welcome to the bot! Let's get started with some suggestions." + await ctx.send(welcome_message) + await send_suggested_actions(ctx) + + +# Handle user messages and respond based on input +@app.on_message +async def on_message_activity(ctx: ActivityContext[MessageActivity]): + """Handle message activities.""" + user_message = ctx.activity.text + + if user_message == "Hello": + await ctx.send("Hello! How can I assist you today?") + elif user_message == "Welcome": + await ctx.send("Welcome! How can I assist you today?") + elif user_message == "@SuggestedActionsBot": + await ctx.send("@SuggestedActionsBot! How can I assist you today?") + else: + await ctx.send("I didn't understand that. Please choose one of the suggested actions.") + + # Send suggested actions for the next turn + await send_suggested_actions(ctx) + + +async def send_suggested_actions(ctx: ActivityContext): + """ + Sends a message with suggested actions to the user. + Uses native Teams SDK SuggestedActions with IMBack action type. + When the user clicks a button, the text value will be displayed in the channel + as if the user typed it, and will automatically trigger the on_message handler. + """ + # Create MessageActivity with SuggestedActions using native Teams SDK + suggested_actions = SuggestedActions( + to=[ctx.activity.from_.id], + actions=[ + CardAction( + type=CardActionType.IM_BACK, + title="Hello", + value="Hello" + ), + CardAction( + type=CardActionType.IM_BACK, + title="Welcome", + value="Welcome" + ), + CardAction( + type=CardActionType.IM_BACK, + title="@SuggestedActionsBot", + value="@SuggestedActionsBot" + ) + ] + ) + + message = MessageActivityInput( + text="Choose one of the action from the suggested actions", + suggested_actions=suggested_actions + ) + + await ctx.send(message) + + +# Start the bot application +async def main(): + """Start the application.""" + await app.start() + print(f"\nBot started, app listening to port 3978") + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/samples/bot-suggested-actions/python/src/config.py b/samples/bot-suggested-actions/python/src/config.py new file mode 100644 index 0000000000..53439d4a02 --- /dev/null +++ b/samples/bot-suggested-actions/python/src/config.py @@ -0,0 +1,18 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os + +from dotenv import load_dotenv + +load_dotenv() + +class Config: + """Bot Configuration""" + + PORT = int(os.environ.get("PORT", 3978)) + APP_ID = os.environ.get("CLIENT_ID", "") + APP_PASSWORD = os.environ.get("CLIENT_SECRET", "") + APP_TYPE = os.environ.get("BOT_TYPE", "") + APP_TENANTID = os.environ.get("TENANT_ID", "") + diff --git a/samples/bot-suggested-actions/python/src/requirements.txt b/samples/bot-suggested-actions/python/src/requirements.txt new file mode 100644 index 0000000000..74c20c6239 --- /dev/null +++ b/samples/bot-suggested-actions/python/src/requirements.txt @@ -0,0 +1,3 @@ +# Teams SDK v2 for Python +microsoft-teams-apps>=2.0.0a5,<3.0.0 +python-dotenv>=1.0.0