diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/AdapterWithErrorHandler.cs b/AdapterWithErrorHandler.cs new file mode 100644 index 0000000..a5429b9 --- /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 DomvsChatBot; + +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..955131e --- /dev/null +++ b/Bot/ChatIABot.cs @@ -0,0 +1,171 @@ + using DomvsChatBot.Comm; +using DomvsChatBot.LocalData; +using DomvsChatBot.MSGraph; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Teams; +using Microsoft.Bot.Connector.Authentication; +using Microsoft.Bot.Connector; +using Microsoft.Bot.Schema; +using Microsoft.Extensions.Configuration; +using System.Net.Http.Headers; +using System.Web; +using System.Text; + +namespace DomvsChatBot.Bot; + +public class ChatIABot : TeamsActivityHandler +{ + private readonly IConfigurationManager _configuration; + private readonly string _clientSecret; + private readonly string _clientId; + private readonly ApiService _apiService; + private readonly LocalStorage _localStorage; + private readonly TokenManager _tokenManager; + private readonly IHttpClientFactory _clientFactory; + + public ChatIABot(IConfigurationManager configuration, ApiService apiService, LocalStorage localStorage, TokenManager tokenManager, IHttpClientFactory clientFactory) + { + _configuration = configuration; + _apiService = apiService; + _localStorage = localStorage; + _tokenManager = tokenManager; + _clientFactory = clientFactory; + _clientSecret = _configuration.GetSection("AppClientSecret").Value; + _clientId = _configuration.GetSection("AppClientID").Value; + } + 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 = turnContext.Activity.From.AadObjectId.ToString(); + + var credentials = new MicrosoftAppCredentials(_clientId, _clientSecret); + var connector = new ConnectorClient(new Uri(turnContext.Activity.ServiceUrl), credentials); + var sessionId = turnContext.Activity.Conversation.Id; + var localId = turnContext.Activity.Conversation.TenantId; + + try + { + //var userInfo = await connector.Conversations.GetConversationMembersAsync(conversationId); + //var token = await TokenManager.ObterToken(userId); + //var replyText = await GetMessage(userId, turnContext.Activity.From.AadObjectId, turnContext.Activity.From.Name, messageText); + var token = await GetToken(userName, tenantId, userId); + await turnContext.SendActivityAsync(new Activity() { Type = ActivityTypes.Typing }); + + var taskResponse = _apiService.GetMessage(token, messageText, sessionId); + + var waitTime = 1000; + + while (!taskResponse.IsCompleted) + { + await Task.Delay(waitTime); + if (!taskResponse.IsCompleted) await turnContext.SendActivityAsync(new Activity() { Type = ActivityTypes.Typing }); + waitTime = GetTime(waitTime); + }; + + var replyText = await taskResponse; + replyText = SepararCodigo(replyText); + + await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); + //await turnContext.SendActivityAsync(new Activity() { Type = ActivityTypes.Typing }); + + } + catch (Exception ex) + { + await turnContext.SendActivityAsync(MessageFactory.Text(ex.Message), cancellationToken); + } + } + + private string SepararCodigo(string texto) + { + if (texto.Contains("```")) + { + return texto; + } + return texto.Replace("\n", "
"); + } + + + private int GetTime(int waitTime) + { + return waitTime==1000 ? waitTime=2000 : waitTime == 2000 ? waitTime = 5000 : waitTime = 10000; + } + + protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) + { + var welcomeText = "Olá! Eu sou a Domvs IA. A inteligência artificial 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(new Activity() { Type = ActivityTypes.Typing }); + + await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText), cancellationToken); + //TODO: Verificar se o turnContext.Activity.Conversation.Id existe ou não bo BD do Bot (MongoDB localhost:27017) + + var userId = turnContext.Activity.From.Id; + var userName = turnContext.Activity.From.Name; + var localId = turnContext.Activity.Conversation.TenantId; + + await GetToken(userName, localId, userId); + + //var token = await TokenManager.ObterToken(userId); + //var replyText = await GetMessage(token, messageText); + //await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); + + } + } + } + private async Task GetToken(string name, string company, string id) + { + if (_localStorage?.Token != null && _tokenManager.CheckTokenIsValid(_localStorage.Token)) + { + return _localStorage.Token; + } + + var secret = await _apiService.GetSecret(id, company, name); + var token = await _apiService.GetToken(id, name, secret); + + _localStorage.Secret = secret; + _localStorage.Token = token; + _localStorage.Name = name; + _localStorage.Company = company; + _localStorage.Id = id; + + return token; + } + + private async Task GetMessage(string jwt, string message) + { + var httpClient = _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; + } + + private async Task GetMessage(string id, string email, string name, string message) + { + var httpClient = _clientFactory.CreateClient(); + var content = ""; + using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:5020/Chat?SessionId={HttpUtility.UrlEncode(id)}&Message={HttpUtility.UrlEncode(message)}&Email={HttpUtility.UrlEncode(email)}&Name={HttpUtility.UrlEncode(name)}")) + { + //requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); + + var responseGet = await httpClient.SendAsync(requestMessage); + content = responseGet.Content.ReadAsStringAsync().Result; + } + + return content; + } +} + diff --git a/Comm/ApiService.cs b/Comm/ApiService.cs new file mode 100644 index 0000000..3de1bdc --- /dev/null +++ b/Comm/ApiService.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.DataProtection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Net.Http.Headers; +using System.Text; +using System.Web; + +namespace DomvsChatBot.Comm +{ + public class ApiService + { + private readonly IHttpClientFactory _clientFactory; + private readonly IConfigurationManager _configurationManager; + private readonly string _baseUrl; + + public ApiService(IHttpClientFactory clientFactory, IConfigurationManager configurationManager) + { + _clientFactory = clientFactory; + _configurationManager = configurationManager; + _baseUrl = _configurationManager.GetSection("BaseUrlApi").Value; + } + + public async Task GetSecret(string id, string company, string name) + { + var httpClient = _clientFactory.CreateClient(); + var content = ""; + var secret = ""; + using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/login/newclient")) + { + //requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); + var sendNew = new + { + localId = id, + companyTenant = company, + name = name + }; + requestMessage.Content = new StringContent(JsonConvert.SerializeObject(sendNew), Encoding.UTF8, "application/json"); + + var responseGet = await httpClient.SendAsync(requestMessage); + content = responseGet.Content.ReadAsStringAsync().Result; + + dynamic returned = JObject.Parse(content); + secret = returned.secret; + } + + return secret; + } + + public async Task GetToken(string clientId, string clientName, string clientSecret) + { + var httpClient = _clientFactory.CreateClient(); + var content = ""; + var token = ""; + using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/login/token")) + { + //requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); + var sendNew = new + { + clientId = clientId, + clientName = clientName, + clientSecret = clientSecret + }; + requestMessage.Content = new StringContent(JsonConvert.SerializeObject(sendNew), Encoding.UTF8, "application/json"); + + var responseGet = await httpClient.SendAsync(requestMessage); + content = responseGet.Content.ReadAsStringAsync().Result; + + dynamic returned = JObject.Parse(content); + token = returned.token; + } + + return token; + } + + public async Task GetMessage(string token, string message, string sessionId) + { + var httpClient = _clientFactory.CreateClient(); + var content = ""; + using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{_baseUrl}/chat/response?SessionId={HttpUtility.UrlEncode(sessionId)}&Message={HttpUtility.UrlEncode(message)}")) + { + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + + 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..0da06de --- /dev/null +++ b/Config.cs @@ -0,0 +1,8 @@ +namespace DomvsChatBot +{ + 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..f561ba5 --- /dev/null +++ b/Controllers/BotController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; + +namespace DomvsChatBot.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); + } + + [HttpGet] + [Route("health")] + public async Task Health() + { + return Ok("It´s ready!"); + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ddbfde4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# This stage is used when running from VS in fast mode (Default for Debug configuration) +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 + + +# This stage is used to build the service project +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["DomvsChatBot.csproj", "."] +RUN dotnet restore "./DomvsChatBot.csproj" +COPY . . +WORKDIR "/src/." +RUN dotnet build "./DomvsChatBot.csproj" -c $BUILD_CONFIGURATION -o /app/build + +# This stage is used to publish the service project to be copied to the final stage +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./DomvsChatBot.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "DomvsChatBot.dll"] \ No newline at end of file diff --git a/DomvsChatBot.csproj b/DomvsChatBot.csproj new file mode 100644 index 0000000..cb4da43 --- /dev/null +++ b/DomvsChatBot.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + Linux + . + + + + + + + + + + diff --git a/DomvsChatBot.sln b/DomvsChatBot.sln new file mode 100644 index 0000000..320d9f4 --- /dev/null +++ b/DomvsChatBot.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}") = "DomvsChatBot", "DomvsChatBot.csproj", "{9F6D5611-03E4-4272-AAAC-4F1E9447A1E9}" +EndProject +Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "TeamsApp", "TeamsApp\TeamsApp.ttkproj", "{EA5FD4C0-E5A1-4E35-A713-350E3249BB83}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9F6D5611-03E4-4272-AAAC-4F1E9447A1E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F6D5611-03E4-4272-AAAC-4F1E9447A1E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F6D5611-03E4-4272-AAAC-4F1E9447A1E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F6D5611-03E4-4272-AAAC-4F1E9447A1E9}.Release|Any CPU.Build.0 = Release|Any CPU + {EA5FD4C0-E5A1-4E35-A713-350E3249BB83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA5FD4C0-E5A1-4E35-A713-350E3249BB83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA5FD4C0-E5A1-4E35-A713-350E3249BB83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA5FD4C0-E5A1-4E35-A713-350E3249BB83}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B82DBB78-4321-4286-85F1-497096661AAA} + EndGlobalSection +EndGlobal diff --git a/LocalData/LocalStorage.cs b/LocalData/LocalStorage.cs new file mode 100644 index 0000000..803c49e --- /dev/null +++ b/LocalData/LocalStorage.cs @@ -0,0 +1,11 @@ +namespace DomvsChatBot.LocalData +{ + public class LocalStorage + { + public string Token { get; set; } + public string Secret { get; set; } + public string Name { get; set; } + public string Company { get; set; } + public string Id { get; set; } + } +} diff --git a/MSGraph/TokenManager.cs b/MSGraph/TokenManager.cs new file mode 100644 index 0000000..31a83b3 --- /dev/null +++ b/MSGraph/TokenManager.cs @@ -0,0 +1,116 @@ +using Microsoft.Identity.Client; +using System.IdentityModel.Tokens.Jwt; + +namespace DomvsChatBot.MSGraph +{ + public class TokenManager + { + private readonly IConfiguration _configuration; + private readonly string _clienteSecret; + private readonly string _tenantId; + private readonly string _clientId; + + public TokenManager(IConfigurationManager configuration) + { + _configuration = configuration; + _clienteSecret = _configuration.GetSection("AppClientSecret").Value; + _tenantId = _configuration.GetSection("AppTenantId").Value; + _clientId = _configuration.GetSection("AppClientID").Value; + } + + //public async Task ObterEmail(string aadObjectId) + //{ + // // The Microsoft Entra ID tenant ID or a verified domain (e.g. contoso.onmicrosoft.com) + // var tenantId = _tenantId; + + // // The client ID of the app registered in Microsoft Entra ID + // var clientId = _clientId; + + // // *Never* include client secrets in source code! + // var clientSecret = _clienteSecret; // Or some other secure place. + + // // Create an instance of a TokenCredential. Since we're using the Client Credentials + // // flow with a client secret, we use ClientSecretCredential. + // var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret); + + // // The app registration should be configured to require access to permissions + // // sufficient for the Microsoft Graph API calls the app will be making, and + // // those permissions should be granted by a tenant administrator. + // var scopes = new[] { "https://graph.microsoft.com/.default" }; + + // // Build the Microsoft Graph client + // GraphServiceClient graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes); + + // // Make a Microsoft Graph API request + // var users = await graphServiceClient.Users.GetAsync(); + + // return users.Value.FirstOrDefault(x => x.Id == aadObjectId).Mail; + //} + //public async Task ObterToken(string userEmail) + //{ + // try + // { + // var app = PublicClientApplicationBuilder + // .Create(_clientId) + // .WithAuthority(AzureCloudInstance.AzurePublic, _tenantId) + // .WithDefaultRedirectUri() + // .Build(); + + // string[] scopes = new string[] { + // //"https://graph.microsoft.com/.default", + // "User.Read", // Exemplo de escopo básico + // "email", + // "profile", + // "openid" + // }; + + // var accounts = await app.GetAccountsAsync(); + // string accessToken = ""; + // if (accounts.Any()) + // { + // var result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault()) + // .ExecuteAsync(); + // accessToken = result.AccessToken; + // } + // else + // { + // var interactiveResult = await app + // .AcquireTokenInteractive(scopes) + // .ExecuteAsync(); + + // accessToken = interactiveResult.AccessToken; + // var email = interactiveResult.Account.Username; + // } + + // return accessToken; + // } + // catch (Exception ex) + // { + // // Tratamento de erro + // Console.WriteLine($"Erro ao obter token: {ex.Message}"); + // return null; + // } + //} + + public long GetTokenExpirationTime(string token) + { + var handler = new JwtSecurityTokenHandler(); + var jwtSecurityToken = handler.ReadJwtToken(token); + var tokenExp = jwtSecurityToken.Claims.First(claim => claim.Type.Equals("exp")).Value; + var ticks = long.Parse(tokenExp); + return ticks; + } + + public bool CheckTokenIsValid(string token) + { + var tokenTicks = GetTokenExpirationTime(token); + var tokenDate = DateTimeOffset.FromUnixTimeSeconds(tokenTicks).UtcDateTime; + + var now = DateTime.Now.ToUniversalTime(); + + var valid = tokenDate >= now; + + return valid; + } + } +} 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..4b35f93 --- /dev/null +++ b/Program.cs @@ -0,0 +1,61 @@ +using DomvsChatBot; +using DomvsChatBot.Bot; +using DomvsChatBot.Comm; +using DomvsChatBot.LocalData; +using DomvsChatBot.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); + +// Create the Bot Framework Adapter with error handling enabled. +builder.Services.AddSingleton(); + + +//TODO: Remover quando adicionar LiteDB +builder.Services.AddSingleton(); + +builder.Services.AddScoped(); +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/PublishProfiles/registry.redecarneir.us.pubxml b/Properties/PublishProfiles/registry.redecarneir.us.pubxml new file mode 100644 index 0000000..5d933b8 --- /dev/null +++ b/Properties/PublishProfiles/registry.redecarneir.us.pubxml @@ -0,0 +1,18 @@ + + + + + Container + NetSdk + registry.redecarneir.us + latest + ContainerRegistry + Release + Any CPU + linux-x64 + 9f6d5611-03e4-4272-aaac-4f1e9447a1e9 + <_TargetId>NetSdkCustomContainerRegistry + + \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..08a037e --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "profiles": { + "Start Project": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5130", + "hotReloadProfile": "aspnetcore" + }, + "Teams App Test Tool": { + "commandName": "Project", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "TestTool", + "TEAMSFX_NOTIFICATION_STORE_FILENAME": ".notification.testtoolstore.json" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5130", + "hotReloadProfile": "aspnetcore" + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": false + } + } +} \ No newline at end of file diff --git a/PushContainer.bat b/PushContainer.bat new file mode 100644 index 0000000..db6a68e --- /dev/null +++ b/PushContainer.bat @@ -0,0 +1,4 @@ +cd C:\vscode\OpenRHCall +docker build -t open-rh-call . +docker tag domvs-chat-bot registry.redecarneir.us/domvs-chat-bot:latest +docker push registry.redecarneir.us/domvs-chat-bot:latest 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..1833645 --- /dev/null +++ b/TeamsApp/TeamsApp.ttkproj @@ -0,0 +1,12 @@ + + + + ea5fd4c0-e5a1-4e35-a713-350e3249bb83 + + + + + + + + \ No newline at end of file diff --git a/TeamsApp/appPackage.stg.zip b/TeamsApp/appPackage.stg.zip new file mode 100644 index 0000000..1a71360 Binary files /dev/null and b/TeamsApp/appPackage.stg.zip differ 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.json b/TeamsApp/appPackage/manifest.json new file mode 100644 index 0000000..e93ffd3 --- /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.1", + "id": "${{TEAMS_APP_ID}}", + "developer": { + "name": "Domvs iT - Ricardo Carneiro", + "websiteUrl": "https://www.domvsit.com", + "privacyUrl": "https://www.domvsit.com/privacy", + "termsOfUseUrl": "https://www.domvsit.com/termofuse" + }, + "icons": { + "color": "technical-support192.png", + "outline": "outline.png" + }, + "name": { + "short": "Domvs IA", + "full": "Chat com IA e robô de solicitações" + }, + "description": { + "short": "Chat com IA e robô de solicitações", + "full": "Esta aplicação permite conversar e ter assitencia em liguamgem natural. Além disso, você pode fazer perguntas sobre o portifólio de serviços Domvs It e até abrir solicitações ao RH" + }, + "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.stg.json b/TeamsApp/appPackage/manifest.stg.json new file mode 100644 index 0000000..a1ac078 --- /dev/null +++ b/TeamsApp/appPackage/manifest.stg.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": "Domvs iT - Ricardo Carneiro", + "websiteUrl": "https://www.domvsit.com", + "privacyUrl": "https://www.domvsit.com/privacy", + "termsOfUseUrl": "https://www.domvsit.com/termofuse" + }, + "icons": { + "color": "technical-support192.png", + "outline": "outline.png" + }, + "name": { + "short": "Domvs IA", + "full": "Chat com IA e robô de solicitações" + }, + "description": { + "short": "Chat com IA e robô de solicitações", + "full": "Esta aplicação permite conversar e ter assitencia em liguamgem natural. Além disso, você pode fazer perguntas sobre o portifólio de serviços Domvs It e até abrir solicitações ao RH" + }, + "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..10d9476 --- /dev/null +++ b/TeamsApp/env/.env.dev @@ -0,0 +1,16 @@ +# 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= +SECRET_BOT_PASSWORD= \ No newline at end of file diff --git a/TeamsApp/env/.env.local b/TeamsApp/env/.env.local new file mode 100644 index 0000000..730c0f0 --- /dev/null +++ b/TeamsApp/env/.env.local @@ -0,0 +1,19 @@ +# 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=1457d032-b35b-4553-b5fc-3da43ceba3f9 +TEAMS_APP_ID=04dff49e-3e13-4f42-a79e-6bd8035bcf1f +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 + +#BOT_ENDPOINT=https://vrzg2z27-5130.brs.devtunnels.ms +BOT_ENDPOINT=https://api.lhost.dynu.net +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/env/.env.prod b/TeamsApp/env/.env.prod new file mode 100644 index 0000000..5566649 --- /dev/null +++ b/TeamsApp/env/.env.prod @@ -0,0 +1,18 @@ +# 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=stg +APP_NAME_SUFFIX=stg + +# Generated during provision, you can also add your own variables. +BOT_ID=1457d032-b35b-4553-b5fc-3da43ceba3f9 +TEAMS_APP_ID=d7b58e81-c48e-436f-964c-0ab57b52ba95 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 + +TEAMSFX_M365_USER_NAME=ricardo.carneiro@domvsit.com.br + +BOT_ENDPOINT=https://vrzg2z27-5130.brs.devtunnels.ms +BOT_DOMAIN=vrzg2z27-5130.brs.devtunnels.ms \ No newline at end of file diff --git a/TeamsApp/env/.env.stg b/TeamsApp/env/.env.stg new file mode 100644 index 0000000..4137157 --- /dev/null +++ b/TeamsApp/env/.env.stg @@ -0,0 +1,19 @@ +# 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=stg +APP_NAME_SUFFIX=stg + +# Generated during provision, you can also add your own variables. +BOT_ID=1457d032-b35b-4553-b5fc-3da43ceba3f9 +TEAMS_APP_ID=d7b58e81-c48e-436f-964c-0ab57b52ba95 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc +BOT_OBJECT_ID=dee85fe4-1f93-4f33-98eb-2ce668621232 + +TEAMSFX_M365_USER_NAME=ricardo.carneiro@domvsit.com.br + +#BOT_ENDPOINT=https://vrzg2z27-5130.brs.devtunnels.ms +BOT_ENDPOINT=https://api.lhost.dynu.net +BOT_DOMAIN=vrzg2z27-5130.brs.devtunnels.ms \ 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..edae770 --- /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": "Domvs IA" + } + } + } \ 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..b132cb6 --- /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: DomvsChatBot${{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: DomvsChatBot${{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: DomvsChatBot + 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.stg.yml b/TeamsApp/teamsapp.stg.yml new file mode 100644 index 0000000..b132cb6 --- /dev/null +++ b/TeamsApp/teamsapp.stg.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: DomvsChatBot${{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: DomvsChatBot${{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: DomvsChatBot + 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..1a4199b --- /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: DomvsIA-${{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: DomvsIA-${{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 DomvsChatBot.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: 8c10d301-cb55-4445-b100-971f13405827 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..b72702f --- /dev/null +++ b/appsettings.json @@ -0,0 +1,17 @@ +{ + "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", + "BaseUrlApi": "https://api.lhost.dynu.net" +}