🔓

UNLOCK: интерактивные открытки и адвент-календарь

собственный сервис студии: открытка по ссылке с паролем, управление из Telegram

Собственный сервис интерактивных открыток и адвент-календарей: получатель разгадывает закрытый терминал, владелец правит всё из Telegram-бота и веб-кабинета. Python, aiohttp, PostgreSQL.

В продакшене · собственный продукт
1 установка
сколько угодно открыток и адвент-календарей, у каждой свой адрес и тема
72 надписи
правятся из бота и кабинета без кода и деплоя
7 тем
оформления открытки и ещё 4 оформления календаря

Задача

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

Дополнительные требования:

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

Решение

Интерактивная открытка — страница, в которой получатель что-то делает сам: разгадывает, выбирает, открывает. Мы сделали её как закрытый терминал и добавили админку в Telegram.

Что реализовано:

  • Открытка по ссылке с экраном пароля, Recovery Protocol, тремя экранами и финалом; выбор получателя уходит владельцу в Telegram.
  • Мастер создания открытки в боте из пяти вопросов: повод, кому, от кого, когда, ключ.
  • Семь тем оформления: «Романтика», «Стрелковый», «Бункер», «День рождения», «Свадьба», «Неон», «Нуар».
  • Веб-кабинет с предпросмотром: поля слева, открытка справа, отчёт «Как прошло» о том, докуда дошёл получатель.
  • Одна открытка на многих гостей: имя в ссылке, массовый выпуск ссылок, выгрузка гостей и заявок в CSV.
  • Адвент-календарь: окошки по дням, четыре оформления, пароль и дату проверяет сервер.
  • Тарифы, роли владельца, оператора и клиента, коды приглашения, личные открытки и закрытые черновики.
  • Отложенная публикация, QR-код ссылки, вебхук с заявками и витрина-лендинг сервиса с демо-открытками.

Как это устроено

Открытка из одного HTML-файла

Страница открытки — один статический HTML-файл: без сборки, без зависимостей, без бэкенда. Шрифт лежит рядом, салют рисуется своим кодом, звуковые эффекты синтезируются через Web Audio. Она получает тему и тексты от сервиса, но хранит свою копию значений, поэтому при недоступном сервисе открывается на них и на кэше прошлого визита. Эту копию не пишут руками: команда sync_site_defaults генерирует её из схемы сервиса, а смоук-тесты падают, если файл разошёлся со схемой.

Веб-кабинет как Mini App в Telegram

Mini App — веб-страница, которая открывается внутри Telegram. Вход в кабинет без паролей: Telegram передаёт подписанные данные о пользователе, а сервис проверяет подпись токеном бота на каждый запрос. Права те же, что в чате: клиент видит только свои открытки. С компьютера кабинет открывается по одноразовой ссылке из бота: она живёт 10 минут и тратится только нажатием кнопки, поэтому превью мессенджеров её не сжигают. В базе хранятся только хэши токенов, сессия действует 30 дней.

Одна открытка на многих: имя гостя в ссылке

День рождения, свадьба, корпоратив: адресатов много, открытка одна. Ссылка вида «/c/адрес?to=Имя» подставляет имя в терминал, приветствие, билет и превью. Без параметра работает имя из самой открытки, поэтому разосланные ранее ссылки не ломаются. В отчёте видно, кто открыл, докуда дошёл и кто прислал ответ, включая молчащих.

Адвент-календарь: пароль и дату проверяет сервер

У открытки ключ — часть игры, у календаря он охраняет подарок. Содержимое окошка и его файлы уходят в браузер только после двух проверок на сервере: пароль верный и нужный день настал. Окошко открывается в полночь по поясу получателя, пропущенные дни не сгорают. Регистр, буква «ё» и лишние пробелы в пароле не учитываются.

Превью ссылки в мессенджерах для каждой открытки

Робот мессенджера не выполняет скрипты и читает только то, что зашито в файл. Поэтому страницу по адресу «/c/адрес» отдаёт сервис и сам подставляет заголовок, описание и картинку именно этой открытки. Без этого у всех клиентов в чате была бы одна и та же карточка ссылки.

Тарифы и роли клиентов

