using Amazon.Auth.AccessControlPolicy; using ChatApi.Models; using Microsoft.SemanticKernel.ChatCompletion; using System.Text; 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, bool needsRestart = false) { var stops = new List() { "ABORTAR", "ABORTE", "SAIR", "SAIA", "REINICIAR", "REINICIE" }; if (!this.HasNextQuestion()) { SetQuestionIndex(_usuarioId, true); return ""; } var state = _persistence.GetState(_usuarioId); SetQuestionIndex(_usuarioId, needsRestart); if (state != null) { _questions[_indiceAtual].CountTry = state.TentativasPerguntaAtual; } else { _knowParameters = new Dictionary() { { "Nome", _userData.Name } }; } //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 abort = resposta.ToUpper(); var validResp = perguntaAtual.TryToAnswer(resposta); //if (string.IsNullOrEmpty(validResp) && !abort.Contains("ABORTAR") && perguntaAtual.Validator(resposta) && testByChat.Contains("SIM")) if (string.IsNullOrEmpty(validResp) && !stops.Contains(abort)) { _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(_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}"; return $"Resposta inválida. Tente novamente. \n{perguntaAtual.Text} \nSua resposta: {resposta}"; } 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++; } return null; } public async Task CallFinalAction() { _knowParameters = new Dictionary() { { "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 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() { 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; } } } }