GiftsHub — из чат-бота в полноценный backend-продукт

Страницы:  1

Ответить
 

Professor Seleznov


Привет! Меня зовут Кирилл Семенко, я AQA в SDET-команде Битрикс24. Сегодня расскажу о проекте GiftsHub — веб-платформе для координации мероприятий и дней рождений, которой пользуются сотрудники компании.
С 2019 года у нас в компании проводят внутренние Битрикс-хакатоны. Эта традиция даёт возможность поработать в новых командах, узнать что-то новое и сделать уникальный и интересный проект. Для лидеров команд это ещё и шанс развить свои способности менеджера и руководителя для достижения цели командой.
На хакатоне 2024 года начался проект, о котором я рассказываю сегодня — Telegram-бот для организации подготовки к праздникам. Потом появились Telegram WebApp / MiniApp, а следом полноценная платформа: отдельный сайт, публичные ссылки, email-вход, realtime и нормальная инфраструктура вокруг backend и frontend.
Технически задача такого приложения выглядит простой: хранить вишлисты и помогать выбрать, что подарить. Самой сложной частью оказалась координация действий всех участников: кто участвует в сборе, какая сумма уже собрана, кто отвечает за покупку подарка и как сохранить сюрприз до нужного момента.
В статье расскажу про путь от быстрого bot-first MVP к самостоятельной продуктовой системе: с доменной моделью, несколькими интерфейсами, realtime и инфраструктурой вокруг backend и frontend.
pic
Посмотреть и попробовать можно здесь
Содержание:

Откуда появилась задача
Исходная боль была жизненной и практической: организовать подарок человеку так, чтобы остальные участники успели все обсудить, собрать деньги, выбрать вариант и не раскрыть сюрприз.
До автоматизации процесс держался на переписках и памяти организатора. Где-то обсуждали идею подарка, где-то уточняли участников, где-то вспоминали, какая сумма уже собрана. Пожелания именинника могли лежать отдельно, договоренности терялись, ответственность расползалась между людьми.
Список желаний сам по себе решается просто: заметки, таблица или любой чат. Сложность начинается вокруг процесса: собрать людей, скрыть подготовку от именинника, избежать дублей при выборе подарка (если все дарят по подарку от себя), сохранить договоренности и вовремя напомнить о событии.
Если такие события происходят раза в 1-2 месяца, все можно вести руками. Когда сценарий повторяется регулярно, ручная организация начинает съедать слишком много времени и внимания. В какой-то момент стало ясно, что люди готовы участвовать, но процесс слишком завязан на одном человеке и множестве мелких сообщений.
Для хакатона это была удобная задача: боль понятная, цикл проверки короткий, сценарий уже существовал в жизни. Первая версия проекта была быстрой проверкой идеи: получится ли убрать часть ручной операционки из подготовки подарков.
pic

Почему стартовали через Telegram
Telegram выбрали как самый быстрый канал для MVP. Пользователи уже там, сценарий организации подарка похож на переписку — кого поздравляем, когда день рождения, кто участвует. Ещё бота можно добавить в группу и сразу проверить базовый путь без отдельного frontend.
Поэтому на хакатоне с Telegram было удобно: можно быстро собрать реально работающий сценарий. Команды, кнопки, callback-события и сообщения позволяли провести пользователя по шагам: добавить бота, создать группу, присоединиться, указать день рождения, начать собирать данные.
Backend на этом этапе обслуживал bot-flow. В центре были обработчики, состояния диалога и тексты сообщений. Это нормальная цена быстрого старта: сначала появляется рабочий путь, потом становится понятно, какие сущности и правила за ним стоят.
Первая версия закрывала базовые вещи: профили пользователей, группы, дни рождения, вишлисты и участников поздравления. Но уже тогда стало видно ограничение: Telegram удобен как вход и канал общения, но в его возможности не помещаются все нужные функции.

