Quizspiele bieten ein fesselndes und lehrreiches Erlebnis, bei dem Sie neue Fakten erfahren und Ihr Wissen über verschiedene Themen hinweg erweitern können. Heutzutage sind Trivia-Quiz-Mobil- und Webanwendungen die häufigsten Anlaufstellen für eine solche Aktivität. Wie wäre es mit einem Quizspiel auf WhatsApp?
In diesem Tutorial-Leitfaden erfahren Sie, wie Sie mit Twilio für WhatsApp, ASP.NET Core und eine Trivia-Quizanwendung erstellen
Um diese Fragen abzurufen, verwenden Sie die Trivia API, eine REST-API, die es Entwicklern erleichtert, Quiz-Apps zu erstellen, indem sie Multiple-Choice-Trivia-Fragen bereitstellt. Um mehr über die Trivia-API zu erfahren, besuchen Sie bitte die__ Trivia-API-Dokumentation__ .
Um dieses Tutorial abzuschließen, benötigen Sie:
Führen Sie zunächst über Ihr Shell-Terminal in einem bevorzugten Arbeitsverzeichnis die folgenden Befehle aus, um ein neues Web-API-Projekt zu erstellen:
dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi
Der zweite Befehl im obigen Snippet erstellt ein neues Web-API-Projekt mit dem angegebenen Namen und ohne OpenAPI-Unterstützung (Swagger). Wenn Sie Swagger im Projekt verwenden möchten, lassen Sie im obigen Befehl einfach --no-openapi
weg.
Wechseln Sie in das Projektverzeichnis, indem Sie diesen Befehl ausführen:
cd TwilioWhatsAppTriviaApp
Installiere das
dotnet add package Twilio.AspNet.Core
Diese Bibliothek vereinfacht die Arbeit mit Twilio-Webhooks und APIs in einer ASP.NET Core-Anwendung.
Öffnen Sie das Projekt mit Ihrer bevorzugten IDE. Entfernen Sie im Ordner „Controllers“ die Boilerplate-Controller-Vorlagendatei „WeatherForecastController.cs “ und entfernen Sie auch „ WeatherForcast.cs“ im Projektverzeichnis.
Erstellen Sie Ihr Projekt und führen Sie es aus, um sicherzustellen, dass alles, was Sie bisher getan haben, ordnungsgemäß funktioniert, indem Sie die folgenden Befehle verwenden:
dotnet build dotnet run
Notieren Sie sich nach erfolgreicher Ausführung des Projekts alle Localhost-URLs, die in der Debugging-Konsole angezeigt werden. Sie können jede dieser URLs verwenden, um mit ngrok einen öffentlich zugänglichen lokalen Webserver einzurichten.
Sitzungen sind eine von mehreren Möglichkeiten, die Daten eines Benutzers in einer ASP.NET Core-Anwendung zu speichern. Dies ist wichtig, wenn Sie Benutzerdaten zwischen Anfragen beibehalten möchten, da das HTTP-Protokoll standardmäßig zustandslos ist – das bedeutet, dass Daten nicht erhalten bleiben.
Fügen Sie den In-Memory-Sitzungsanbieter hinzu, indem Sie Program.cs ändern, wie im folgenden Code gezeigt:
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()
registriert den verteilten Speicher-Cache-Dienst. Dieser Dienst stellt einen In-Memory-Cache bereit, der zum Speichern und Abrufen von Daten über mehrere Anfragen oder Sitzungen hinweg verwendet werden kann.
AddSession()
registriert die Sitzungsdienste und ermöglicht der Anwendung, den Sitzungsstatus beizubehalten. Mit dem options
können Sie verschiedene sitzungsbezogene Optionen konfigurieren. IdleTimeout
wird verwendet, um die Dauer der Inaktivität festzulegen, nach der eine Sitzung als inaktiv betrachtet wird. In diesem Fall ist sie auf 40 Sekunden eingestellt. Cookie.IsEssential
stellt sicher, dass der Sitzungsstatus auch in Szenarien, in denen die Cookie-Ablehnung aktiviert ist, funktionsfähig bleibt.
Die Sitzungsunterstützung wird durch das Hinzufügen der UseSession
Middleware zur Anwendungspipeline aktiviert, d. h. Ihre Anwendung erhält Zugriff auf ein Sitzungsobjekt, das zum Speichern und Abrufen von Daten verwendet werden kann.
Erstellen Sie einen neuen Ordner, Models, in Ihrem Projektverzeichnis. Fügen Sie zwei Modellklassendateien hinzu, TriviaApiResponse.cs und Question.cs , mit Eigenschaften, wie in den folgenden Codebeispielen gezeigt:
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; } }
Das TriviaApiResponse
Modell enthält Eigenschaften, die die Felder der Trivia-API-Antwort darstellen. Das JsonProperty
Attribut stellt sicher, dass jede Eigenschaft korrekt mit den entsprechenden JSON-Daten gefüllt wird.
Für eine optimierte Bearbeitung trivialer Fragen kommt die Question
Klasse zum Einsatz. Diese Klasse kapselt die notwendigen Informationen für eine Trivia-Frage, einschließlich des Fragetextes und einer Liste von Optionen. Jede Option wird durch ein Tupel dargestellt, das den Optionstext und einen booleschen Wert enthält, der angibt, ob es sich um die richtige Option handelt.
Erstellen Sie einen Services- Ordner in Ihrem Projektverzeichnis und fügen Sie eine neue Klassendatei mit dem Namen TriviaService.cs hinzu. Ändern Sie den Inhalt, wie im folgenden Code gezeigt:
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; } }
Die Klasse TriviaService
enthält zwei Methoden: GetTrivia
und ConvertTriviaToQuestions
. Die GetTrivia
Methode sendet die HTTP-GET-Anfrage mit dem Abfrageparameter limit=3
an die Trivia-API, der angibt, dass nur drei Fragen zurückgegeben werden sollen. Ohne den limit-Parameter gibt die API standardmäßig 10 Fragen zurück.
Die ConvertTriviaToQuestions
Methode wandelt die Antwort von der API in eine organisierte Form um. Die Methode mischt außerdem alle Frageoptionen nach dem Zufallsprinzip, sodass eine einzelne Option nicht die Antwort auf alle Fragen ist.
Um TriviaService
und den HTTP-Client im Dependency Injection (DI)-Container Ihrer Anwendung zu registrieren, ändern Sie Program.cs wie im folgenden Code gezeigt:
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();
Fügen Sie eine leere API-Controller-Klasse in einer Datei namens TriviaController.cs zum Ordner „Controllers“ hinzu und ändern Sie deren Inhalt wie im folgenden Code gezeigt:
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; } }
Diese Controller-Klasse ist für die Verarbeitung eingehender Nachrichten, die Verwaltung des Sitzungsstatus und die Generierung von Antworten verantwortlich. Es erbt von der TwilioController
Klasse, die von der Twilio.AspNet.Core-Bibliothek bereitgestellt wird und Ihnen Zugriff auf die TwiML
Methode ermöglicht. Mit dieser Methode können Sie antwortenTriviaController
Klasse verwendet HttpContext.Session
Methoden, um mit der Sitzung zu interagieren.
Die gültigen Eingaben sind Elemente in den schreibgeschützten Arrays StartCommands
und OptionValues
. Der Text der eingehenden Nachricht wird mit diesen Elementen verglichen, um sicherzustellen, dass der Benutzer eine korrekte Eingabe gesendet hat. Andernfalls wird eine Nachricht an den Benutzer gesendet, die ihn auffordert, basierend auf dem aktuellen Status des Spiels die richtige Eingabe vorzunehmen. Andere Felder mit dem Präfix „SessionKey“ werden verwendet, um private Konstantenzeichenfolgen für Sitzungsschlüssel im Programm zu definieren.
Die Index
Methode ist die Hauptaktionsmethode, die eingehende HTTP-POST-Anfragen von WhatsApp über die /Trivia- Route verarbeitet. Es lädt die Sitzungsdaten mit HttpContext.Session.LoadAsync()
und ruft mit den Methoden HttpContext.Session.GetString()
und HttpContext.Session.GetInt32()
Daten zum Spielstatus aus der Sitzung ab.
Durch die Verwendung von Unterstrichen (_) und Sternchen (*) am Anfang und Ende bestimmter Zeichenfolgen wird eine kursive bzw. fette Textformatierung in gerenderten WhatsApp-Nachrichten erreicht.
Jede Hilfsmethode im TriviaController
führt eine bestimmte Aufgabe aus, die die Hauptfunktionalität der Klasse unterstützt.
StartGame
Methode initialisiert das Spiel, indem sie Quizfragen abruft, sie in ein für das Spiel geeignetes Format konvertiert und sie in der Sitzung speichert.ProcessUserAnswer
Methode verarbeitet die Antwort des Benutzers auf eine Frage und bestimmt, ob sie richtig ist oder nicht.PresentQuestionWithOptions
ist für die Formatierung und Darstellung einer Frage zusammen mit ihren Optionen verantwortlich.AddNewQuestionsToSession
Methode speichert eine Liste von Fragen in der Sitzung. Es konvertiert die Fragen in das JSON-Format und speichert die JSON-Zeichenfolge in der Sitzung.RetrieveQuestionFromSession
Methode ruft mithilfe des Fragenindex eine Frage aus der Sitzung ab.EndTrivia
Methode generiert eine Nachricht zum Beenden des Quizspiels. Diese Methode entfernt auch Sitzungsdaten im Zusammenhang mit dem Spiel. Basierend auf der Konfiguration des Sitzungsdiensts in Program.cs geschieht dies automatisch, wenn die Sitzung 40 Sekunden lang inaktiv ist.
Um die Anwendung zu testen, müssen Sie Twilio Sandbox für WhatsApp einrichten, Ihren Anwendungsendpunkt öffentlich zugänglich machen und die Endpunkt-URL in der Sandbox-Konfiguration als Webhook hinzufügen.
Gehe zum
Befolgen Sie die Anweisungen auf der Seite, um eine Verbindung zur Sandbox herzustellen, indem Sie eine WhatsApp-Nachricht von Ihrem Gerät an die angegebene Twilio-Nummer senden, um eine erfolgreiche Verbindung mit der WhatsApp-Sandbox herzustellen. Ebenso müssen andere Personen, die Ihre App mit ihren jeweiligen Nummern testen möchten, das gleiche Verfahren befolgen.
Öffnen Sie nun ein Shell-Terminal und führen Sie den folgenden Befehl aus, um ngrok zu starten und Ihre lokale ASP.NET Core-App verfügbar zu machen, indem Sie <localhost-url>
durch die vollständige URL Ihres ursprünglich kopierten Localhosts ersetzen:
ngrok http <localhost-url>
ngrok generiert eine öffentliche URL, die Anfragen an Ihre lokale ASP.NET-App weiterleitet. Suchen Sie im ngrok-Terminalfenster nach der Weiterleitungs-URL mit der Bezeichnung „Forwarding“ und kopieren Sie sie.
Gehen Sie zurück zur Twilio Try WhatsApp-Seite, klicken Sie auf Sandbox-Einstellungen und ändern Sie die Endpunkt-URL „Wenn eine Nachricht eingeht“ durch die von ngrok generierte Weiterleitungs -URL sowie die Route „/Trivia “ und stellen Sie sicher, dass die Methode auf „POST“ eingestellt ist. Klicken Sie dann auf Speichern, um die neue Sandbox-Konfiguration zu speichern.
Führen Sie Ihr ASP.NET Core-Projekt mit dem folgenden Befehl aus:
dotnet run
Testen Sie nun Ihre Anwendung, indem Sie im ersten Gespräch eine Nachricht mit der Twilio Sandbox-Nummer senden.
Durch die Nutzung der Leistungsfähigkeit der Twilio-Plattform und WhatsApp haben Sie ein fesselndes Quizspielerlebnis geschaffen, das die Benutzer genießen können. Sie haben außerdem gelernt, wie Sie Daten aus Sitzungen speichern und abrufen.
Es gibt mehrere Möglichkeiten, dieses Projekt zu verbessern. Sie können dieses Projekt weiter verbessern, indem Sie einen Timer hinzufügen, Ausnahmen behandeln, Benutzern die Auswahl des Schwierigkeitsgrads ermöglichen und den gewählten Schwierigkeitsgrad als Abfrageparameter über die Trivia-API-URL anwenden (z. B