admin d5225e2e95 docs: порядок работы по правке бота в IDEA.MD
Правки -> версия -> коммит -> пуш в dev -> деплой -> вебхук -> отчет.
Отмечено, что статус в Coolify API поллить не нужно.
2026-10-07 11:32:35 +02:00
2026-10-04 19:00:16 +02:00

Social Post Writer Bot — development

Telegram-бот для генерации постов для соцсетей. Готовит текст по явной просьбе пользователя, адаптируя под его Tone of Voice, и работает по модели подписки (триал 3 дня, затем 100 звёзд в месяц).


Содержание


Как работает бот (UX)

Reply-клавиатура

Основное меню показывается в /start:

✍️ Написать пост   💡 Идея
🎨 Мой стиль       📖 Помощь
💳 Подписка        👤 Баланс

Если подписки нет, _show_subscription_info ставит LOCKED_MENU — с кнопкой «💳 Оплатить подписку» на первом месте. Нажатие кнопки отправляет текст, который resolve_button_text превращает либо в slash-команду (/help, /balance, /subscribe, /style), либо в готовый запрос («Напиши пост», «предложи идею для нового поста») — то есть кнопки не обходят роутинг, а идут через него.

Slash-команды публикуются в Telegram через set_my_commands при старте (set_main_menu).

Онбординг (/start)

Первое сообщение пользователя запускает онбординг — два сообщения с паузой 0.8 сек между ними:

  1. Welcome (Rich Message) — WELCOME_RICH_HTML: приветствие, примеры запросов, упоминание скриншотов для Tone of Voice.
  2. Вызов help_command — пауза 0.8 сек, затем меню справки с инлайн-кнопками (тот же хендлер, что и по команде /help).

Одновременно /start создаёт запись триала на 3 дня.

Логика: welcome содержит только golden path, всё остальное (тон, голоса, правки, /clear, /idea) — в /help. Онбординг не дублирует текст справки, а вызывает help_command, поэтому правки HELP_RICH_HTML автоматически меняют и онбординг.

Основные сценарии

Что хочет пользователь Что делает Что видит
Написать пост Пишет «напиши пост про запуск продукта» Индикатор «печатает», затем 3 отдельных сообщения — по одному варианту в каждом
Изменить пост Отвечает реплаем на вариант бота: «сделай короче» Снова 3 новых варианта, с учётом исходного текста
Придумать идею «предложи тему» или /idea Одна идея одним сообщением (заголовок, угол, зачем)
Настроить стиль Присылает скриншот(ы) своих постов Бот распознаёт тексты, накапливает их и обновляет ToV; показывает итоговый профиль стиля
Голосом Отправляет голосовое STT расшифровывает → как обычный текстовый запрос
Узнать статус /balance Триал активен (сколько дней) / подписка активна / период истёк
Оплатить /subscribe Просит 100 звёзд, подтверждает активацию на 30 дней
Начать заново /clear Полный сброс данных + обнуление подписки

Индикаторы и обратная связь

  • Пока идёт обработка (классификация, расшифровка, чтение скриншота, генерация) — бот показывает «печатает» (typing_status), обновляя каждые 4 секунды.
  • При ошибке пользователю уходит понятное сообщение: «❌ Не получилось: {этап}. Причина: …» — секреты (ключи, токены) и URL вырезаются.
  • Служебные тексты — всегда на русском. Язык самого поста определяется языком запроса.

Блокировка по подписке

Если подписка не активна (триал/подписка истекли или is_active = 0):

  • Генерация поста и идеи блокируется, бот пишет, что период истёк, и предлагает /subscribe.
  • /help, /start, /balance, /subscribe доступны всегда — пользователь не остаётся в тупике.

Архитектура кода

bot.py               — роутинг, команды, обработка сообщений (aiogram 3.x)
payments.py          — оплата Stars: sendInvoice, pre_checkout_query, successful_payment, защита от двойного списания
apinex_client.py     — клиент APInex (LLM, STT, vision), промпты, логирование вызовов
user_store.py        — SQLite: пользователи, ToV, посты, идеи, API-логи, подписки
keyboards.py         — reply-клавиатура: `MAIN_MENU` (основная) и `LOCKED_MENU` (без подписки), маппинг кнопок на команды
help_message.py      — Rich Message: `WELCOME_RICH_HTML` (онбординг) + `HELP_SECTIONS`/`help_view()` (справка с кнопками), plain fallback
VERSION              — версия релиза (сейчас 0.1.0+26)

