Trivia oyunları, yeni gerçekleri öğrenebileceğiniz ve çeşitli konularda bilginizi genişletebileceğiniz ilgi çekici ve eğitici bir deneyim sunar. Günümüzde Trivia quiz mobil ve web uygulamaları bu tür bir aktivite için en sık başvurulan alanlardır. WhatsApp'ta bilgi yarışması oyunu oynamaya ne dersiniz?
Bu eğitim kılavuzunda, Twilio for WhatsApp, ASP.NET Core ve ASP.NET Core'u kullanarak bir bilgi yarışması uygulamasının nasıl oluşturulacağını öğreneceksiniz.
Bu soruları almak için geliştiricilerin çoktan seçmeli bilgi soruları sunarak sınav uygulamaları oluşturmasını kolaylaştıran bir REST API olan Trivia API'yi kullanacaksınız. Trivia API'si hakkında daha fazla bilgi edinmek için lütfen__ Trivia API belgelerini__ ziyaret edin.
Bu öğreticiyi tamamlamak için ihtiyacınız olacak:
Başlamak için tercih edilen bir çalışma dizininde kabuk terminalinizi kullanarak yeni bir web API projesi oluşturmak üzere aşağıdaki komutları çalıştırın:
dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi
Yukarıdaki kod parçasındaki ikinci komut, belirtilen adla ve OpenAPI (Swagger) desteği olmadan yeni bir web API projesi oluşturacaktır. Projede Swagger'ı kullanmak istiyorsanız yukarıdaki komutta --no-openapi
komutunu atlamanız yeterlidir.
Bu komutu çalıştırarak proje dizinine geçin:
cd TwilioWhatsAppTriviaApp
Yükle
dotnet add package Twilio.AspNet.Core
Bu kitaplık, bir ASP.NET Core uygulamasında Twilio web kancaları ve API'lerle çalışmayı kolaylaştırır.
Tercih ettiğiniz IDE'yi kullanarak projeyi açın. Denetleyiciler klasöründe, ortak şablon denetleyici dosyası WeatherForecastController.cs'yi kaldırın ve ayrıca proje dizinindeki WeatherForcast.cs dosyasını da kaldırın.
Şu ana kadar yaptığınız her şeyin iyi çalıştığından emin olmak için aşağıdaki komutları kullanarak projenizi oluşturun ve çalıştırın:
dotnet build dotnet run
Projeyi başarıyla çalıştırdıktan sonra hata ayıklama konsolunda görünen localhost URL'lerinden herhangi birini not edin. Ngrok'u kullanarak herkesin erişebileceği bir yerel web sunucusu kurmak için bu URL'lerden herhangi birini kullanabilirsiniz.
Oturumlar, bir kullanıcının verilerini ASP.NET Core uygulamasında depolamanın çeşitli yollarından biridir. Varsayılan olarak HTTP protokolü durum bilgisiz olduğundan, yani veriler korunmadığından, kullanıcı verilerini istekler arasında tutmak istediğinizde bu çok önemlidir.
Aşağıdaki kodda gösterildiği gibi Program.cs dosyasını değiştirerek bellek içi oturum sağlayıcısını ekleyin:
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()
dağıtılmış bellek önbellek hizmetini kaydeder. Bu hizmet, birden fazla istek veya oturumda veri depolamak ve almak için kullanılabilecek bir bellek içi önbellek sağlar.
AddSession()
, oturum hizmetlerini kaydederek uygulamanın oturum durumunu korumasını sağlar. options
parametresi oturumla ilgili çeşitli seçenekleri yapılandırmanıza olanak tanır. IdleTimeout
oturumun boşta sayılacağı eylemsizlik süresini ayarlamak için kullanılır. Bu durumda 40 saniyeye ayarlanır. Cookie.IsEssential
, çerez reddinin etkinleştirildiği senaryolarda bile oturumun durumunun işlevsel kalmasını sağlar.
Oturum desteği, UseSession
ara yazılımının uygulama hattına eklenmesiyle etkinleştirilir; yani uygulamanız, verileri depolamak ve almak için kullanılabilecek bir oturum nesnesine erişim kazanır.
Proje dizininizde Modeller adında yeni bir klasör oluşturun. Aşağıdaki kod örneklerinde gösterildiği gibi özelliklere sahip iki model sınıf dosyası ( TriviaApiResponse.cs ve Question.cs) ekleyin:
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; } }
TriviaApiResponse
modeli, Trivia API yanıtının alanlarını temsil eden özellikleri içerir. JsonProperty
özelliği, her özelliğin karşılık gelen JSON verileriyle doğru şekilde doldurulmasını sağlar.
Trivia sorularını basitleştirilmiş bir şekilde ele almak için Question
sınıfı kurtarmaya geliyor. Bu sınıf, soru metni ve seçenekler listesi de dahil olmak üzere, önemsiz bir soru için gerekli bilgileri içerir. Her seçenek, seçenek metnini içeren bir tanımlama grubu ve bunun doğru seçenek olup olmadığını gösteren bir boole değeriyle temsil edilir.
Proje dizininizde bir Hizmetler klasörü oluşturun ve TriviaService.cs adlı yeni bir sınıf dosyası ekleyin. Aşağıdaki kodda gösterildiği gibi içeriğini değiştirin:
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; } }
TriviaService
sınıfı iki yöntem içerir: GetTrivia
ve ConvertTriviaToQuestions
. GetTrivia
yöntemi, HTTP GET isteğini Trivia API'sine, yalnızca 3 sorunun döndürülmesi gerektiğini belirten limit=3
sorgu parametresiyle gönderir. Limit parametresi olmadan API varsayılan olarak 10 soru döndürür.
ConvertTriviaToQuestions
yöntemi, API'den gelen yanıtı organize bir şekilde dönüştürür. Yöntem ayrıca tüm soru seçeneklerini rastgele karıştırır, böylece tek bir seçenek tüm soruların yanıtı olmaz.
TriviaService
ve HTTP istemcisini uygulamanızın Dependency Injection (DI) kapsayıcısına kaydetmek için Program.cs'yi aşağıdaki kodda gösterildiği gibi değiştirin:
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();
TriviaController.cs adlı bir dosyaya boş bir API denetleyici sınıfını Denetleyiciler klasörüne ekleyin ve içeriğini aşağıdaki kodda gösterildiği gibi değiştirin:
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; } }
Bu denetleyici sınıfı, gelen iletilerin işlenmesinden, oturum durumunun yönetilmesinden ve yanıtların oluşturulmasından sorumludur. Twilio.AspNet.Core kitaplığı tarafından sağlanan ve TwiML
yöntemine erişmenizi sağlayan TwilioController
sınıfından miras alır. Yanıt vermek için bu yöntemi kullanabilirsiniz.TriviaController
sınıfı, oturumla etkileşim kurmak için HttpContext.Session
yöntemlerini kullanır.
Geçerli girişler, StartCommands
ve OptionValues
salt okunur dizilerindeki öğelerdir. Kullanıcının doğru bir giriş gönderdiğinden emin olmak için gelen mesajın gövdesi bu öğelerle karşılaştırılır, aksi takdirde kullanıcıya oyunun mevcut durumuna göre doğru girişi yapmasını isteyen bir mesaj gönderilir. “SessionKey” ön ekine sahip diğer alanlar, programdaki oturum anahtarlarına özel sabit dizileri tanımlamak için kullanılır.
Index
yöntemi, WhatsApp'tan /Trivia yolu aracılığıyla gelen HTTP POST isteklerini işleyen ana eylem yöntemidir. Oturum verilerini HttpContext.Session.LoadAsync()
kullanarak yükler ve HttpContext.Session.GetString()
ve HttpContext.Session.GetInt32()
yöntemlerini kullanarak oyun durumuna ilişkin verileri oturumdan alır.
Belirli dizelerin başında ve sonunda alt çizgi (_) ve yıldız işaretlerinin (*) kullanılması, oluşturulan WhatsApp mesajlarında sırasıyla italik ve kalın metin formatı elde etmek içindir.
TriviaController
her yardımcı yöntem, sınıfın ana işlevselliğini destekleyen belirli bir görevi gerçekleştirir.
StartGame
yöntemi trivia sorularını alarak, bunları oyuna uygun formata dönüştürerek ve oturumda saklayarak oyunu başlatır.ProcessUserAnswer
yöntemi kullanıcının bir soruya verdiği yanıtı işleyerek yanıtın doğru olup olmadığını belirler.PresentQuestionWithOptions
yöntemi, bir soruyu seçenekleriyle birlikte biçimlendirmekten ve sunmaktan sorumludur.AddNewQuestionsToSession
yöntemi oturumdaki soruların bir listesini saklar. Soruları JSON formatına dönüştürür ve JSON dizesini oturuma kaydeder.RetrieveQuestionFromSession
yöntemi, soru dizinini kullanarak oturumdan bir soruyu alır.EndTrivia
yöntemi trivia oyununu sonlandırmak için bir mesaj üretir. Bu yöntem aynı zamanda oyuna ilişkin oturum verilerini de kaldırır. Program.cs'deki oturum hizmetinin yapılandırmasına bağlı olarak, oturum 40 saniye boyunca boşta kaldığında bu işlem otomatik olarak gerçekleşir.
Uygulamayı test etmek için WhatsApp için Twilio Sandbox'ı kurmanız, uygulama uç noktanızı herkese açık hale getirmeniz ve uç nokta URL'sini Sandbox yapılandırmasına bir web kancası olarak eklemeniz gerekir.
Şuraya git:
WhatsApp sanal alanıyla başarılı bir bağlantı oluşturmak için cihazınızdan verilen Twilio numarasına bir WhatsApp mesajı göndererek sanal alana bağlanmak için sayfadaki talimatları izleyin. Benzer şekilde uygulamanızı kendi numaralarıyla test etmek isteyen diğer kişilerin de aynı prosedürü izlemesi gerekecektir.
Şimdi, bir kabuk terminali açın ve ngrok'u başlatmak ve yerel ASP.NET Core uygulamanızı ortaya çıkarmak için <localhost-url>
yerine başlangıçta kopyaladığınız localhost'un tam URL'sini koymak için aşağıdaki komutu çalıştırın:
ngrok http <localhost-url>
ngrok, istekleri yerel ASP.NET uygulamanıza ileten genel bir URL oluşturacaktır. Ngrok terminal penceresinde Yönlendirme etiketli yönlendirme URL'sini arayın ve kopyalayın.
Twilio WhatsApp'ı Deneyin sayfasına geri dönün, Sandbox Ayarları'na tıklayın ve Uç noktadaki mesaj geldiğinde URL'yi ngrok artı /Trivia rotası tarafından oluşturulan yönlendirme URL'si ile değiştirin ve yöntemin POST olarak ayarlandığından emin olun. Daha sonra yeni korumalı alan yapılandırmasını kaydetmek için Kaydet'i tıklayın.
ASP.NET Core projenizi aşağıdaki komutu kullanarak çalıştırın:
dotnet run
Şimdi, ilk görüşmede Twilio Sandbox numarasıyla bir mesaj göndererek uygulamanızı test edin.
Twilio platformunun ve WhatsApp'ın gücünden yararlanarak, kullanıcıların keyif alabileceği sürükleyici bir bilgi oyunu deneyimi yarattınız. Ayrıca oturumlardaki verileri nasıl kaydedeceğinizi ve alacağınızı da öğrendiniz.
Bu projenin geliştirilebilmesinin birkaç yolu vardır. Zamanlayıcı ekleyerek, istisnaları ele alarak, kullanıcıların zorluk seçmesine izin vererek ve seçilen zorluğu Trivia API URL'si (örn.