Когда одного Telegram стало недостаточно
Поворот случился из-за конкретного тупика.
Главный сценарий сервиса — подготовка подарка для конкретного человека. Остальным участникам нужно место для обсуждения деталей: что дарить, кто участвует в сборе, какая сумма уже собрана. Подготовка при этом остается скрытой от самого именинника.
В Telegram это почти решаемо: можно попробовать тред, топик или отдельное обсуждение. Но если обсуждение живет внутри общего пространства, из него нельзя аккуратно убрать одного человека: то есть человек видит обсуждение собственного подарка, и сценарий ломается.
Второе ограничение рядом: боту недоступно создание управляемого приватного чата нужного состава. То есть бот должен создать отдельный закрытый чат, добавить туда только нужных участников и исключить именинника. В Telegram это невозможно, и для безопасности мессенджера это логично, но для продукта — тупик.
pic
После этого стало ясно, что Telegram не может быть центром модели. Он оставался полезным каналом: привести пользователя, отправить уведомление, дать быструю кнопку. Но правила сценария уже должны были жить внутри продукта: кто участник события, кто именинник, кто может присоединиться и что нужно скрыть.
При создании события дня рождения именинник находится среди пользователей группы, но в участники события не попадает и не видит новый чат с обсуждением подарка на свой день рождения или другой праздник.
const participants = users.filter((user) => {
return user !== birthdayPerson;
});
const event = new EventEntity();
event.birthdayPerson = birthdayPerson;
event.participants = participants;
Этот кусок хорошо показывает переход: сначала задача звучала как "создать обсуждение без именинника". Потом она стала backend-правилом. Именинник существует в событии как birthdayPerson и остается вне обсуждения подготовки.

Telegram WebApp / MiniApp как промежуточный этап
Между ботом и полноценной платформой был важный этап — Telegram WebApp / MiniApp.
WebApp позволил оставить Telegram входной точкой, но вынести сложный интерфейс туда, где уже можно нормально управлять состоянием, навигацией, событиями, списками и чатами.
pic
Первая версия Telegram MiniApp
3 февраля 2025 рядом с ботом вышло в релиз наше приложение Telegram WebApp, которое открывался прямо из Telegram. Внутри уже были NestJS, Angular, Postgres, Socket.IO и JWT. Часть функций оставалась в боте, часть переезжала на frontend.
WebApp закрыл то, что плохо помещалось в формат команд и сообщений: просмотр хотелок, личные списки, обсуждение подарка во внутреннем чате. Там, где бот превращался бы в длинную цепочку кнопок, WebApp давал нормальный экран.
Авторизация тоже стала переходной. Пользователь приходит из Telegram, backend проверяет initData, дальше выдает access и refresh JWT в cookie. Вход еще был завязан на Telegram-контекст, но приложение уже работало как web-клиент с собственной сессией.
if (this.authService.validateInitData(body.initData, body.hash)) {
const user = await this.createUserIfNotExists(body.initData, body.userId);
const accessToken = this.authService.generateAccessToken(user.id, user.tokenVersion, user.role);
const refreshToken = this.authService.generateRefreshToken(user.id, user.tokenVersion, user.role);
AuthService.setTokensCookie(res, { accessToken, refreshToken });
}
На этом этапе проект получил два слоя взаимодействия: Telegram приводил пользователя и отправлял уведомления, WebApp забирал на себя продуктовый интерфейс.

Что на самом деле делает продукт
Список сущностей звучит скучно: пользователи, группы, события, вишлисты, сообщения. Польза проекта раскрывается в сценариях между ними.
pic
Первый сценарий — вишлист. Это список желаний, у которых есть название, ссылка, описание, цена и картинка. В подарочной логике есть важная деталь: участвующие в поздравлении могут забронировать пункт из вишлиста, который решили купить. Тогда несколько человек не купят случайно одно и то же. Сам владелец wishlist не видит, что кто-то забронировал в его вишлисте один из подарков.
Эту логику держит API. Когда wishlist смотрит владелец, backend скрывает данные бронирования.
if (isOwner) {
dto.isLocked = false;
dto.lockedBy = null;
} else if (dto.lockedBy && dto.lockedBy.id !== req.userId) {
dto.lockedBy = null;
}
Одна и та же запись выглядит по-разному для разных пользователей:
  • Для владельца это просто пункт его вишлиста.
  • Для участника подготовки — подарок, который можно забронировать или увидеть как занятый.
  • Для пользователя, который забронировал желание — его ответственность.