Основные потоки

Текстовый запрос:

handle_text → _classify_and_respond
  ├─ /help? → help_command (без проверки подписки)
  ├─ _check_subscription (блокировка, если нет подписки)
  ├─ classify_intent (APInex LLM) → intent
  │    ├─ help → help_command
  │    ├─ generate_ideas → generate_ideas → _send_idea
  │    └─ write_post / revise_post → generate_posts → _send_three_posts
  └─ typing_status (пока всё идёт)

Голосовое:

transcribe_voice → _process_voice → transcribe_audio (APInex STT) → _classify_and_respond

Скриншоты (ToV):

update_tone_from_image → _handle_image_message
  ├─ _collect_media_group (собирает галерею, ждёт 1 сек)
  ├─ extract_posts_from_images (APInex vision, inline base64)
  ├─ add_post_samples (накапливает по пользователю)
  └─ create_tone_profile → save_tone_of_voice → показывает стиль

Ключевые функции (bot.py)

Функция Что делает
start(message) Онбординг (welcome Rich Message + вызов help_command) + создание триала
handle_command_text Роутинг команд: /help, /start, /clear, /idea, /balance, /subscribe
_classify_and_respond Ядро: проверка подписки → классификация → генерация
_check_subscription Проверяет активную подписку; если нет — показывает статус и блокирует
_show_subscription_info Формирует сообщение о статусе (триал / подписка / истёк)
balance_command /balance — статус подписки
subscribe_command /subscribe — отправляет Stars-инвойс на 30 дней (подписка активируется только после оплаты)
handle_pre_checkout Подтверждает только свой инвойс в XTR на 100 звёзд, всё остальное отклоняет
handle_successful_payment Активирует подписку после реальной оплаты; telegram_payment_charge_id пишется в payments для защиты от повторного начисления
clear_command /clear — сброс данных + обнуление подписки
help_command /help — меню справки (Rich Message с кнопками) + fallback на plain text
help_navigation Callback help:<section> → edit_message_text(rich_message=...) — переключение разделов
edit_rich_message Редактирование текущего rich-сообщения на новый раздел
typing_status Индикатор «печатает» во время обработки
report_error Понятная ошибка пользователю, секреты вырезаны

Ключевые функции (user_store.py)

Функция Что делает
init_db Создаёт таблицы: users, user_post_samples, generated_posts, generated_ideas, api_call_logs, subscriptions
ensure_subscription Создаёт запись подписки с триалом, если нет (INSERT OR IGNORE)
activate_subscription Активирует/продлевает подписку на 30 дней от момента оплаты
deactivate_subscription is_active = 0
is_subscribed Проверяет: запись есть, is_active = 1, срок не истёк
subscription_row Возвращает строку подписки
_days_until Сколько дней осталось (триал — trial_started_at + 3 дня; подписка — last_paid_at); -1 если истёкло/отключено

Подписка и монетизация

Модель: триал 3 дня → подписка 30 дней за 100 звёзд (Telegram Stars).

Константы (user_store.py):

TRIAL_DAYS = 3
SUBSCRIPTION_MONTH_DAYS = 30
SUBSCRIPTION_PRICE_STARS = 100

Таблица subscriptions:

  • telegram_user_id (PK)
  • trial_started_at — момент создания (SQLite CURRENT_TIMESTAMP, UTC-нейтральный)
  • last_paid_at — момент/конец оплаченного периода; NULL = ещё триал
  • is_active — 1/0
  • monthly_days — длительность периода (30)
  • updated_at

