paint-brush
Создайте бота WhatsApp для викторины с помощью Twilio и ASP.NET Coreк@zadok
2,818 чтения
2,818 чтения

Создайте бота WhatsApp для викторины с помощью Twilio и ASP.NET Core

к Zadok J.15m2023/09/15
Read on Terminal Reader
Read this story w/o Javascript

Слишком долго; Читать

В этом учебном руководстве вы узнаете, как создать викторину с помощью Twilio для WhatsApp, ASP.NET Core и The Trivia API.
featured image - Создайте бота WhatsApp для викторины с помощью Twilio и ASP.NET Core
Zadok J. HackerNoon profile picture
0-item
1-item

Игры-викторины предоставляют увлекательный и познавательный опыт, где вы можете узнать новые факты и расширить свои знания по различным предметам. В настоящее время мобильные и веб-приложения викторин являются наиболее распространенными областями для такой деятельности. Как насчет того, чтобы сыграть в викторину в WhatsApp?


В этом учебном руководстве вы узнаете, как создать приложение викторины с помощью Twilio для WhatsApp, ASP.NET Core и API викторины ( под лицензией CC BY-NC 4.0 ). Целью этого руководства является создание приложения-викторины, которое позволит пользователям играть и отвечать на вопросы с несколькими вариантами ответов с помощью WhatsApp. Вы будете использовать сеансы в ASP.NET Core для хранения и извлечения прогресса пользователя, отслеживания счета и поддержания состояния игры.


Чтобы получить эти вопросы, вы будете использовать The Trivia API, REST API, который позволяет разработчикам легко создавать приложения-викторины, предоставляя викторины с несколькими вариантами ответов. Чтобы узнать больше об API Trivia, посетите __ документацию API Trivia __.


Предварительные условия

Чтобы выполнить это руководство, вам понадобится:


Исходный код этого руководства можно найти на GitHub. .


Настройте новый проект ASP.NET Core.

Для начала, используя терминал оболочки в предпочтительном рабочем каталоге, выполните следующие команды, чтобы создать новый проект веб-API:


 dotnet new webapi -n TwilioWhatsAppTriviaApp --no-openapi


Вторая команда в приведенном выше фрагменте создаст новый проект веб-API с указанным именем и без поддержки OpenAPI (Swagger). Если вы хотите использовать Swagger в проекте, просто опустите --no-openapi в приведенной выше команде.


Перейдите в каталог проекта, выполнив эту команду:


 cd TwilioWhatsAppTriviaApp


Установите Вспомогательная библиотека Twilio для ASP.NET Core Пакет NuGet:


 dotnet add package Twilio.AspNet.Core


Эта библиотека упрощает работу с веб-перехватчиками и API Twilio в приложении ASP.NET Core.


Откройте проект, используя предпочитаемую вами IDE. В папке «Контроллеры» удалите шаблонный файл контроллера шаблона WeatherForecastController.cs , а также удалите WeatherForcast.cs в каталоге проекта.


Создайте и запустите свой проект, чтобы убедиться, что все, что вы сделали до сих пор, работает хорошо, используя следующие команды:


 dotnet build dotnet run


После успешного запуска проекта обратите внимание на все URL-адреса локального хоста, которые появляются в консоли отладки. Вы можете использовать любой из этих URL-адресов для настройки общедоступного локального веб-сервера с помощью ngrok.


URL-адреса локального хоста


Реализация сеансов

Сеансы — это один из нескольких способов хранения пользовательских данных в приложении 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() регистрирует службу кэширования распределенной памяти. Эта служба предоставляет кэш в памяти, который можно использовать для хранения и извлечения данных по нескольким запросам или сеансам.


AddSession() регистрирует службы сеанса, позволяя приложению поддерживать состояние сеанса. Параметр options позволяет вам настроить различные параметры, связанные с сеансом. IdleTimeout используется для установки продолжительности бездействия, после которой сеанс будет считаться бездействующим. В данном случае оно установлено на 40 секунд. Cookie.IsEssential гарантирует, что состояние сеанса останется работоспособным даже в тех сценариях, где включено отклонение файлов cookie.


Поддержка сеансов включается путем добавления промежуточного программного обеспечения 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; } }


Модель TriviaApiResponse включает свойства, которые представляют поля ответа Trivia API. Атрибут JsonProperty гарантирует, что каждое свойство правильно заполнено соответствующими данными JSON.


Для упрощения обработки викторинных вопросов на помощь приходит класс 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; } }


Класс TriviaService содержит два метода: GetTrivia и ConvertTriviaToQuestions . Метод GetTrivia отправляет HTTP-запрос GET в Trivia API с параметром запроса limit=3 , который указывает, что должны быть возвращены только 3 вопроса. Без параметра limit API по умолчанию возвращает 10 вопросов.


Метод ConvertTriviaToQuestions преобразует ответ API в организованный вид. Этот метод также случайным образом перемешивает все варианты вопросов, так что один вариант не будет ответом на все вопросы.


Чтобы зарегистрировать TriviaService и HTTP-клиент в контейнере внедрения зависимостей (DI) вашего приложения, измените 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; } }


