📜 ⬆️ ⬇️

We write chat bot quiz using Microsoft Bot Framework

We have a tradition - every spring we participate in the Career Days of our beloved Novosibirsk State University, the main forge of our cadres. And every year we come up with something curious for students. This year we made a master class on how to write a chat bot. To register for a master class, Telegram launched its own Akademik bot @academic_quiz_bot. He was all together and collected at the master class.


image


If you haven’t got a nice bot yet, now we’ll tell you how to choose a theme and, in fact, make a bot.



Step 1. Chat bot and career days, where is the connection?


It was not easy to come up with an interesting topic for the master class, because you have to compete with yourself. Two years ago, in an hour and a half of the master class, in front of the students, we wrote an iPhone mobile app that shows the weather around their alma mater NGU (more than 5000 downloads and continues to download ). A year ago, we made an IoT solution in 1.5 hours and learned how to light a light bulb from a smartphone. This year it was necessary to do something equally incendiary, and we decided to teach students how to do chat bots, because these virtual interlocutors are now experiencing an explosive growth in popularity.


The idea of ​​making a chat bot traditionally came from us not from the ceiling - for one of our customers, we just developed a chat bot in Telegram, which allows territorial managers of the company without access to a stationary computer and fast internet, to manage projects quickly - to appoint, delegate tasks and monitor the status of their execution.
In addition, the bot was a convenient way to collect student contacts in a playful way and invite them to a master class.


image


Step 2. What are we going to chat about?


Bot we certainly wanted to make it useful for him to continue to live and delight everyone after the Career Days. After much deliberation and controversy, we have found such a topic! It was based on Alexey Kozionov’s popular online questionnaire on the knowledge of the Novosibirsk Academgorodok. And a week before the master class, we launched our Academician @academic_quiz_bot chat bot in the Telegram.


“Akademik” asks curious questions (101 questions altogether) with answers to the life of the beloved district, like: “What is called“ Toadstool ”at Academgorodok?”, “What American president visited Akademgorodok?”, “Why is the neighborhood called Sh)? , “How many steps does a staircase leading from the central beach to the bridge have?”, “Which of the Nobel Prize winners lived in Akademgorodok?” And so on.


image


“Akademika” we added a sticker with encouraging phrases from our talisman - ebtman.


image


You can download Stickpack here.


In the first week of life, the bot also worked as a registrar for a master class and carefully collected the contacts of the participants. And now it's just an entertaining test. Anyone who knows Akademgorodok, welcome the quiz to our colleges.


Step. 3. Recipe Chat Boot Quiz


About creating a chat bot using the Microsoft Bot Framework on Habré has been written a lot. But tutorials - units. Therefore, for all those who have not yet written down a virtual “talker” for themselves, we tell our recipe.


image


First, create a project in Visual Studio using the Bot Application Template.


image


After creation, we immediately receive a finished “Echo” bot, which repeats the message sent to it in response. We can start the project with the F5 button. In the browser, the default page will look like this:


image


Copy the address and run the emulator.


image


Where to paste the copied address and append api / messages. We check that everything works.
Next, we will improve our bot. Make him ask his questions and check the answers.
First we need data for questions. You can do this in different ways. Can be stored locally or downloaded from the cloud. We chose the second option.
Create a simple class for loading questions.


public class QuestService { private static QuestService _instance; public static QuestService Instance => _instance ?? (_instance = new QuestService()) private static string _url = "https://academicquizbot.blob.core.windows.net/content/questions.txt"; private readonly List<Question> _questions; public int QuestionsCount => _questions.Count; public QuestService() { var root = DoRequest<QuestionRoot>(_url); _questions = root.Questions; for (int i = 0; i < _questions.Count; i++) { _questions[i].Index = i; } } private T DoRequest<T>(string requestStr) where T : class { var req = WebRequest.Create(new Uri(requestStr)); req.Method = "GET"; var response = req.GetResponse(); var stream = response.GetResponseStream(); if (stream == null) { return null; } using (var rstream = new StreamReader(stream)) { var stringResult = rstream.ReadToEnd(); var result = JsonConvert.DeserializeObject<T>(stringResult); return result; } } public Question GetQuestion(List<int> exceptIndexes) { var questions = _questions.Select(q => q) .Where(q => !exceptIndexes.Contains(q.Index)).ToList(); if (questions.Count == 0) { return null; } Random rand = new Random(); return questions[rand.Next(questions.Count)]; } } 

When creating this class, it will download questions from the server and, if necessary, return the next question excluding those that the user has already answered.


Create a new class in Visual Studio called QuizDialog. And we implement the IDialog <string> interface in it.


 [Serializable] public class QuizDialog : IDialog<string> { async Task IDialog<string>.StartAsync(IDialogContext context) { context.Wait<string>(MessageReceived); } private async Task MessageReceived(IDialogContext context, IAwaitable<string> message) { await ShowQuestion(context); } private async Task ShowQuestion(IDialogContext context) { } } 

Implement the ShowQuestion function. First, download a list of questions already passed from the repository.


 var history = DataProviderService.Instance.GetAnswerStatistic(context.Activity.From.Id); 

And ask for the next question.


 var exceptIndexes = history.Select(h => h.Index).ToList(); _question = QuestService.Instance.GetQuestion(exceptIndexes); if (_question == null) { context.Done(""); return; } 

If there are no questions, then we consider the quiz passed and return control to the parent dialogue.
Next, send the question text to the user.


 StringBuilder questionText = new StringBuilder(); questionText.AppendLine(BoldString($" №{history.Count + 1}:")); questionText.AppendLine(_question.Text); questionText.AppendLine(_question.ImageUrl); var reply = context.MakeMessage(); reply.Text = questionText.ToString(); reply.TextFormat = "xml"; await context.PostAsync(reply); 

