Compare commits

..

2 Commits

Author SHA1 Message Date
Ricardo Carneiro
bd0f910f1b fix: ajustes 2024-12-22 22:18:13 -03:00
Ricardo Carneiro
0efb2ac75d fix: versão funcional 2024-12-22 15:49:43 -03:00
27 changed files with 833 additions and 201 deletions

View File

@ -2,6 +2,8 @@
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ActiveDebugProfile>http</ActiveDebugProfile> <ActiveDebugProfile>http</ActiveDebugProfile>
<Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor> <DebuggerFlavor>ProjectDebugger</DebuggerFlavor>

View File

@ -50,7 +50,7 @@ namespace ChatApi
else else
{ {
var msg = new List<ChatMessageContent>(); var msg = new List<ChatMessageContent>();
PromptIan(msg); PromptLiliana(msg);
string json = JsonSerializer.Serialize(msg); string json = JsonSerializer.Serialize(msg);
var history = new ChatHistory(JsonSerializer.Deserialize<List<ChatMessageContent>>(json)); var history = new ChatHistory(JsonSerializer.Deserialize<List<ChatMessageContent>>(json));
_keyValues[sessionId] = history; _keyValues[sessionId] = history;
@ -72,10 +72,12 @@ namespace ChatApi
msg.Add(new ChatMessageContent(AuthorRole.User, "Responda sempre em portugues do Brasil as minhas perguntas.")); msg.Add(new ChatMessageContent(AuthorRole.User, "Responda sempre em portugues do Brasil as minhas perguntas."));
} }
public void PromptIan(List<ChatMessageContent> msg) public void PromptLiliana(List<ChatMessageContent> msg)
{ {
msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é Ian, um assistente ajuda empresas de Consultoria e RH. ")); msg.Add(new ChatMessageContent(AuthorRole.System, "Seu nome é LiliAna, um assistente virtual da Domvs It (Consultoria e RH)."));
msg.Add(new ChatMessageContent(AuthorRole.System, "Você responde sempre em português do Brasil.")); msg.Add(new ChatMessageContent(AuthorRole.System, "Você responde sempre em português do Brasil e fala sobre serviços prestados pela Domvs iT."));
msg.Add(new ChatMessageContent(AuthorRole.System, "Você consegue fazer solicitações no portal do RH da Domvs iT."));
msg.Add(new ChatMessageContent(AuthorRole.System, "Você responde perguntas voltadas ao mercado de trabalho de tecnologia."));
msg.Add(new ChatMessageContent(AuthorRole.System, "Em breve, você será capaz de consultar o linkedin.")); msg.Add(new ChatMessageContent(AuthorRole.System, "Em breve, você será capaz de consultar o linkedin."));
msg.Add(new ChatMessageContent(AuthorRole.User, "Use sempre portugues do Brasil.")); msg.Add(new ChatMessageContent(AuthorRole.User, "Use sempre portugues do Brasil."));
} }

View File

@ -15,80 +15,62 @@ using ChatApi.Services.Classifier;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Security.Claims; using System.Security.Claims;
using ChatApi.Models; using ChatApi.Models;
using ChatApi.Data;
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
namespace ChatApi.Controllers namespace ChatApi.Controllers
{ {
[Authorize]
[ApiController] [ApiController]
[Route("[controller]")] [Route("[controller]")]
[Authorize]
public class ChatController : ControllerBase public class ChatController : ControllerBase
{ {
private readonly ILogger<ChatController> _logger; private readonly ILogger<ChatController> _logger;
private readonly TextFilter _textFilter; private readonly TextFilter _textFilter;
private readonly ResponseFactory _responseFactory; private readonly ResponseFactory _responseFactory;
private readonly ClassifierPersistence _classifierPersistence; private readonly ClassifierPersistence _classifierPersistence;
private readonly UserDataRepository _userDataRepository;
private readonly TextData _textData;
public ChatController( public ChatController(
ILogger<ChatController> logger, ILogger<ChatController> logger,
TextFilter textFilter, TextFilter textFilter,
ResponseFactory responseFactory, ResponseFactory responseFactory,
ClassifierPersistence classifierPersistence) ClassifierPersistence classifierPersistence,
UserDataRepository userDataRepository,
TextData textData)
{ {
_logger = logger; _logger = logger;
_textFilter = textFilter; _textFilter = textFilter;
_responseFactory = responseFactory; _responseFactory = responseFactory;
_classifierPersistence = classifierPersistence; _classifierPersistence = classifierPersistence;
_userDataRepository = userDataRepository;
_textData = textData;
} }
[HttpGet(Name = "Response")] [HttpGet]
public async Task<string?> Get([FromQuery] ChatRequest chatRequest) [Route("response")]
public async Task<string?> GetResponse([FromQuery] ChatRequest chatRequest)
{ {
var userData = UserData.Create(User); //var userData = UserData.Create(User);
var textClassifier = new TextClassifier(_textFilter, _classifierPersistence); var textClassifier = new TextClassifier(_textFilter, _classifierPersistence);
var textType = await textClassifier.ClassifyQuestion(chatRequest.SessionId, chatRequest.Message); var textType = await textClassifier.ClassifyQuestion(chatRequest.SessionId, chatRequest.Message);
var needsRestart = textClassifier.NeedsRestart();
var responseText = _responseFactory.GetService(textType); var responseText = _responseFactory.GetService(textType);
var response = await responseText.GetResponse(HttpContext, userData, chatRequest.SessionId, chatRequest.Message); var userData = await _userDataRepository.GeByToekntAsync(AppDomain.CurrentDomain.GetData("Token") as string);
var response = await responseText.GetResponse(HttpContext, userData, chatRequest.SessionId, chatRequest.Message, needsRestart);
return response; return response;
} }
[HttpPost(Name = "LoadDBData")] [HttpPost]
public async Task SaveData([FromQuery] DBLoadRequest loadRequest) [Route("loaddata")]
//public async Task SaveData([FromQuery] DBLoadRequest loadRequest)
public async Task SaveData()
{ {
//string readText = System.IO.File.ReadAllText("C:\\vscode\\ChatApi\\bin\\Debug\\net8.0\\Servicos.txt"); string readText = System.IO.File.ReadAllText("C:\\vscode\\ChatApi\\bin\\Debug\\net8.0\\Servicos.txt");
string readText = loadRequest.Content; //string readText = loadRequest.Content;
await SalvarTextoComEmbeddingNoMongoDB(readText); await _textData.SalvarTextoComEmbeddingNoMongoDB(readText);
}
async Task SalvarTextoComEmbeddingNoMongoDB(string textoCompleto)
{
var textoArray = new List<string>();
string[] textolinhas = textoCompleto.Split(
new string[] { "\n" },
StringSplitOptions.None
);
var title = textolinhas[0];
var builder = new StringBuilder();
foreach (string line in textolinhas)
{
if (line.StartsWith("**") || line.StartsWith("\r**"))
{
if (builder.Length > 0)
{
textoArray.Add(title.Replace("**", "").Replace("\r", "") + ": " + Environment.NewLine + builder.ToString());
builder = new StringBuilder();
title = line;
}
}
else
{
builder.AppendLine(line);
}
}
} }
} }
} }

