Перейти к контенту

Napkin Random Bot: как мы создали бота для честных розыгрышей

В статье я делюсь опытом создания Telegram-бота для розыгрышей, который помогает избежать спама, обеспечивает честность выбора победителей и даёт возможность кастомизировать процессы. Рассказываю о технологии, функционале, и первом розыгрыше в честь двухлетия проекта.

Napkin Random Bot: как мы создали бота для честных розыгрышей

Всем привет!

Этим летом моему проекту «Код на салфетке» исполнилось два года. Отметить это событие я решил проведением розыгрыша среди подписчиков. Разыгрывались 10 книг по программированию, которые, надеюсь, оказались полезными победителям.

Но было бы скучно использовать готового Telegram-бота для этого (дело не только в скуке, но об этом дальше по тексту). Тогда я решил сделать своего бота для проведения розыгрышей — Napkin Random Bot.

О том, почему я решил его сделать и как всё прошло, расскажу в этой статье.

Если вам интересны подобные материалы, подписывайтесь на наш Telegram-канал «Код на салфетке» — будем рады новым подписчикам! Впереди ещё больше розыгрышей и активностей.

 


Для чего писать свой бот для розыгрышей?

Самый первый вопрос, который я слышал, озвучивая эту идею знакомым или в нашем чате: «есть же готовые инструменты, зачем свой?». Ответ на него не так прост.

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

  • Спам и реклама: многие боты рассылают участникам не просто рекламу, а порой и откровенно незаконные вещи (казино, средства обхода и тому подобное).
  • Продажа данных: данные об участниках могут продаваться третьим лицам.
  • Ограниченный функционал: нет кастомизации под конкретный розыгрыш.
  • Платные функции: многие возможности доступны только по подписке.

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


Основа проекта

Бот не является Open Source проектом, тем не менее, немного о его технической составляющей я расскажу.

Стек технологий

  • Backend: AIOgram 3.x для работы с Telegram Bot API.
  • Web API: FastAPI для обработки запросов от WebApp и WebHook Telegram.
  • Frontend: Nuxt.js для создания пользовательского интерфейса WebApp.
  • База данных: PostgreSQL для хранения данных о розыгрышах и участниках.
  • Файловое хранилище: S3-совместимое хранилище для логотипов розыгрышей.

Команда

Разработал бота не я один — нас было трое:

  • Backend, DevOps и вся техническая составляющая: Иван Ашихмин, то есть я.
  • Frontend: Виктор Вангели.
  • UX/UI: Евгений Акопян.

Архитектура Backend представляет собой нечто приближённое к DDD (Domain-Driven Design). Поскольку подразумевается расширение функционала и для удобной поддержки проекта в будущем, был выбран именно DDD-подход.

Общее время разработки составило примерно две недели на всю функциональность.


Функциональность бота

Я хотел сделать простого в управлении и достаточно функционального бота. Насколько это получилось — судить вам.

Бот разделён на два уровня взаимодействия:

  • С администратором канала.
  • С участником розыгрыша.

Администратор канала

Администратор канала может:

  • Добавить канал (или несколько) в бота.
  • Создать розыгрыш.
  • Управлять (настраивать) розыгрышем.

Добавление канала

Добавление канала в бота достаточно простое и происходит по стандартной схеме:

  1. Администратор добавляет бота в канал с правами на создание и редактирование сообщений.
  2. Бот фиксирует новый канал в базе данных.
  3. Администратор в боте переходит в меню добавления канала и вводит ссылку на канал.
  4. Если канал не найден (не добавлен в канал или не выданы права), бот оповещает администратора об ошибке.
  5. Если канал найден и права корректны, он становится доступен для создания розыгрышей.

Пять шагов, но на деле всё занимает считанные минуты.

Важно отметить, что «кто попало» не сможет себе добавить канал, если в нём уже есть бот с правами. При добавлении также идёт проверка, что пользователь, добавляющий канал, является администратором этого самого канала.

Создание розыгрыша

Создание розыгрыша состоит из двух этапов: обязательных настроек и дополнительных возможностей.

Минимальные (обязательные) настройки:

  • Выбор канала (или нескольких каналов).
  • Название и текст поста розыгрыша.
  • Дата начала и окончания (по МСК). Если указать начало розыгрыша в прошедшем времени на момент окончания настройки, то он будет запущен сразу.
  • Текст кнопки участия.
  • Количество победителей (призовых мест).

Дополнительные возможности:

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

Всё достаточно просто и интуитивно понятно. После создания розыгрыша создаётся задача на выполнение. Если дата начала ещё не подошла, то розыгрыш помечается как «Создан», иначе он будет помечен как «Начат».

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

 

