Как создать квест в телеграмме
![]()
BORIS B T alpha
- Тарифы
- ПродуктыБлог
- Маркетплейс
- ru
- RU
Войти


Привет, я Борис бот для квестов, загадок и экскурсий
А что ты можешь?
Вы избавитесь от бумажек и сможете масштабировать свой образовательно развлекательных продукт
А кому такое нужно?
Меня используют: Event — агенства, Сценаристы, Активные родители, Экскурсионные бюро, Музеи и Парки

А как все работает?
Из блоков конструктора можно автоматизировать любой сценарий без разработчика
А где это работает?
В нашем музее Борис уже несколько лет проводит квест экскурсию. Сценарии квеста придумали и внедрили сотрудники музея. Отдельно хочется отметить профессионализм и клиентоориентированность команды разработчиков — все в срок, оперативно, вежливо и компетентно: работать вместе над проектом было в удовольствие!

Алена Жук директор Дом-музей Марины Цветаевой
На этом конструкторе был собран бот для гостей Чемпионата мира по футболу. На 3х языках бот водил экскурсии по интересным маршрутам в Москве, загадывал загадки и рассказывал интересный контент за правильные отчеты.

Мосгортур
С чего начать?
Заказать под ключ
Вы можете заказать квест у нас или у наших авторизованных настройщиков. для этого пришлите ТЗ на boris(собака)borisbot.com
Бесплатная версия
Если у вас до 100 человек и у вас небольшой скрипт, то вы сможете использовать конструктор бесплатно.
Быстрый старт
Оплатив тариф Бизнес на 1 месяц вы получите 2 часа skype настройки, где вам помогут разобраться с настройками
Квест бот


Используйте этого бота как пример, чтобы создать свой образовательный или развлекательный квест: проверить знания учеников с помощью головоломок, найти подарок именинника или развлечь гостей на детской вечеринке.
Полезен, если вы хотите:
- Добавить геймификацию в обучение.
- Нескучно провести образовательное мероприятие.
- Разнообразить отдых, детские праздники, корпоративы.
Уровень сложности: легкий — может настроить любой пользователь.
Время настройки: зависит от вашего сценария.
Универсальность: высокая — подойдет для городских мероприятий, экскурсий, домашних праздников, уроков, корпоративов.
Как это работает?

Пользователь активирует бота по ссылке или QR-коду и переходит в браузер или мессенджер, где знакомится с ботом.

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

Бот может не только давать задания, но проверять их и комментировать неверные ответы.