View File

@ -1,68 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Connectors.InMemory;
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
namespace ChatApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class ChatController : ControllerBase
{
private readonly ILogger<ChatController> _logger;
private readonly ChatHistoryService _chatHistoryService;
private readonly IChatCompletionService _chatCompletionService;
private readonly Kernel _kernel;
public ChatController(ILogger<ChatController> logger, ChatHistoryService chatHistoryService, IChatCompletionService chatCompletionService, Kernel kernel)
{
_logger = logger;
_chatHistoryService = chatHistoryService;
_chatCompletionService = chatCompletionService;
_kernel = kernel;
}
[HttpGet(Name = "Response")]
public async Task<string> Get([FromQuery] ChatRequest chatRequest)
{
var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start();
SessionIdStore sessionIdStore = new SessionIdStore(HttpContext);
var sessionId = sessionIdStore.GetSessionId();
var history = _chatHistoryService.Get(sessionId);
history.AddUserMessage(chatRequest.Message);
var embeddingGenerator = _kernel.GetRequiredService<ITextEmbeddingGenerationService>();
// Setup a memory store and create a memory out of it
#pragma warning disable SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var memoryStore = new InMemoryVectorStore();
#pragma warning restore SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var memory = new SemanticTextMemory(memoryStore, embeddingGenerator);
// Loading it for Save, Recall and other methods
_kernel.ImportPluginFromObject(new TextMemoryPlugin(memory));
string MemoryCollectionName = "MyCustomDataCollection";
var option = new PromptExecutionSettings
{
Memory = memory,
MemoryCollectionName = MemoryCollectionName
};
var response = await _chatCompletionService.GetChatMessageContentAsync(history);
history.AddMessage(response.Role, response.Content ?? "");
_chatHistoryService.UpdateHistory(sessionId, history);
stopWatch.Stop();
return $"{response.Content ?? ""}\n\nTempo: {stopWatch.ElapsedMilliseconds/1000}s";
}
}
}
#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

View File