Жизненный цикл:

  1. /start → ensure_subscription → триал 3 дня, is_active = 1.
  2. Генерация доступна, пока _days_until > 0.
  3. Триал кончился → _check_subscription блокирует, предлагает /subscribe.
  4. /subscribe → sendInvoice (XTR, 100 звёзд) → pre_checkout_query (проверка суммы/валюты/payload) → successful_payment → activate_subscription(months=1) → +30 дней.
  5. /clear → deactivate_subscription → is_active = 0; нужен /subscribe.

Оплата реальная: payments.py проводит настоящий платёж Telegram Stars. Подписка активируется только в successful_payment — сам /subscribe её не продлевает. telegram_payment_charge_id каждого платежа пишется в таблицу payments, поэтому один платёж не может активировать подписку дважды; по этому же id делается возврат через refundStarPayment. Платится валюта XTR, provider_token пустой (цифровой товар).

Часовые пояса: SQLite CURRENT_TIMESTAMP возвращает UTC без tz. Код аккуратно приводит все значения к naive UTC перед сравнением, чтобы не было сдвигов.


Данные и хранилище

  • SQLite: /app/data/social_writer.sqlite3 (persistent Coolify volume).
  • Per-user данные: ToV, образцы постов со скриншотов, история сгенерированных постов, история идей. История диалогов и исходные изображения не сохраняются.
  • API-логи: JSON запросов/ответов APInex + ошибки разбора, 30 дней. Системные промпты не дублируются — только имя и SHA-256. base64 изображений и бинарное аудио не пишутся. Содержимое логов — приватные данные пользователя.
  • /clear удаляет пользовательские данные (ToV, скриншоты, посты, идеи, настройки) и обнуляет подписку; API-логи сохраняются.

Команды

Команда Что делает Доступна без подписки
/start Онбординг (welcome Rich Message + справка) + создание триала ✅
/help Меню справки: полноширинные кнопки разделов, переход в любой раздел + plain fallback ✅
/balance Статус подписки / триала ✅
/subscribe Счёт на оплату: 100 звёзд (XTR) за 30 дней; активация — после оплаты ✅
/idea Одна идея для поста ❌ (нужна подписка)
/clear Полный сброс данных + обнуление подписки ✅

Русские фразы («придумай идею», «о чём написать?») работают как /idea.


Деплой и конфигурация

  • Gitea: admin/social-post-writer-bot, ветка dev. Push → автодеплой.
  • Coolify: проект Social Post Writer Bot, окружение development, приложение social-post-writer-bot-development (d32u5zcq2g4odmdpq9hw91yn) на spb-easy-dale.
  • Деплой: .hermes/scripts/deploy.sh (POST /api/v1/deploy?uuid=...).
  • VERSION: файл VERSION (0.1.0+19). При DEPLOY_ENV=development и изменении версии бот шлёт уведомление «🚀 Бот обновлён / Версия: X» в DEV_CHAT_ID.
  • Переменные Coolify: BOT_TOKEN, APINEX_API_KEY, APINEX_STT_MODEL, APINEX_INTENT_CLASSIFIER_MODEL, APINEX_POST_WRITER_MODEL, APINEX_IMAGE_TONE_MODEL, BOT_DB_PATH, DEPLOY_ENV, DEV_CHAT_ID.
  • Docker: python:3.12-slim, копирует bot.py, apinex_client.py, user_store.py, help_message.py, VERSION; CMD ["python", "bot.py"].

Тесты

python -m unittest discover -s tests -v

Покрытие: STT, LLM, vision, ToV, reply-поля, изоляция пользователей, API-логи, reply-клавиатура (маппинг кнопок, роутинг, /style), платежи (инвойс, pre_checkout, successful_payment, защита от двойного списания), справка с кнопками (меню, разделы, callback-навигация, plain fallback), онбординг (welcome → вызов help_command), подписки (6 тестов: таблица, триал после /start, оплата, баланс, блокировка, обнуление через /clear).

S
Description
Telegram-бот для генерации постов для соцсетей: три варианта на запрос, Tone of Voice из скриншотов, голосовой ввод (STT), идеи и подписка (триал 3 дня, затем 100 звёзд/месяц). Python 3.12 + aiogram 3.x + APInex LLM/STT/vision + SQLite.
https://t.me/post_master_bot Readme
416 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 99.5%
Shell 0.4%