Бот Телеграмм как задать вопрос с правильным ответом

Одним днём мы, редакция pythonist.ru, от нечего делать, стали кидать друг другу задачки и смотреть, кто быстрее решит. В какой-то момент нам пришла в голову идея автоматизировать этот процесс. Нам понадобился бот, который отправлял бы нам случайные задачки, а мы бы уже их наперегонки решали.

Итак, что мы имеем:

  • Наша редакция предпочитает общение в telegram
  • Мы все пишем на Python

Следовательно, нам нужно написать на python что-то, что будет отправлять нам задачки прямо в чат. Источником задач мы, конечно же, взяли наш цикл статей по проекту Эйлера. Он ещё только в процессе заполнения, но его вполне можно использовать для наших задач.

Теперь, нужно разобраться с библиотеками, которыми мы будем пользоваться при написании бота. Ими станут:

  • pytelegrambotapi — основная библиотека для написания самого бота
  • beautifulsoup4 — для парсинга сайта и обработки ссылок на задачи

Итак, приступим, для начала необходимо в новом проекте установить необходимые библиотеки:

Как создавать тесты с вариантами ответов в Telegram?


pip install pytelegrambotapi pip install beautifulsoup4

Отлично, бот создан, самое веселое ждет нас впереди, начинаем писать бота. Где писать — выбор каждого, главное, что внутри.

В последнем сообщении от BotFather, мы получили токен нашего бота, им нельзя делиться, так как это ключ к боту, позволяющий делать с ним всё что угодно.

Для начала нам нужно подключить бота к нашему python-коду, напишем следующее:

import telebot TOKEN = ‘СЮДА ПИШЕМ ТОКЕН’ bot = telebot.TeleBot(TOKEN)

Таким образом, наш скрипт будет понимать, с чем он работает, и отправлять запросы именно через нашего бота. Повторюсь, что очень важно не показывать никому свой токен, это может стоить вам бота.

Для того, чтобы убедиться, что всё в порядке, напишем небольшую функцию, для обработки команды ‘/start’. Telebot предоставляет удобные инструменты для обработки сообщений, собственно поэтому мы его и используем.

Итак, напишем декоратор, а потом разберемся, что к чему.

Честно, даже с первого раза получилось. А теперь давайте разбираться что и как работает.

Первой строкой мы обратились к декоратору message_handler, он обрабатывает все входящие сообщения, если не передать ему никаких параметров. Мы же передали ему commands=[‘start’]. Это значит, что он будет реагировать только на сообщения-команды (начинающиеся со слэша), а в нашем случае, только на команду /start. Другие сообщения его не интересуют.

Затем мы прописываем функцию, которую декорируем, и говорим нашему боту, чтоб отвечал на сообщение ‘/start’ неким сообщением.

Последние две строчки нужны для того, чтобы бот работал постоянно, пока запущен. Просто оборачиваем bot.polling() в бесконечный цикл.

Бот работает, надо разобраться с тем, как мы будем получать задачи.

Для этих целей отлично подойдет BS4, который мы сразу установили. Это жутко удобная библиотека для парсинга совершенно любых сайтов, все зависит от скилла.

Но перед тем, как отдать сайт на съедение beautifulsoup, мы должны получить его html-код, делается это очень просто, импортируем встроенную библиотеку urllib, и отдаем ей url нашего сайта.

import urllib site = urllib.request.urlopen(‘https://pythonist.ru/spisok-zadach-proekt-ejlera-s-resheniyami/‘).read()

Передадим библиотеке beautifulsoup наш html-код, записанный в переменную site и, обработав улучшалкой beautifulsoup.prettify(), выведем полученный результат, чтобы убедиться, что все идет по плану.

soup = bs4.BeautifulSoup(site) print(soup.prettify())

На выводе мы получим огромное полотно кода, среди которого нас интересует только вот этот кусок:

Задача 1 «Числа, кратные 3 или 5»

Задача 2 «Четные числа Фибоначчи»

Задача 20 «Сумма цифр факториала»

Задача 21 «Дружественные числа»

Задача 23 «Неизбыточные суммы»

Задача 24 «Словарные перестановки:

Еще по теме:  Узнать по инн Телеграм

Именно тут хранятся нужные нам ссылки на задачи, а мы находимся всё ближе к своей цели. Теперь нам нужно достать эти ссылки, чтобы у нас была возможность отправлять их. Для этого немного переписываем предыдущий код, не переживайте, без объяснений не останетесь:)

