Los juegos de trivia brindan una experiencia atractiva y educativa en la que puedes aprender nuevos datos y ampliar tus conocimientos en diversos temas. Hoy en día, las aplicaciones web y móviles de Trivia Quiz son las áreas de acceso más comunes para este tipo de actividad. ¿Qué tal jugar un juego de preguntas en WhatsApp? En esta guía tutorial, aprenderá cómo crear una aplicación de prueba de trivia usando Twilio para WhatsApp, ASP.NET Core y ( ). El objetivo de este tutorial es crear una aplicación de juego de trivia que permita a los usuarios jugar y responder preguntas de opción múltiple usando WhatsApp. Aprovecharás para almacenar y recuperar el progreso del usuario, realizar un seguimiento de la puntuación y mantener el estado del juego. La API de trivia Licenciado bajo CC BY-NC 4.0. sesiones en ASP.NET Core Para obtener estas preguntas, utilizará The Trivia API, una API REST, que facilita a los desarrolladores la creación de aplicaciones de cuestionarios al proporcionar preguntas de trivia de opción múltiple. Para obtener más información sobre Trivia API, visite la __ __. documentación de Trivia API Requisitos previos Para completar este tutorial, necesitará: Un sistema operativo que admita .NET (Windows/macOS/Linux) SDK de .NET 7 Un editor de código o IDE (Recomendado: , con el , o ) Estudio visual Código de estudio visual complemento C# Jinete de JetBrains CLI ngrok Una cuenta Twilio gratuita o de pago (si no tiene una, puede ) prueba Twilio gratis Experiencia con C# y ASP.NET Core . El código fuente de este tutorial se puede encontrar en GitHub. Configurar un nuevo proyecto ASP.NET Core Para comenzar, usando su terminal shell en un directorio de trabajo preferido, ejecute los siguientes comandos para crear un nuevo proyecto API web: dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi El segundo comando en el fragmento anterior creará un nuevo proyecto de API web con el nombre especificado y sin soporte OpenAPI (Swagger). Si desea utilizar Swagger en el proyecto, simplemente omita en el comando anterior. --no-openapi Cambie al directorio del proyecto ejecutando este comando: cd TwilioWhatsAppTriviaApp Instala el Paquete NuGet: Biblioteca Twilio Helper para ASP.NET Core dotnet add package Twilio.AspNet.Core Esta biblioteca simplifica el trabajo con webhooks y API de Twilio en una aplicación ASP.NET Core. Abra el proyecto usando su IDE preferido. En la carpeta , elimine el archivo de controlador de plantilla repetitiva, , y también elimine en el directorio del proyecto. Controladores WeatherForecastController.cs WeatherForcast.cs Construya y ejecute su proyecto para asegurarse de que todo lo que ha hecho hasta ahora funcione bien usando los siguientes comandos: dotnet build dotnet run Después de ejecutar exitosamente el proyecto, tome nota de cualquiera de las URL de localhost que aparecen en la consola de depuración. Puede utilizar cualquiera de estas URL para configurar un servidor web local de acceso público utilizando ngrok. Implementar sesiones Las sesiones son una de las varias formas de almacenar los datos de un usuario en una aplicación ASP.NET Core. Esto es esencial cuando desea conservar los datos del usuario entre solicitudes porque, de forma predeterminada, el protocolo HTTP no tiene estado, lo que significa que los datos no se conservan. Agregue el proveedor de sesión en memoria modificando , como se muestra en el siguiente código: Program.cs var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromSeconds(40); options.Cookie.IsEssential = true; }); var app = builder.Build(); app.UseSession(); app.MapControllers(); app.Run(); registra el servicio de caché de memoria distribuida. Este servicio proporciona un caché en memoria que se puede utilizar para almacenar y recuperar datos en múltiples solicitudes o sesiones. AddDistributedMemoryCache() registra los servicios de la sesión, lo que permite que la aplicación mantenga el estado de la sesión. El parámetro le permite configurar varias opciones relacionadas con la sesión. se utiliza para establecer la duración de la inactividad después de la cual una sesión se considerará inactiva. En este caso, se establece en 40 segundos. garantiza que el estado de la sesión siga siendo funcional incluso en escenarios donde el rechazo de cookies está habilitado. AddSession() options IdleTimeout Cookie.IsEssential El soporte de sesión se habilita agregando el middleware a la canalización de la aplicación, es decir, su aplicación obtiene acceso a un objeto de sesión que se puede usar para almacenar y recuperar datos. UseSession Crear los modelos Cree una nueva carpeta, en el directorio de su proyecto. Agregue dos archivos de clase de modelo, y con propiedades como se muestra en los siguientes ejemplos de código: Modelos, TriviaApiResponse.cs Question.cs using Newtonsoft.Json; namespace TwilioWhatsAppTriviaApp.Models; public class TriviaApiResponse { [JsonProperty("category")] public string Category { get; set; } [JsonProperty("correctAnswer")] public string CorrectAnswer { get; set; } [JsonProperty("incorrectAnswers")] public List<string> IncorrectAnswers { get; set; } [JsonProperty("question")] public string Question { get; set; } [JsonProperty("type")] public string? Type { get; set; } [JsonProperty("difficulty")] public string Difficulty { get; set; } } namespace TwilioWhatsAppTriviaApp.Models; public class Question { public string QuestionText { get; set; } public List<(string option, bool isCorrect)> Options { get; set; } } El modelo incluye propiedades que representan los campos de la respuesta de Trivia API. El atributo garantiza que cada propiedad se complete correctamente con los datos JSON correspondientes. TriviaApiResponse JsonProperty Para una forma simplificada de manejar las preguntas de trivia, la clase viene al rescate. Esta clase resume la información necesaria para una pregunta de trivia, incluido el texto de la pregunta y una lista de opciones. Cada opción está representada por una tupla que contiene el texto de la opción y un valor booleano que indica si es la opción correcta. Question Agregar la clase de servicio Trivia Cree una carpeta en el directorio de su proyecto y agregue un nuevo archivo de clase llamado . Modifique su contenido, como se muestra en el siguiente código: de Servicios TriviaService.cs using Newtonsoft.Json; using TwilioWhatsAppTriviaApp.Models; namespace TwilioWhatsAppTriviaApp.Services; public class TriviaService { private const string TheTriviaApiUrl = @"https://the-trivia-api.com/api/questions?limit=3"; private HttpClient httpClient; public TriviaService(HttpClient httpClient) { this.httpClient = httpClient; } public async Task<IEnumerable<TriviaApiResponse>> GetTrivia() { var response = await httpClient.GetAsync(TheTriviaApiUrl); var triviaJson = await response.Content.ReadAsStringAsync(); var trivia = JsonConvert.DeserializeObject<IEnumerable<TriviaApiResponse>>(triviaJson); return trivia; } public List<Question> ConvertTriviaToQuestions(IEnumerable<TriviaApiResponse> questions) { List<Question> newQuestions = new(); foreach (var question in questions) { var options = new List<(string option, bool isCorrect)>() { (question.CorrectAnswer, true), (question.IncorrectAnswers[0], false), (question.IncorrectAnswers[1], false), (question.IncorrectAnswers[2], false) }; // Shuffle the options randomly Random random = new(); options = options.OrderBy(_ => random.Next()).ToList(); newQuestions.Add(new Question { QuestionText = question.Question, Options = options }); } return newQuestions; } } La clase contiene dos métodos: y . El método envía la solicitud HTTP GET a la API de Trivia con un parámetro de consulta, , que especifica que solo se deben devolver 3 preguntas. Sin el parámetro de límite, la API devuelve 10 preguntas de forma predeterminada. TriviaService GetTrivia ConvertTriviaToQuestions GetTrivia limit=3 El método convierte la respuesta de la API de forma organizada. El método también mezcla aleatoriamente todas las opciones de preguntas, de modo que una sola opción no será la respuesta a todas las preguntas. ConvertTriviaToQuestions Para registrar y el cliente HTTP en el contenedor de inyección de dependencia (DI) de su aplicación, modifique como se muestra en el siguiente código: TriviaService Program.cs using TwilioWhatsAppTriviaApp.Services; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromSeconds(40); options.Cookie.IsEssential = true; }); builder.Services.AddHttpClient(); builder.Services.AddScoped<TriviaService>(); var app = builder.Build(); app.UseSession(); app.MapControllers(); app.Run(); Crear el controlador de trivia Agregue una clase de controlador API vacía en un archivo llamado a la carpeta y modifique su contenido como se muestra en el siguiente código: TriviaController.cs Controladores using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Twilio.AspNet.Core; using Twilio.TwiML; using Twilio.TwiML.Messaging; using TwilioWhatsAppTriviaApp.Models; using TwilioWhatsAppTriviaApp.Services; namespace WhatsappTrivia.Controllers; [Route("[controller]")] [ApiController] public class TriviaController : TwilioController { private const string SessionKeyIsGameOn = "IsGameOn"; private const string SessionKeyScore = "Score"; private const string SessionKeyCurrentQuestionIndex = "CurrentQuestionIndex"; private const string SessionKeyTotalQuestions = "TotalQuestions"; private const string SessionKeyQuestions = "Questions"; private static readonly string[] StartCommands = { "START", "S" }; private static readonly string[] OptionValues = { "A", "B", "C", "D" }; private readonly TriviaService triviaService; public TriviaController(TriviaService triviaService) { this.triviaService = triviaService; } [HttpPost] public async Task<IActionResult> Index() { var response = new MessagingResponse(); var form = await Request.ReadFormAsync(); var body = form["Body"].ToString().ToUpper().Trim(); await HttpContext.Session.LoadAsync(); var isGameOn = Convert.ToBoolean(HttpContext.Session.GetString(SessionKeyIsGameOn)); int currentQuestionIndex = HttpContext.Session.GetInt32(SessionKeyCurrentQuestionIndex) ?? 0; int totalQuestions = HttpContext.Session.GetInt32(SessionKeyTotalQuestions) ?? 0; if (StartCommands.Contains(body) && !isGameOn) { await StartGame(); HttpContext.Session.SetString(SessionKeyIsGameOn, "true"); response.Message(PresentQuestionWithOptions(currentQuestionIndex)); return TwiML(response); } if (OptionValues.Contains(body) && isGameOn) { var result = ProcessUserAnswer(body, currentQuestionIndex); response.Message(result); currentQuestionIndex++; if (currentQuestionIndex <= totalQuestions - 1) { HttpContext.Session.SetInt32(SessionKeyCurrentQuestionIndex, currentQuestionIndex); response.Append(new Message(PresentQuestionWithOptions(currentQuestionIndex))); } else { response.Append(new Message(EndTrivia())); } return TwiML(response); } response.Message(!isGameOn ? "*Hello! Send 'Start' or 'S' to play game*" : "*Invalid Input! Send a correct option 'A', 'B', 'C' or 'D'*"); return TwiML(response); } private async Task StartGame() { if (HttpContext.Session.GetString(SessionKeyQuestions) != null) { HttpContext.Session.Remove(SessionKeyQuestions); } var trivia = await this.triviaService.GetTrivia(); var questions = this.triviaService.ConvertTriviaToQuestions(trivia); AddNewQuestionsToSession(questions); HttpContext.Session.SetInt32(SessionKeyTotalQuestions, questions.Count); } private string ProcessUserAnswer(string userAnswer, int questionIndex) { bool optionIsCorrect = false; int score = HttpContext.Session.GetInt32(SessionKeyScore) ?? 0; var question = RetrieveQuestionFromSession(questionIndex); switch (userAnswer) { case "A": optionIsCorrect = question.Options[0].isCorrect; break; case "B": optionIsCorrect = question.Options[1].isCorrect; break; case "C": optionIsCorrect = question.Options[2].isCorrect; break; case "D": optionIsCorrect = question.Options[3].isCorrect; break; } if (optionIsCorrect) { score++; HttpContext.Session.SetInt32(SessionKeyScore, score); } return optionIsCorrect ? "_Correct ✅_" : $"_Incorrect ❌ Correct answer is {question.Options.Find(o => o.isCorrect).option.TrimEnd()}_"; } private string PresentQuestionWithOptions(int questionIndex) { var question = RetrieveQuestionFromSession(questionIndex); return $""" {questionIndex + 1}. {question.QuestionText} {OptionValues[0]}. {question.Options[0].option} {OptionValues[1]}. {question.Options[1].option} {OptionValues[2]}. {question.Options[2].option} {OptionValues[3]}. {question.Options[3].option} """; } private void AddNewQuestionsToSession(List<Question> questions) => HttpContext.Session.SetString(SessionKeyQuestions, JsonConvert.SerializeObject(questions)); private Question RetrieveQuestionFromSession(int questionIndex) { var questionsFromSession = HttpContext.Session.GetString(SessionKeyQuestions); return JsonConvert.DeserializeObject<List<Question>>(questionsFromSession)[questionIndex]; } private string EndTrivia() { var score = HttpContext.Session.GetInt32(SessionKeyScore) ?? 0; var totalQuestions = HttpContext.Session.GetInt32(SessionKeyTotalQuestions) ?? 0; var userResult = $""" Thanks for playing! 😊 You answered {score} out of {totalQuestions} questions correctly. To play again, send 'Start' or 'S' """; HttpContext.Session.Clear(); return userResult; } } Esta clase de controlador es responsable de manejar los mensajes entrantes, administrar el estado de la sesión y generar respuestas. Hereda de la clase proporcionada por la biblioteca Twilio.AspNet.Core, que le brinda acceso al método . Puede utilizar este método para responder con . La clase utiliza métodos para interactuar con la sesión. Las entradas válidas son elementos de las matrices de solo lectura y . El cuerpo del mensaje entrante se compara con estos elementos para garantizar que el usuario envió una entrada adecuada; de lo contrario, se enviará un mensaje al usuario solicitándole que realice la entrada correcta según el estado actual del juego. Otros campos con el prefijo "SessionKey" se utilizan para definir cadenas constantes privadas para las claves de sesión en el programa. TwilioController TwiML TwiML, que es el lenguaje de marcado Twilio TriviaController HttpContext.Session StartCommands OptionValues El método es el método de acción principal que maneja las solicitudes HTTP POST entrantes de WhatsApp a través de la ruta . Carga los datos de la sesión usando y recupera datos sobre el estado del juego de la sesión usando los métodos y . Index /Trivia HttpContext.Session.LoadAsync() HttpContext.Session.GetString() HttpContext.Session.GetInt32() El uso de guiones bajos (_) y asteriscos (*) al principio y al final de ciertas cadenas es para lograr formato de texto en cursiva y negrita, respectivamente, en los mensajes de WhatsApp renderizados. Cada método auxiliar en realiza una tarea específica que admite la funcionalidad principal de la clase. TriviaController El método inicializa el juego recuperando preguntas de trivia, convirtiéndolas a un formato adecuado para el juego y almacenándolas en la sesión. StartGame El método procesa la respuesta del usuario a una pregunta y determina si es correcta o no. ProcessUserAnswer El método es responsable de formatear y presentar una pregunta junto con sus opciones. PresentQuestionWithOptions El método almacena una lista de preguntas en la sesión. Convierte las preguntas al formato JSON y guarda la cadena JSON en la sesión. AddNewQuestionsToSession El método recupera una pregunta de la sesión utilizando el índice de preguntas. RetrieveQuestionFromSession El método genera un mensaje para finalizar el juego de trivia. Este método también elimina los datos de la sesión relacionados con el juego. Según la configuración del servicio de sesión en , esto sucede automáticamente cuando la sesión está inactiva durante 40 segundos. EndTrivia Program.cs Pruebe la aplicación Para probar la aplicación, debe configurar Twilio Sandbox para WhatsApp, hacer que el punto final de su aplicación sea de acceso público y agregar la URL del punto final en la configuración de Sandbox como un webhook. Configurar Twilio Sandbox para WhatsApp Ve a la , navega hasta . Consola Twilio Mensaje > Pruébalo > Enviar un mensaje de WhatsApp Siga las instrucciones en la página para conectarse al sandbox, enviando un mensaje de WhatsApp desde su dispositivo al número de Twilio proporcionado para crear una conexión exitosa con el sandbox de WhatsApp. De manera similar, otras personas que deseen probar su aplicación con sus respectivos números deberán seguir el mismo procedimiento. Exponga su webhook usando ngrok para realizar pruebas Ahora, abra una terminal de shell y ejecute el siguiente comando para iniciar ngrok y exponer su aplicación ASP.NET Core local reemplazando con la URL completa de su localhost que copió inicialmente: <localhost-url> ngrok http <localhost-url> ngrok generará una URL pública que reenviará solicitudes a su aplicación ASP.NET local. Busque la URL de reenvío denominada en la ventana del terminal ngrok y cópiela. Reenvío Vuelva a la página Twilio Pruebe WhatsApp, haga clic en y cambie la URL del punto final con la URL generada por ngrok más la ruta y asegúrese de que el método esté configurado en POST. Luego haga clic en Guardar para guardar la nueva configuración de la zona de pruebas. Configuración de Sandbox Cuando llega un mensaje de reenvío /Trivia Demostración del proyecto Ejecute su proyecto ASP.NET Core usando el siguiente comando: dotnet run Ahora, pruebe su aplicación enviando un mensaje en la conversación inicial con el número de Twilio Sandbox. Conclusión Al aprovechar el poder de la plataforma Twilio y WhatsApp, ha creado una experiencia de juego de trivia inmersiva para que disfruten los usuarios. También aprendió a guardar y recuperar datos de sesiones. Hay varias formas en que se puede mejorar este proyecto. Puede mejorar aún más este proyecto agregando un temporizador, manejando excepciones, permitiendo a los usuarios elegir la dificultad y aplicando la dificultad elegida como parámetro de consulta a través de la URL de la API de Trivia (p. ej. ). Además, se puede explorar la posibilidad de crear una solución que permita a los usuarios completar encuestas a través de WhatsApp. https://the-trivia-api.com/api/questions?difficulty=hard