Этот класс контроллера отвечает за обработку входящих сообщений, управление состоянием сеанса и генерацию ответов. Он наследуется от класса TwilioController , предоставленного библиотекой Twilio.AspNet.Core, который предоставляет доступ к методу TwiML . Вы можете использовать этот метод, чтобы ответить TwiML — язык разметки Twilio. . Класс TriviaController использует методы HttpContext.Session для взаимодействия с сеансом.

Допустимыми входными данными являются элементы массивов StartCommands и OptionValues , доступных только для чтения. Тело входящего сообщения сравнивается с этими элементами, чтобы убедиться, что пользователь отправил правильный ввод. В противном случае пользователю будет отправлено сообщение с предложением сделать правильный ввод на основе текущего состояния игры. Другие поля с префиксом «SessionKey» используются для определения частных константных строк для сеансовых ключей в программе.


Метод Index — это основной метод действия, который обрабатывает входящие HTTP-запросы POST от WhatsApp по маршруту /Trivia . Он загружает данные сеанса с помощью HttpContext.Session.LoadAsync() и извлекает данные о состоянии игры из сеанса с помощью методов HttpContext.Session.GetString() и HttpContext.Session.GetInt32() .


Использование подчеркиваний (_) и звездочек (*) в начале и конце определенных строк позволяет добиться форматирования текста курсивом и полужирным шрифтом соответственно в отображаемых сообщениях WhatsApp.


Каждый вспомогательный метод в TriviaController выполняет определенную задачу, поддерживающую основную функциональность класса.

  • Метод StartGame инициализирует игру, извлекая викторины, преобразуя их в формат, подходящий для игры, и сохраняя их в сеансе.
  • Метод ProcessUserAnswer обрабатывает ответ пользователя на вопрос и определяет, правильный он или нет.
  • Метод PresentQuestionWithOptions отвечает за форматирование и представление вопроса вместе с его параметрами.
  • Метод AddNewQuestionsToSession хранит список вопросов в сеансе. Он преобразует вопросы в формат JSON и сохраняет строку JSON в сеансе.
  • Метод RetrieveQuestionFromSession извлекает вопрос из сеанса, используя индекс вопроса.
  • Метод EndTrivia генерирует сообщение о завершении викторины. Этот метод также удаляет данные сеанса, связанные с игрой. В зависимости от конфигурации службы сеансов в Program.cs это происходит автоматически, когда сеанс бездействует в течение 40 секунд.


Протестируйте приложение

Чтобы протестировать приложение, вам необходимо настроить Twilio Sandbox для WhatsApp, сделать конечную точку вашего приложения общедоступной и добавить URL-адрес конечной точки в конфигурацию песочницы в качестве веб-перехватчика.

Настройка Twilio Sandbox для WhatsApp

Перейти к Консоль Твилио , выберите «Сообщение» > «Попробовать» > «Отправить сообщение WhatsApp» .


Консоль Твилио


Следуйте инструкциям на странице, чтобы подключиться к песочнице, отправив сообщение WhatsApp со своего устройства на указанный номер Twilio, чтобы создать успешное соединение с песочницей WhatsApp. Аналогичным образом, другие люди, желающие протестировать ваше приложение со своими номерами, должны будут выполнить ту же процедуру.

Предоставьте свой вебхук, используя ngrok для тестирования.

Теперь откройте терминал оболочки и выполните следующую команду, чтобы запустить ngrok и предоставить доступ к локальному приложению ASP.NET Core, заменив <localhost-url> полным URL-адресом вашего локального хоста, который вы изначально скопировали:


 ngrok http <localhost-url>


ngrok сгенерирует общедоступный URL-адрес, который перенаправит запросы в ваше локальное приложение ASP.NET. Найдите URL-адрес пересылки с надписью «Пересылка» в окне терминала ngrok и скопируйте его.


URL-адрес пересылки


Вернитесь на страницу Twilio Try WhatsApp, нажмите «Настройки песочницы » и измените URL-адрес конечной точки «Когда сообщение приходит на URL-адрес пересылки , сгенерированный ngrok, плюс маршрут /Trivia », и убедитесь, что для метода установлено значение POST. Затем нажмите «Сохранить», чтобы сохранить новую конфигурацию песочницы.


Песочница Твилио


Демо проекта

Запустите проект ASP.NET Core, используя следующую команду:


 dotnet run


Теперь протестируйте свое приложение, отправив сообщение в первом разговоре с номером Twilio Sandbox.


Тестирование вашего приложения в WhatsApp


Заключение

Используя возможности платформы Twilio и WhatsApp, вы создали для пользователей захватывающую викторину. Вы также узнали, как сохранять и извлекать данные из сеансов.


Есть несколько способов улучшить этот проект. Вы можете улучшить этот проект, добавив таймер, обработав исключения, позволив пользователям выбирать сложность и применив выбранную сложность в качестве параметра запроса через URL-адрес Trivia API (например, https://the-trivia-api.com/api/questions?difficulty=hard ). Кроме того, вы можете изучить возможность создания решения, позволяющего пользователям заполнять опросы через WhatsApp.