diff --git a/AdapterWithErrorHandler.cs b/AdapterWithErrorHandler.cs new file mode 100644 index 0000000..45479d7 --- /dev/null +++ b/AdapterWithErrorHandler.cs @@ -0,0 +1,33 @@ +using Microsoft.Bot.Builder.Integration.AspNet.Core; +using Microsoft.Bot.Builder.TraceExtensions; +using Microsoft.Bot.Connector.Authentication; +using Microsoft.Bot.Schema; + +namespace IAChat; + +public class AdapterWithErrorHandler : CloudAdapter +{ + public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger logger) + : base(auth, 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}"); + + // Only send error message for user messages, not for other message types so the bot doesn't spam a channel or chat. + if (turnContext.Activity.Type == ActivityTypes.Message) + { + // Send a message to the user + await turnContext.SendActivityAsync("The bot encountered an error or bug."); + await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); + + // 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"); + } + }; + } +} \ No newline at end of file diff --git a/Bot/ChatIABot.cs b/Bot/ChatIABot.cs new file mode 100644 index 0000000..303a914 --- /dev/null +++ b/Bot/ChatIABot.cs @@ -0,0 +1,62 @@ +using IAChat.MSGraph; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Teams; +using Microsoft.Bot.Schema; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Web; + +namespace IAChat.Bot; + +public class ChatIABot : TeamsActivityHandler +{ + private readonly IHttpClientFactory clientFactory; + private readonly TokenManager tokenManager; + + //public ChatIABot(IHttpClientFactory clientFactory, IConfigurationManager configuration) + public ChatIABot(IServiceProvider serviceProvider) + { + this.clientFactory = serviceProvider.GetService(); + this.tokenManager = serviceProvider.GetService(); + } + protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) + { + string messageText = turnContext.Activity.RemoveRecipientMention()?.Trim(); + var userId = turnContext.Activity.From.Id; + var userName = turnContext.Activity.From.Name; + var tenantId = turnContext.Activity.Conversation.TenantId; + var aadObjectId = turnContext.Activity.From.AadObjectId; + + var token = await tokenManager.ObterToken(userId); + var replyText = await GetMessage(token, messageText); + await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); + } + + protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) + { + var welcomeText = "Olá! Eu sou o Ian. Uma inteligência artificial interna da Domvs iT.\nSe perguntar algo sobre o conteúdo do SahePoint da Domvs, eu conseguirei responder."; + foreach (var member in membersAdded) + { + if (member.Id != turnContext.Activity.Recipient.Id) + { + await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText), cancellationToken); + } + } + } + + private async Task GetMessage(string jwt, string message) + { + var httpClient = this.clientFactory.CreateClient(); + var content = ""; + using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:5020/Chat?Message={HttpUtility.UrlEncode(message)}")) + { + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); + + var responseGet = await httpClient.SendAsync(requestMessage); + content = responseGet.Content.ReadAsStringAsync().Result; + } + + return content; + } +} + diff --git a/Config.cs b/Config.cs new file mode 100644 index 0000000..e20860e --- /dev/null +++ b/Config.cs @@ -0,0 +1,8 @@ +namespace IAChat +{ + public class ConfigOptions + { + public string BOT_ID { get; set; } + public string BOT_PASSWORD { get; set; } + } +} diff --git a/Controllers/BotController.cs b/Controllers/BotController.cs new file mode 100644 index 0000000..ec9c1a8 --- /dev/null +++ b/Controllers/BotController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; + +namespace IAChat.Controllers; + +// 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, HttpGet] + 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); + } +} \ No newline at end of file diff --git a/IAChat.csproj b/IAChat.csproj new file mode 100644 index 0000000..855ec8e --- /dev/null +++ b/IAChat.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + + + + + + + + + + diff --git a/IAChat.sln b/IAChat.sln new file mode 100644 index 0000000..a5363af --- /dev/null +++ b/IAChat.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IAChat", "IAChat.csproj", "{1E07A203-25BE-4F59-B865-F4A08D776E74}" +EndProject +Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "TeamsApp", "TeamsApp\TeamsApp.ttkproj", "{71A41EF5-AC32-4E43-A247-FF63E7F0CF39}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E07A203-25BE-4F59-B865-F4A08D776E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E07A203-25BE-4F59-B865-F4A08D776E74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E07A203-25BE-4F59-B865-F4A08D776E74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E07A203-25BE-4F59-B865-F4A08D776E74}.Release|Any CPU.Build.0 = Release|Any CPU + {71A41EF5-AC32-4E43-A247-FF63E7F0CF39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71A41EF5-AC32-4E43-A247-FF63E7F0CF39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71A41EF5-AC32-4E43-A247-FF63E7F0CF39}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71A41EF5-AC32-4E43-A247-FF63E7F0CF39}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1DA1A381-FC1E-44CE-AAD5-E15F6F6BA5CA} + EndGlobalSection +EndGlobal diff --git a/MSGraph/TokenManager.cs b/MSGraph/TokenManager.cs new file mode 100644 index 0000000..92ae128 --- /dev/null +++ b/MSGraph/TokenManager.cs @@ -0,0 +1,49 @@ +using Microsoft.Identity.Client; + +namespace IAChat.MSGraph +{ + public class TokenManager + { + private readonly IConfiguration _configuration; + private readonly string _tenantId; + private readonly string _clientId; + + public TokenManager(IConfigurationManager configuration) + { + _configuration = configuration; + _tenantId = _configuration.GetSection("AppTenantId").Value; + _clientId = _configuration.GetSection("AppClientID").Value; + } + + + public async Task ObterToken(string userEmail) + { + // Configuração do contexto de autenticação + var app = PublicClientApplicationBuilder + .Create(_clientId) + .WithAuthority(AzureCloudInstance.AzurePublic, _tenantId) + .Build(); + + // Definir os escopos necessários + string[] scopes = new string[] { + "https://graph.microsoft.com/.default", + "User.Read" + }; + + try + { + var result = await app.AcquireTokenInteractive(scopes) + .WithLoginHint(userEmail) + .ExecuteAsync(); + + return result.AccessToken; + } + catch (Exception ex) + { + // Tratamento de erro + Console.WriteLine($"Erro ao obter token: {ex.Message}"); + return null; + } + } + } +} diff --git a/MSGraph/akjkpvjm.5o3~ b/MSGraph/akjkpvjm.5o3~ new file mode 100644 index 0000000..f5aa6aa --- /dev/null +++ b/MSGraph/akjkpvjm.5o3~ @@ -0,0 +1,51 @@ +using Microsoft.Identity.Client; + +namespace IAChat.MSGraph +{ + public class TokenManager + { + public class GetToken + { + private readonly IConfiguration _configuration; + private readonly string _tenantId; + private readonly string _clientId; + + public GetToken(IConfiguration configuration) + { + _configuration = configuration; + _tenantId = _configuration.GetSection("AppTenantId").Value; + _clientId = _configuration.GetSection("AppClientID").Value; + } + + public async Task ObterTokenParaTeams(string userEmail) + { + // Configuração do contexto de autenticação + var app = PublicClientApplicationBuilder + .Create(_clientId) + .WithAuthority(AzureCloudInstance.AzurePublic, _tenantId) + .Build(); + + // Definir os escopos necessários + string[] scopes = new string[] { + "https://graph.microsoft.com/.default", + "User.Read" + }; + + try + { + var result = await app.AcquireTokenInteractive(scopes) + .WithLoginHint(userEmail) + .ExecuteAsync(); + + return result.AccessToken; + } + catch (Exception ex) + { + // Tratamento de erro + Console.WriteLine($"Erro ao obter token: {ex.Message}"); + return null; + } + } + } + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..93505e4 --- /dev/null +++ b/Program.cs @@ -0,0 +1,49 @@ +using IAChat; +using IAChat.Bot; +using IAChat.MSGraph; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; +using Microsoft.Bot.Connector.Authentication; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600)); +builder.Services.AddHttpContextAccessor(); + +builder.Services.AddHttpClient(); + +// Create the Bot Framework Authentication to be used with the Bot Adapter. +var config = builder.Configuration.Get(); +builder.Configuration["MicrosoftAppType"] = "MultiTenant"; +builder.Configuration["MicrosoftAppId"] = config.BOT_ID; +builder.Configuration["MicrosoftAppPassword"] = config.BOT_PASSWORD; +builder.Services.AddSingleton(); +builder.Services.AddSingleton(builder.Configuration); +builder.Services.AddScoped(); + +// Create the Bot Framework Adapter with error handling enabled. +builder.Services.AddSingleton(); + +// Create the bot as a transient. In this case the ASP Controller is expecting an IBot. +builder.Services.AddTransient(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +app.UseStaticFiles(); + +app.UseRouting(); + +//app.UseAuthentication(); +//app.UseAuthorization(); + +app.UseEndpoints(endpoints => +{ + endpoints.MapControllers(); +}); + +app.Run(); \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..4343fa0 --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "profiles": { + // Debug project within Teams + "Start Project": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5130", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "hotReloadProfile": "aspnetcore" + }, + // Debug project within Teams App Test Tool + "Teams App Test Tool": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5130", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "TestTool", + "TEAMSFX_NOTIFICATION_STORE_FILENAME": ".notification.testtoolstore.json" + }, + "hotReloadProfile": "aspnetcore" + }, + } +} \ No newline at end of file diff --git a/TeamsApp/README.md b/TeamsApp/README.md new file mode 100644 index 0000000..f680e12 --- /dev/null +++ b/TeamsApp/README.md @@ -0,0 +1,28 @@ +# Welcome to Teams Toolkit! + +## Quick Start +1. In the debug dropdown menu, select Dev Tunnels > Create A Tunnel (set authentication type to Public) or select an existing public dev tunnel +
![image](https://raw.githubusercontent.com/OfficeDev/TeamsFx/dev/docs/images/visualstudio/debug/create-devtunnel-button.png) +2. Right-click the 'TeamsApp' project in Solution Explorer and select Teams Toolkit > Prepare Teams App Dependencies +3. If prompted, sign in to Visual Studio with a Microsoft 365 work or school account +4. Press F5, or select Debug > Start Debugging menu in Visual Studio to start your app +
![image](https://raw.githubusercontent.com/OfficeDev/TeamsFx/dev/docs/images/visualstudio/debug/debug-button.png) +5. In the opened web browser, select Add button to test the app in Teams +6. In the message input field, type and send anything to your bot to get a response + +## Debug in Test Tool +Teams App Test Tool allows developers test and debug bots locally without needing Microsoft 365 accounts, development tunnels, or Teams app and bot registration. See https://aka.ms/teams-toolkit-vs-test-tool for more details. + +## Run the app on other platforms + +The Teams app can run in other platforms like Outlook and Microsoft 365 app. See https://aka.ms/vs-ttk-debug-multi-profiles for more details. + +## Get more info + +New to Teams app development or Teams Toolkit? Explore Teams app manifests, cloud deployment, and much more in the https://aka.ms/teams-toolkit-vs-docs. + +## Report an issue + +Select Visual Studio > Help > Send Feedback > Report a Problem. +Or, create an issue directly in our GitHub repository: +https://github.com/OfficeDev/TeamsFx/issues diff --git a/TeamsApp/TeamsApp.ttkproj b/TeamsApp/TeamsApp.ttkproj new file mode 100644 index 0000000..443dfc4 --- /dev/null +++ b/TeamsApp/TeamsApp.ttkproj @@ -0,0 +1,9 @@ + + + + 71a41ef5-ac32-4e43-a247-ff63e7f0cf39 + + + + + \ No newline at end of file diff --git a/TeamsApp/appPackage/color.png b/TeamsApp/appPackage/color.png new file mode 100644 index 0000000..2d7e85c Binary files /dev/null and b/TeamsApp/appPackage/color.png differ diff --git a/TeamsApp/appPackage/manifest - Copy.json b/TeamsApp/appPackage/manifest - Copy.json new file mode 100644 index 0000000..203cb7b --- /dev/null +++ b/TeamsApp/appPackage/manifest - Copy.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json", + "manifestVersion": "1.17", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Teams App, Inc.", + "websiteUrl": "https://www.example.com", + "privacyUrl": "https://www.example.com/privacy", + "termsOfUseUrl": "https://www.example.com/termofuse" + }, + "icons": { + "color": "color.png", + "outline": "outline.png" + }, + "name": { + "short": "IAChat${{APP_NAME_SUFFIX}}", + "full": "full name for IAChat" + }, + "description": { + "short": "Short description of IAChat", + "full": "Full description of IAChat" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "${{BOT_ID}}", + "scopes": [ + "personal", + "team", + "groupChat" + ], + "supportsFiles": false, + "isNotificationOnly": false + } + ], + "composeExtensions": [ + ], + "configurableTabs": [], + "staticTabs": [], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/TeamsApp/appPackage/manifest.json b/TeamsApp/appPackage/manifest.json new file mode 100644 index 0000000..7f602ad --- /dev/null +++ b/TeamsApp/appPackage/manifest.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json", + "manifestVersion": "1.17", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Jobmaker.com.br", + "websiteUrl": "https://jobmaker.com.br", + "privacyUrl": "https://jobmaker.com.br/privacy", + "termsOfUseUrl": "https://jobmaker.com.br/termofuse" + }, + "icons": { + "color": "technical-support192.png", + "outline": "outline.png" + }, + "name": { + "short": "Ian - ${{APP_NAME_SUFFIX}}", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "description": { + "short": "Ian é um robô interno Domvs iT", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "${{BOT_ID}}", + "scopes": [ + "personal", + "team", + "groupChat" + ], + "supportsFiles": false, + "isNotificationOnly": false + } + ], + "composeExtensions": [ + ], + "configurableTabs": [], + "staticTabs": [], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/TeamsApp/appPackage/manifest.json~RFc8742a7.TMP b/TeamsApp/appPackage/manifest.json~RFc8742a7.TMP new file mode 100644 index 0000000..c08f239 --- /dev/null +++ b/TeamsApp/appPackage/manifest.json~RFc8742a7.TMP @@ -0,0 +1,46 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json", + "manifestVersion": "1.17", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Jobmaker.com.br", + "websiteUrl": "https://jobmaker.com.br", + "privacyUrl": "https://jobmaker.com.br/privacy", + "termsOfUseUrl": "https://jobmaker.com.br/termofuse" + }, + "icons": { + "color": "color.png", + "outline": "technical-support32.png" + }, + "name": { + "short": "Ian - ${{APP_NAME_SUFFIX}}", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "description": { + "short": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "${{BOT_ID}}", + "scopes": [ + "personal", + "team", + "groupChat" + ], + "supportsFiles": false, + "isNotificationOnly": false + } + ], + "composeExtensions": [ + ], + "configurableTabs": [], + "staticTabs": [], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/TeamsApp/appPackage/manifest.json~RFc97232b.TMP b/TeamsApp/appPackage/manifest.json~RFc97232b.TMP new file mode 100644 index 0000000..936c056 --- /dev/null +++ b/TeamsApp/appPackage/manifest.json~RFc97232b.TMP @@ -0,0 +1,46 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json", + "manifestVersion": "1.17", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Jobmaker.com.br", + "websiteUrl": "https://jobmaker.com.br", + "privacyUrl": "https://jobmaker.com.br/privacy", + "termsOfUseUrl": "https://jobmaker.com.br/termofuse" + }, + "icons": { + "color": "technical-support192.png", + "outline": "technical-support32.png" + }, + "name": { + "short": "Ian - ${{APP_NAME_SUFFIX}}", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "description": { + "short": "Ian é um robô interno Domvs iT", + "full": "Ian é um robô com inteligência artificial para realizar tarefas internas à Domvs iT" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "${{BOT_ID}}", + "scopes": [ + "personal", + "team", + "groupChat" + ], + "supportsFiles": false, + "isNotificationOnly": false + } + ], + "composeExtensions": [ + ], + "configurableTabs": [], + "staticTabs": [], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file diff --git a/TeamsApp/appPackage/outline.png b/TeamsApp/appPackage/outline.png new file mode 100644 index 0000000..245fa19 Binary files /dev/null and b/TeamsApp/appPackage/outline.png differ diff --git a/TeamsApp/appPackage/technical-support192.png b/TeamsApp/appPackage/technical-support192.png new file mode 100644 index 0000000..a4a00e0 Binary files /dev/null and b/TeamsApp/appPackage/technical-support192.png differ diff --git a/TeamsApp/appPackage/technical-support32.png b/TeamsApp/appPackage/technical-support32.png new file mode 100644 index 0000000..ba817fa Binary files /dev/null and b/TeamsApp/appPackage/technical-support32.png differ diff --git a/TeamsApp/appPackage/technical-support32n.png b/TeamsApp/appPackage/technical-support32n.png new file mode 100644 index 0000000..899922d Binary files /dev/null and b/TeamsApp/appPackage/technical-support32n.png differ diff --git a/TeamsApp/env/.env.dev b/TeamsApp/env/.env.dev new file mode 100644 index 0000000..df4f9da --- /dev/null +++ b/TeamsApp/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/TeamsApp/env/.env.local b/TeamsApp/env/.env.local new file mode 100644 index 0000000..8584d42 --- /dev/null +++ b/TeamsApp/env/.env.local @@ -0,0 +1,16 @@ +# 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=local +APP_NAME_SUFFIX=local + +# Generated during provision, you can also add your own variables. +BOT_ID=8f4248fc-ee30-4f54-8793-66edcca3fd20 +TEAMS_APP_ID=ea0047b8-d686-4e93-b987-d2c039d7d0ef +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=76009812-3d88-43a2-97c6-3920e1ad2223 +BOT_ENDPOINT=https://vrzg2z27-5130.brs.devtunnels.ms +BOT_DOMAIN=vrzg2z27-5130.brs.devtunnels.ms + + +TEAMSFX_M365_USER_NAME=ricardo.carneiro@domvsit.com.br \ No newline at end of file diff --git a/TeamsApp/infra/azure.bicep b/TeamsApp/infra/azure.bicep new file mode 100644 index 0000000..96ed8d8 --- /dev/null +++ b/TeamsApp/infra/azure.bicep @@ -0,0 +1,73 @@ +@maxLength(20) +@minLength(4) +@description('Used to generate names for all resources in this file') +param resourceBaseName string + +@description('Required when create Azure Bot service') +param botAadAppClientId string + +@secure() +@description('Required by Bot Framework package in your bot project') +param botAadAppClientSecret string + +param webAppSKU string + +@maxLength(42) +param botDisplayName string + +param serverfarmsName string = resourceBaseName +param webAppName string = resourceBaseName +param location string = resourceGroup().location + +// Compute resources for your Web App +resource serverfarm 'Microsoft.Web/serverfarms@2021-02-01' = { + kind: 'app' + location: location + name: serverfarmsName + sku: { + name: webAppSKU + } +} + +// Web App that hosts your bot +resource webApp 'Microsoft.Web/sites@2021-02-01' = { + kind: 'app' + location: location + name: webAppName + properties: { + serverFarmId: serverfarm.id + httpsOnly: true + siteConfig: { + appSettings: [ + { + name: 'WEBSITE_RUN_FROM_PACKAGE' + value: '1' + } + { + name: 'BOT_ID' + value: botAadAppClientId + } + { + name: 'BOT_PASSWORD' + value: botAadAppClientSecret + } + ] + ftpsState: 'FtpsOnly' + } + } +} + +// Register your web service as a bot with the Bot Framework +module azureBotRegistration './botRegistration/azurebot.bicep' = { + name: 'Azure-Bot-registration' + params: { + resourceBaseName: resourceBaseName + botAadAppClientId: botAadAppClientId + 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 diff --git a/TeamsApp/infra/azure.parameters.json b/TeamsApp/infra/azure.parameters.json new file mode 100644 index 0000000..5e680fe --- /dev/null +++ b/TeamsApp/infra/azure.parameters.json @@ -0,0 +1,21 @@ +{ + "$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": "${{BOT_ID}}" + }, + "botAadAppClientSecret": { + "value": "${{SECRET_BOT_PASSWORD}}" + }, + "webAppSKU": { + "value": "B1" + }, + "botDisplayName": { + "value": "IAChat" + } + } + } \ No newline at end of file diff --git a/TeamsApp/infra/botRegistration/azurebot.bicep b/TeamsApp/infra/botRegistration/azurebot.bicep new file mode 100644 index 0000000..ab67c7a --- /dev/null +++ b/TeamsApp/infra/botRegistration/azurebot.bicep @@ -0,0 +1,37 @@ +@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 botAadAppClientId 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: botAadAppClientId + } + 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/TeamsApp/infra/botRegistration/readme.md b/TeamsApp/infra/botRegistration/readme.md new file mode 100644 index 0000000..d541624 --- /dev/null +++ b/TeamsApp/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/TeamsApp/launchSettings.json b/TeamsApp/launchSettings.json new file mode 100644 index 0000000..600bcab --- /dev/null +++ b/TeamsApp/launchSettings.json @@ -0,0 +1,15 @@ +{ + "profiles": { + // 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 App Test Tool + "Teams App Test Tool (browser)": { + "commandName": "Project", + "launchTestTool": true, + "launchUrl": "http://localhost:56150", + }, + } +} \ No newline at end of file diff --git a/TeamsApp/teamsapp.local.yml b/TeamsApp/teamsapp.local.yml new file mode 100644 index 0000000..c2b30d6 --- /dev/null +++ b/TeamsApp/teamsapp.local.yml @@ -0,0 +1,74 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.5/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.5 + +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: IAChat${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + # Create or reuse an existing Microsoft Entra application for bot. + - uses: aadApp/create + with: + # The Microsoft Entra application's display name + name: IAChat${{APP_NAME_SUFFIX}} + generateClientSecret: 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 + + # Generate runtime appsettings to JSON file + - uses: file/createOrUpdateJsonFile + with: + target: ../appsettings.Development.json + content: + BOT_ID: ${{BOT_ID}} + BOT_PASSWORD: ${{SECRET_BOT_PASSWORD}} + + # Create or update the bot registration on dev.botframework.com + - uses: botFramework/create + with: + botId: ${{BOT_ID}} + name: IAChat + 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 + # 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 + # Teams 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 diff --git a/TeamsApp/teamsapp.yml b/TeamsApp/teamsapp.yml new file mode 100644 index 0000000..66a87d4 --- /dev/null +++ b/TeamsApp/teamsapp.yml @@ -0,0 +1,103 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/v1.5/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.5 + +environmentFolderPath: ./env + +# Triggered when 'teamsapp provision' is executed +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: IAChat${{APP_NAME_SUFFIX}} + # Write the information of created resources into environment file for + # the specified environment variable(s). + writeToEnvironmentFile: + teamsAppId: TEAMS_APP_ID + + # Create or reuse an existing Microsoft Entra application for bot. + - uses: aadApp/create + with: + # The Microsoft Entra application's display name + name: IAChat${{APP_NAME_SUFFIX}} + generateClientSecret: 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 + + - 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 + # Teams 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 + outputJsonPath: ./appPackage/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: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip + # Apply the Teams app manifest to an existing Teams app in + # Teams 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 IAChat.csproj + workingDirectory: .. + # 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/net8.0/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: .. +projectId: a0e11721-e5ae-4dbd-8047-057a772737e7 diff --git a/appsettings.TestTool.json b/appsettings.TestTool.json new file mode 100644 index 0000000..1a3d21d --- /dev/null +++ b/appsettings.TestTool.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "BOT_ID": "", + "BOT_PASSWORD": "" +} \ No newline at end of file diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..da0e54e --- /dev/null +++ b/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "BOT_ID": "$botId$", + "BOT_PASSWORD": "$bot-password$", + "AppClientID": "8f4248fc-ee30-4f54-8793-66edcca3fd20", + "AppClientSecret": "4ea5e989-37dc-4ace-96a6-0fa0f74ef059", + "AppClientSecretValue": "AMI8Q~YCMY4ffjypwWMxOQlDI9I3TCkk3Lhfga8e", + "AppTenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc" +}