Этот пример мы завершили выдачей геолокации для экскурсии, но вы можете придумать другой финал: посчитать количество баллов, подарить сертификат или что-то другое — зависит от вашего квеста.
Что делать с этим шаблоном?
- Добавьте шаблон в проект.
- Создайте дизайн бота — подберите фон, аватар и шрифт.
- Продумайте сценарий вашего квеста и соберите его на платформе. Используйте блоки «таймаут», «предсказуемый ответ», «кнопки с одним вариантом ответа», «кнопки с множественным вариантом ответа», «карусель, «фото респондента», «открытый текст», «файл». Добавляйте тексты, фото, видео, файлы, координаты. Чтобы настроить подсчет баллов — укажите действие «Добавить/вычесть баллы» напротив правильных/неправильных ответов. Результат отражается через глобальную переменную «score».
- Если требуется, добавьте бота в мессенджер: Telegram/Вконтакте/Facebook.
- Протестируйте бота.
Создаем собственный текстовый квест в Telegram
Телеграм боты — это крутой способ взаимодействия с пользователем прямо в привычном ему мессенджере. Это гораздо быстрее и чаще удобнее, чем писать полноценное мобильное приложение. В этой статье мы постраемся создать собственного Telegram бота, который будет предлагать пользователю разыграть полноценный текстовый квест.
Нюансы сюжета вам придется продумать самостоятельно, ну а вся техническая часть — под катом!
Для начала давайте познакомимся с Telegram ботами, по ссылке представлено множество примеров таких программ, от прогноза погоды до общения с рандомным собеседником: https://uip.me/2016/04/50-popular-telegram-bots/.
Технически, телеграм бот — это программа, которая запущена на вашем компьютере и общается с серверами телеграма через интернет. Можно представить, что при регистрации бота, телеграм выделяет нам почтовый ящик, через который мы можем получать сообщения от пользователей и отправлять их им.
Чтобы создать собственного бота, сперва его надо зарегистрировать. Для этого в телеграме нужно добавить бота @BotFather и следовать инструкциям. После того, как мы введем название бота, @BotFather сообщит нам токен — пароль к нашему почтовому ящику.
После получения токена нам потребуется установить библиотеку pyTelegramBotApi, как и обычно, это делается через pip:
pip install pytelegrambotapi
Пробуем создать бота
Давайте сделаем нашего первого бота — бота-попугая. На все сообщения он будет отвечать повторением.
import telebot token = "ВСТАВЬТЕ СЮДА ТОКЕН" # Обходим блокировку с помощью прокси telebot.apihelper.proxy = # подключаемся к телеграму bot = telebot.TeleBot(token=token) # content_types=['text'] - сработает, если нам прислали текстовое сообщение @bot.message_handler(content_types=['text']) def echo(message): # message - входящее сообщение # message.text - это его текст # message.chat.id - это номер его автора text = message.text user = message.chat.id #отправляем картинку с попугаем bot.send_photo(user, "https://i.ytimg.com/vi/R-RbmqzRC9c/maxresdefault.jpg") #отправляем сообщение тому же пользователю с тем же текстом bot.send_message(user, text) # поллинг - вечный цикл с обновлением входящих сообщений bot.polling(none_stop=True)

Помимо текстовых сообщений, в телеграме есть команды, они начинаются со слэша, например, /start или /help . Их тоже можно обрабатывать.
Давайте добавим пояснение к нашему боту, которое объяснит пользователю, что он делает.
import telebot token = "ВСТАВЬ СЮДА ТОКЕН" # подключаемся к телеграму bot = telebot.TeleBot(token=token) # реагируем на команды /start и /help @bot.message_handler(commands=['start', 'help']) def help(message): user = message.chat.id bot.send_message(user, "Это бот попугай! Просто пришли и я повторю.") # content_types=['text'] - сработает, если нам прислали текстовое сообщение @bot.message_handler(content_types=['text']) def echo(message): # message - входящее сообщение # message.text - это его текст # message.chat.id - это номер его автора text = message.text user = message.chat.id #отправляем картинку с попугаем bot.send_photo(user, "https://i.ytimg.com/vi/R-RbmqzRC9c/maxresdefault.jpg") #отправляем сообщение тому же пользователю с тем же текстом bot.send_message(user, text) # поллинг - вечный цикл с обновлением входящих сообщений bot.polling(none_stop=True)
Взаимодействуем с несколькими пользователями одновременно
Предположим мы хотим сделать бота, которы будет запоминать какую-то фразу, а затем по просьбе пользователя напоминать ее ему. Чтобы решить эту задачу, нам понадобится где-то хранить последнее сообщение пользователя.
Если мы будем использовать переменную, то сможем сохранить сообщение только одного пользователя. Например, Вася попросил запомнить слово kitten . Мы положим эту строку в переменную note . А затем Петя, попросить запомнить слово puppy , и мы снова положим это переменную note . Когда Вася попросит нам напомнить его последнее сообщение, мы напишем ему puppy вместо kitten . Совершенно не годится!

> Удобнее всего хранить все данные, которые привязаны к конкретному пользователю в словаре. Ключем в этом словаре будет id пользователя, а значением — произвольные данные.