site = urllib.request.urlopen(‘https://pythonist.ru/spisok-zadach-proekta-ejlera-s-resheniyami/’).read() soup = bs4.BeautifulSoup(site) raw_excersises = soup.find(‘div’, ) #забираем интересующий нас кусок кода excersises = raw_excersises.find_all(‘a’) links_to_excersises = [] for i in range(len(excersises)): links_to_excersises.append(excersises[i].get(‘href’)) print(‘I have a list’)

Что происходит в этом коде:

  • забираем html код сайта
  • скармливаем этот код bs4
  • находим нужный нам фрагмент кода, в котором хранятся ссылки
  • забираем непосредственно блоки с ссылками
  • в цикле for собираем список, состоящий только из ссылок
  • убеждаемся, что код выполнился

У этого кода есть один недостаток — при добавлении новой задачи на сайт, придется перезапускать бота, чтобы он заново спарсил список. Но так он работает намного быстрее, так как парсинг — дело долгое.

Ну все, осталось самое простое — по запросу выбрасывать ссылку на случайную задачу в чат. Для этого напишем декоратор-обработчик команды, назовем ее /task.

Тут особо сложного ничего нет, пройдемся по порядку:

  • импортируем модуль random, он нужен для выбора случайной статьи
  • задаем обработчику параметр, обеспечивающий работу только при сообщении /task
  • выбираем ссылку, которую будем отправлять
  • отправляем эту ссылку

Создаем телеграм-бота с использованием Spring Boot Pt.2: Quiz Bot

Java-университет

Далее мы создадим пакет bot/handler, в котором объявим интерфейс handler:

package com.whiskels.telegram.bot.handler; import com.whiskels.telegram.bot.State; import com.whiskels.telegram.model.User; import org.telegram.telegrambots.meta.api.methods.PartialBotApiMethod; import java.io.Serializable; import java.util.List; public interface Handler < // основной метод, который будет обрабатывать действия пользователя List> handle(User user, String message); // метод, который позволяет узнать, можем ли мы обработать текущий State у пользователя State operatedBotState(); // метод, который позволяет узнать, какие команды CallBackQuery мы можем обработать в этом классе List operatedCallBackQuery(); >

Создаем телеграм-бота с использованием Spring Boot Pt.2: Quiz Bot - 6

Обработчики мы создадим чуть позже, а пока давайте делегируем обработку событий новому классу UpdateReceiver , который мы создадим в корне пакета bot: ВНИМАНИЕ! Здесь и далее будут методы, которые отображаются, как List> handle(args); на деле они выглядят так, но форматтер кода их сломал:

И делегируем ему обработку в классе Bot:

Теперь наш бот делегирует обработку событий классу UpdateReceiver , но обработчиков у нас еще нет. Давайте их создадим! DISCLAIMER! Мне очень хотелось поделиться возможностями по написанию такого бота, поэтому дальнейший код (как в принципе и код UpdateReceiver) можно очень хорошо отрефакторить с применением различных паттернов. Но мы учимся и наша цель — минимально жизнеспособный бот, так что в качестве еще одного домашнего задания можно отрефакторить все, что вы увидели 🙂 Создаем пакет util, а в нем — класс TelegramUtil :

package com.whiskels.telegram.util; import com.whiskels.telegram.model.User; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; public class TelegramUtil < public static SendMessage createMessageTemplate(User user) < return createMessageTemplate(String.valueOf(user.getChatId())); >// Создаем шаблон SendMessage с включенным Markdown public static SendMessage createMessageTemplate(String chatId) < return new SendMessage() .setChatId(chatId) .enableMarkdown(true); >// Создаем кнопку public static InlineKeyboardButton createInlineKeyboardButton(String text, String command) < return new InlineKeyboardButton() .setText(text) .setCallbackData(command); >>

Простой Telegram бот, который задаёт 1 вопрос

gimp творчество

Основная идея бота — это противодействие спам — регистрациям в группе Telegram.

Ниже расскажу о процессе создания Telegram бота который выполняет простую функцию — задаёт новому пользователю группы вопрос и предоставляет возможностью выбрать ответ на него.

Для всех желающих увидеть код сразу и целиком — добро пожаловать в конец статьи ( там есть ссылки)

Инструменты

Всё началось с того что одна из моих групп доросла до некоторого количество пользователей и стала интересна для различных спам сервисов. Удаление спам сообщений не составляет труда, но постоянно мониторить группы на предмет таких сообщений не самый лучший способ провести время. При этом давно хотел попробовать указанные выше инструменты для реализации хоть сколько полезного бота Telegram.

Идея с тем что бы задавать вопрос пользователю при помощи бота, далеко не новая и успешно была реализована в таких сервисах — ботах как Combot, Terminator (не помню точное название, там ещё мужик похожий на Арнольда в куртке был на логотипе) и другие.

Еще по теме:  Как добавить друга в Телеграмм чат

Как это должно работать в теории

  1. Пользователь (человек, авто-спам, злой бот) вступает в сообщество
  2. Бот (добрый бот) — администратор сообщества реагирует на каждое новое вступление и задаёт простой вопрос. (Кто ты?). При этом есть только два варианта ответов — «Я человек» и «Я робот». Так же бот забирает все права и разрешения у пользователя в этой группе.
  3. По сути можно оставить только одну кнопку — «Я человек» и предположить, то что ответить, не ткнув на кнопку пальцем не представляется возможным.
  4. После нажатия на кнопку с ответом, backend этого бота обрабатывает ответ и принимает дальнейшее решение.
  5. Если ответ принадлежит пользователю вступившему в группу — и ответ «Я человек», пользователю возвращаются права и разрешения в группе в которую он вступил.
  6. Бот что-нибудь пишет, например, «добро пожаловать»

Для понимания того что происходит в группе необходимо обрабатывать json-ы которые сервис Telegram будет присылать на URL адрес (webhook) закреплённый за созданным вами ботом.

Достаточно вольное изображение схемы

Наш backend обрабатывает все сообщения приходящие от сервиса Telegram, и по мере необходимости наступления некоторых событий формирует запросы к сервису Telegram. В свою очередь последний — через вашего бота доводит эти команды до группы (где вы можете видеть результат).

Для реализации взаимодействия с сервисом Telegram есть несколько API библиотек на языке PHP

  1. danog/MadelineProto — Мощная, гибкая, с асинхронными «фичами».
  2. php-telegram-bot/core — Выбрал её, так как захотелось попробовать что то новое. Как оказалась она достаточно простая для понимания, достаточно пробежаться по ней xdebug-ом

Немного о php-telegram-bot/core

Вся «сила» библиотеки крутится возле одного крупного класса class LongmanTelegramBotTelegram. При этом есть возможность создавать отдельные обработчики поступающих json-ов через создание файлов-классов команд.

При инициализации объекта класса LongmanTelegramBotTelegram необходимо указать путь к папке с вашими обработчиками команд, что бы затем в этих обработчиках описать всю необходимую логику реагирования на события происходящие в вашей группе (события обновления группы).

Так же что бы лаконично внедриться в этот механизм взаимодействия с библиотекой, потребовалось расширить базовый класс библиотеки своим собственным, добавить новый метод для встраивания InteropContainerContainerInterface от Laminas и организовать фабрику для удовлетворения всех зависимостей.

Немного кода — реализации

Код сильно упрощён (В конце будут ссылки на репозитарий с модулем и отдельно приложением Laminas)

Пример обработчика поступающих команд

getMessage(); // Здесь какие либо действия или обработки return Request::emptyResponse(); > >

В момент поступления сообщения от сервиса Telegram — библиотека вызовет необходимый обработчик из указанной вами папки. Внутри этого обработчика можно получить данные о сообщение, отправителе, получателе, а так же сформировать новое сообщение для отправки — в группу от имени бота.

Так образом, мы можем получить уведомление о новом событие в группе. В данном случае нас интересует событие связанное с вступлением в группу нового пользователя.

Теперь необходимо сформировать сообщение которое будет иметь вопрос и две кнопки с ответом.

Ниже код, формирующий две кнопки. Поле callback_data — содержит значение ответа. В нашем случае это некоторая строка к которой добавлен (методом конкатенации) id вступившего в группу пользователя. Значение id пользователя добавляется в объект при его создании в вызывающем коде.

InlineKeyboard] */ public function getQuestion() < $keyboard = new InlineKeyboard([ [‘text’ =>’Я бот!’, ‘callback_data’ => QuestionKeyboardMap::CALLBACK_ANSWER_BOT.$this->curentUserId], [‘text’ => ‘Я человек!’, ‘callback_data’ => QuestionKeyboardMap::CALLBACK_ANSWER_HUMAN.$this->curentUserId], ]); return [‘reply_markup’ => $keyboard]; > public function setCurentUserId(string $curentUserId) < $this->curentUserId = $curentUserId; return $this; > >