Участник розыгрыша

Со стороны пользователя всё значительно проще.

Когда бот публикует в канал пост с розыгрышем, в нём есть кнопка для участия. Нажав на неё, открывается WebApp и начинается процесс регистрации участника:

  1. Проверяется подписка пользователя на все указанные каналы.
  2. При выполнении всех условий пользователь регистрируется как участник.
  3. Повторное участие блокируется с соответствующим уведомлением.

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

Алгоритм выбора победителей

Читателей обычно больше всего интересует: «как именно выбираются победители?» и «есть ли риск, что автор повлиял на результаты?»

Для честного выбора используется алгоритм частичной перестановки Фишера–Йетса:

while len(winners) < winners_count and participants:  
    rand_idx = random.randrange(len(participants))  
    candidate = participants[rand_idx]  

    is_member = all(  
        [  
            await check_chat_members(chat_id=post.channel_id, user_id=candidate.telegram_id)  
            for post in giveaway_data.posts  
        ]  
    )  
    if is_member:  
        winners.append(candidate)  

    participants[rand_idx] = participants[-1]  
    participants.pop()

Этот алгоритм гарантирует равную вероятность выбора для каждого участника и исключает повторения. Дополнительно на момент выбора победителей проверяется, что они всё ещё подписаны на каналы.

Окончание розыгрыша

По окончании розыгрыша происходит автоматический выбор победителей. На их основе формируется сообщение, которое публикуется во всех каналах, участвующих в розыгрыше, с перечислением победителей и, если призы установлены, с их описанием.

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

Перерозыгрыш призов

Бывают случаи, когда победитель отказывается от приза или вовсе не выходит на контакт. В таких ситуациях необходимо проводить дополнительный розыгрыш.

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

Суть в том, что администратор может ввести номера призовых мест для переопределения. После этого будет проведена дополнительная жеребьёвка, а в канал будет опубликовано сообщение с новыми победителями на указанных администратором местах.


Практический опыт: розыгрыш книг

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

Условия были простыми: подписаться на канал, нажать кнопку участия и находиться на территории РФ (для возможности доставки).

Результаты

  • Участников: 115 человек за 2,5 недели.
  • География: участники не только из России, несмотря на ограничение по доставке.
  • Стабильность: никаких критических сбоев в работе бота, за исключением одной ошибки с перерозыгрышем.

Интересные случаи

Самый забавный момент: одним из победителей оказался школьник из нашего чата «Кот на салфетке», который выиграл книгу «Чистый код». Парадокс в том, что именно эту книгу я лично дарил ему на день рождения! В итоге, поскольку он всё-таки честно выиграл, мы подарили ему другую книгу, а «Чистый код» ушёл в перерозыгрыш.

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

Сложности и решения

  1. География участников: некоторые участники были из стран, куда отправка в 2, а то и три раза перекрывала стоимость книги.
  2. Технические проблемы при перерозыгрыше: изначально не учёл все особенности перерозыгрыша, и возникла путаница с призовыми местами, но проблему удалось быстро исправить.
  3. Мониторинг работы: система логирования помогла быстро выявлять и решать возникающие проблемы.

Планы развития

Сейчас бот находится в полностью рабочем состоянии (я надеюсь), но тем не менее пока буду считать его бета-версией. Всегда есть вероятность неожиданных сценариев использования.

Сбор обратной связи

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

Планируемые улучшения

  • Расширение типов розыгрышей: добавление новых механик и условий участия.
  • Улучшение архитектуры: оптимизация производительности и масштабируемости.
  • Аналитика и статистика: предоставление администраторам полезных метрик (без персональных данных участников).
  • Дополнительные настройки: больше возможностей кастомизации розыгрышей.

Приглашение администраторов Telegram-каналов

Если вас заинтересовал бот или вы искали альтернативу имеющимся инструментам для проведения розыгрышей (существующие решения, конечно, мастодонты индустрии, и я ни в коем случае не заявляю своего бота как прямого конкурента!), то буду рад получить обратную связь!

Ссылка на бота: Napkin Random Bot

Обратную связь можно отправлять в личные сообщения канала «Код на салфетке» или напрямую мне.


Заключение

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

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

Подписывайтесь на наш Telegram-канал «Код на салфетке» — у нас много качественных материалов для новичков по Python и DevOps, а также интересные проекты и розыгрыши!

Аватар автора

Автор

Иван Ашихмин

Программист, фрилансер и автор гайдов. Занимаюсь разработкой ботов, сайтов и не только.

Войдите, чтобы оставить комментарий.

Комментариев пока нет.