Les jeux-questionnaires offrent une expérience engageante et éducative où vous pouvez apprendre de nouveaux faits et élargir vos connaissances sur divers sujets. De nos jours, les applications mobiles et Web Trivia quiz sont les domaines de prédilection les plus courants pour une telle activité. Que diriez-vous de jouer à un jeu-questionnaire sur WhatsApp ?
Dans ce guide didacticiel, vous apprendrez à créer une application de quiz à l'aide de Twilio pour WhatsApp, ASP.NET Core et
Pour récupérer ces questions, vous utiliserez l'API Trivia, une API REST, qui permet aux développeurs de créer facilement des applications de quiz en proposant des questions à choix multiples. Pour en savoir plus sur l'API Trivia, veuillez consulter la__ documentation de l'API Trivia __.
Pour réaliser ce tutoriel, vous aurez besoin de :
Pour commencer, en utilisant votre terminal shell dans un répertoire de travail préféré, exécutez les commandes suivantes pour créer un nouveau projet d'API Web :
dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi
La deuxième commande de l'extrait ci-dessus créera un nouveau projet d'API Web avec le nom spécifié et sans prise en charge d'OpenAPI (Swagger). Si vous souhaitez utiliser Swagger dans le projet, omettez simplement --no-openapi
dans la commande ci-dessus.
Accédez au répertoire du projet en exécutant cette commande :
cd TwilioWhatsAppTriviaApp
Installez le
dotnet add package Twilio.AspNet.Core
Cette bibliothèque simplifie l'utilisation des webhooks et des API Twilio dans une application ASP.NET Core.
Ouvrez le projet en utilisant votre IDE préféré. Dans le dossier Controllers , supprimez le fichier de contrôleur de modèle standard, WeatherForecastController.cs , et supprimez également WeatherForcast.cs dans le répertoire du projet.
Créez et exécutez votre projet pour vous assurer que tout ce que vous avez fait jusqu'à présent fonctionne bien à l'aide des commandes suivantes :
dotnet build dotnet run
Après avoir exécuté le projet avec succès, notez toutes les URL localhost qui apparaissent dans la console de débogage. Vous pouvez utiliser n'importe laquelle de ces URL pour configurer un serveur Web local accessible au public à l'aide de ngrok.
Les sessions sont l'une des nombreuses façons de stocker les données d'un utilisateur dans une application ASP.NET Core. Ceci est essentiel lorsque vous souhaitez conserver les données utilisateur entre les requêtes car par défaut, le protocole HTTP est sans état, ce qui signifie que les données ne sont pas conservées.
Ajoutez le fournisseur de session en mémoire en modifiant Program.cs , comme indiqué dans le code suivant :
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();
AddDistributedMemoryCache()
enregistre le service de cache de mémoire distribuée. Ce service fournit un cache en mémoire qui peut être utilisé pour stocker et récupérer des données sur plusieurs requêtes ou sessions.
AddSession()
enregistre les services de session, permettant à l'application de maintenir l'état de session. Le paramètre options
vous permet de configurer diverses options liées à la session. IdleTimeout
est utilisé pour définir la durée d'inactivité après laquelle une session sera considérée comme inactive. Dans ce cas, elle est fixée à 40 secondes. Cookie.IsEssential
garantit que l'état de la session reste fonctionnel et même dans les scénarios où le rejet des cookies est activé.
La prise en charge des sessions est activée en ajoutant le middleware UseSession
au pipeline d'application, c'est-à-dire que votre application accède à un objet de session qui peut être utilisé pour stocker et récupérer des données.
Créez un nouveau dossier, Models, dans le répertoire de votre projet. Ajoutez deux fichiers de classe de modèle, TriviaApiResponse.cs et Question.cs avec les propriétés indiquées dans les exemples de code suivants :
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; } }
Le modèle TriviaApiResponse
inclut des propriétés qui représentent les champs de la réponse de l'API Trivia. L'attribut JsonProperty
garantit que chaque propriété est correctement renseignée avec les données JSON correspondantes.
Pour une manière simplifiée de gérer les questions triviales, la classe Question
vient à la rescousse. Cette classe encapsule les informations nécessaires pour une question triviale, y compris le texte de la question et une liste d'options. Chaque option est représentée par un tuple contenant le texte de l'option et une valeur booléenne indiquant si c'est la bonne option.
Créez un dossier Services dans le répertoire de votre projet et ajoutez un nouveau fichier de classe nommé TriviaService.cs . Modifiez son contenu, comme indiqué dans le code suivant :
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 classe TriviaService
contient deux méthodes : GetTrivia
et ConvertTriviaToQuestions
. La méthode GetTrivia
envoie la requête HTTP GET à l'API Trivia avec un paramètre de requête, limit=3
, qui spécifie que seules 3 questions doivent être renvoyées. Sans le paramètre limit, l'API renvoie 10 questions par défaut.
La méthode ConvertTriviaToQuestions
convertit la réponse de l'API de manière organisée. La méthode mélange également toutes les options de questions de manière aléatoire, de sorte qu'une seule option ne soit pas la réponse à toutes les questions.
Pour enregistrer TriviaService
et le client HTTP dans le conteneur Dependency Injection (DI) de votre application, modifiez Program.cs comme indiqué dans le code suivant :
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();
Ajoutez une classe de contrôleur API vide dans un fichier nommé TriviaController.cs au dossier Controllers et modifiez son contenu comme indiqué dans le code suivant :
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; } }
Cette classe de contrôleur est responsable du traitement des messages entrants, de la gestion de l'état de la session et de la génération des réponses. Il hérite de la classe TwilioController
fournie par la bibliothèque Twilio.AspNet.Core, qui vous donne accès à la méthode TwiML
. Vous pouvez utiliser cette méthode pour répondre avecTriviaController
utilise les méthodes HttpContext.Session
pour interagir avec la session.
Les entrées valides sont des éléments des tableaux en lecture seule StartCommands
et OptionValues
. Le corps du message entrant est comparé à ces éléments pour garantir que l'utilisateur a envoyé une entrée appropriée. Dans le cas contraire, un message sera envoyé à l'utilisateur l'invitant à effectuer la bonne entrée en fonction de l'état actuel du jeu. D'autres champs avec le préfixe « SessionKey » sont utilisés pour définir des chaînes constantes privées pour les clés de session dans le programme.
La méthode Index
est la méthode d'action principale qui gère les requêtes HTTP POST entrantes de WhatsApp via la route /Trivia . Il charge les données de session à l'aide de HttpContext.Session.LoadAsync()
et récupère les données concernant l'état du jeu de la session à l'aide des méthodes HttpContext.Session.GetString()
et HttpContext.Session.GetInt32()
.
L'utilisation de traits de soulignement (_) et d'astérisques (*) au début et à la fin de certaines chaînes permet d'obtenir respectivement un formatage de texte en italique et en gras dans les messages WhatsApp rendus.
Chaque méthode d'assistance du TriviaController
effectue une tâche spécifique qui prend en charge les fonctionnalités principales de la classe.
StartGame
initialise le jeu en récupérant les questions triviales, en les convertissant dans un format adapté au jeu et en les stockant dans la session.ProcessUserAnswer
traite la réponse de l'utilisateur à une question et détermine si elle est correcte ou non.PresentQuestionWithOptions
est responsable du formatage et de la présentation d’une question avec ses options.AddNewQuestionsToSession
stocke une liste de questions dans la session. Il convertit les questions au format JSON et enregistre la chaîne JSON dans la session.RetrieveQuestionFromSession
récupère une question de la session à l’aide de l’index des questions.EndTrivia
génère un message pour terminer le jeu-questionnaire. Cette méthode supprime également les données de session liées au jeu. Basé sur la configuration du service de session dans Program.cs , cela se produit automatiquement lorsque la session est inactive pendant 40 secondes.
Pour tester l'application, vous devez configurer Twilio Sandbox pour WhatsApp, rendre le point de terminaison de votre application accessible au public et ajouter l'URL du point de terminaison dans la configuration Sandbox en tant que webhook.
Allez au
Suivez les instructions sur la page pour vous connecter au bac à sable, en envoyant un message WhatsApp depuis votre appareil au numéro Twilio fourni afin de créer une connexion réussie avec le bac à sable WhatsApp. De même, les autres personnes souhaitant tester votre application avec leurs numéros respectifs devront suivre la même procédure.
Maintenant, ouvrez un terminal shell et exécutez la commande suivante pour démarrer ngrok et exposer votre application ASP.NET Core locale en remplaçant <localhost-url>
par l'URL complète de votre hôte local que vous avez copié initialement :
ngrok http <localhost-url>
ngrok générera une URL publique qui transmettra les requêtes à votre application ASP.NET locale. Recherchez l'URL de transfert intitulée Forwarding dans la fenêtre du terminal ngrok et copiez-la.
Revenez à la page Twilio Try WhatsApp, cliquez sur Sandbox Settings et modifiez l'URL When a message comes in endpoint url avec l'URL de transfert générée par ngrok plus la route /Trivia et assurez-vous que la méthode est définie sur POST. Cliquez ensuite sur Enregistrer pour enregistrer la nouvelle configuration du bac à sable.
Exécutez votre projet ASP.NET Core à l'aide de la commande suivante :
dotnet run
Maintenant, testez votre application en envoyant un message lors de la conversation initiale avec le numéro Twilio Sandbox.
En tirant parti de la puissance de la plate-forme Twilio et de WhatsApp, vous avez créé une expérience de jeu-questionnaire immersive dont les utilisateurs peuvent profiter. Vous avez également appris à enregistrer et récupérer les données des sessions.
Il existe plusieurs façons d’améliorer ce projet. Vous pouvez encore améliorer ce projet en ajoutant une minuterie, en gérant les exceptions, en permettant aux utilisateurs de choisir la difficulté et en appliquant la difficulté choisie comme paramètre de requête via l'URL de l'API Trivia (par exemple