Also, pictures can be transmitted not via url (although I believe that it is more reliable in a telegram that way), but through Attachment.


 if (!string.IsNullOrWhiteSpace(_question.ImageUrl)) { reply.Attachments.Add(new Attachment("image/jpg", _question.ImageUrl)); } 

BoldString function code:


 private string BoldString(string text) { if (string.IsNullOrEmpty(text)) { return text; } return $"<b>{text}</b>"; } 

The text is formatted in "xml" to divide the text into lines and highlight the line with the question numbers in bold color. Here it should be borne in mind that not all instant messengers support the same formats; here it is worth reading the documentation.


Next we need to display a list of answers. We will display in two ways. Since we have answers with long text. We will simply display such numbers as numbers and buttons with answer numbers after. And if the answers are short (up to 30 characters), then we will display them immediately in the form of buttons.


 bool isLongAnswer = _question.Answers.Any(a => a.Length > LongTextLength); var answers = _question.Answers; if (isLongAnswer) { var answersReply = context.MakeMessage(); StringBuilder text = new StringBuilder(); answers = new List<string>(); for (int i = 0; i < _question.Answers.Count; i++) { text.AppendLine($"{i+1}) {_question.Answers[i]}"); answers.Add($"{i + 1}"); } answersReply.Text = text.ToString(); answersReply.TextFormat = "xml"; await context.PostAsync(answersReply); } ResumeAfter<string> answerRecived = AnswerReceived; if (isLongAnswer) { answerRecived = LongAnswerReceived; } PromptDialog.Choice(context, answerRecived, new PromptOptions<string>( prompt: "   . \n /exit -  ", retry: null, tooManyAttempts: "", options: answers, attempts: 0, promptStyler: new PromptStyler(), descriptions: answers)); 

Actually, the PromptDialog.Choice function shows a dialog with buttons. Next we need two functions to handle the user's selection.


 private async Task AnswerReceived(IDialogContext context, IAwaitable<string> message) { bool isCorrect = false; try { var answer = await message; isCorrect = answer == _question.CorrectAnswer; } catch { // ignored } await ShowResult(context, isCorrect); } private async Task LongAnswerReceived(IDialogContext context, IAwaitable<string> message) { bool isCorrect = false; try { var answer = await message; var answerNumber = int.Parse(answer) - 1; isCorrect = _question.Answers[answerNumber] == _question.CorrectAnswer; } catch { // ignored } await ShowResult(context, isCorrect); } 

If the user has written something incorrect, we consider his answer to be incorrect. Here we are the villains.


Next, you need to show the correct answer and statistics. And small bonuses if the user communicates with the bot via a “telegram” to encourage him with a sticker.


 private async Task ShowResult(IDialogContext context, bool isCorrect) { string isCorrectStr; Random rand = new Random(); string sticker = ""; if (isCorrect) { var index = rand.Next(Consts.CorrectAnswers.Length); isCorrectStr = Consts.CorrectAnswers[index]; var stickerIndex = rand.Next(Consts.GoodStickers.Length); sticker = Consts.GoodStickers[stickerIndex]; } else { var index = rand.Next(Consts.BadAnswers.Length); isCorrectStr = Consts.BadAnswers[index]; var stickerIndex = rand.Next(Consts.BadStickers.Length); sticker = Consts.BadStickers[stickerIndex]; } bool isTelegram = context.Activity.ChannelId == "telegram"; if (isTelegram && rand.Next(100) > 30) { var bot = new TelegramBotClient(Consts.Telegram.Token); await bot.SendStickerAsync(context.Activity.From.Id, sticker); } var reply = context.MakeMessage(); reply.Text = BoldString($"{isCorrectStr}"); reply.TextFormat = "xml"; await context.PostAsync(reply); var answer = new AnswerStatistic { Index = _question.Index, IsCorrect = isCorrect }; DataProviderService.Instance.AddAnswerStatistics(context.Activity.From.Id, answer); await ShowAnswerDescribe(context); await ShowStatistic(context); await ShowQuestion(context); } private async Task ShowAnswerDescribe(IDialogContext context) { var reply = context.MakeMessage(); reply.Text = _question.DescribeAnswer + _question.DescribeAnswerImageUrl; await context.PostAsync(reply); } private async Task ShowStatistic(IDialogContext context) { var user = DataProviderService.Instance.GetUser(context.Activity.From.Id); var history = DataProviderService.Instance.GetAnswerStatistic(context.Activity.From.Id); var reply = context.MakeMessage(); int correctCount = history.Count(h => h.IsCorrect); int questionsCount = QuestService.Instance.QuestionsCount; string text = $"{history.Count}/{questionsCount}   : {correctCount}"; reply.Text = text; await context.PostAsync(reply); } 

Further in MessagesController it is necessary to cause our dialogue.


 public async Task<HttpResponseMessage> Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new QuizDialog()); } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return response; } 

After debugging in the emulator, you can publish the bot. Here it is already described.


And our project is entirely here.


Step 4. Summing up


This student spring, we taught young people how to write their own chat bots and gathered a record number of participants for us at the master class. We met the fans again  We have guys, in the sense of a boy and a girl, who have been attending master classes for the third year in a row and, finally, have grown to 4 courses. They say they will write a diploma and come to us right away (we will not take students anyway before the 4th year students).


And also, they themselves enjoyed a great deal of communication with young people and inspiration for new projects! Well, we really love our quiz and recommend it to anyone who is familiar with Academ - @academic_quiz_bot.


image


')

Source: https://habr.com/ru/post/327094/


All Articles