.env.example: понятная инструкция по активации + защита config от пустых значений

- .env.example переписан как пошаговый гид: разделы (подключение к сообществу,
  кому приходят заявки, почта, ссылки, /favi-stop), пометки «ОБЯЗАТЕЛЬНО»,
  правила заполнения (без инлайн-комментариев, числа не оставлять пустыми).
- config: безопасный разбор чисел (_int_env) — пустое/кривое VK_GROUP_ID/порт/часы
  больше не роняют бот при старте, откатываются на дефолт. '#' в значениях (URL
  #prices) не трогаем.

pytest 43 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kenik
2026-07-26 11:11:38 +03:00
parent d2ca750a2d
commit 9ded7a513d
2 changed files with 89 additions and 33 deletions
+71 -27
View File
@@ -1,42 +1,72 @@
# VK API Configuration
# Токен сообщества ВКонтакте (с правами на управление сообщениями)
# ============================================================================
# НАСТРОЙКА БОТА «Кибершкола БАЗА» — файл .env
# ============================================================================
# Это ШАБЛОН. Что делать:
# 1) Скопируйте этот файл рядом и назовите ровно .env (без .example).
# Проще: запустите start_bot.bat — он сам создаст .env и откроет его в
# Блокноте. Заполните, сохраните, запустите start_bot.bat снова.
# 2) Заполните строки из раздела 1 и 2 (помечены «ОБЯЗАТЕЛЬНО»). Остальное —
# по желанию.
#
# ПРАВИЛА ЗАПОЛНЕНИЯ (важно!):
# • Формат строки: ПЕРЕМЕННАЯ=значение — без пробелов вокруг «=», без кавычек.
# • НЕ пишите комментарии в одной строке со значением. Комментарий — только
# на отдельной строке, начинается с #.
# • Числовые поля (ID, порт) не оставляйте пустыми — впишите число.
# ============================================================================
# ───────────────── 1. ПОДКЛЮЧЕНИЕ К СООБЩЕСТВУ (ОБЯЗАТЕЛЬНО) ──────────────
# VK_TOKEN — ключ доступа ВАШЕГО сообщества:
# Сообщество → Управление → Работа с API → Ключи доступа → Создать ключ
# (право «Сообщения сообщества») → скопировать сюда.
# Там же: Работа с API → Long Poll API → включить, версия — последняя.
VK_TOKEN=your_vk_community_token_here
# Идентификатор (ID) сообщества ВКонтакте (только цифры)
# VK_GROUP_ID — числовой ID сообщества (только цифры). Замените 0 на свой ID.
# Узнать ID: Управление → Работа с API (виден там), либо через vk.com.
VK_GROUP_ID=0
# ID оператора / администратора ВКонтакте для отправки лидов и оповещений (только цифры)
OPERATOR_PEER_ID=12345678
# Список ID администраторов (через запятую) для управления услугами через чат
ADMIN_IDS=12345678
# ───────────────── 2. КОМУ ПРИХОДЯТ ЗАЯВКИ (ОБЯЗАТЕЛЬНО) ──────────────────
# OPERATOR_PEER_ID — VK ID человека, которому падают заявки/запросы клиентов.
# Узнать свой ID можно, написав боту @idbot во ВКонтакте.
OPERATOR_PEER_ID=0
# Разрешённый источник (Origin) для CORS веб-формы (домен сайта). Для локальной отладки можно '*'
ALLOWED_ORIGIN=https://infocyber.pro
# ADMIN_IDS — у кого есть «Админка» в боте (через запятую, только цифры).
# Обычно тот же ID, что OPERATOR_PEER_ID. Пример: 350793273,12345678
ADMIN_IDS=0
# Промокоды на скидку. Пока выключены. Поставь true, когда захочешь их выдавать клиентам.
PROMO_ENABLED=false
# (Необязательно) Несколько операторов — заявки уйдут ВСЕМ. Через запятую.
# OPERATOR_PEER_IDS=350793273,12345678
# ===== Email (SMTP) для уведомлений о заявках =====
# Адрес SMTP-сервера почты (например, smtp.yandex.ru / smtp.gmail.com / smtp.mail.ru)
# (Необязательно) Часы работы — вне их клиенту пишем «ответим утром».
WORK_HOUR_START=9
WORK_HOUR_END=21
# ───────────────── 3. ПОЧТА ДЛЯ ЗАЯВОК (по желанию) ──────────────────────
# Заполните — и каждая заявка продублируется на почту. Оставите пусто — бот
# работает как обычно, просто письма не шлёт.
# SMTP_HOST — сервер почты, напр. smtp.yandex.ru / smtp.mail.ru / smtp.gmail.com
SMTP_HOST=
# Порт: 465 для SSL, 587 для STARTTLS
# SMTP_PORT — 465 для SSL (обычно), 587 для STARTTLS
SMTP_PORT=465
# Логин (полный email) и пароль (для Яндекс/Gmail — пароль приложения, не основной!)
# SMTP_USER — полный email-логин
SMTP_USER=
# SMTP_PASSWORD — ПАРОЛЬ ПРИЛОЖЕНИЯ (для Яндекс/Gmail), не основной пароль!
SMTP_PASSWORD=
# От кого отправлять (по умолчанию = SMTP_USER)
# MAIL_FROM — от кого (пусто = как SMTP_USER)
MAIL_FROM=
# Куда отправлять заявки (можно несколько через запятую)
# MAIL_TO — куда слать заявки (можно несколько через запятую)
MAIL_TO=group-zakaz@infocyber.pro
# Куда отправлять отклики на вакансии/набор в состав. Пусто = на тот же адрес, что MAIL_TO.
# Можно вынести на отдельный HR-адрес, напр. hr@infocyber.pro
HR_MAIL_TO=
# true — SSL (порт 465), false — STARTTLS (порт 587)
# SMTP_USE_SSL — true для порта 465, false для 587
SMTP_USE_SSL=true
# --- Ссылки Кибершколы БАЗА (кнопки записи/оплаты в боте) ---
# UTM добавляются кодом автоматически; здесь только базовые URL. Замените при необходимости.
# ───────────────── 4. ССЫЛКИ КНОПОК ЗАПИСИ/ОПЛАТЫ ────────────────────────
# Куда ведут кнопки в боте. Их также можно менять прямо из «Админки → Ссылки».
# UTM-метки бот добавляет сам.
LINK_TRIAL=https://infocyber.pro/form/index.html
LINK_TARIFFS=https://infocyber.pro/#prices
LINK_GROUP_4=https://infocyber.pro/form/index.html?service_id=web_2
@@ -44,10 +74,24 @@ LINK_GROUP_8=https://infocyber.pro/form/index.html?service_id=web_3
LINK_IND_1=https://infocyber.pro/form/index.html?service_id=web_1
LINK_IND_4=https://infocyber.pro/form/index.html?service_id=web_4
LINK_IND_8=https://infocyber.pro/form/index.html?service_id=web_5
# Летний интенсив: отдельной страницы на сайте нет — ведём на общую форму (замените, когда появится)
# LINK_SUMMER — летний интенсив (замените, когда будет своя страница)
LINK_SUMMER=https://infocyber.pro/form/index.html
# Экстренное отключение бота (/favi-stop) — ХЭШ пароля (PBKDF2), не сам пароль.
# Сгенерировать: py -3.11 -c "import hashlib,os; s=os.urandom(16); import sys; pw=sys.argv[1].encode(); print('pbkdf2_sha256$240000$'+s.hex()+'$'+hashlib.pbkdf2_hmac('sha256',pw,s,240000).hex())" "ВАШ_ПАРОЛЬ"
# Пусто — команда /favi-stop отключена.
# ───────────────── 5. ЭКСТРЕННЫЙ СТОП /favi-stop (по желанию) ─────────────
# Команда /favi-stop у супер-админа отключает бота по паролю. Хранится ХЭШ
# пароля, а не сам пароль. Пусто — команда отключена.
# Сгенерировать хэш своего пароля (замените ВАШ_ПАРОЛЬ):
# py -3.11 -c "import hashlib,os,sys; s=os.urandom(16); print('pbkdf2_sha256$240000$'+s.hex()+'$'+hashlib.pbkdf2_hmac('sha256',sys.argv[1].encode(),s,240000).hex())" "ВАШ_ПАРОЛЬ"
FAVI_STOP_HASH=
# ───────────────── ПРОЧЕЕ (можно не трогать) ─────────────────────────────
ALLOWED_ORIGIN=https://infocyber.pro
PROMO_ENABLED=false
# ============================================================================
# СУПЕР-АДМИН (кто добавляет админов и делает /favi-stop) задаётся НЕ здесь,
# а в коде — файл config.py, список SUPER_ADMIN_IDS.
# Готово? Запускайте start_bot.bat.
# ============================================================================
+18 -6
View File
@@ -23,14 +23,26 @@ def _load_env():
_load_env()
def _int_env(key: str, default) -> int:
"""Безопасно читает целое из .env: пустое/кривое значение → default (без краха)."""
raw = os.getenv(key, None)
if raw is None or str(raw).strip() == "":
return int(default)
try:
return int(str(raw).strip())
except (ValueError, TypeError):
return int(default)
# VK API Configuration
# In production, these should be loaded from environment variables or a secure .env file.
VK_TOKEN = os.getenv("VK_TOKEN", "your_vk_community_token_here")
GROUP_ID = int(os.getenv("VK_GROUP_ID", "0")) # Community ID (must be integer)
VK_TOKEN = os.getenv("VK_TOKEN", "your_vk_community_token_here") or "your_vk_community_token_here"
GROUP_ID = _int_env("VK_GROUP_ID", 0) # Community ID (must be integer)
# Operator / Admin configuration
# ID of the user or chat where notifications about new leads and operator requests will be sent
OPERATOR_PEER_ID = int(os.getenv("OPERATOR_PEER_ID", "12345678")) # Change to actual VK user ID of operator
OPERATOR_PEER_ID = _int_env("OPERATOR_PEER_ID", 12345678) # Change to actual VK user ID of operator
# Parse ADMIN_IDS as a list of integers from comma-separated env values
admin_ids_raw = os.getenv("ADMIN_IDS")
@@ -69,8 +81,8 @@ else:
OPERATOR_PEER_IDS = [OPERATOR_PEER_ID]
# Часы работы (для статуса оператора). Вне этого интервала — «ответим утром».
WORK_HOUR_START = int(os.getenv("WORK_HOUR_START", "9"))
WORK_HOUR_END = int(os.getenv("WORK_HOUR_END", "21"))
WORK_HOUR_START = _int_env("WORK_HOUR_START", 9)
WORK_HOUR_END = _int_env("WORK_HOUR_END", 21)
# Разрешённый источник (Origin) для CORS веб-формы. '*' допустим только для локальной отладки.
ALLOWED_ORIGIN = os.getenv("ALLOWED_ORIGIN", "https://infocyber.pro")
@@ -81,7 +93,7 @@ PROMO_ENABLED = os.getenv("PROMO_ENABLED", "false").strip().lower() in ("1", "tr
# Email (SMTP) configuration для уведомлений о заявках
SMTP_HOST = os.getenv("SMTP_HOST", "")
SMTP_PORT = int(os.getenv("SMTP_PORT", "465"))
SMTP_PORT = _int_env("SMTP_PORT", 465)
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
MAIL_FROM = os.getenv("MAIL_FROM", "") or SMTP_USER