Trò chơi đố vui mang lại trải nghiệm hấp dẫn và mang tính giáo dục, nơi bạn có thể tìm hiểu các sự kiện mới và mở rộng kiến thức của mình về nhiều chủ đề khác nhau. Ngày nay, các ứng dụng web và di động đố vui Trivia là những lĩnh vực phổ biến nhất cho hoạt động như vậy. Chơi một trò chơi đố vui trên WhatsApp thì sao? Trong hướng dẫn hướng dẫn này, bạn sẽ tìm hiểu cách tạo một ứng dụng đố vui bằng Twilio cho WhatsApp, ASP.NET Core và ( ). Mục đích của hướng dẫn này là tạo ra một ứng dụng trò chơi đố cho phép người dùng chơi và trả lời các câu hỏi trắc nghiệm bằng WhatsApp. Bạn sẽ tận dụng để lưu trữ và truy xuất tiến trình của người dùng, theo dõi điểm số và duy trì trạng thái trò chơi. API câu đố được cấp phép theo CC BY-NC 4.0 phiên trong ASP.NET Core Để tìm nạp những câu hỏi này, bạn sẽ sử dụng API Trivia, một API REST, giúp các nhà phát triển dễ dàng xây dựng ứng dụng bài kiểm tra bằng cách cung cấp các câu hỏi đố trắc nghiệm. Để tìm hiểu thêm về API Trivia, vui lòng truy cập__ __. Tài liệu API Trivia Điều kiện tiên quyết Để hoàn thành hướng dẫn này, bạn sẽ cần: Hệ điều hành hỗ trợ .NET (Windows/macOS/Linux) SDK .NET 7 Trình chỉnh sửa mã hoặc IDE (Được khuyến nghị: , với , hoặc ) Visual Studio Mã Visual Studio Trình cắm C# JetBrains Rider ngrok CLI Tài khoản Twilio miễn phí hoặc trả phí (Nếu chưa có, bạn có thể ) dùng thử Twilio miễn phí Có kinh nghiệm với C# và ASP.NET Core . Mã nguồn của hướng dẫn này có thể được tìm thấy trên GitHub Thiết lập dự án ASP.NET Core mới Để bắt đầu, hãy sử dụng thiết bị đầu cuối shell của bạn trong thư mục làm việc ưa thích, hãy chạy các lệnh sau để tạo dự án API web mới: dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi Lệnh thứ hai trong đoạn mã trên sẽ tạo một dự án API web mới với tên được chỉ định và không hỗ trợ OpenAPI (Swagger). Nếu bạn muốn sử dụng Swagger trong dự án, chỉ cần bỏ qua trong lệnh trên. --no-openapi Thay đổi thư mục dự án bằng cách chạy lệnh này: cd TwilioWhatsAppTriviaApp Cài đặt Gói NuGet: Thư viện trợ giúp Twilio cho ASP.NET Core dotnet add package Twilio.AspNet.Core Thư viện này đơn giản hóa hoạt động với các webhook và API Twilio trong ứng dụng ASP.NET Core. Mở dự án bằng IDE ưa thích của bạn. Trong thư mục , hãy xóa tệp bộ điều khiển mẫu soạn sẵn, , đồng thời xóa trong thư mục dự án. Bộ điều khiển WeatherForecastController.cs WeatherForcast.cs Xây dựng và chạy dự án của bạn để đảm bảo mọi thứ bạn đã làm cho đến nay hoạt động tốt bằng cách sử dụng các lệnh sau: dotnet build dotnet run Sau khi chạy dự án thành công, hãy ghi lại bất kỳ URL localhost nào xuất hiện trong bảng điều khiển gỡ lỗi. Bạn có thể sử dụng bất kỳ URL nào trong số này để thiết lập máy chủ web cục bộ có thể truy cập công khai bằng ngrok. Phiên triển khai Phiên là một trong nhiều cách lưu trữ dữ liệu của người dùng trong ứng dụng ASP.NET Core. Điều này rất cần thiết khi bạn muốn giữ lại dữ liệu người dùng giữa các yêu cầu vì theo mặc định, giao thức HTTP không có trạng thái - điều đó có nghĩa là dữ liệu không được bảo toàn. Thêm nhà cung cấp phiên trong bộ nhớ bằng cách sửa đổi , như minh họa trong đoạn mã sau: 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(); đăng ký dịch vụ bộ nhớ đệm được phân phối. Dịch vụ này cung cấp bộ đệm trong bộ nhớ có thể được sử dụng để lưu trữ và truy xuất dữ liệu qua nhiều yêu cầu hoặc phiên. AddDistributedMemoryCache() đăng ký dịch vụ phiên, cho phép ứng dụng duy trì trạng thái phiên. Tham số cho phép bạn định cấu hình các tùy chọn khác nhau liên quan đến phiên. được sử dụng để đặt khoảng thời gian không hoạt động, sau đó phiên sẽ được coi là không hoạt động. Trong trường hợp này, nó được đặt thành 40 giây. đảm bảo rằng trạng thái của phiên vẫn hoạt động và ngay cả trong các trường hợp bật tính năng từ chối cookie. AddSession() options IdleTimeout Cookie.IsEssential Hỗ trợ phiên được kích hoạt bằng cách thêm phần mềm trung gian vào đường dẫn ứng dụng, nghĩa là ứng dụng của bạn có quyền truy cập vào một đối tượng phiên có thể được sử dụng để lưu trữ và truy xuất dữ liệu. UseSession Tạo mô hình Tạo một thư mục mới, trong thư mục dự án của bạn. Thêm hai tệp lớp mô hình, và với các thuộc tính như trong các mẫu mã sau: Models TriviaApiResponse.cs Câu hỏi.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; } } Mô hình bao gồm các thuộc tính đại diện cho các trường của phản hồi API Trivia. Thuộc tính đảm bảo rằng mỗi thuộc tính được điền chính xác với dữ liệu JSON tương ứng. TriviaApiResponse JsonProperty Để có cách xử lý các câu hỏi đố một cách hợp lý, lớp sẽ giải cứu bạn. Lớp này gói gọn các thông tin cần thiết cho một câu hỏi đố, bao gồm nội dung câu hỏi và danh sách các tùy chọn. Mỗi tùy chọn được biểu thị bằng một bộ chứa văn bản tùy chọn và giá trị boolean cho biết liệu đó có phải là tùy chọn chính xác hay không. Question Thêm lớp dịch vụ đố Tạo thư mục trong thư mục dự án của bạn và thêm tệp lớp mới có tên . Sửa đổi nội dung của nó, như được hiển thị trong đoạn mã sau: Dịch vụ 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; } } Lớp chứa hai phương thức: và . Phương thức gửi yêu cầu HTTP GET tới API Trivia với tham số truy vấn, , chỉ định rằng chỉ nên trả về 3 câu hỏi. Nếu không có tham số giới hạn, API sẽ trả về 10 câu hỏi theo mặc định. TriviaService GetTrivia ConvertTriviaToQuestions GetTrivia limit=3 Phương thức chuyển đổi phản hồi từ API thành một cách có tổ chức. Phương pháp này cũng xáo trộn ngẫu nhiên tất cả các tùy chọn câu hỏi, do đó một tùy chọn duy nhất sẽ không phải là câu trả lời cho tất cả các câu hỏi. ConvertTriviaToQuestions Để đăng ký và ứng dụng khách HTTP trong vùng chứa Dependency Insert (DI) của ứng dụng của bạn, hãy sửa đổi như trong mã sau: 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(); Tạo Bộ điều khiển đố Thêm một lớp trình điều khiển API trống trong tệp có tên vào thư mục và sửa đổi nội dung của nó như trong mã sau: TriviaController.cs Bộ điều khiển 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; } } Lớp trình điều khiển này chịu trách nhiệm xử lý các tin nhắn đến, quản lý trạng thái phiên và tạo phản hồi. Nó kế thừa từ lớp do thư viện Twilio.AspNet.Core cung cấp, cho phép bạn truy cập vào phương thức . Bạn có thể sử dụng phương pháp này để trả lời với . Lớp sử dụng các phương thức để tương tác với phiên. Đầu vào hợp lệ là các phần tử trong mảng chỉ đọc và . Nội dung của tin nhắn đến được so sánh với các thành phần này để đảm bảo rằng người dùng đã gửi thông tin đầu vào phù hợp, nếu không, một thông báo sẽ được gửi đến người dùng để nhắc họ thực hiện thông tin đầu vào phù hợp dựa trên trạng thái hiện tại của trò chơi. Các trường khác có tiền tố “SessionKey” được sử dụng để xác định các chuỗi hằng số riêng tư cho các khóa phiên trong chương trình. TwilioController TwiML TwiML, là Ngôn ngữ đánh dấu Twilio TriviaController HttpContext.Session StartCommands OptionValues Phương thức là phương thức hành động chính xử lý các yêu cầu HTTP POST đến từ WhatsApp thông qua tuyến . Nó tải dữ liệu phiên bằng cách sử dụng và truy xuất dữ liệu liên quan đến trạng thái trò chơi từ phiên bằng cách sử dụng các phương thức và . Index /Trivia HttpContext.Session.LoadAsync() HttpContext.Session.GetString() HttpContext.Session.GetInt32() Việc sử dụng dấu gạch dưới (_) và dấu hoa thị (*) ở đầu và cuối của một số chuỗi nhất định là để đạt được định dạng văn bản in nghiêng và in đậm tương ứng trong tin nhắn WhatsApp được hiển thị. Mỗi phương thức trợ giúp trong thực hiện một nhiệm vụ cụ thể hỗ trợ chức năng chính của lớp. TriviaController Phương thức khởi tạo trò chơi bằng cách truy xuất các câu hỏi đố, chuyển đổi chúng thành định dạng phù hợp với trò chơi và lưu trữ chúng trong phiên. StartGame Phương thức xử lý câu trả lời của người dùng cho một câu hỏi và xác định xem nó có đúng hay không. ProcessUserAnswer Phương thức chịu trách nhiệm định dạng và trình bày câu hỏi cùng với các tùy chọn của nó. PresentQuestionWithOptions Phương thức lưu trữ danh sách các câu hỏi trong phiên. Nó chuyển đổi các câu hỏi sang định dạng JSON và lưu chuỗi JSON trong phiên. AddNewQuestionsToSession Phương thức truy xuất câu hỏi từ phiên bằng cách sử dụng chỉ mục câu hỏi. RetrieveQuestionFromSession Phương thức tạo ra một thông báo để kết thúc trò chơi đố vui. Phương pháp này cũng loại bỏ dữ liệu phiên liên quan đến trò chơi. Dựa trên cấu hình của dịch vụ phiên trong , điều này tự động xảy ra khi phiên không hoạt động trong 40 giây. EndTrivia Program.cs Kiểm tra ứng dụng Để kiểm tra ứng dụng, bạn cần thiết lập Twilio Sandbox cho WhatsApp, làm cho điểm cuối ứng dụng của bạn có thể truy cập công khai và thêm URL điểm cuối trong cấu hình Sandbox dưới dạng webhook. Thiết lập Hộp cát Twilio cho WhatsApp đi đến , điều hướng đến . Bảng điều khiển Twilio Tin nhắn > Dùng thử > Gửi tin nhắn WhatsApp Làm theo hướng dẫn trên trang để kết nối với hộp cát bằng cách gửi tin nhắn WhatsApp từ thiết bị của bạn đến số Twilio được cung cấp để tạo kết nối thành công với hộp cát WhatsApp. Tương tự, những cá nhân khác muốn thử nghiệm ứng dụng của bạn bằng số tương ứng của họ sẽ cần phải làm theo quy trình tương tự. Hiển thị webhook của bạn bằng ngrok để thử nghiệm Bây giờ, hãy mở shell terminal và chạy lệnh sau để khởi động ngrok và hiển thị ứng dụng ASP.NET Core cục bộ của bạn bằng cách thay thế bằng URL đầy đủ của localhost mà bạn đã sao chép ban đầu: <localhost-url> ngrok http <localhost-url> ngrok sẽ tạo một URL công khai chuyển tiếp yêu cầu tới ứng dụng ASP.NET cục bộ của bạn. Tìm URL chuyển tiếp có nhãn trong cửa sổ terminal ngrok và sao chép nó. Chuyển tiếp Quay lại trang Twilio Dùng thử WhatsApp, nhấp vào và thay đổi url điểm cuối với URL được tạo bởi ngrok cộng với tuyến và đảm bảo phương thức được đặt thành POST. Sau đó nhấn Save để lưu cấu hình sandbox mới. Cài đặt hộp cát Khi tin nhắn đến ở chuyển tiếp /Trivia Trình diễn dự án Chạy dự án ASP.NET Core của bạn bằng lệnh sau: dotnet run Bây giờ, hãy kiểm tra ứng dụng của bạn bằng cách gửi tin nhắn trong cuộc trò chuyện đầu tiên bằng số Twilio Sandbox. Phần kết luận Bằng cách tận dụng sức mạnh của nền tảng Twilio và WhatsApp, bạn đã tạo ra trải nghiệm trò chơi đố vui hấp dẫn để người dùng thưởng thức. Bạn cũng đã học cách lưu và truy xuất dữ liệu từ các phiên. Có một số cách mà dự án này có thể được cải thiện. Bạn có thể cải thiện hơn nữa dự án này bằng cách thêm bộ đếm thời gian, xử lý các ngoại lệ, cho phép người dùng chọn độ khó và áp dụng độ khó đã chọn làm tham số truy vấn thông qua URL API Trivia (ví dụ: ). Ngoài ra, bạn có thể khám phá khả năng tạo giải pháp cho phép người dùng điền vào khảo sát thông qua WhatsApp. https://the-trivia-api.com/api/questions?difficulty=hard