@ -0,0 +1,121 @@
using ChatApi.Models;
using ChatApi.Services.Crypt;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace ChatApi.Controllers
{
[Route("[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
private readonly IConfigurationManager _configuration;
private readonly UserDataRepository _userDataRepository;
private readonly CryptUtil _cryptUtil;
public LoginController(IConfigurationManager configuration, UserDataRepository userDataRepository, CryptUtil cryptUtil)
{
_configuration = configuration;
_userDataRepository = userDataRepository;
_cryptUtil = cryptUtil;
}
[AllowAnonymous]
[HttpPost]
[Route("token")]
public async Task<IActionResult> Post([FromBody] LoginRequest loginRequest)
{
if (ModelState.IsValid)
{
try
{
var userDataFrom = await _userDataRepository.GetAsync(loginRequest.ClientName, loginRequest.ClientId, loginRequest.ClientSecret);
if (userDataFrom==null)
{
return Unauthorized();
}
var token = "";
if (userDataFrom.LastToken == null && (userDataFrom.DateTimeToken != null && userDataFrom.DateTimeToken.Value.AddHours(24) > DateTime.Now))
{
token = userDataFrom.LastToken;
}
else
{
var claims = new[]
{
new Claim("Sub", userDataFrom.CompanyTenant),
new Claim("NameId", userDataFrom.Name),
new Claim(ClaimTypes.NameIdentifier, loginRequest.ClientId),
new Claim("DhCriado", DateTime.Now.ToString(new CultureInfo("pt-BR"))),
new Claim("TenantId", userDataFrom.CompanyTenant),
new Claim(ClaimTypes.Role, "TeamsUser")
};
var expires = DateTime.UtcNow.AddMinutes(30);
var tokenGen = new JwtSecurityToken
(
issuer: _configuration["Issuer"],
audience: _configuration["Audience"],
claims: claims,
expires: expires,
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["SigningKey"])),
SecurityAlgorithms.HmacSha256)
);
token = new JwtSecurityTokenHandler().WriteToken(tokenGen);
}
userDataFrom.LastToken = token;
userDataFrom.DateTimeToken = DateTime.Now;
await _userDataRepository.UpdateAsync(userDataFrom.Id, userDataFrom);
return Ok(new { token = token });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
return BadRequest();
}
[AllowAnonymous]
[HttpPost]
[Route("newclient")]
public async Task<IActionResult> NewClient([FromBody] UserRequest userDataFrom)
{
if (ModelState.IsValid)
{
try
{
var userData = await _userDataRepository.GetAsync(userDataFrom.Name, userDataFrom.LocalId);
if (userData == null)
{
var secret = _cryptUtil.Encrypt(JsonSerializer.Serialize(userDataFrom));
userData = UserData.Create(userDataFrom, secret);
await _userDataRepository.CreateAsync(userData);
}
return Created("newclient", userData);
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
return BadRequest();
}
}
}

View File

@ -0,0 +1,9 @@
namespace ChatApi.Controllers
{
public class LoginRequest
{
public string ClientId { get; set; }
public string ClientName { get; set; }
public string ClientSecret { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using System.Text.Json.Serialization;
namespace ChatApi.Controllers
{
public class UserRequest
{
public string Name { get; set; }
public string CompanyTenant { get; set; }
public string LocalId { get; set; }
[JsonIgnore]
public string? Secret { get; set; }
}
}

73
Data/TextData.cs Normal file
View File

@ -0,0 +1,73 @@
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Embeddings;
using System.Text;
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
namespace ChatApi.Data
{
public class TextData
{
private readonly ITextEmbeddingGenerationService _textEmbeddingGenerationService;
private readonly SharepointDomvsService _sharepointDomvsService;
public TextData(ITextEmbeddingGenerationService textEmbeddingGenerationService, SharepointDomvsService sharepointDomvsService)
{
_textEmbeddingGenerationService = textEmbeddingGenerationService;
_sharepointDomvsService = sharepointDomvsService;
}
public async Task SalvarTextoComEmbeddingNoMongoDB(string textoCompleto)
{
var textoArray = new List<string>();
string[] textolinhas = textoCompleto.Split(
new string[] { "\n" },
StringSplitOptions.None
);
var title = textolinhas[0];
var builder = new StringBuilder();
foreach (string line in textolinhas)
{
if (line.StartsWith("**") || line.StartsWith("\r**"))
{
if (builder.Length > 0)
{
textoArray.Add(title.Replace("**", "").Replace("\r", "") + ": " + Environment.NewLine + builder.ToString());
builder = new StringBuilder();
title = line;
}
}
else
{
builder.AppendLine(line);
}
}
foreach(var item in textoArray)
{
await SalvarNoMongoDB(item);
}
}
private async Task SalvarNoMongoDB(string texto)
{
// Gerar embedding para o texto
var embedding = await _textEmbeddingGenerationService.GenerateEmbeddingAsync(texto);
// Converter embedding para um formato serializável (como um array de floats)
var embeddingArray = embedding.ToArray().Select(e => (double)e).ToArray();
// Criar documento MongoDB com conteúdo e embedding
var documento = new TextoComEmbedding
{
Conteudo = texto,
Embedding = embeddingArray
};
await _sharepointDomvsService.CreateAsync(documento);
}
}
}
#pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

View File

@ -0,0 +1,45 @@
using ChatApi.Models;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace ChatApi
{
public class UserDataRepository
{
private readonly IMongoCollection<UserData> _userCollection;
public UserDataRepository(
IOptions<DomvsDatabaseSettings> bookStoreDatabaseSettings)
{
var mongoClient = new MongoClient(
bookStoreDatabaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
bookStoreDatabaseSettings.Value.DatabaseName);
_userCollection = mongoDatabase.GetCollection<UserData>(
bookStoreDatabaseSettings.Value.UserDataName);
}
public async Task<List<UserData>> GetAsync() =>
await _userCollection.Find(_ => true).ToListAsync();
public async Task<UserData?> GeByToekntAsync(string token) =>
await _userCollection.Find(x => x.LastToken == token).FirstOrDefaultAsync();
public async Task<UserData?> GetAsync(string id) =>
await _userCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
public async Task<UserData?> GetAsync(string name, string localId) =>
await _userCollection.Find(x => x.Name == name && x.LocalId == localId).FirstOrDefaultAsync();
public async Task<UserData?> GetAsync(string name, string localId, string secret) =>
await _userCollection.Find(x => x.Name == name && x.LocalId == localId && x.Secret == secret).FirstOrDefaultAsync();
public async Task CreateAsync(UserData newBook) =>
await _userCollection.InsertOneAsync(newBook);
public async Task UpdateAsync(string id, UserData updatedBook) =>
await _userCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
public async Task RemoveAsync(string id) =>
await _userCollection.DeleteOneAsync(x => x.Id == id);
}
}

View File

@ -8,6 +8,8 @@
public string SharepointCollectionName { get; set; } = null!; public string SharepointCollectionName { get; set; } = null!;
public string UserDataName { get; set; } = null!;
public string ChatBotRHCollectionName { get; set; } = null!; public string ChatBotRHCollectionName { get; set; } = null!;
public string ClassifierCollectionName { get; set; } = null!; public string ClassifierCollectionName { get; set; } = null!;

View File

@ -1,29 +1,37 @@
using System.Security.Claims; using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using System.Security.Claims;
using ChatApi.Controllers;
namespace ChatApi.Models namespace ChatApi.Models
{ {
public class UserData public class UserData
{ {
private UserData() public UserData()
{ {
} }
public string Email { get; set; } [BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string LocalId { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Secret { get; set; }
public string CompanyTenant { get; set; }
public string? Email { get; set; }
public string? LastToken { get; set; }
public DateTime? DateTimeToken { get; set; }
public static UserData Create(ClaimsPrincipal user) public static UserData Create(UserRequest userRequest, string secret)
{ {
var email = user.FindFirst(ClaimTypes.Email)?.Value
?? user.FindFirst("email")?.Value;
var name = user.FindFirst(ClaimTypes.Name)?.Value
?? user.FindFirst("name")?.Value;
return new UserData return new UserData
{ {
Email = email, Id = Guid.NewGuid().ToString("N"),
Name = name Name = userRequest.Name,
CompanyTenant = userRequest.CompanyTenant,
LocalId = userRequest.LocalId,
Secret = secret
}; };
} }
} }

View File

@ -1,15 +1,25 @@
using ChatApi; using ChatApi;
using ChatApi.Data;
using ChatApi.Services; using ChatApi.Services;
using ChatApi.Services.Bot; using ChatApi.Services.Bot;
using ChatApi.Services.Bot.Structs; using ChatApi.Services.Bot.Structs;
using ChatApi.Services.Classifier; using ChatApi.Services.Classifier;
using ChatApi.Services.Crypt;
using ChatApi.Services.ResponseService; using ChatApi.Services.ResponseService;
using ChatApi.Settings; using ChatApi.Settings;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel; using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.ChatCompletion;
using OllamaSharp; using OllamaSharp;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection.Metadata;
using System.Text;
using static OllamaSharp.OllamaApiClient;
using static System.Net.Mime.MediaTypeNames;
#pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
@ -20,7 +30,34 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "apichat", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer'[space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
builder.Services.Configure<DomvsDatabaseSettings>( builder.Services.Configure<DomvsDatabaseSettings>(
builder.Configuration.GetSection("DomvsDatabase")); builder.Configuration.GetSection("DomvsDatabase"));
@ -38,6 +75,9 @@ builder.Services.AddScoped<ChatBotRHCall>();
builder.Services.AddScoped<IResponseService, ResponseChatService>(); builder.Services.AddScoped<IResponseService, ResponseChatService>();
builder.Services.AddScoped<IResponseService, ResponseCompanyService>(); builder.Services.AddScoped<IResponseService, ResponseCompanyService>();
builder.Services.AddScoped<IResponseService, ResponseBotRHService>(); builder.Services.AddScoped<IResponseService, ResponseBotRHService>();
builder.Services.AddTransient<UserDataRepository>();
builder.Services.AddTransient<TextData>();
builder.Services.AddSingleton<CryptUtil>();
//builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://localhost:11435")); //builder.Services.AddOllamaChatCompletion("phi3.5", new Uri("http://localhost:11435"));
@ -63,6 +103,7 @@ builder.Services.AddOllamaTextEmbeddingGeneration("all-minilm", new Uri("http://
//); //);
builder.Services.AddKernel(); builder.Services.AddKernel();
//builder.Services.AddKernel() //builder.Services.AddKernel()
// .AddOllamaChatCompletion("phi3", new Uri("http://localhost:11435")) // .AddOllamaChatCompletion("phi3", new Uri("http://localhost:11435"))
// .AddOllamaTextEmbeddingGeneration() // .AddOllamaTextEmbeddingGeneration()
@ -74,22 +115,34 @@ builder.Services.AddHttpClient();
var tenantId = builder.Configuration.GetSection("AppTenantId"); var tenantId = builder.Configuration.GetSection("AppTenantId");
var clientId = builder.Configuration.GetSection("AppClientID"); var clientId = builder.Configuration.GetSection("AppClientID");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
options.Audience = "api://" + clientId; // Client ID da sua API
options.TokenValidationParameters = new TokenValidationParameters builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
{ {
ValidateIssuer = true, ValidateActor = true,
ValidateAudience = true, ValidateAudience = true,
ValidateLifetime = true, ValidateLifetime = true,
ValidateIssuerSigningKey = true, ValidateIssuerSigningKey = true,
ValidIssuer = $"https://login.microsoftonline.com/{tenantId}/v2.0", ValidIssuer = builder.Configuration["Issuer"],
ValidAudience = "api://" + clientId ValidAudience = builder.Configuration["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["SigningKey"]))
}; };
}); jwtBearerOptions.Events = new JwtBearerEvents()
{
OnTokenValidated = async context =>
{
var token = context.SecurityToken as JsonWebToken;
context.HttpContext.Items["Token"] = token.EncodedToken;
AppDomain.CurrentDomain.SetData("Token", token.EncodedToken);
}
};
})
;
//builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
// .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllers(); builder.Services.AddControllers();
@ -119,6 +172,7 @@ builder.Services.AddControllers();
// }; // };
// }); // });
builder.Services.AddSingleton<IConfigurationManager>(builder.Configuration);
var app = builder.Build(); var app = builder.Build();
@ -131,8 +185,6 @@ if (app.Environment.IsDevelopment())
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.Use(async (context, next) => app.Use(async (context, next) =>

View File

@ -20,6 +20,7 @@ namespace ChatApi.Services.Bot
public Task<Result<string>> Call() public Task<Result<string>> Call()
{ {
var httpClient =_httpClientFactory.CreateClient(); var httpClient =_httpClientFactory.CreateClient();
httpClient.Timeout = TimeSpan.FromSeconds(120);
var url = new Uri(new Uri(_configuration.Url), _configuration.Create); var url = new Uri(new Uri(_configuration.Url), _configuration.Create);
var request = new HttpRequestMessage(HttpMethod.Post, url); var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = new StringContent(System.Text.Json.JsonSerializer.Serialize(_callRH), Encoding.UTF8, "application/json"); request.Content = new StringContent(System.Text.Json.JsonSerializer.Serialize(_callRH), Encoding.UTF8, "application/json");
@ -35,6 +36,7 @@ namespace ChatApi.Services.Bot
try try
{ {
errorContent = response.Content.ReadAsStringAsync().Result; errorContent = response.Content.ReadAsStringAsync().Result;
if (errorContent.ToLower().Contains("timeout")) errorContent = "O servidor do pipefy demorou um pouco para responder. Verifique se a solicitação foi aberta em seu email!";
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -51,10 +53,10 @@ namespace ChatApi.Services.Bot
_callRH = new _callRH = new
{ {
Nome = knowParameters["Nome"], Nome = knowParameters["Nome"],
Email = knowParameters["Email"], Email = answers[2],
WhatsApp = answers[0], WhatsApp = answers[1],
TipoSolicitacao = answers[1], TipoSolicitacao = answers[3],
Descricao = answers[2], Descricao = answers[4],
}; };
return Task.FromResult(Result.Ok()); return Task.FromResult(Result.Ok());

View File

@ -1,5 +1,6 @@
using ChatApi.Models; using ChatApi.Models;
using ChatApi.Services.Bot.Structs; using ChatApi.Services.Bot.Structs;
using ChatApi.Services.Emails;
using ChatApi.Settings; using ChatApi.Settings;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.ChatCompletion;
@ -51,7 +52,7 @@ namespace ChatApi.Services.Bot
_chatRHApiConfig = chatRHApiConfig; _chatRHApiConfig = chatRHApiConfig;
_config = config; _config = config;
} }
public string SetAnswer(string sessionId, string resposta) public string SetAnswer(string sessionId, string resposta, bool needsRestart = false)
{ {
if (_chatbot==null) if (_chatbot==null)
{ {
@ -61,7 +62,7 @@ namespace ChatApi.Services.Bot
return _chatbot.SetAnswer(resposta); return _chatbot.SetAnswer(resposta);
} }
public async Task<string> GetNextQuestion() public async Task<string?> GetNextQuestion()
{ {
return await _chatbot.GetNextQuestion(); return await _chatbot.GetNextQuestion();
} }
@ -76,6 +77,11 @@ namespace ChatApi.Services.Bot
return _chatbot.HasNextQuestion(); return _chatbot.HasNextQuestion();
} }
public async Task<string?> CallFinalAction()
{
return await _chatbot.CallFinalAction();
}
public void Init(string sessionId) public void Init(string sessionId)
{ {
var persist = ChatPersistence.Create(_config.Value).SetBotName("RHCall"); var persist = ChatPersistence.Create(_config.Value).SetBotName("RHCall");
@ -92,20 +98,27 @@ namespace ChatApi.Services.Bot
"Qual seu número de celular (com WhatsApp)?", "Qual seu número de celular (com WhatsApp)?",
resposta => !string.IsNullOrEmpty(resposta) && resposta.Length >= 10)); resposta => !string.IsNullOrEmpty(resposta) && resposta.Length >= 10));
_chatbot.AddQuestion(new Question(
"Qual seu e-mail?",
resposta => EmailValidate.IsValidEmail(resposta)));
var builder = new StringBuilder(); var builder = new StringBuilder();
_opcoesSolicitação.Select(s => s.Value).ToList().ForEach(s => builder.AppendLine(s)); _opcoesSolicitação.Select(s => s.Value).ToList().ForEach(s => builder.AppendLine(s));
var validNumbers = _opcoesSolicitação.Select(s => s.Value.Substring(0, s.Value.IndexOf("-")-1)).ToList();
_chatbot.AddQuestion(new Question( _chatbot.AddQuestion(new Question(
$"Indique o tipo de solicitação: \n {builder}", $"Indique o tipo de solicitação: \n {builder}",
resposta => resposta == "1" || resposta == "2" || resposta == "3")); resposta => validNumbers.IndexOf(resposta) != -1,
null,
text => _opcoesSolicitação.FirstOrDefault(s => s.Value.Substring(0, s.Value.IndexOf("-")-1) == text).Key));
_chatbot.AddQuestion(new Question( _chatbot.AddQuestion(new Question(
"Texto/Descrição da solicitação (em caso de dúvidas)", "Texto/Descrição da solicitação (em caso de dúvidas)",
resposta => !string.IsNullOrEmpty(resposta))); resposta => !string.IsNullOrEmpty(resposta)));
_chatbot.AddQuestion(new Question( _chatbot.AddQuestion(new Question(
"Tudo bem? Posso enviar sua solicitação? Ou prefere que eu tente fazer algum ajuste no texto da descrição?", "Tudo bem? Posso enviar sua solicitação? Ou prefere que eu tente fazer algum ajuste no texto da descrição? (SIM ou NÃO)",
resposta => !string.IsNullOrEmpty(resposta))); resposta => resposta.ToUpper() == "SIM" || resposta.ToUpper() == "NÃO"));
} }
private void AddNumbersBeforShow(Dictionary<string, string> dict) private void AddNumbersBeforShow(Dictionary<string, string> dict)
{ {

View File

@ -0,0 +1,171 @@
using Amazon.Auth.AccessControlPolicy;
using ChatApi.Models;
using Microsoft.SemanticKernel.ChatCompletion;
namespace ChatApi.Services.Bot.Structs
{
public class ChatBot
{
private readonly List<Question> _questions;
private readonly ChatPersistence _persistence;
private readonly Dictionary<int, string> _answers;
private readonly IChatCompletionService _chatCompletionService;
private readonly IActionCall _actionCall;
private readonly string _usuarioId;
private readonly UserData _userData;
private Dictionary<string, string> _knowParameters;
private int _indiceAtual;
public ChatBot(ChatPersistence persistence, string usuarioId, UserData userData, IChatCompletionService chatCompletionService, IActionCall actionCall)
{
_chatCompletionService = chatCompletionService;
_actionCall = actionCall;
_questions = new List<Question>();
_answers = new Dictionary<int, string>();
_knowParameters = new Dictionary<string, string>();
_indiceAtual = 0;
_persistence = persistence;
_usuarioId = usuarioId;
_userData = userData;
var estado = _persistence.GetState(_usuarioId);
if (estado != null)
{
_indiceAtual = estado.IndicePerguntaAtual;
_answers = estado.Respostas;
}
else
{
_indiceAtual = 0;
}
}
public void SetParameter(string key, string value)
{
_knowParameters[key] = value;
}
public void AddQuestion(Question Question)
{
_questions.Add(Question);
}
public string SetAnswer(string resposta)
{
if (!this.HasNextQuestion()) return "";
var state = _persistence.GetState(_usuarioId);
if (state != null)
{
_questions[_indiceAtual].CountTry = state.TentativasPerguntaAtual;
}
else
{
_knowParameters = new Dictionary<string, string>()
{
{ "Nome", _userData.Name }
};
}
var perguntaAtual = _questions[_indiceAtual];
var testByChat = TestAnswerByChat(resposta);
var abort = TestIfWantToAbort(resposta);
var validResp = perguntaAtual.TryToAnswer(resposta);
if (string.IsNullOrEmpty(validResp) && !abort.Contains("ABORTAR") && perguntaAtual.Validator(resposta) && testByChat.Contains("SIM"))
{
_answers[_indiceAtual] = perguntaAtual.ConvertToSave == null ? resposta : perguntaAtual.ConvertToSave(resposta);
_indiceAtual++;
if (_indiceAtual < _questions.Count())
{
_persistence.SaveState(new ChatState
{
Id = _usuarioId,
UsuarioId = _usuarioId,
IndicePerguntaAtual = _indiceAtual,
TentativasPerguntaAtual = _questions[_indiceAtual].CountTry,
Respostas = new Dictionary<int, string>(_answers)
});
}
return "";
}
else if (validResp.Contains("reiniciar") || abort.Contains("ABORTAR"))
{
_persistence.DeleteState(_usuarioId);
return "REINICIAR";
}
_persistence.SaveState(new ChatState
{
Id = _usuarioId,
UsuarioId = _usuarioId,
IndicePerguntaAtual = _indiceAtual,
TentativasPerguntaAtual = _questions[_indiceAtual].CountTry,
DadosConhecidos = _knowParameters,
Respostas = new Dictionary<int, string>(_answers)
});
return $"Resposta inválida. Tente novamente. {(testByChat.Contains("NÃO") ? "Motivo: " + testByChat.Replace("NÂO", "") : "")} \n\n {perguntaAtual.Text}";
}
public string GetCurrentQuestion() => _questions[_indiceAtual].Text;
public async Task<string> GetNextQuestion()
{
while (_indiceAtual < _questions.Count)
{
var perguntaAtual = _questions[_indiceAtual];
if (perguntaAtual.MustShow(_answers))
{
return perguntaAtual.Text;
}
_indiceAtual++;
}
//_knowParameters = new Dictionary<string, string>()
// {
// { "Nome", _userData.Name }
// };
//var resp = await _actionCall.Populate(_knowParameters, _answers);
//if (!resp.Success) return "Obrigado por responder a nossas perguntas!";
//var result = await _actionCall.Call();
//return result.Success ? result.Value : "Obrigado por responder a nossas perguntas!";
}
public async Task<string?> CallFinalAction()
{
_knowParameters = new Dictionary<string, string>()
{
{ "Nome", _userData.Name }
};
var resp = await _actionCall.Populate(_knowParameters, _answers);
if (!resp.Success) return "Obrigado por responder a nossas perguntas!";
var result = await _actionCall.Call();
return result.Success ? result.Value : "Obrigado por responder a nossas perguntas!";
}
public string TestAnswerByChat(string resposta)
{
var resp = _chatCompletionService.GetChatMessageContentAsync($"Por favor, responda com SIM/NÂO e o motivo para eu saber se a pergunta: ({this.GetCurrentQuestion()}) foi respondida pelo usuário quando ele digitou: ({resposta})? É um chatbot, por isso preciso saber apenas se a resposta faz sentido e/ou tem o formato certo.").Result;
return resp.Content;
}
public string TestIfWantToAbort(string resposta)
{
if (_indiceAtual==0) return "";
var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Foi feita a pergunta para o usuario: ({this.GetCurrentQuestion()}) e ele digitou: ({resposta})? Ele respondeu a pergunta com a informação solicitada, ou ele está pedindo para sair do chat? Responda com (SIM = Ele quer sair ou não quer responder) ou (NÃO = Ele responde a pergunta de maneira coerente) e indique o motivo.").Result;
return resp.Content.Contains("SIM") ? "ABORTAR" : "";
}
public bool HasNextQuestion()
{
return _indiceAtual < _questions.Count;
}
}
}

View File

@ -1,6 +1,7 @@
using Amazon.Auth.AccessControlPolicy; using Amazon.Auth.AccessControlPolicy;
using ChatApi.Models; using ChatApi.Models;
using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.ChatCompletion;
using System.Text;
namespace ChatApi.Services.Bot.Structs namespace ChatApi.Services.Bot.Structs
{ {
@ -49,11 +50,27 @@ namespace ChatApi.Services.Bot.Structs
_questions.Add(Question); _questions.Add(Question);
} }
public string SetAnswer(string resposta) public string SetAnswer(string resposta, bool needsRestart = false)
{ {
if (!this.HasNextQuestion()) return ""; var stops = new List<string>()
{
"ABORTAR",
"ABORTE",
"SAIR",
"SAIA",
"REINICIAR",
"REINICIE"
};
if (!this.HasNextQuestion()) {
SetQuestionIndex(_usuarioId, true);
return "";
}
var state = _persistence.GetState(_usuarioId); var state = _persistence.GetState(_usuarioId);
SetQuestionIndex(_usuarioId, needsRestart);
if (state != null) if (state != null)
{ {
_questions[_indiceAtual].CountTry = state.TentativasPerguntaAtual; _questions[_indiceAtual].CountTry = state.TentativasPerguntaAtual;
@ -62,20 +79,33 @@ namespace ChatApi.Services.Bot.Structs
{ {
_knowParameters = new Dictionary<string, string>() _knowParameters = new Dictionary<string, string>()
{ {
{ "Nome", _userData.Name }, { "Nome", _userData.Name }
{ "Email", _userData.Email }
}; };
} }
//var testAll = TestAllQuestions(resposta);
//if (int.TryParse(testAll, out int index))
// _indiceAtual = index < 0 ? _indiceAtual : index - 1;
//var abort = resposta.ToUpper();
//if (index < 0) abort= "ABORTAR";
//var perguntaAtual = _questions[_indiceAtual];
//var testByChat = TestAnswerByChat(resposta);
var perguntaAtual = _questions[_indiceAtual]; var perguntaAtual = _questions[_indiceAtual];
var testByChat = TestAnswerByChat(resposta); var abort = resposta.ToUpper();
var abort = TestIfWantToAbort(resposta);
var validResp = perguntaAtual.TryToAnswer(resposta); var validResp = perguntaAtual.TryToAnswer(resposta);
if (string.IsNullOrEmpty(validResp) && !abort.Contains("ABORTAR") && perguntaAtual.Validator(resposta) && testByChat.Contains("SIM")) //if (string.IsNullOrEmpty(validResp) && !abort.Contains("ABORTAR") && perguntaAtual.Validator(resposta) && testByChat.Contains("SIM"))
if (string.IsNullOrEmpty(validResp) && !stops.Contains(abort))
{ {
_answers[_indiceAtual] = resposta; _answers[_indiceAtual] = perguntaAtual.ConvertToSave == null ? resposta : perguntaAtual.ConvertToSave(resposta);
_indiceAtual++; _indiceAtual++;
if (_indiceAtual < _questions.Count())
{
_persistence.SaveState(new ChatState _persistence.SaveState(new ChatState
{ {
Id = _usuarioId, Id = _usuarioId,
@ -84,6 +114,7 @@ namespace ChatApi.Services.Bot.Structs
TentativasPerguntaAtual = _questions[_indiceAtual].CountTry, TentativasPerguntaAtual = _questions[_indiceAtual].CountTry,
Respostas = new Dictionary<int, string>(_answers) Respostas = new Dictionary<int, string>(_answers)
}); });
}
return ""; return "";
} }
@ -103,7 +134,8 @@ namespace ChatApi.Services.Bot.Structs
Respostas = new Dictionary<int, string>(_answers) Respostas = new Dictionary<int, string>(_answers)
}); });
return $"Resposta inválida. Tente novamente. {(testByChat.Contains("NÃO") ? "Motivo: " + testByChat.Replace("NÂO", "") : "")} \n\n {perguntaAtual.Text}"; //return $"Resposta inválida. Tente novamente. {(testByChat.Contains("NÃO") ? "Motivo: " + testByChat.Replace("NÂO", "") : "")} \n\n {perguntaAtual.Text}";
return $"Resposta inválida. Tente novamente. \n{perguntaAtual.Text} \nSua resposta: {resposta}";
} }
public string GetCurrentQuestion() => _questions[_indiceAtual].Text; public string GetCurrentQuestion() => _questions[_indiceAtual].Text;
@ -119,6 +151,16 @@ namespace ChatApi.Services.Bot.Structs
_indiceAtual++; _indiceAtual++;
} }
return null;
}
public async Task<string?> CallFinalAction()
{
_knowParameters = new Dictionary<string, string>()
{
{ "Nome", _userData.Name }
};
var resp = await _actionCall.Populate(_knowParameters, _answers); var resp = await _actionCall.Populate(_knowParameters, _answers);
if (!resp.Success) return "Obrigado por responder a nossas perguntas!"; if (!resp.Success) return "Obrigado por responder a nossas perguntas!";
@ -135,14 +177,42 @@ namespace ChatApi.Services.Bot.Structs
public string TestIfWantToAbort(string resposta) public string TestIfWantToAbort(string resposta)
{ {
if (_indiceAtual==0) return ""; if (_indiceAtual == 0) return "";
var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Foi feita a pergunta para o usuario: ({this.GetCurrentQuestion()}) e ele digitou: ({resposta})? Ele respondeu a pergunta com a informação solicitada, ou ele está pedindo para sair do chat? Responda com (SIM = Ele quer sair ou não quer responder) ou (NÃO = Ele responde a pergunta de maneira coerente) e indique o motivo.").Result; var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Foi feita a pergunta para o usuario: ({this.GetCurrentQuestion()}) e ele digitou: ({resposta})? Ele respondeu a pergunta com a informação solicitada, ou ele está pedindo para sair do chat? Responda com (SIM = Ele quer sair ou não quer responder) ou (NÃO = Ele responde a pergunta de maneira coerente) e indique o motivo.").Result;
return resp.Content.Contains("SIM") ? "ABORTAR" : ""; return resp.Content.Contains("SIM") ? "ABORTAR" : "";
} }
public string? TestAllQuestions(string resposta)
{
if (_indiceAtual == 0) return "";
var builder = new StringBuilder();
for (int i = 0; i < _questions.Count; i++)
{
builder.AppendLine($"{i + 1} - {_questions[i].Text}");
}
var resp = _chatCompletionService.GetChatMessageContentAsync($"Este é um chatbot. Você pode identificar se a resposta do usuario: \"{resposta}\" tem a inteção de responder uma das perguntas: { builder.ToString() } ? Por favor, reponda com o número da pergunta que o usuário tentou responder, ou com a palavra -1 se não identificou, ou por ultimo com -2 se identificar que o usuário não quis responder nenhuma das perguntas.").Result;
return resp.Content;
}
public bool HasNextQuestion() public bool HasNextQuestion()
{ {
return _indiceAtual < _questions.Count; return _indiceAtual < _questions.Count;
} }
private void SetQuestionIndex(string usuarioId, bool needsRestart)
{
var estado = _persistence.GetState(usuarioId);
if (estado != null && !needsRestart)
{
_indiceAtual = estado.IndicePerguntaAtual;
}
else
{
_indiceAtual = 0;
}
}
} }
} }

View File

@ -6,15 +6,17 @@
public Func<string, bool> Validator { get; } public Func<string, bool> Validator { get; }
public int CountTry { get; set; } public int CountTry { get; set; }
public Func<Dictionary<int, string>, bool> Condition { get; } public Func<Dictionary<int, string>, bool> Condition { get; }
public Func<string, string> ConvertToSave { get; }
private const int MaxTentativas = 2; private const int MaxTentativas = 2;
public Question(string texto, Func<string, bool> validador, Func<Dictionary<int, string>, bool> condition=null) public Question(string texto, Func<string, bool> validador, Func<Dictionary<int, string>, bool> condition=null, Func<string,string> convertToSave = null)
{ {
Text = texto; Text = texto;
Validator = validador; Validator = validador;
CountTry = 0; CountTry = 0;
Condition = condition; Condition = condition;
ConvertToSave = convertToSave;
} }
public bool MustShow(Dictionary<int, string> respostasAnteriores) public bool MustShow(Dictionary<int, string> respostasAnteriores)

View File

@ -13,5 +13,6 @@ namespace ChatApi.Services.Classifier
public EnumClassification Classification { get; set; } public EnumClassification Classification { get; set; }
public DateTime DhInicio { get; set; } public DateTime DhInicio { get; set; }
public EnumClassificationType ClassificationType { get; set; } public EnumClassificationType ClassificationType { get; set; }
public EnumClassification LastClassification { get; set; }
} }
} }