У каждого тарифа три числа: сколько открыток, сколько мегабайт на открытку и сколько дней она живёт. Тариф приезжает вместе с кодом приглашения: владелец нажимает «Код клиенту», человек присылает код боту и сразу работает по своему тарифу. За три дня до конца срока владелец открытки получает предупреждение в чат.

Результат

  • Новый клиент подключается кодом приглашения: без деплоя, без отдельного сайта и без правки кода.
  • Открытка превращается из картинки в короткий сценарий, а ответ получателя приходит в Telegram.
  • Открытка остаётся рабочей при недоступном сервисе.
  • База данных не публикует порт наружу и видна только сервису.
  • Тарифы задают пять вариантов: от пробной открытки на 14 дней до тарифа «Студия» без ограничения числа открыток.
  • Смоук-тесты в CI запускаются на каждый push; в README отмечено 2305 проверок.

Технологии: что и зачем

  • Python 3.13 и aiohttp — API, которое отдаёт настройки открытки и принимает выбор получателя.
  • aiogram 3 — бот-админка на long polling: мастер создания, панели правки, коды приглашения.
  • PostgreSQL 17 — открытки, заявки, тарифы и сессии; отметки времени хранятся как TIMESTAMPTZ.
  • Один HTML-файл — сама открытка, без сборки и зависимостей.
  • Docker Compose и nginx — два контейнера, сервис и база; порт наружу публикуется один.
  • GitHub Actions — смоук-тесты с Postgres на каждый push.

Статус

Версия 3.16.1 от 29 сентября 2026 года, в продакшене, собственный продукт студии. Последние выпуски: витрина-лендинг сервиса и кабинет, удобный на телефоне и компьютере. Чего пока нет: приёма оплаты, тарифы назначаются вручную кодом приглашения или командой владельца. Живой прогон с настоящим токеном Telegram не выполнялся, тесты идут на заглушке. Список задач ведётся в ROADMAP репозитория.

Вопросы по проекту

Что такое интерактивная открытка по ссылке с паролем?
Это ссылка, которая открывает не картинку, а закрытый терминал: нужен пароль. Если забыл, включается Recovery Protocol с личными вопросами. Ввёл верно, и терминал расцветает в тему, а затем идут три экрана: вопрос, приглашение и финал, где получатель сам собирает вечер. Выбор приходит владельцу в Telegram.
Как сделать одну открытку на всех гостей?
Имя гостя передаётся в ссылке параметром «?to=», а не хранится в открытке. Каждый видит своё имя в терминале, приветствии, на билете и в превью ссылки, а ответ приходит подписанным. В кабинете есть вкладка «Гости»: вставляете список имён и получаете личную ссылку на каждого, список можно выгрузить в CSV.
Как сделать подарок-квест с адвент-календарём онлайн?
Календарь заводится из чата с ботом: «Новая», «Адвент-календарь», четыре вопроса, и ссылка готова. Это сетка окошек от 1 до 31, каждое открывается в свой день по поясу получателя. Внутри записка, фото, гифка, видео, кружочек, голосовое, музыка, кнопка-ссылка или промокод. Пароль и дату проверяет сервер.
Как владелец правит тексты и оформление, не трогая код?
Из Telegram-бота кнопками или из веб-кабинета, который открывается прямо в Telegram, а с компьютера по одноразовой ссылке из бота. Можно менять любую из 72 надписей, тему из семи, ключ, медиа и превью ссылки. Файлы для открытки можно просто прислать боту в чат.
Что будет, если сервис недоступен?
Открытка продолжает работать на значениях, зашитых в файл, и на кэше прошлого визита. Страница — один HTML-файл, шрифт лежит рядом, а салют нарисован своим кодом, поэтому внешних запросов нет.
Как ограничивается доступ клиентов и сроки открыток?
Тарифы задают три числа: сколько открыток, сколько места на каждую и сколько дней она живёт. Клиента в админку пускают по коду приглашения, он видит только свои открытки. Просроченная открытка уходит в архив, ссылка перестаёт открываться, тексты и статистика остаются.

Нужно похожее?

Расскажите о задаче — покажем, как решали такое, и прикинем объём работ.