Второй сценарий — группы и события. Это пространства подготовки подарка: участники, обсуждение, именинник, invite-ссылка, ответственный за сбор суммы на подарок и правила доступа. И ещё публичность: если пользователь не зарегистрирован на платформе и ему скинули ссылку на вишлист пользователя, ссылка откроется без авторизации.
Третий сценарий — внутренний чат события. Telegram не дал управляемое пространство для приватного обсуждения подарка, поэтому обсуждение переехало внутрь продукта.
В WebSocket-слое тоже есть проверка доступа на основе встроенной механики room. Пользователь не может подключиться к комнате события, если он не участник.
const hasAccess = user?.events?.some((event) => {
return event.id.toString() === payload.room.toString();
});
if (!hasAccess) {
return;
}
client.join(payload.room);
Чтобы участники не возвращались к обсуждению в обычных чатах, внутренний чат должен закрывать привычные сценарии: показывать не прочитанные сообщения, поддерживать реакции, ответы на конкретные сообщения. Тогда обсуждение подарка остается удобным внутри события, и договоренности не расползаются обратно по личным сообщениям и отдельным чатам.
Четвертый сценарий — сбор суммы на подарок. В раннем посте это была одна из главных болей: нужно понимать, кто участвует в сборе и какая сумма уже собрана. В текущей системе у события есть ответственный за сбор, собранная сумма, платежные данные и отдельная логика платежей.
В итоге сервис занимается не хранением данных о подарках, а координирует подготовку: кто участвует в сборе, что обсуждают, какая сумма уже собрана и кто отвечает за покупку подарка.

Почему backend стал центром
Когда был только бот, часть логики можно было держать в обработчиках. Пользователь нажал кнопку, бот ответил, состояние изменилось. То есть backend был, но только в роли сервера для Telegram-бота: принимал события из Telegram и отвечает на них.
Когда появились Telegram WebApp, отдельный сайт, публичные ссылки, email-вход, realtime и админские сценарии, такая схема перестала работать. Один и тот же пользователь может прийти из MiniApp, зайти через браузер, открыть публичный wishlist или попасть в событие через invite. Во всех случаях система должна точно понимать, кто он и что ему можно делать.
Так backend перестал быть службой при боте. Он стал местом, где живут правила: доступ к событиям, состав участников, скрытие бронирований, авторизация. A Telegram — один из клиентов рядом с web, публичными страницами и realtime.
Frontend здесь тоже важен: WebApp и отдельный сайт сделали продукт понятным для пользователя. Backend держит правила, но ценность видна через интерфейсы: список желаний, карточки событий, чат, публичные страницы, регистрацию, invite-ссылки и нормальную навигацию.
pic

Когда проект вышел за пределы Telegram
Следующий большой шаг — отдельный сайт и публичные сценарии.
До этого Telegram все еще оставался обязательным контекстом. Даже с WebApp-интерфейсом регистрация была возможна только через бот. Это удобно для старта, но ограничивает продукт. Поэтому понадобилась вторая точка входа.
Публичные wishlist-страницы поменяли модель и стали первым сценарием, где Telegram больше не нужен: пользователь может открыть ссылку вида /u/:username и посмотреть желания человека, если тот дал на это согласие.
В backend появился публичный контроллер, который отдает wishlist по username, но не раскрывает лишнего. В публичной выдаче остаются только данные, которые можно показывать внешнему пользователю.
return {
name: dto.name,
description: dto.description,
link: dto.link,
price: dto.price,
image: dto.image,
isGifted: dto.isGifted,
};
Во frontend под это есть отдельный route без авторизации.
{
path: 'u/:username',
children: [ ... ]
}
Похожая история с invite flow. Событие можно открыть по токену, посмотреть базовую информацию и присоединиться, если правила это позволяют. Канал входа меняется, правила сценария остаются общими.
Позже появилась email-регистрация. Она сняла обязательную привязку к Telegram, который перестал быть условием существования пользователя в системе.
Изначально Telegram-группы помогали управлять составом внутренних групп событий: через них добавляли и исключали пользователей. Сейчас Telegram можно подключить как канал уведомлений о новых событиях, назначенных ответственных и днях рождения в календаре Ещё авторизованным через Telegram пользователям приходят личные уведомления через бота.
На этом этапе проект стал платформой с несколькими точками входа: Telegram MiniApp, отдельный сайт, публичные ссылки, email-аккаунт. Все они обращаются к одной продуктовой модели.
pic