Пример обработки события о вступлении нового пользователя в группу. В данном случае используется триггер события framework-а Laminas, что бы выполнить всю логику обработки этого кода в другой части модуля.

Подписываемся на событие при инициализации модуля и обрабатываем его в методе processRequestToJoinGroup класса Events

Еще по теме:  Стикеры на которые можно нажимать в Телеграмме

getApplication()->getServiceManager()->get(Events::class); $eventManager = $e->getApplication()->getServiceManager()->get(EventManager::class); $eventManager->attach(EventsMap::NEW_USER_SENT_REQUEST_TO_JOIN_GROUP,[$eventsService,’processRequestToJoinGroup’]); > >

Теперь необходимо отправить приветственное сообщение новому пользователю и добавить к сообщению наши кнопки с ответами. А так же лишить пользователя всех прав в группе до того момента пока он не предоставит ответ на вопрос.

Дальше, нужно обработать ответ от пользователя, снять ограничения и написать ему что нибудь в ответ, например «Добро пожаловать»

Опять большой код

Это метод так же вызывается как триггер на событие созданное при инициализации модуля Laminas. (В рамках статьи этот код здесь не указан)

Так же в этом методе есть триггер успешного прохождения проверки пользователя (здесь пример реализации не приводится) — его можно использовать для записи каких-либо данных о пользователе в базу данных.

