Причина 'бот в моменте перестаёт работать' без ошибок в логах:
vk_api ставит таймаут только на сам LongPoll-запрос (wait+10 с), а
обычные вызовы API (messages.send и т.п.) идут без таймаута вообще.
Если VK/сеть один раз не ответит на такой вызов — соединение виснет
навсегда, а так как обработка сообщений однопоточная (внутри цикла
LongPoll), весь бот намертво стопорится без единой строки в логе.
Docker restart: unless-stopped не спасает — процесс не падает, а просто
висит.
- socket.setdefaulttimeout(60) — подстраховка для всех сетевых вызовов
без явного timeout (vk_api, requests в mailer.py и загрузке фото).
- Ручной вызов longpoll.check() вместо for event in listen() — даёт
watchdog 'пульс' на каждом опросе сервера (~25-35 с), а не только при
реальных сообщениях.
- Watchdog-поток: если цикл не подавал признаков жизни дольше 5 минут,
принудительно завершает процесс (os._exit), чтобы Docker реально
перезапустил контейнер.
Раньше сменить уже заданный пояс можно было только скрытой командой
'часовой пояс <±N>', нигде не упомянутой в интерфейсе. Добавлена кнопка
«🌐 Часовой пояс» — открывает тот же выбор (кнопки городов + ручной ввод)
заново, показывая текущее значение.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
При гейте часового пояса теперь показывается клавиатура с частыми
российскими городами-поясами (Калининград..Камчатка). Клик по кнопке
шлёт 'Город, UTC+N' — _parse_tz_offset распознаёт и это, и обычный
ручной ввод вида '+3'/'-5', если своего города в списке нет.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
По ревью Task 4: команда 'напоминалка удалить <id>' была единственной точкой
входа в раздел, не защищённой гейтом _require_reminder_tz — админ без заданного
пояса мог через неё попасть в _send_reminders_admin и увидеть список с
подписью (UTC+0) по фолбэку, так и не указав реальный часовой пояс.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Инлайн-клавиатура в _send_reminders_admin не заменяла нижнюю (reply) клавиатуру
предыдущего шага мастера — после создания напоминания кнопки «Разово / Каждую
неделю / …» оставались висеть внизу. Теперь раздел использует обычную нижнюю
клавиатуру get_reminders_admin_keyboard(), как и раздел «Быстрые ответы».
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- В разделе «Напоминалки» добавлена кнопка «🗑 Удалить напоминание» с выбором
из списка (как в разделе «Быстрые ответы»), не только текстовая команда.
- При выборе получателей и в списке напоминаний теперь показывается ФИО
админа из VK рядом со ссылкой (если удалось получить), чтобы не кликать
по каждой ссылке для опознания.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
В логах на каждое приветствие сыпалось [15] Access denied (у токена нет прав на
фото). Теперь неудача загрузки кэшируется — приветствие уходит без логотипа, без
повторных ошибок. (Чтобы логотип показывался — токену нужно право «Фотографии».)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Причина случайного фолбэка «Пока не получилось определить запрос»:
- бот реагировал на сообщения из бесед/групповых чатов (peer_id != from_id) и слал
отправителю в ЛС фолбэк — теперь такие сообщения игнорируются (работаем только в личке);
- сообщения без текста (стикер/фото/голосовое/гифка) давали text="" → фолбэк в
главном меню — теперь пустые сообщения игнорируются.
Тест: test_empty_message_ignored. pytest 44 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dockerfile (python:3.11-slim): установка зависимостей, запуск bot.py.
- docker-compose.yml: авто-перезапуск (restart: unless-stopped), настройки из .env
(env_file), база в именованном volume (данные не теряются при пересборке).
- .dockerignore: в образ не тащим .git/тесты/докумен/базу/.env.
- bot.py: путь к БД через DB_PATH (для volume в Docker), по умолчанию — как раньше.
- requirements.txt: убраны неиспользуемые Pillow и openpyxl (образ легче).
pytest passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) Кнопки меню: раздел «Кнопки меню 🧩» в админке — показать/скрыть любую кнопку
главного меню (Подобрать/Интенсив/Цены/Вопрос/Вебинар). Флаги в БД (bot_flags),
дефолты в config.MENU_FEATURES (вебинар по умолчанию скрыт). Скрытый раздел не
реагирует и на кодовые слова (интенсив выкл → «лето» игнорируется). Вебинар
получил кнопку в меню (для теста ветки) + вход по callback из поста работает всегда.
2) Экстренное отключение (только супер-админ): /favi-stop → запрос пароля → бот
уходит в аварийный режим и молчит для всех; /favi-start возвращает. Пароль хранится
как PBKDF2-хэш в .env (FAVI_STOP_HASH), не в коде; сравнение константное, 3 попытки.
pytest 43 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) _kb при пустых рядах создавал ПУСТОЙ первый ряд (add_line до кнопок) — VK
отклонял такую клавиатуру, из-за чего сообщения шага «Задать вопрос»,
«другая игра», «нужен совет» не отправлялись (состояние при этом менялось).
Теперь add_line ставится только между реальными кнопками — пустых рядов нет.
2) _resolve_vk_user_id теперь понимает домен vk.ru (и www./m.), а не только vk.com —
ссылку https://vk.ru/screenname на добавление админа бот принимает.
Тесты: test_funnel_keyboards_no_empty_rows, test_resolve_vk_links. pytest 40 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
В админ-панели кнопка «Уведомления: вкл 🔔 / выкл 🔕» — админ (в т.ч. из ADMIN_IDS/
супер-админ) может лично отключить приём уведомлений о заявках и сообщениях клиентов.
Флаг notify_muted хранится в профиле; _notify_operators пропускает замьюченных
получателей (у остальных операторов уведомления идут как обычно).
pytest 37 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Удалены изображения от удалённых фич (карусель услуг, карьера, веб-форма):
service_1/2/3/5/6/7.jpg, career_player/worker.jpg, bg.jpg. Остался только Logo.jpg
(приветствие). _prewarm_media упрощён — грузит только логотип.
pytest 36 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Отображаемое название дисциплины расписано полностью: кнопка выбора игры,
приветствие, список направлений, вопрос об уровне, значение game, инфо интенсива,
описание услуги. Ввод «CS2» по-прежнему распознаётся (+ counter/strike).
Метка game_cs2 и UTM summer_intensive не тронуты (внутренние). Название продукта
«Летний интенсив CS2» оставлено как есть.
pytest 36 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Тикеты: add_lead возвращает id заявки; при передаче менеджеру клиент получает
«🎫 Номер вашей заявки: №N», тот же номер — в карточке оператору и в письме.
Один тикет на заход (сбрасывается при новом старте воронки/интенсива).
- Кнопка «Записаться на пробное» → «Записаться на пробное занятие» (лейблы + распознавание).
- Ветка вебинара: вход по callback-кнопке и по тексту «записаться на вебинар»,
текст выровнен по ТЗ, после регистрации состояние FREE_CHAT — основное меню
остаётся доступным (не блокирует продающую ветку). Дата/время — в config.
Тесты: test_baza_question_ticket, test_webinar_registration. pytest 36 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Воронка перестала писать в leads после ребрендинга — блок «Заявки» был всегда 0.
Теперь при конверсии (нажал оплату/пробное или передан менеджеру) бот фиксирует
заявку в leads (source=bot, имя из VK, услуга = тариф/формат/игра), один раз на заход
(флаг lead_recorded сбрасывается при новом старте воронки/интенсива). Разбивка «по
тарифу/формату» снова показывает реальные цифры.
Статистика почищена: убраны «из бота» (все заявки теперь из бота) и строка «согласны
на рассылку» (воронка не собирает это согласие) — не вводят в заблуждение.
Тест: test_baza_records_lead_once. pytest 34 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- database.py: удалены таблица web_registrations, методы add_web_registration и
next_web_registration_id, поле from_web из get_lead_stats (форму убрали).
- bot.py: из статистики убрана строка «с сайта» (веб-заявки больше не пишутся).
pytest 33 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _baza_advance: воронка переходит к первому НЕзаполненному шагу, пропуская роль/
возраст/игру/уровень, если они уже известны; формат (выбор действия) спрашивается всегда.
- На повторном входе — короткая подсказка, что известные ответы не переспрашиваются
и их можно изменить кнопкой «Назад» (back-навигация остаётся линейной = override).
- Интенсив: известный возраст (из подбора) не переспрашивается — сразу уровень CS2.
- Данные из ветки «лето» сохраняются и переиспользуются в подборе, и наоборот.
Тесты: test_baza_smart_skip_known_fields, test_baza_intensive_skips_known_age. pytest 33 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Пользовательские сообщения и подписи с «ИКП» переименованы в «Кибершкола БАЗА»
(права админа, стартовый пинг, отправитель писем, докстроки).
- Удалён мёртвый код: метод _notify_allowed + связанные _last_notify/NOTIFY_COOLDOWN_SEC
(нигде не вызывались), осиротевшие комментарии-заголовки от удалённой карьеры,
лишние пустые строки.
- Удалена скрытая фича «Наши соцсети» (нет в ТЗ, вела на старые ИКП-ссылки) + её клавиатура.
- Обновлён логотип приветствия (help_img/Logo.jpg).
Не тронуто намеренно: веб-форма (form/server.py) — отдельный компонент; слой БД.
pytest 31 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Полный прогон всех веток по ТЗ: тексты/кнопки/подтверждения на месте. Единственная
находка — двойное «Спасибо! Передаю запрос менеджеру» при выборе другой игры; второе
сообщение заменено на нейтральное уведомление о передаче менеджеру.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Тексты:
- «Сколько лет игроку?» (было «…будущему ученику?») во всех местах;
- сообщение для «Младше 12 лет» приведено дословно к ТЗ (§4);
- убраны отсебятины-преамбулы, которых нет в ТЗ (приветствие, старт воронки,
цены «Что дальше?», интенсив «Хотите записаться?»); кнопка младше-12 «Вернуться назад».
Смена ссылок в админке:
- таблица bot_links (key, url) + методы get_link/get_all_links/set_link;
- _link(key): БД-переопределение → дефолт из config/.env; используется на шагах оплаты;
- раздел «Ссылки 🔗» в админ-панели: список текущих адресов + «✏️ Изменить ссылку»
(выбор из 8 кнопок → ввод URL с валидацией http/https). UTM добавляются автоматически.
Тесты: добавлен test_admin_edit_link. pytest 31 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
py -3.11 на машине резолвится в sandbox-сборку Python (ExamPython311), которая не
добавляет директорию скрипта в sys.path — из-за этого при запуске через start_bot.bat
падало «ModuleNotFoundError: No module named 'database'». Теперь bot.py в самом начале
вставляет свой каталог в sys.path, поэтому запускается любым Python и из любой папки.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Кнопки записи/оплаты ведут на форму сайта infocyber.pro (service_id web_1..web_5);
тарифы — на главную с якорем #prices; пробное и интенсив — на общую форму записи
(отдельной страницы интенсива на сайте пока нет).
- _utm(): UTM вставляется ДО якоря (?utm...#prices), иначе браузер не доскроллит
до раздела. Значения ссылок вынесены в .env / задокументированы в .env.example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Из ТЗ Кибершколы БАЗА эти сценарии заменены воронкой подбора, поэтому убран мёртвый код:
- клиентские FSM: FORM_* (форма-заявка, возвращающийся клиент, редактирование, согласия,
выбор услуги, карусель) и VAC_* (карьера: игроки/сотрудники);
- меню «Частые вопросы» и config.FAQ_ANSWERS;
- осиротевшая админка: управление услугами + карусель, вакансии, кандидаты
(в панели и в мастере ADMIN_SVC_*/ADMIN_VAC_*), блок статистики по откликам;
- старый _handoff_to_operator и _notify_operator_about_lead (заменены _baza_to_manager);
- неиспользуемые клавиатуры (форма/услуги/вакансии/согласия) и импорты (validators, random, string).
Оставлено (используется/по ТЗ): воронка БАЗА, быстрые ответы (custom_faq), стата по лидам,
рассылка, управление админами, чат с оператором, логотип. Слой БД не тронут — существующие
установки не ломаются (таблицы services/applications/vacancy_options просто не используются).
bot.py: -1490 строк. Тесты: pytest 28 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Меню Кибершколы БАЗА (4 кнопки): Подобрать тренировку / Летний интенсив CS2 /
Посмотреть цены / Задать вопрос.
Воронка «Подобрать тренировку» (FSM BAZA_*): для кого → возраст → игра → уровень →
формат → групповые / индивидуальные / нужен совет. На каждом шаге — «Назад» и «В
главное меню», ветки: младше 12 и «другая игра» уводят к менеджеру.
Ветка «Летний интенсив CS2»: инфо → возраст → уровень → оплата. Запуск по кнопке и
по кодовым словам (лето/интенсив), основные триггеры и приветствие — из config.
Шаг оплаты: инлайн-кнопка «Перейти к оплате» (ссылки-плейсхолдеры из config.LINKS +
UTM), «Проблема с оплатой» и «Задать вопрос менеджеру» уводят к оператору.
Карточка менеджеру собирается из профиля воронки (пустые поля пропускаются) + метки;
дублируется на почту. Данные не переспрашиваются, после передачи менеджеру бот не
вмешивается (OPERATOR_CHAT). Тесты воронки — pytest (30 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Бот принимает ссылку (https://vk.com/gruzdevas), короткое имя (gruzdevas),
@имя или числовой ID и сам определяет user_id (короткое имя — через VK API
users.get). Резолвер _resolve_vk_user_id применён к добавлению и удалению админа.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Возвращающийся клиент: «Да/Отмена» — нижняя клавиатура, а «Сменить имя/
телефон/Заполнить заново» — отдельные инлайн-кнопки под сообщением (ровно 3,
как просил).
- start_bot.bat: перед запуском убивает старые экземпляры бота — устраняет
«рандомные» цвета от нескольких параллельных longpoll-инстансов.
- Маска телефона в приветствии возвращающегося клиента.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Админка — всегда зелёная (POSITIVE).
- Меню изменения данных: «Назад к проверке» — красная.
- Возвращающийся клиент: «Да/Сменить имя/телефон/Заполнить заново/Отмена» —
инлайн-кнопки под сообщением.
- Уведомление оператору: добавлено реальное имя из VK с флагом «не совпадает
с введённым» — помогает отсеивать рандомные/фейковые данные (профиль VK уже был).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Зелёного цвета (POSITIVE) в оригинальных клавиатурах не было — он появился
при добавлении карьеры и админ-разделов. Заменён на PRIMARY (синий) во всех
7 местах, чтобы вернуть исходную палитру (PRIMARY/SECONDARY/NEGATIVE).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _prewarm_media грузит ВСЕ *.jpg из help_img при старте (кроме bg.jpg),
чтобы смена картинок услуг не подгружалась лениво в момент сообщения.
- Веб-форма: ThreadingHTTPServer — медленный запрос не блокирует остальные;
приём заявки сериализован _submit_lock (без гонок в SQLite/Excel).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bot.py разгружен: чистые функции вынесены без изменения поведения:
- validators.py — регэкспы, normalize_phone, format_phone_display, is_valid_name
- keyboards.py — все get_*_keyboard
Поведение проверено регрессионным прогоном (полный флоу заявки, админка, меню).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Картинки дисциплин теперь хранятся как service_<id>.jpg (после смены через
админку). Предзагрузка медиа при старте теперь подхватывает их по маске,
чтобы первая карусель открывалась мгновенно.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Отмена в «Заявке»/«Карьере» возвращает меню с кнопкой «Админка» для админов.
- Рассылка: инлайн-кнопки «Отправить»/«Отмена» в предпросмотре.
- Управление админами — инлайн-кнопки «Добавить»/«Удалить» (без текстовых команд),
выбор удаляемого админа кнопкой.
- Быстрые ответы из админки показываются в «Частые вопросы» инлайн-кнопками.
- Вакансии «Карьеры» перенесены в БД (vacancy_options): направления игроков и
должности сотрудников редактируются из админки (раздел «Вакансии»), а в опросе
показываются инлайн-кнопками из БД.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Приём фото-вложений от админа (handle_message получает attachments).
- Раздел «Услуги»: кнопки Добавить / Изменить / Удалить / Сменить картинку.
Картинка скачивается, обрезается под 13:8 (650×400) и сохраняется как
service_<id>.jpg; карусель берёт её приоритетно. Мастера на состояниях ADMIN_*.
- Раздел «Быстрые ответы»: кнопки Добавить / Изменить / Удалить с выбором.
- Контекст мастеров в памяти (_admin_ctx), отмена из любого шага.
Заметка: CRUD «Карьеры» (направления игроков/должности) — отдельным заходом,
т.к. опции там пока захардкожены и требуют перевода на БД.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Супер-админ (config.SUPER_ADMIN_IDS, только через код): добавляет/удаляет
обычных админов из бота (раздел «Администраторы 👑»), таблица admins.
_is_admin теперь учитывает супер-админов, .env и добавленных из бота.
- «Когда удобно созвониться» → выбор конкретного дня (кнопки на ближайшие даты)
и времени (слоты), сохраняется как «День, ЧЧ:ММ».
- Шаг согласия: инлайн-кнопки со ссылками на оферту и политику ПД (PDF), затем
подтверждение. Старое вложение .docx убрано.
- Маска отображения телефона: +7 (999) 123-45-67 в карточке и уведомлениях.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Бот:
- Прогресс-бар (▰▰▱ Шаг N из 3) в шагах заявки
- Быстрые ответы по ключевым словам (таблица custom_faq) — отвечают до зова оператора;
редактирование через админ-панель (раздел «Быстрые ответы»: добавить/список/удалить)
- Отклики в статистике: счётчик игроков/сотрудников по дисциплинам и должностям
- Статусы кандидатов (новый/на собесе/отказ/принят) — раздел «Кандидаты» в админке
- Несколько операторов: уведомления уходят всем из OPERATOR_PEER_IDS
- Часы работы (WORK_HOUR_START/END): вне часов хендофф сообщает «ответим утром»
- Временный бан при повторном флуде (усиление анти-DDoS)
- Старт-пинг администратору «бот запущен» (лёгкий healthcheck)
Док и зависимости:
- README.md (запуск, .env, автозапуск, админ-команды, структура)
- requirements.txt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- В главном меню у администраторов появляется кнопка «Админка ⚙️»
(видна только в сообщениях админу; не-админа код всё равно не пускает).
- Админ-панель с кнопками: Статистика, Управление услугами, Рассылка,
В главное меню — вместо запоминания текстовых команд.
- Доступ строго по config.ADMIN_IDS на сервере.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Добавлены career_player.jpg и career_worker.jpg (13:8, 650×400),
карточки в разделе «Карьера» теперь с фото. Картинки добавлены
в предзагрузку медиа при старте.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Обрыв longpoll-соединения (RemoteDisconnected) больше не роняет бота —
цикл ловит сетевые ошибки, ждёт и переподключается. Ошибка обработки
одного сообщения тоже не останавливает работу.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Контент и брендинг:
- Реальные данные ИКP (infocyber.pro): услуги CS2/Dota 2/WoT, цены, FAQ, контакты, площадки
- Приветствие и тексты приведены к бренду ИКП
Новые функции:
- Раздельные согласия (152-ФЗ): маркетинговая рассылка — отдельный необязательный шаг
- Шаг «удобное время звонка» в форме заявки
- Раздел «Карьера в ИКП»: набор игроков и сотрудников (таблица applications)
- Админ-команды: статистика (стата) и рассылка по подписчикам с троттлингом и отпиской
- Журнал заявок leads, «бот помнит клиента», международные телефоны, валидация имени
- Email-уведомления о заявках и откликах (mailer.py); HR-адрес через HR_MAIL_TO
Защита:
- Анти-флуд по пользователю и кулдаун уведомлений оператору (анти-DDoS)
- Веб-форма: серверная валидация, honeypot, рейт-лимит, CORS по ALLOWED_ORIGIN, SQLite-first
Визуал:
- Убрана Markdown-разметка (VK её не рендерит)
- Логотип в приветствии, карусели услуг и «Карьеры» (фото 13:8), предзагрузка медиа
- Кнопки-ссылки на соцсети
- Веб-форма: анимации, конфетти на успехе
Промокоды добавлены, но пока ВЫКЛЮЧЕНЫ флагом PROMO_ENABLED (по умолчанию false).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>