diff --git a/IABotTeste.sln b/IABotTeste.sln new file mode 100644 index 0000000..f99358a --- /dev/null +++ b/IABotTeste.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35122.118 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IABotTeste", "IABotTeste\IABotTeste.csproj", "{BC00122B-19BF-47CC-AC96-3D5E0C01C553}" +EndProject +Project("{A9E3F50B-275E-4AF7-ADCE-8BE12D41E305}") = "TeamsApp", "TeamsApp\TeamsApp.ttkproj", "{8307838E-7D1A-420E-8224-A4C34A3CD939}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BC00122B-19BF-47CC-AC96-3D5E0C01C553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC00122B-19BF-47CC-AC96-3D5E0C01C553}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC00122B-19BF-47CC-AC96-3D5E0C01C553}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC00122B-19BF-47CC-AC96-3D5E0C01C553}.Release|Any CPU.Build.0 = Release|Any CPU + {8307838E-7D1A-420E-8224-A4C34A3CD939}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8307838E-7D1A-420E-8224-A4C34A3CD939}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8307838E-7D1A-420E-8224-A4C34A3CD939}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8307838E-7D1A-420E-8224-A4C34A3CD939}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ED04290E-CA0D-4CCF-B188-7897C233E089} + EndGlobalSection +EndGlobal diff --git a/IABotTeste/.gitignore b/IABotTeste/.gitignore new file mode 100644 index 0000000..2b7f959 --- /dev/null +++ b/IABotTeste/.gitignore @@ -0,0 +1,29 @@ +# TeamsFx files +build +appPackage/build +env/.env.*.user +env/.env.local +appsettings.Development.json +.deployment + +# User-specific files +*.user + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Notification local store +.notification.localstore.json +.notification.testtoolstore.json + +# devTools +devTools/ \ No newline at end of file diff --git a/IABotTeste/AdapterWithErrorHandler.cs b/IABotTeste/AdapterWithErrorHandler.cs new file mode 100644 index 0000000..414f253 --- /dev/null +++ b/IABotTeste/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 IABotTeste; + +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/IABotTeste/AuthService.cs b/IABotTeste/AuthService.cs new file mode 100644 index 0000000..abc952f --- /dev/null +++ b/IABotTeste/AuthService.cs @@ -0,0 +1,55 @@ +namespace IABotTeste +{ + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Text; + using System.Text.Json.Serialization.Metadata; + using System.Threading.Tasks; + + public class AuthService + { + private readonly HttpClient _httpClient; + + public AuthService(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task GetNewTokenAsync() + { + var bodyObj = new + { + firstName = "Haiping", + lastName = "Chen", + email = "rrcgoncalves@gmail.com", + password = "123456" + }; + var json = JsonConvert.SerializeObject(bodyObj); + var content = new System.Net.Http.StringContent(json, Encoding.UTF8, "application/json"); + + string encoded = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1") + .GetBytes(bodyObj.email + ":" + bodyObj.password)); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://botchat.carneiro.ddnsfree.com/token"); + request.Headers.Add("Authorization", "Basic " + encoded); + request.Content = content; + + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + var responseContent = await response.Content.ReadAsStringAsync(); + // Supondo que o token esteja no campo "access_token" do JSON de resposta + var token = ExtractTokenFromResponse(responseContent); + return token; + } + + private string ExtractTokenFromResponse(string responseContent) + { + dynamic jsonObj = JObject.Parse(responseContent); + return jsonObj.access_token; + } + } +} diff --git a/IABotTeste/Bot/EchoBot.cs b/IABotTeste/Bot/EchoBot.cs new file mode 100644 index 0000000..f688088 --- /dev/null +++ b/IABotTeste/Bot/EchoBot.cs @@ -0,0 +1,210 @@ +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Teams; +using Microsoft.Bot.Schema; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System.Net.Http; +using System.Text; +using System.Diagnostics; + +namespace IABotTeste.Bot; + +public class IaTestBot : TeamsActivityHandler +{ + private readonly IHttpClientFactory httpClientFactory; + private Dictionary _dataStore = new Dictionary(); + + public IaTestBot(IHttpClientFactory httpClientFactory) + { + this.httpClientFactory = httpClientFactory; + } + + protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) + { + await turnContext.SendActivityAsync(new Microsoft.Bot.Schema.Activity { Type = ActivityTypes.Typing }, cancellationToken); + + string messageText = turnContext.Activity.RemoveRecipientMention()?.Trim(); + var replyText = ""; + + try + { + // Crie um cliente HTTP usando a fábrica injetada + var client = httpClientFactory.CreateClient(); + client.Timeout = TimeSpan.FromSeconds(240); + var auth = new AuthService(client); + var tokenService = new TokenService(); + var token = ""; + var member = await TeamsInfo.GetMemberAsync(turnContext, turnContext.Activity.From.Id, cancellationToken); + + var memberData = GetData(member.Id); + + if (memberData?.idConversation != null) + { + token = memberData.token; + + if (!string.IsNullOrEmpty(token) && tokenService.IsTokenExpired(token)) + { + token = await auth.GetNewTokenAsync(); + memberData.token = token; + } + } + else + { + token = await auth.GetNewTokenAsync(); + //var client2 = httpClientFactory.CreateClient(); + var idConv = await GetConversationId(client, token); + memberData = new + { + token = token, + idConversation = idConv + }; + } + AddData(member.Id, memberData); + + await turnContext.SendActivityAsync(new Microsoft.Bot.Schema.Activity { Type = ActivityTypes.Typing }, cancellationToken); + + replyText = await SendMessage(client, memberData, messageText); + + + replyText = replyText.Replace("<|assistant|>", ""); + replyText = removeTags(replyText); + if (replyText.Contains("Instruction")) + { + replyText = replyText.Substring(0, replyText.IndexOf("Instruction")); + } + } + catch (Exception ex) + { + replyText = $"{ex.Message}."; + } + + await turnContext.SendActivityAsync(MessageFactory.Text(replyText), cancellationToken); + } + + private string removeTags(string replyText) + { + string result = ""; + try + { + int start = replyText.IndexOf("<"); + int end = replyText.LastIndexOf(">"); + result = replyText.Replace(replyText.Substring(start, end+1), ""); + } + catch (Exception ex) + { + result = replyText; + } + return result; + } + + protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) + { + var welcomeText = "Bem-vindo!"; + foreach (var member in membersAdded) + { + if (member.Id != turnContext.Activity.Recipient.Id) + { + await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText), cancellationToken); + } + } + } + + public void AddData(string key, dynamic value) + { + var json = JsonConvert.SerializeObject(value); + + if (!_dataStore.ContainsKey(key)) + { + _dataStore.Add(key, json); + } + else + { + _dataStore[key] = json; // Atualiza o valor se a chave já existir + } + } + + public dynamic GetData(string key) + { + if (_dataStore.TryGetValue(key, out string value)) + { + return JObject.Parse(value); + } + return null; // Ou algum valor padrão + } + + public async Task GetConversationId(HttpClient client, string token) + { + try + { + var targetUrl = "http://botchat.carneiro.ddnsfree.com/conversation/cc57f9b1-0c07-49c7-aecb-bd56aa4bf260"; + var request = new HttpRequestMessage(HttpMethod.Post, targetUrl); + var content = new StringContent("{}", Encoding.UTF8, "application/json"); + request.Headers.Add("Authorization", "Bearer " + token); + request.Content = content; + // Configuração do HttpClient (opcional) + //client.Timeout = TimeSpan.FromSeconds(240); + + //Faça a chamada POST para o end - point de destino + //var response = await client.PostAsync(targetUrl, content); + var response = await client.SendAsync(request); + response.EnsureSuccessStatusCode(); + + // Verifique se a resposta foi bem-sucedida (código 200 OK) + if (response.IsSuccessStatusCode) + { + // Leia o stream de resposta + using var responseStream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(responseStream); + var result = await reader.ReadToEndAsync(); + dynamic resObj = JObject.Parse(result); + // Processar os objetos individuais e unificar os campos "part" + return resObj.id; + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + return null; + } + return null; + } + + public async Task SendMessage(HttpClient client, dynamic memberData, string message) + { + var targetUrl = "http://botchat.carneiro.ddnsfree.com/conversation/cc57f9b1-0c07-49c7-aecb-bd56aa4bf260/" + memberData.idConversation; + var request = new HttpRequestMessage(HttpMethod.Post, targetUrl); + var sendContent = new + { + text = message + }; + var contentString = JsonConvert.SerializeObject(sendContent); + var content = new System.Net.Http.StringContent(contentString, Encoding.UTF8, "application/json"); + + request.Headers.Add("Authorization", "Bearer " + memberData.token); + request.Content = content; + // Configuração do HttpClient (opcional) + //client.Timeout = TimeSpan.FromSeconds(240); + + //Faça a chamada POST para o end - point de destino + var response = await client.SendAsync(request); + + // Verifique se a resposta foi bem-sucedida (código 200 OK) + if (response.IsSuccessStatusCode) + { + // Leia o stream de resposta + using var responseStream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(responseStream); + var result = await reader.ReadToEndAsync(); + dynamic resObj = JObject.Parse(result); + // Processar os objetos individuais e unificar os campos "part" + return resObj.text; + } + else + { + return response.ToString(); + } + return null; + } + +} + diff --git a/IABotTeste/Config.cs b/IABotTeste/Config.cs new file mode 100644 index 0000000..1c34f38 --- /dev/null +++ b/IABotTeste/Config.cs @@ -0,0 +1,8 @@ +namespace IABotTeste +{ + public class ConfigOptions + { + public string BOT_ID { get; set; } + public string BOT_PASSWORD { get; set; } + } +} diff --git a/IABotTeste/Controllers/BotController.cs b/IABotTeste/Controllers/BotController.cs new file mode 100644 index 0000000..2b5bd21 --- /dev/null +++ b/IABotTeste/Controllers/BotController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Bot.Builder; +using Microsoft.Bot.Builder.Integration.AspNet.Core; + +namespace IABotTeste.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/IABotTeste/IABotTeste.csproj b/IABotTeste/IABotTeste.csproj new file mode 100644 index 0000000..3dc7920 --- /dev/null +++ b/IABotTeste/IABotTeste.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + + + + + + + + + diff --git a/IABotTeste/Program.cs b/IABotTeste/Program.cs new file mode 100644 index 0000000..c8c8467 --- /dev/null +++ b/IABotTeste/Program.cs @@ -0,0 +1,46 @@ +using IABotTeste; +using IABotTeste.Bot; +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(); + +// 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(); + +// 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(); + +builder.Services.AddHttpClient(); + +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/IABotTeste/Properties/launchSettings.json b/IABotTeste/Properties/launchSettings.json new file mode 100644 index 0000000..4343fa0 --- /dev/null +++ b/IABotTeste/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/IABotTeste/TokenService.cs b/IABotTeste/TokenService.cs new file mode 100644 index 0000000..bfba09a --- /dev/null +++ b/IABotTeste/TokenService.cs @@ -0,0 +1,20 @@ +using System; +using System.IdentityModel.Tokens.Jwt; + +namespace IABotTeste +{ + public class TokenService + { + public bool IsTokenExpired(string token) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; + + if (jwtToken == null) + throw new ArgumentException("Token inválido"); + + var expirationDate = jwtToken.ValidTo; + return expirationDate < DateTime.UtcNow; + } + } +} diff --git a/IABotTeste/appsettings.TestTool.json b/IABotTeste/appsettings.TestTool.json new file mode 100644 index 0000000..1a3d21d --- /dev/null +++ b/IABotTeste/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/IABotTeste/appsettings.json b/IABotTeste/appsettings.json new file mode 100644 index 0000000..d7290d1 --- /dev/null +++ b/IABotTeste/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "BOT_ID": "$botId$", + "BOT_PASSWORD": "$bot-password$" +} diff --git a/TeamsApp/GettingStarted.md b/TeamsApp/GettingStarted.md new file mode 100644 index 0000000..b96d975 --- /dev/null +++ b/TeamsApp/GettingStarted.md @@ -0,0 +1,25 @@ +# 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 + +## 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..b16f96b --- /dev/null +++ b/TeamsApp/TeamsApp.ttkproj @@ -0,0 +1,9 @@ + + + + 8307838e-7d1a-420e-8224-a4c34a3cd939 + + + + + \ No newline at end of file diff --git a/TeamsApp/appPackage/build/appPackage.local.zip b/TeamsApp/appPackage/build/appPackage.local.zip new file mode 100644 index 0000000..ecd103e Binary files /dev/null and b/TeamsApp/appPackage/build/appPackage.local.zip differ diff --git a/TeamsApp/appPackage/build/manifest.local.json b/TeamsApp/appPackage/build/manifest.local.json new file mode 100644 index 0000000..f0a1145 --- /dev/null +++ b/TeamsApp/appPackage/build/manifest.local.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json", + "manifestVersion": "1.16", + "version": "1.0.0", + "id": "2c04e52e-5fa2-4a7f-bee5-3f1945d4b7b7", + "packageName": "com.microsoft.teams.extension", + "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": "DoMinante local", + "full": "IA capaz de explicar os serviços da Domvs iT." + }, + "description": { + "short": "Short description of DoMinante", + "full": "Full description of DoMinante" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "b0db6007-73c3-4991-917e-febe4a006bbc", + "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/color.png b/TeamsApp/appPackage/color.png new file mode 100644 index 0000000..4f3706c Binary files /dev/null and b/TeamsApp/appPackage/color.png differ diff --git a/TeamsApp/appPackage/colorXXX.png b/TeamsApp/appPackage/colorXXX.png new file mode 100644 index 0000000..2d7e85c Binary files /dev/null and b/TeamsApp/appPackage/colorXXX.png differ diff --git a/TeamsApp/appPackage/manifest.json b/TeamsApp/appPackage/manifest.json new file mode 100644 index 0000000..bbd6180 --- /dev/null +++ b/TeamsApp/appPackage/manifest.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json", + "manifestVersion": "1.16", + "version": "1.0.0", + "id": "${{TEAMS_APP_ID}}", + "packageName": "com.microsoft.teams.extension", + "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": "DoMinante ${{APP_NAME_SUFFIX}}", + "full": "IA capaz de explicar os serviços da Domvs iT." + }, + "description": { + "short": "Short description of DoMinante", + "full": "Full description of DoMinante" + }, + "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/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..0b8dcae --- /dev/null +++ b/TeamsApp/env/.env.local @@ -0,0 +1,14 @@ +# 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=b0db6007-73c3-4991-917e-febe4a006bbc +TEAMS_APP_ID=2c04e52e-5fa2-4a7f-bee5-3f1945d4b7b7 +TEAMS_APP_TENANT_ID=20190830-5fd4-4a72-b8fd-1c1cb35b25bc + +BOT_ENDPOINT=https://8cvh8mwm-5130.brs.devtunnels.ms +BOT_DOMAIN=8cvh8mwm-5130.brs.devtunnels.ms +TEAMSFX_M365_USER_NAME=ricardo.carneiro@domvsit.com.br 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..d710f2f --- /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": "IABotTeste" + } + } + } \ 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..1b1f13e --- /dev/null +++ b/TeamsApp/teamsapp.local.yml @@ -0,0 +1,70 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/1.1.0/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: 1.1.0 + +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: IABotTeste${{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: botAadApp/create + with: + # The Microsoft Entra application's display name + name: IABotTeste${{APP_NAME_SUFFIX}} + writeToEnvironmentFile: + # The Microsoft Entra application's client id created for bot. + botId: BOT_ID + # The Microsoft Entra application's client secret created for bot. + botPassword: SECRET_BOT_PASSWORD + + # Generate runtime appsettings to JSON file + - uses: file/createOrUpdateJsonFile + with: + target: ../IABotTeste/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: IABotTeste + messagingEndpoint: ${{BOT_ENDPOINT}}/api/messages + description: "Aplicativo de teste de IA." + 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..8f16ed2 --- /dev/null +++ b/TeamsApp/teamsapp.yml @@ -0,0 +1,99 @@ +# yaml-language-server: $schema=https://aka.ms/teams-toolkit/1.1.0/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: 1.1.0 + +environmentFolderPath: ./env + +# Triggered when 'teamsapp provision' is executed +provision: + # Creates a Teams app + - uses: teamsApp/create + with: + # Teams app name + name: IABotTeste${{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: botAadApp/create + with: + # The Microsoft Entra application's display name + name: IABotTeste${{APP_NAME_SUFFIX}} + writeToEnvironmentFile: + # The Microsoft Entra application's client id created for bot. + botId: BOT_ID + # The Microsoft Entra application's client secret created for bot. + botPassword: SECRET_BOT_PASSWORD + + - 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 IABotTeste.csproj + workingDirectory: ../IABotTeste + # 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: ../IABotTeste +projectId: 9c8fb6c9-2afe-4e09-bbda-a23b6c3f07e8