Предположим, что наш словарь называется notes — заметки. Теперь, когда Вася (id88000) пришлет слово kitten мы положим его в notes[88000] , а слово puppy от Пети (id5300) — в notes[5300] . Посколько теперь мы используем разные переменные для хранения слова, сообщения от разных пользователей не будут путаться.
Реализация:
import telebot token = "ВАШ ТОКЕН" # Обходим блокировку с помощью прокси telebot.apihelper.proxy = bot = telebot.TeleBot(token=token) notes = <> @bot.message_handler(commands=['remind']) def remind(message): user_id = message.chat.id if user_id not in notes: bot.send_message(user_id, "Вы мне еще не писали.") else: bot.send_message(user_id, notes[user_id]) @bot.message_handler(content_types=['text']) def remember(message): user_id = message.chat.id notes[user_id] = message.text bot.send_message(user_id, "Я запомнил") bot.polling(none_stop=True)
Добавляем кнопки
Следующий пример демонстрирует, как добавить несколько кнопок к сообщению и реагировать на их нажатия.
import telebot from telebot import types token = "ВАШ ТОКЕН" # Обходим блокировку с помощью прокси telebot.apihelper.proxy = bot = telebot.TeleBot(token=token) @bot.message_handler(commands=["start"]) def repeat_all_messages(message): # создаем клавиатуру keyboard = types.InlineKeyboardMarkup() # добавляем на нее две кнопки button1 = types.InlineKeyboardButton(text="Кнопка 1", callback_data="button1") button2 = types.InlineKeyboardButton(text="Кнопка 2", callback_data="button2") keyboard.add(button1) keyboard.add(button2) # отправляем сообщение пользователю bot.send_message(message.chat.id, "Нажмите кнопку!", reply_markup=keyboard) # функция запустится, когда пользователь нажмет на кнопку @bot.callback_query_handler(func=lambda call: True) def callback_inline(call): if call.message: if call.data == "button1": bot.send_message(call.message.chat.id, "Вы нажали на первую кнопку.") if call.data == "button2": bot.send_message(call.message.chat.id, "Вы нажали на вторую кнопку.") bot.polling(none_stop=True)
Собираем текстовый квест
from telebot import TeleBot, types from random import randint from secret import token pictures = < 0: "https://storage.geekclass.ru/images/760e484b-a099-4a7a-a722-5aec9a933614.jpg", 1: "https://storage.geekclass.ru/images/4637fc41-08df-466a-b112-aa577dba6c1d.jpg", 2: "https://storage.geekclass.ru/images/c2a2a60c-9c7b-4c3a-b663-42d2559bf869.jpg" >states = <> inventories = <> # Обходим блокировку с помощью прокси telebot.apihelper.proxy = bot = TeleBot(token) @bot.message_handler(commands=["start"]) def start_game(message): user = message.chat.id states[user] = 0 inventories[user] = [] bot.send_message(user, "Добро пожаловать в игру!") process_state(user, states[user], inventories[user]) @bot.callback_query_handler(func=lambda call: True) def user_answer(call): user = call.message.chat.id process_answer(user, call.data) def process_state(user, state, inventory): kb = types.InlineKeyboardMarkup() bot.send_photo(user, pictures[state]) if state == 0: kb.add(types.InlineKeyboardButton(text="пойти направо", callback_data="1")) kb.add(types.InlineKeyboardButton(text="пойти налево", callback_data="2")) bot.send_message(user, "Вы в оказались в темном подземелье, перед вами два прохода.", reply_markup=kb) if state == 1: kb.add(types.InlineKeyboardButton(text="переплыть", callback_data="1")) kb.add(types.InlineKeyboardButton(text="вернуться", callback_data="2")) bot.send_message(user, "Перед вами большое подземное озеро, а вдали виднеется маленький остров.", reply_markup=kb) if state == 2: bot.send_message(user, "Вы выиграли.") def process_answer(user, answer): if states[user] == 0: if answer == "1": states[user] = 1 else: if "key" in inventories[user]: bot.send_message(user, "Перед вами закрытая дверь. Вы пробуете открыть ее ключем, и дверь поддается. Кажется, это выход.") states[user] = 2 else: bot.send_message(user, "Перед вами закрытая дверь, и, кажется, без ключа ее не открыть. Придется вернуться обратно.") states[user] = 0 elif states[user] == 1: if answer == "2": bot.send_message(user, "И правда, не стоит штурмовать неизвестные воды. Возвращаемся назад. ") states[user] = 0 else: bot.send_message(user, "Вы пробуете переплыть озеро. ") chance = randint(0, 100) if chance > 30: bot.send_message(user, "Вода оказалось теплой, а в сундуке на острове вы нашли старый ключ. Стоит вернутся обратно.") inventories[user].append("key") states[user] = 0 else: bot.send_message(user, "На середине озера вас подхватывают волны и возвращают обратно.") states[user] = 1 process_state(user, states[user], inventories[user]) bot.polling(none_stop=True)
Telegram бот для сложных квестов
«В чем же заключается преимущество?» — спросите вы, ну все дело в том, что в нем можно построить логику примерно из следующих выражений:
– Пользователь на шаге N?
– Сообщение содержит изображение и смайлик?
– Текст подходит под регулярное выражению «I am [a-zA-Z]+»?
– Время получения раньше/позже заданного?
– Это было нажатие на клавиатуре/обычное сообщение/inline-кнопка?
Большинство из этих правил могут быть в зависимости друг от друга, но об этом чуть позже.
Для начала, о чем я хочу рассказать:
- Об идее проекта – зачем я это сделал, какие есть аналоги, но почему мне они не нравятся.
- Архитектурных решениях, какие возникли трудности, как были решены.
- Что получилось в итоге и стоило ли оно того, дальнейшее развитие.
Цель проекта
Это было в конце ноября, я понимал что скоро новый год и необходимо дарить подарки.
Идея с Telegram Bot–ом, в качестве карты поиска, казалась крайней простой и в должном исполнении интересной. Единственное что необходимо было сделать – взять и загуглить. Что собственно я и сделал. Основной посыл запроса – платформа для создания квестов, или же просто чат-бот c дополнительной логикой, написанный на python (желательно Django Framework)
Большинство рассмотренных приложений либо имели захордкоженные данные, либо же были своего рода викторинами. Ни то ни другое не подходило.
Интересными вариантами показались: Django Telegram Bot, PermaBot.
На первом репозитории долго задерживаться нет смысла, так как в описании недвусмысленно сказано «Try Permabots: more stable django app for bots.». Потому выбираем второй.
Стоит отметить что данная платформа сделана достаточно качественно, хоть и без соблюдения pep8 (не камень в огород, но это же де-факто стандарт 🙂 ), однако с комментариями, тестами, а главное документацией по REST API.
При наличии всех этих плюсов были обнаружены и существенные недочеты:
- старая версия telegram API (версия 4.2.0 против 9.0.0, последний коммит Июнь 2016)
- полное отсутствие поддержки media (здесь моя идея с изображениями накрылась медным тазом)
- некоторые отсутствующие фичи (отложенная отправка уведомлений с настройками возможного ответа, гибкая настройка клавиатуры чата).
Веб фреймворк – Django, для асинхронных задач – Celery, брокер сообщений –
Redis, база данных – SQLite. В дальнейшем есть возможность переехать на Postgres/MySQL, но пока тащить всю эту махину с собой бессмысленно.
Что стоит отметить, для разработки и запуска проекта нужен внешний https-адрес, на который будут прилетать веб-хуки. Для этого можно использовать туннелирование на публичный домен посредством Ngrok или LocalTunnel.
Я использовал localtunnel локально и на сервере в связке c nginx и let’s encrypt сертификатами.
Архитектура
Bot – содержит общую информацию и token (аналог telegram.Bot в оф. документации)
Quest – модель квеста, все просто: имя, описание, бот. вынесено в отдельную сущность для логического разграничения
Step – возможные состояния переходов. если имеет флаг is_init=True, то будет выбрано в качестве начального при инициализации квеста
Handler – выступает в роли dispatcher-a, имеет логическое выражение, которое может выполняться либо нет, в зависимости от этого переводит пользователя на определенный шаг и отправляет соответствующие ответы (представлены моделью Response)
Condition – содержит возможные правила для какого–то определенного поля.
Update – объект, который прилетает с веб – хуком и содержит всю информацию
CallbackQuery – используется для inline – сообщений, отправлять таковые возможности пока нет (основной упор был на базовую клавиатуру), однако варианты расширения учитывались.
Reponse – хранит информацию о тексте сообщения, клавиатуре, ее поведении (скрыть, удалить, установить по умолчанию, показать дефолтную)
Message – полученный объект сообщения (telegram.Message)
Chat – хранит информацию о чате (telegram.Chat)
Photo – модель хранящая информацию о изображении (telegram.PhotoSize)
Event – отвечает за отправку сообщений в определенный чат, и имеет возможность одновременной установки состояния (шага) пользователя. все дополнительные данные (текст, клавиатура, настройки к ней) берет из заданной модели Reponse
User – модель пользователя унаследовал от contrib.auth.models.AbstractUser c указанием device_uid, step и еще некоторых дополнительных полей
Полную UML диаграмму можно просмотреть в draw.io, файл db-diagram.xml
Как мне кажется, один из любопытных аспектов это то, как происходит решение о выборе того или иного обработчика.

