Live · Beauty · 2025 – 2026

Manicure Bot —
онлайн-запись в салон через Telegram.
Manicure Bot —
salon booking via Telegram.

Коммерческий бот записи для бьюти-салонов. Клиент выбирает услугу, мастера и слот за 30 секунд. Админ управляет расписанием прямо из бота. Per-salon деплой, лицензирование Ed25519, 86+ тестов. Commercial booking bot for beauty salons. A client picks a service, master, and time slot in 30 seconds. Admins manage schedules right inside the bot. Per-salon deploy, Ed25519 licensing, 86+ tests.

Telegram · @manicure_bot live
user> /book
→ Выберите услугу:
  1. Маникюр классический — 120 000 сум
  2. Маникюр + покрытие — 180 000 сум
  3. Педикюр — 150 000 сум

user> 2
→ Дополнения:
  ☐ Снятие старого покрытия +30 000
  ☐ Дизайн 2 ногтя +25 000
  ☐ Без дополнений

user> Снятие
→ Мастер: Диана · 15 мая, чт
  10:00  11:30  14:00  16:00

user> 14:00
→ ✓ Записаны!
  Маникюр + покрытие + снятие
  Мастер: Диана
  15.05 в 14:00
  Итого: 210 000 сум

  Напомню за 24ч и за 2ч.

§ 01О чём проектWhat this is

Типичный бьюти-салон в Ташкенте ведёт запись через WhatsApp и телефонные звонки. Администратор жонглирует бумажным журналом, тремя чатами и памятью. Результат предсказуем: двойные записи на одного мастера, потерянные клиенты, которые не дождались ответа, и no-show без напоминаний. A typical beauty salon in Tashkent handles bookings via WhatsApp and phone calls. The receptionist juggles a paper ledger, three chats, and memory. The result is predictable: double-bookings for the same master, lost clients who never got a reply, and no-shows without reminders.

Проблемы ручной записи: клиент пишет в WhatsApp — ответ через 2 часа, когда слот уже занят. Два мастера записаны на одно время. Клиент не приходит, потому что забыл — час простоя стоит салону 150–200 тыс. сум. Manual booking problems: a client texts WhatsApp — gets a reply 2 hours later when the slot is already taken. Two masters booked at the same time. A client doesn't show up because they forgot — an idle hour costs the salon 150–200k UZS.

Задача: бот, который позволяет клиенту записаться за 30 секунд без звонков и ожидания, а администратору — управлять расписанием без Excel и бумаги. Telegram — потому что в Узбекистане это мессенджер номер один. Goal: a bot that lets clients book in 30 seconds without calls or waiting, and lets admins manage the schedule without Excel or paper. Telegram — because it's the number one messenger in Uzbekistan.

§ 02Как устроеноHow it's built

FSM-первый дизайн. Запись — это стейт-машина из шести шагов: услуга → дополнения → мастер → дата → время → подтверждение. Каждый шаг валидирует входные данные и проверяет доступность слота в реальном времени. Aiogram 3 FSM идеально ложится на этот поток. FSM-first design. A booking is a state machine of six steps: service → add-ons → master → date → time → confirmation. Each step validates input and checks slot availability in real time. Aiogram 3 FSM maps perfectly to this flow.

architecture · booking fsm
┌──────────────────────────────────────────────────────────┐
│  /book                                                    │
│       │                                                   │
│       ▼                                                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                 │
│  │ Service  │─▶│ Add-ons  │─▶│ Master   │                 │
│  │ select   │  │ select   │  │ select   │                 │
│  └──────────┘  └──────────┘  └────┬─────┘                 │
│                                   │                       │
│       ┌───────────────────────────┘                       │
│       ▼                                                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                 │
│  │ Date     │─▶│ Time     │─▶│ Confirm  │                 │
│  │ pick     │  │ slot     │  │ + pay    │                 │
│  └──────────┘  └──────────┘  └────┬─────┘                 │
│                                   │                       │
│       ┌───────────────────────────┘                       │
│       ▼                                                   │
│  ┌──────────────────────────────────────────────────┐     │
│  │ SQLite WAL · Redis cache · APScheduler reminders │     │
│  └──────────────────────────────────────────────────┘     │
└──────────────────────────────────────────────────────────┘
Рис. 1 · Стейт-машина записи. Доменный слой не зависит от Telegram.Fig. 1 · Booking state machine. Domain layer is Telegram-independent.