Как это работает на самом деле, и какие есть оправданные сомнения

  • «Хороший» пользователь вступает в группу, получает вопрос и отвечает «Я человек». У него есть все возможности для общения в группе и за ним больше ни кто не наблюдает и его действия ни как не обрабатываются. Хотя при желании можно и дальше отслеживать, сервис Telegram будет отправлять изменения в группе на webhook.
  • «Плохой бот» вступает в группу (именно бот в понятии Telegram), эта «единица» пользователей отмечены специальным полем. Такие по задумке блокируются на «подлёте» без дополнительных вопросов.
  • «Плохой» пользователь (чаще всего автоматизированный) — вступает в группу и кидает сообщение с здоровенной картинкой (сейчас модно почему то про Биткоин спамить таким способом). Вот тут возникает предположение, что наш «добрый» бот не успеет сделать всё как надо, потому — что ему потребуется время на отправку команды для лишения пользователя всех прав в группе и отправка команды с приветствием и вопросом. (два запроса, в обоих случаях это вызов LongmanTelegramBotRequest). И с большей долей вероятности «Плохой» пользователь сможет выполнить свой корыстный спам-запрос и нагадить в нашу группу между Request.
  • Если групп очень крупная, популярная, тогда есть вероятность что сообщения по линии webhook от сервиса Telegram на наш backend встанут в очередь и часть пользователей не сможет дать ответ на вопрос (тупо запрос повиснет в воздухе) или по достижению 100-а запросов в единицу времени, 101-й будет проходить мимо «доброго» бота. От части можно снизить очередь сообщений об обновлениях в группе, указав при создании webhook-а какие обновления от сервиса Телеграм отправлять (через параметр allowed_updates)

Какие ещё были мысли перед реализацией и что остаётся за рамками сейчас

Для проверки отправителя ответа нужно записывать информацию в базу данных. Т.е с начало сохранить данные о попытке вступления, потом сверить эти данные с теми что придут после ответа на вопрос (если они придут).

Показалось что это слишком сложно, и как указал выше, можно просто «приплюсовать» конкатенировать id пользователя к значению ответа перед формированием кнопок с ответами. Потом сопоставить ответ с тем значением от кого пришёл ответ.

Пример Json от сервиса Telegram c полем конкатенации строки и ИД пользователя

Удалять все сервисные сообщения от бота. Группа может быть просто завален вопросами от бота без ответа. Так как при нормальной логике (после верного ответа), вопрос удаляется. Для очистки нужно подключать cron, а так же вести лог с номерами сервисных сообщений в базе данных.

Что делать если бот не доступен, тогда новые пользователи в чат не попадут без ручного модерирования.

Пользователя после ответа на вопрос можно наделить «избыточными» разрешениями. Но как оказалось во время исследования, наделить пользователя полномочиями выше тех что установлены на группу глобально — не получится.

Это не будет работать. Да, действительно, решение достаточно спорное и пока оно тестируется в весьма «тепличных» условиях.

Перед вступлениемПользователь собрался вступить в группуПользователь ответил - Я человек

Ссылки

  • Библиотека для запросов а API Telegram php-telegram-bot/core здесь
  • Весь код модуля бота для Laminas здесь
  • Скелетон приложения Laminas с модулем для развёртывания здесь

P.S.: Весь код в примерах на PHP, выбрать язык не позволил новый редактор, отправил баг в специальный раздел

Источник: temofeev.ru

Рейтинг
( Пока оценок нет )
Загрузка ...