View File

@ -0,0 +1,66 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace ChatApi.Services.Crypt
{
public class CryptUtil
{
private readonly IConfigurationManager _configuration;
public CryptUtil(IConfigurationManager configuration)
{
_configuration = configuration;
}
public string Encrypt(string text)
{
string key = _configuration.GetSection("DataKey").Value;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
aes.GenerateIV(); // Cria um vetor de inicialização aleatório
byte[] iv = aes.IV;
using (var encryptor = aes.CreateEncryptor(aes.Key, iv))
using (var ms = new MemoryStream())
{
// Primeiro, escreva o IV para o fluxo (será necessário para descriptografia)
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var writer = new StreamWriter(cs))
{
writer.Write(text);
}
return Convert.ToBase64String(ms.ToArray());
}
}
}
public string Descrypt(string text)
{
string key = _configuration.GetSection("DataKey").Value;
byte[] bytes = Convert.FromBase64String(text);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key.PadRight(32).Substring(0, 32));
// Extrair o IV do início dos dados criptografados
byte[] iv = new byte[aes.BlockSize / 8];
Array.Copy(bytes, 0, iv, 0, iv.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
using (var ms = new MemoryStream(bytes, iv.Length, bytes.Length - iv.Length))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (var reader = new StreamReader(cs))
{
return reader.ReadToEnd();
}
}
}
}
}