Инфраструктура как следствие боли
Инфраструктура обычно появляется, когда менять проект становится страшно.
Пока проект был ботом, многое можно было проверить руками: поправить обработчик, нажать пару кнопок, посмотреть ответ Telegram. Для MVP терпимо.
Когда один backend обслуживает Telegram, MiniApp, отдельный web, публичные страницы, realtime, фоновые задачи и уведомления, цена ошибки возрастает. Одно изменение может сломать авторизацию, чат, деплой.
Отсюда вырос отдельный orchestration-слой. Он собирает вокруг продукта backend, frontend, nginx, Redis, docker-compose, CI/CD и Makefile-команды. Этот слой не виден пользователю, зато продукт удобнее развивать и выкатывать.
В docker-compose видно, что система живет как набор связанных сервисов.
services:
nginx:
depends_on:
backend:
condition: service_healthy
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
backend:
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:${APP_PORT}/api/health || exit 1"]
Nginx ждет здоровый backend, Redis поднимается отдельно, backend имеет healthcheck, frontend собирается своим слоем. На этом этапе продукт уже нужно собирать, выкатывать и проверять как систему.
CI/CD закрепил ту же логику. В pipeline появились стадии validate, prepare, build, deploy, verify. Makefile закрыл повторяемые команды: деплой всего продукта, отдельно frontend, отдельно backend, dev-ветка, проверки статуса и синхронизация submodules.
Redis здесь тоже появился из практической нагрузки: Telegram-сессии, throttling, rate limiting и кеширование tokenVersion для auth-запросов. Он снижает лишние обращения к базе и убирает часть состояния из памяти процесса.

Текущая форма продукта
К этому этапу система уже объединяла вишлисты, события, участников, обсуждения, бронирования, сбор суммы, уведомления и публичные ссылки. Telegram MiniApp и бот остались удобными точками входа, а общая логика переехала в backend и frontend.
Пользователь может:
  • Прийти через Telegram, открыть MiniApp и работать внутри привычного мессенджера.
  • Зайти на сайт. 
  • Зарегистрироваться по email. 
  • Поделиться публичной ссылкой на wishlist. 
  • Попасть в событие через invite. 
Для продукта это разные входы в одну и ту же модель.
В отчетном посте на апрель 2026 уже было 455 человек — без дополнительного привлечения и рекламы. Для pet/side/hackathon-проекта это хороший сигнал: сценарий не был выдуманным. Людям действительно нужен способ проще координировать подарки и не держать все в чатах.
Итоговая схема: Telegram остался каналом входа и уведомлений. MiniApp дал удобный интерфейс внутри Telegram. Отдельный сайт убрал обязательную зависимость от Telegram. Backend стал местом, где живут правила продукта. Realtime закрыл обсуждения событий. Инфраструктура сделала все это сопровождаемым.
pic

Что я бы сделал раньше
Старт через Telegram был правильным решением для первой проверки идеи. Пользователи уже были в мессенджере, сценарий был разговорным, а бот позволял быстро собрать рабочий путь без большого frontend.
Но границу между каналом и продуктовой моделью я бы провел раньше.
Удачный старт легко спутать с удачной долгосрочной архитектурой. Telegram помог быстро появиться, но потом начал диктовать ограничения там, где продукту уже нужны были собственные правила: приватность обсуждений, роли участников, события, бронирования, invite-ссылки и разные точки входа.
Backend быстро выходит за рамки CRUD, если продукт не просто хранит данные, а координирует действия нескольких людей. Пользователь участвует в процессе: обсуждает, бронирует, присоединяется, участвует в сборе, получает уведомления, видит одну часть информации и не видит другую.
Инфраструктуру лучше не откладывать до момента, когда менять уже страшно. Миграции, health checks, CI/CD, Redis, воспроизводимое окружение и нормальная схема деплоя выглядят избыточно, пока проект маленький. Потом внезапно оказывается, что без них каждое изменение превращается в маленькое гадание.
Ссылка на текущую версию приложения: https://gifth.ru/ 
Если сжать весь путь проекта до одной мысли, она будет такой: быстрый внешний канал помогает стартовать, но продукт должен вовремя стать самостоятельной системой.
Проект перерос Telegram по простой причине: реальный сценарий организации подарков потребовал собственной модели приватности, ролей, событий, обсуждений и точек входа.-Источник
 
Loading...
Error