Игры-викторины предоставляют увлекательный и познавательный опыт, где вы можете узнать новые факты и расширить свои знания по различным предметам. В настоящее время мобильные и веб-приложения викторин являются наиболее распространенными областями для такой деятельности. Как насчет того, чтобы сыграть в викторину в WhatsApp? В этом учебном руководстве вы узнаете, как создать приложение викторины с помощью Twilio для WhatsApp, ASP.NET Core и ( ). Целью этого руководства является создание приложения-викторины, которое позволит пользователям играть и отвечать на вопросы с несколькими вариантами ответов с помощью WhatsApp. Вы будете использовать для хранения и извлечения прогресса пользователя, отслеживания счета и поддержания состояния игры. API викторины под лицензией CC BY-NC 4.0 сеансы в ASP.NET Core Чтобы получить эти вопросы, вы будете использовать The Trivia API, REST API, который позволяет разработчикам легко создавать приложения-викторины, предоставляя викторины с несколькими вариантами ответов. Чтобы узнать больше об API Trivia, посетите __ __. документацию API Trivia Предварительные условия Чтобы выполнить это руководство, вам понадобится: ОС, поддерживающая .NET (Windows/macOS/Linux) .NET 7 SDK Редактор кода или IDE (рекомендуется: , с , или ) Визуальная Студия Код Visual Studio Плагин С# JetBrains Райдер интерфейс командной строки нгрока Бесплатная или платная учетная запись Twilio (если у вас ее нет, вы можете ) попробуйте Твилио бесплатно Опыт работы с C# и ASP.NET Core. . Исходный код этого руководства можно найти на GitHub. Настройте новый проект ASP.NET Core. Для начала, используя терминал оболочки в предпочтительном рабочем каталоге, выполните следующие команды, чтобы создать новый проект веб-API: dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi Вторая команда в приведенном выше фрагменте создаст новый проект веб-API с указанным именем и без поддержки OpenAPI (Swagger). Если вы хотите использовать Swagger в проекте, просто опустите в приведенной выше команде. --no-openapi Перейдите в каталог проекта, выполнив эту команду: cd TwilioWhatsAppTriviaApp Установите Пакет NuGet: Вспомогательная библиотека Twilio для ASP.NET Core dotnet add package Twilio.AspNet.Core Эта библиотека упрощает работу с веб-перехватчиками и API Twilio в приложении ASP.NET Core. Откройте проект, используя предпочитаемую вами IDE. В папке удалите шаблонный файл контроллера шаблона , а также удалите в каталоге проекта. «Контроллеры» WeatherForecastController.cs WeatherForcast.cs Создайте и запустите свой проект, чтобы убедиться, что все, что вы сделали до сих пор, работает хорошо, используя следующие команды: dotnet build dotnet run После успешного запуска проекта обратите внимание на все URL-адреса локального хоста, которые появляются в консоли отладки. Вы можете использовать любой из этих URL-адресов для настройки общедоступного локального веб-сервера с помощью ngrok. Реализация сеансов Сеансы — это один из нескольких способов хранения пользовательских данных в приложении ASP.NET Core. Это важно, если вы хотите сохранять пользовательские данные между запросами, поскольку по умолчанию протокол HTTP не сохраняет состояние — это означает, что данные не сохраняются. Добавьте поставщик сеансов в памяти, изменив , как показано в следующем коде: 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(); регистрирует службу кэширования распределенной памяти. Эта служба предоставляет кэш в памяти, который можно использовать для хранения и извлечения данных по нескольким запросам или сеансам. AddDistributedMemoryCache() регистрирует службы сеанса, позволяя приложению поддерживать состояние сеанса. Параметр позволяет вам настроить различные параметры, связанные с сеансом. используется для установки продолжительности бездействия, после которой сеанс будет считаться бездействующим. В данном случае оно установлено на 40 секунд. гарантирует, что состояние сеанса останется работоспособным даже в тех сценариях, где включено отклонение файлов cookie. AddSession() options IdleTimeout Cookie.IsEssential Поддержка сеансов включается путем добавления промежуточного программного обеспечения в конвейер приложения, то есть ваше приложение получает доступ к объекту сеанса, который можно использовать для хранения и извлечения данных. UseSession Создайте модели Создайте новую папку в каталоге вашего проекта. Добавьте два файла классов модели: и со свойствами, как показано в следующих примерах кода: Models 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; } } Модель включает свойства, которые представляют поля ответа Trivia API. Атрибут гарантирует, что каждое свойство правильно заполнено соответствующими данными JSON. TriviaApiResponse JsonProperty Для упрощения обработки викторинных вопросов на помощь приходит класс . Этот класс инкапсулирует необходимую информацию для викторинного вопроса, включая текст вопроса и список вариантов. Каждый параметр представлен кортежем, содержащим текст параметра и логическое значение, указывающее, является ли этот параметр правильным. Question Добавьте класс обслуживания Trivia Создайте папку в каталоге вашего проекта и добавьте новый файл класса с именем . Измените его содержимое, как показано в следующем коде: Services 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; } } Класс содержит два метода: и . Метод отправляет HTTP-запрос GET в Trivia API с параметром запроса , который указывает, что должны быть возвращены только 3 вопроса. Без параметра limit API по умолчанию возвращает 10 вопросов. TriviaService GetTrivia ConvertTriviaToQuestions GetTrivia limit=3 Метод преобразует ответ API в организованный вид. Этот метод также случайным образом перемешивает все варианты вопросов, так что один вариант не будет ответом на все вопросы. ConvertTriviaToQuestions Чтобы зарегистрировать и HTTP-клиент в контейнере внедрения зависимостей (DI) вашего приложения, измените , как показано в следующем коде: 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(); Создайте контроллер викторины Добавьте пустой класс контроллера API в файл с именем в папку и измените его содержимое, как показано в следующем коде: TriviaController.cs Controllers 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; } } Этот класс контроллера отвечает за обработку входящих сообщений, управление состоянием сеанса и генерацию ответов. Он наследуется от класса , предоставленного библиотекой Twilio.AspNet.Core, который предоставляет доступ к методу . Вы можете использовать этот метод, чтобы ответить . Класс использует методы для взаимодействия с сеансом. Допустимыми входными данными являются элементы массивов и , доступных только для чтения. Тело входящего сообщения сравнивается с этими элементами, чтобы убедиться, что пользователь отправил правильный ввод. В противном случае пользователю будет отправлено сообщение с предложением сделать правильный ввод на основе текущего состояния игры. Другие поля с префиксом «SessionKey» используются для определения частных константных строк для сеансовых ключей в программе. TwilioController TwiML TwiML — язык разметки Twilio. TriviaController HttpContext.Session StartCommands OptionValues Метод — это основной метод действия, который обрабатывает входящие HTTP-запросы POST от WhatsApp по маршруту . Он загружает данные сеанса с помощью и извлекает данные о состоянии игры из сеанса с помощью методов и . Index /Trivia HttpContext.Session.LoadAsync() HttpContext.Session.GetString() HttpContext.Session.GetInt32() Использование подчеркиваний (_) и звездочек (*) в начале и конце определенных строк позволяет добиться форматирования текста курсивом и полужирным шрифтом соответственно в отображаемых сообщениях WhatsApp. Каждый вспомогательный метод в выполняет определенную задачу, поддерживающую основную функциональность класса. TriviaController Метод инициализирует игру, извлекая викторины, преобразуя их в формат, подходящий для игры, и сохраняя их в сеансе. StartGame Метод обрабатывает ответ пользователя на вопрос и определяет, правильный он или нет. ProcessUserAnswer Метод отвечает за форматирование и представление вопроса вместе с его параметрами. PresentQuestionWithOptions Метод хранит список вопросов в сеансе. Он преобразует вопросы в формат JSON и сохраняет строку JSON в сеансе. AddNewQuestionsToSession Метод извлекает вопрос из сеанса, используя индекс вопроса. RetrieveQuestionFromSession Метод генерирует сообщение о завершении викторины. Этот метод также удаляет данные сеанса, связанные с игрой. В зависимости от конфигурации службы сеансов в это происходит автоматически, когда сеанс бездействует в течение 40 секунд. EndTrivia Program.cs Протестируйте приложение Чтобы протестировать приложение, вам необходимо настроить Twilio Sandbox для WhatsApp, сделать конечную точку вашего приложения общедоступной и добавить URL-адрес конечной точки в конфигурацию песочницы в качестве веб-перехватчика. Настройка Twilio Sandbox для WhatsApp Перейти к , выберите . Консоль Твилио «Сообщение» > «Попробовать» > «Отправить сообщение WhatsApp» Следуйте инструкциям на странице, чтобы подключиться к песочнице, отправив сообщение WhatsApp со своего устройства на указанный номер Twilio, чтобы создать успешное соединение с песочницей WhatsApp. Аналогичным образом, другие люди, желающие протестировать ваше приложение со своими номерами, должны будут выполнить ту же процедуру. Предоставьте свой вебхук, используя ngrok для тестирования. Теперь откройте терминал оболочки и выполните следующую команду, чтобы запустить ngrok и предоставить доступ к локальному приложению ASP.NET Core, заменив полным URL-адресом вашего локального хоста, который вы изначально скопировали: <localhost-url> ngrok http <localhost-url> ngrok сгенерирует общедоступный URL-адрес, который перенаправит запросы в ваше локальное приложение ASP.NET. Найдите URL-адрес пересылки с надписью в окне терминала ngrok и скопируйте его. «Пересылка» Вернитесь на страницу Twilio Try WhatsApp, нажмите » и измените URL-адрес конечной точки URL-адрес , сгенерированный ngrok, плюс маршрут », и убедитесь, что для метода установлено значение POST. Затем нажмите «Сохранить», чтобы сохранить новую конфигурацию песочницы. «Настройки песочницы «Когда сообщение приходит на пересылки /Trivia Демо проекта Запустите проект ASP.NET Core, используя следующую команду: dotnet run Теперь протестируйте свое приложение, отправив сообщение в первом разговоре с номером Twilio Sandbox. Заключение Используя возможности платформы Twilio и WhatsApp, вы создали для пользователей захватывающую викторину. Вы также узнали, как сохранять и извлекать данные из сеансов. Есть несколько способов улучшить этот проект. Вы можете улучшить этот проект, добавив таймер, обработав исключения, позволив пользователям выбирать сложность и применив выбранную сложность в качестве параметра запроса через URL-адрес Trivia API (например, ). Кроме того, вы можете изучить возможность создания решения, позволяющего пользователям заполнять опросы через WhatsApp. https://the-trivia-api.com/api/questions?difficulty=hard