View File

@ -0,0 +1,24 @@
namespace ChatApi.Services.Emails
{
public class EmailValidate
{
public static bool IsValidEmail(string email)
{
var trimmedEmail = email.Trim();
if (trimmedEmail.EndsWith("."))
{
return false; // suggested by @TK-421
}
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == trimmedEmail;
}
catch
{
return false;
}
}
}
}

View File

@ -5,6 +5,6 @@ namespace ChatApi.Services.ResponseService
public interface IResponseService public interface IResponseService
{ {
EnumClassification Classification { get; } EnumClassification Classification { get; }
Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question); Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question, bool needsRestart = false);
} }
} }

View File

@ -32,42 +32,61 @@ namespace ChatApi.Services.ResponseService
} }
public EnumClassification Classification => EnumClassification.BotRHCall; public EnumClassification Classification => EnumClassification.BotRHCall;
public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question) public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question, bool needsRestart = false)
{ {
var stopWatch = new System.Diagnostics.Stopwatch(); var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start(); stopWatch.Start();
SessionIdStore sessionIdStore = new SessionIdStore(context); SessionIdStore sessionIdStore = new SessionIdStore(context);
ChatHistory history = _chatHistoryService.GetSumarizer(sessionId); ChatHistory history = _chatHistoryService.GetSumarizer(sessionId);
_chatBotCall.UserData = userData; _chatBotCall.UserData = userData;
var resp = _chatBotCall.SetAnswer(sessionId, question); var resp = _chatBotCall.SetAnswer(sessionId, question, needsRestart);
var resposta = ""; var resposta = "";
if (string.IsNullOrEmpty(resp) && resp!="REINICIAR")
try
{
if (string.IsNullOrEmpty(resp) && resp != "REINICIAR" && ( question.ToUpper() != "REINICIAR" && question.ToUpper() != "SAIR"))
{ {
resposta = await _chatBotCall.GetNextQuestion(); resposta = await _chatBotCall.GetNextQuestion();
history.AddUserMessage(question);
history.AddMessage(AuthorRole.Assistant, resposta ?? ""); history.AddUserMessage(question);
history.AddMessage(AuthorRole.Assistant, resposta ?? question);
_chatHistoryService.UpdateHistory(sessionId, history); _chatHistoryService.UpdateHistory(sessionId, history);
if (!_chatBotCall.HasNextQuestion()) if (!_chatBotCall.HasNextQuestion())
{ {
resposta = await _chatBotCall.CallFinalAction();
_classifierPersistence.DeleteState(sessionId); _classifierPersistence.DeleteState(sessionId);
_chatBotCall.SetAnswer(sessionId, "REINICIAR", true);
} }
} }
else else
{ {
if (resp == "REINICIAR") if (!needsRestart && resp == "REINICIAR" || (question.ToUpper() != "REINICIAR" || question.ToUpper() != "SAIR"))
{ {
_classifierPersistence.DeleteState(sessionId); _classifierPersistence.DeleteState(sessionId);
_chatBotCall.SetAnswer(sessionId, "REINICIAR", true);
resp = "Ok! Parece que você não quer continuar e/ou eu não entendi sua resposta. Tudo bem! Se quiser, tente novamente depois..."; resp = "Ok! Parece que você não quer continuar e/ou eu não entendi sua resposta. Tudo bem! Se quiser, tente novamente depois...";
} }
else
{
resp = "Quero abrir uma nova solicitação ao RH.";
await GetResponse(context, userData, sessionId, resp, true);
}
resposta = resp; resposta = resp;
} }
}
catch (Exception ex)
{
resposta = "Tivemos um problema. Teremos que reiniciar as perguntas se vc disser que precisa de uma solicitação de rh novamente. Desculpe!";
_classifierPersistence.DeleteState(sessionId);
}
finally
{
stopWatch.Stop(); stopWatch.Stop();
}
return $"{resposta ?? ""}\n\nTempo: {stopWatch.ElapsedMilliseconds / 1000}s"; return $"{resposta ?? ""}\n\nTempo: {stopWatch.ElapsedMilliseconds / 1000}s";
} }
} }

