using Amazon.Auth.AccessControlPolicy; using ChatApi.Models; using Microsoft.SemanticKernel.ChatCompletion; namespace ChatApi.Services.Bot.Structs { public class ChatBot { private readonly List _questions; private readonly ChatPersistence _persistence; private readonly Dictionary _answers; private readonly IChatCompletionService _chatCompletionService; private readonly IActionCall _actionCall; private readonly string _usuarioId; private readonly UserData _userData; private Dictionary _knowParameters; private int _indiceAtual; public ChatBot(ChatPersistence persistence, string usuarioId, UserData userData, IChatCompletionService chatCompletionService, IActionCall actionCall) { _chatCompletionService = chatCompletionService; _actionCall = actionCall; _questions = new List(); _answers = new Dictionary(); _knowParameters = new Dictionary(); _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() { { "Nome", _userData.Name }, { "Email", _userData.Email } }; } 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] = resposta; _indiceAtual++; _persistence.SaveState(new ChatState { Id = _usuarioId, UsuarioId = _usuarioId, IndicePerguntaAtual = _indiceAtual, TentativasPerguntaAtual = _questions[_indiceAtual].CountTry, Respostas = new Dictionary(_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(_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 GetNextQuestion() { while (_indiceAtual < _questions.Count) { var perguntaAtual = _questions[_indiceAtual]; if (perguntaAtual.MustShow(_answers)) { return perguntaAtual.Text; } _indiceAtual++; } 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; } } }