В поле «Mathematics expression» логическое выражение можно задавать двумя способами. Первый – тот что указан в примере, в этом случае условия к данному обработчику будут подставлены в порядке их добавления, во втором варианте – можно указать id условий.
Затем выполняется парсинг. Он проходит два этапа, вначале делается преобразование в булевое отображение, где 1 и 0 – истинность конкретного условия, и затем происходит разбор грамматики с использованием библиотеки pyparsing.
Другая сложность с которой пришлось столкнуться – выбор и указание клавиатуры. Было решено сделать одну по умолчанию и другую предоставлять в ответе. Таким образом появляется возможность задать дефолтную на весь чат, которая может выглядеть примерно так [[«Задать вопрос»], [«Попросить подсказку»]]

Также можно увидеть что telegram api позволяет скрывать текущую клавиатуру при нажатии на кнопку или же удалять ее совсем (в этом случае отправляется дефолтная).
Еще одна вещь связанная с клавиатурой – это попытка узнать что же это было, то ли обычное сообщение написанное пользователем или же нажатие на кнопку. Для чего это нужно?
Предположим, на каком-то шаге мы решили запросить текстовый ответ у пользователя и в то же время там отображается стандартная клавиатура с кнопкой «Поменять задание». Несмотря на то что обработчики (Handlers) содержат опциональные поля Step on success и Step on error, гораздо проще организовать логику ограничив тип действия на который они реагируют. Для этого в модели Chat содержится информация еще и о текущей клавиатуре, по ней определяется нажатие это или другой тип события. Это полезно когда необходимо узнать что творится в каком-то определенном чате.
Помимо этого, из фич, которые показались нужными – это функция redirect message. В каждом обработчике можно указать кому перенаправлять сообщение при вынесении вердикта истинности. Пригождается когда нужно проследить специфические моменты (тот же вопрос администратору) или прохождение некоторых этапов пользователем.
Стоило оно того?
Определенно. Тестирование заняло пару вечеров, после чего я настроил и запустил это на сервере. Квест состоял из 10+ состояний и включал в себя выбор вариантов ответа, рассказ истории (суть была в поиске ключевых слов), выполнение задания по карте местности, отправки фотографии года и еще некоторые.
Пожалуй, реакция человека, для которого я это делал, окупила те вечера перед зачетной неделей, которые я потратил.
Перспективы
На данный момент готов рабочий вариант, который разворачивается в docker-контейнере за пару минут, но для полноценной разработки необходимы тесты, продуманное окружение и еще некоторые настройки. Если кому-то проект покажется интересным, то я с желанием продолжу поддержку/разработку, реализация фронтенда под вопросом, но возможна (на emberJS или vueJS).
Спасибо вам за внимание!
Буду рад услышать вопросы, пожелания, критику