View File

@ -20,7 +20,7 @@ namespace ChatApi.Services.ResponseService
} }
public EnumClassification Classification => EnumClassification.Chat; public EnumClassification Classification => EnumClassification.Chat;
public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question) public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question, bool needsRestart = false)
{ {
var stopWatch = new System.Diagnostics.Stopwatch(); var stopWatch = new System.Diagnostics.Stopwatch();

View File

@ -32,7 +32,7 @@ namespace ChatApi.Services.ResponseService
} }
public EnumClassification Classification => EnumClassification.Company; public EnumClassification Classification => EnumClassification.Company;
public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question) public async Task<string> GetResponse(HttpContext context, UserData userData, string sessionId, string question, bool needsRestart = false)
{ {
var stopWatch = new System.Diagnostics.Stopwatch(); var stopWatch = new System.Diagnostics.Stopwatch();
stopWatch.Start(); stopWatch.Start();

View File

@ -19,6 +19,11 @@ namespace ChatApi.Services
{ {
private readonly TextFilter _textFilter; private readonly TextFilter _textFilter;
private readonly ClassifierPersistence _classifierPersistence; private readonly ClassifierPersistence _classifierPersistence;
private bool _needsRestart = false;
public bool NeedsRestart()
{
return _needsRestart;
}
public TextClassifier(TextFilter textFilter, ClassifierPersistence classifierPersistence) public TextClassifier(TextFilter textFilter, ClassifierPersistence classifierPersistence)
{ {
@ -45,11 +50,14 @@ namespace ChatApi.Services
var classify = classifyCompany.Handle(question); var classify = classifyCompany.Handle(question);
_needsRestart = state != null && classify == EnumClassification.BotRHCall && state.Classification != classify;
_classifierPersistence.SaveState(new ClassifierSate _classifierPersistence.SaveState(new ClassifierSate
{ {
Id = sessionId, Id = sessionId,
UsuarioId = sessionId, UsuarioId = sessionId,
Classification = classify, Classification = classify,
LastClassification = state != null ? state.Classification : classify,
DhInicio = DateTime.Now, DhInicio = DateTime.Now,
ClassificationType = botChat.MyClassification == classify ? EnumClassificationType.Stay : EnumClassificationType.Free ClassificationType = botChat.MyClassification == classify ? EnumClassificationType.Stay : EnumClassificationType.Free
}); });

View File

@ -1,13 +1,13 @@
{ {
"DomvsDatabase": { "DomvsDatabase": {
"ConnectionString": "mongodb://localhost:27017", "ConnectionString": "mongodb+srv://rrcgoncalves:zSYxeWCcY7QlmAmF@cluster0.bmjlo.mongodb.net/?authMechanism=DEFAULT",
"DatabaseName": "DomvsSites", "DatabaseName": "DomvsSites",
"SharepointCollectionName": "SharepointSite", "SharepointCollectionName": "SharepointSite",
"ChatBotRHCollectionName": "ChatBotRHData", "ChatBotRHCollectionName": "ChatBotRHData",
"ClassifierCollectionName": "ClassifierData" "ClassifierCollectionName": "ClassifierData"
}, },
"ChatRHSettings": { "ChatRHSettings": {
"Url": "https://localhost:7070/", "Url": "http://localhost:8070/",
"Create": "/CallRH" "Create": "/CallRH"
}, },
"Logging": { "Logging": {

View File

@ -1,13 +1,14 @@
{ {
"DomvsDatabase": { "DomvsDatabase": {
"ConnectionString": "mongodb://localhost:27017", "ConnectionString": "mongodb+srv://rrcgoncalves:zSYxeWCcY7QlmAmF@cluster0.bmjlo.mongodb.net/?authMechanism=DEFAULT",
"DatabaseName": "DomvsSites", "DatabaseName": "DomvsSites",
"SharepointCollectionName": "SharepointSite", "SharepointCollectionName": "SharepointSite",
"ChatBotRHCollectionName": "ChatBotRHData", "ChatBotRHCollectionName": "ChatBotRHData",
"ClassifierCollectionName": "ClassifierData" "ClassifierCollectionName": "ClassifierData",
"UserDataName": "UserData"
}, },
"ChatRHSettings": { "ChatRHSettings": {
"Url": "mongodb://localhost:27017", "Url": "http://localhost:8070/",
"Create": "DomvsSites" "Create": "DomvsSites"
}, },
"Logging": { "Logging": {
@ -18,5 +19,16 @@
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"AppTenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc", "AppTenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc",
"AppClientID": "8f4248fc-ee30-4f54-8793-66edcca3fd20" "AppClientID": "8f4248fc-ee30-4f54-8793-66edcca3fd20",
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "domvsitbr.onmicrosoft.com",
"TenantId": "20190830-5fd4-4a72-b8fd-1c1cb35b25bc",
"ClientId": "8f4248fc-ee30-4f54-8793-66edcca3fd20"
},
"Issuer": "domvsit.com.br",
"Audience": "domvsit.com.br",
"SigningKey": "D57Ls16KxMPdF4P7qTQtV29slWjJqIJZ",
"DataKey": "NOxGacYtZRJTYCPdSQM75HVSNp3qfH05mPalaE/pL4A6FwxWKQiBhLxhu++LrKsI"
} }