Доменный слой полностью независим от транспорта. Бизнес-логика (проверка слотов, расчёт стоимости, управление расписанием) живёт отдельно от Telegram-хендлеров. SQLite WAL выбран осознанно: одна база на салон, бэкап — cp файла, нет сетевых зависимостей. Redis кеширует расписание мастеров и доступные слоты. The domain layer is fully transport-independent. Business logic (slot checks, price calculation, schedule management) lives separately from Telegram handlers. SQLite WAL was a deliberate choice: one database per salon, backup is a file cp, zero network dependencies. Redis caches master schedules and available slots.

§ 03Модель лицензированияLicensing model

Каждый салон получает годовую лицензию. Ключ подписан Ed25519 — offline-верификация без обращения к серверу лицензий. Grace-период 90 дней: если лицензия истекла, бот продолжает работать, но показывает предупреждение администратору. Это честный подход — салон не теряет данные и записи клиентов. Each salon gets an annual license. The key is signed with Ed25519 — offline verification with no license server calls. 90-day grace period: if the license expires, the bot keeps running but shows a warning to the admin. A fair approach — the salon doesn't lose data or client bookings.

domain/license.py ed25519
def verify_license(key_b64: str, pub: Ed25519PublicKey) -> License:
    # Офлайн-проверка, без обращения к серверу
    payload = base64.b64decode(key_b64)
    signature, data = payload[:64], payload[64:]
    pub.verify(signature, data)

    license = json.loads(data)
    expires = date.fromisoformat(license["expires"])
    grace = expires + timedelta(days=90)

    if date.today() > grace:
        raise LicenseExpired(salon=license["salon"])
    if date.today() > expires:
        logger.warning("License expired, grace period active")

    return License(**license)

Исходный код открыт для аудита заказчиком, но деплой требует валидный ключ. Модель «source-available, deployment-locked» — салон видит, что внутри, но не может поднять второй инстанс без лицензии. Source code is open for client audit, but deployment requires a valid key. The "source-available, deployment-locked" model — the salon sees what's inside but can't spin up a second instance without a license.

§ 04ЦифрыNumbers

86+ TESTS
30s TO BOOK
24/7 BOOKING
0 DOUBLE-BOOKINGS

Автобэкап каждые 6 часов: APScheduler копирует SQLite-файл и отправляет в приватный Telegram-канал. Восстановление — скачать файл и положить на место. Никаких внешних сервисов, никаких S3-бакетов. Auto-backup every 6 hours: APScheduler copies the SQLite file and sends it to a private Telegram channel. Recovery — download the file and drop it in place. No external services, no S3 buckets.

§ 05Что я вынесLessons learned

(а) UX салона — не UX SaaS. Мастер между клиентами смотрит в телефон 10 секунд. Любой экран с больше чем тремя кнопками — провал. Inline-клавиатуры максимально плоские: один ряд выбора, одна кнопка подтверждения. Каждый лишний тап — потерянная запись. (a) Salon UX is not SaaS UX. A master checks their phone for 10 seconds between clients. Any screen with more than three buttons is a failure. Inline keyboards are kept maximally flat: one row of choices, one confirm button. Every extra tap is a lost booking.

(б) Self-hosted бьёт SaaS для единичных салонов. Владелец не хочет ежемесячную подписку на платформу и не хочет, чтобы его клиентская база жила на чужом сервере. VPS за $5/мес + годовая лицензия — модель, которую понимают и принимают. (b) Self-hosted beats SaaS for individual salons. The owner doesn't want a monthly platform subscription and doesn't want their client base living on someone else's server. A $5/mo VPS + annual license — a model they understand and accept.

(в) Лицензирование — не ограничение, а фича продукта. Ed25519-ключ решает три задачи: монетизация, защита от клонирования, и доверие (салон может проверить, что код не изменён). Grace-период снимает страх «бот отключится и я потеряю записи». (c) Licensing is not a restriction — it's a product feature. The Ed25519 key solves three problems: monetization, clone protection, and trust (the salon can verify the code hasn't been tampered with). The grace period removes the fear of "the bot will shut down and I'll lose my bookings."

Нужно похожее решение для вашего бизнеса?Need a similar solution for your business?

Опишите задачу — отвечу в течение дня, оценку дам бесплатно.Describe your task — I'll reply within a day, the estimate is free.