Files
bot_vk_ikp_prodagi/config.py
T
Kenik 3d709267ff Админка, защита и UX: быстрые ответы, статусы кандидатов, мульти-операторы, бан, прогресс-бар
Бот:
- Прогресс-бар (▰▰▱ Шаг 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>
2026-06-23 10:00:56 +03:00

134 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
# Load environment variables from .env in the project root
def _load_env():
# Find the project root (the directory containing config.py)
root_dir = os.path.dirname(os.path.abspath(__file__))
dotenv_path = os.path.join(root_dir, ".env")
if os.path.exists(dotenv_path):
try:
with open(dotenv_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, val = line.split("=", 1)
val = val.strip().strip('"').strip("'")
os.environ[key.strip()] = val
except Exception as e:
print(f"Warning: Failed to load .env file: {e}")
_load_env()
# 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)
# 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
# Parse ADMIN_IDS as a list of integers from comma-separated env values
admin_ids_raw = os.getenv("ADMIN_IDS")
if admin_ids_raw:
try:
ADMIN_IDS = [int(x.strip()) for x in admin_ids_raw.split(",") if x.strip().isdigit()]
except Exception:
ADMIN_IDS = [OPERATOR_PEER_ID]
else:
ADMIN_IDS = [OPERATOR_PEER_ID]
# Несколько операторов: уведомления о заявках/откликах/вызовах уходят ВСЕМ из списка.
# По умолчанию = один OPERATOR_PEER_ID. Задать через .env: OPERATOR_PEER_IDS=111,222,333
_op_ids_raw = os.getenv("OPERATOR_PEER_IDS")
if _op_ids_raw:
OPERATOR_PEER_IDS = [int(x.strip()) for x in _op_ids_raw.split(",") if x.strip().lstrip("-").isdigit()]
if not OPERATOR_PEER_IDS:
OPERATOR_PEER_IDS = [OPERATOR_PEER_ID]
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"))
# Разрешённый источник (Origin) для CORS веб-формы. '*' допустим только для локальной отладки.
ALLOWED_ORIGIN = os.getenv("ALLOWED_ORIGIN", "https://infocyber.pro")
# Промокоды на скидку. Пока ВЫКЛЮЧЕНЫ — включить можно, задав PROMO_ENABLED=true в .env.
# Когда выключено: бот и форма не показывают промокод, в уведомлениях/письмах его нет.
PROMO_ENABLED = os.getenv("PROMO_ENABLED", "false").strip().lower() in ("1", "true", "yes")
# Email (SMTP) configuration для уведомлений о заявках
SMTP_HOST = os.getenv("SMTP_HOST", "")
SMTP_PORT = int(os.getenv("SMTP_PORT", "465"))
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
MAIL_FROM = os.getenv("MAIL_FROM", "") or SMTP_USER
# Куда отправлять уведомления о новых заявках (можно несколько через запятую)
MAIL_TO = os.getenv("MAIL_TO", "group-zakaz@infocyber.pro")
# Куда отправлять отклики на вакансии / набор в состав.
# TODO: пока совпадает с MAIL_TO; возможно вынести на отдельный HR-адрес (напр. hr@infocyber.pro) — задать HR_MAIL_TO в .env.
HR_MAIL_TO = os.getenv("HR_MAIL_TO", "") or MAIL_TO
# True — порт 465 (SSL), False — STARTTLS (обычно порт 587)
SMTP_USE_SSL = os.getenv("SMTP_USE_SSL", "true").strip().lower() in ("1", "true", "yes")
# Standard replies and patterns
FAQ_ANSWERS = {
"цены": (
"💰 Тарифы ИКП на тренировки (направления: CS2, Dota 2, World of Tanks):\n\n"
"• Разовая тренировка — 870 ₽\n"
"• Абонемент на 4 занятия (групповой) — 2 790 ₽\n"
"• Абонемент на 8 занятий (групповой) — 4 990 ₽\n"
"• Абонемент на 4 занятия (индивидуальный) — 3 290 ₽\n"
"• Абонемент на 8 занятий (индивидуальный) — 5 990 ₽\n\n"
"Оставьте заявку через меню — менеджер подберёт оптимальный формат и направление!"
),
"контакты": (
"📞 Связаться с ИКП:\n"
"• Email (поддержка): support@infocyber.pro\n"
"• Заявки: group-zakaz@infocyber.pro\n"
"• Telegram: @ikp_team\n"
"• Сайт: https://infocyber.pro/"
),
"услуги": (
"⚙️ ИКП — тренировки по киберспортивным дисциплинам:\n\n"
"1. Counter-Strike 2 — aim, раскидки гранат, командная тактика\n"
"2. Dota 2 — разбор реплеев, позиционка, фарм и тимплей\n"
"3. World of Tanks — позиционирование, сведение, тактика боёв\n\n"
"Форматы: разовые занятия, групповые и индивидуальные абонементы (4 и 8 занятий)."
),
"о компании": (
"🏢 ИКП — Игра. Киберспорт. Профессионалы.\n\n"
"Мощная экосистема для взрывного роста игроков: лучшие тренеры и глубокая data-аналитика. "
"Организация основана 1 сентября 2025 года.\n\n"
"Мы на площадках:\n"
"• ВКонтакте: https://vk.com/ikpro\n"
"• VK Live (трансляции): https://live.vkvideo.ru/ikp\n"
"• Telegram: https://t.me/ikp_team\n"
"• Discord: https://discord.gg/z92mAw7KEe"
)
}
# Доступные услуги (направления) для выбора в форме заявки
SERVICES = {
"1": {
"name": "Counter-Strike 2 🔫",
"price": "от 870 ₽ / занятие",
"description": "Тренировки по CS2: aim, раскидки гранат, командная тактика. Разовое занятие 870 ₽, абонементы от 2 790 ₽."
},
"2": {
"name": "Dota 2 🎮",
"price": "от 870 ₽ / занятие",
"description": "Тренировки по Dota 2: разбор реплеев, позиционка, фарм и тимплей. Разовое занятие 870 ₽, абонементы от 2 790 ₽."
},
"3": {
"name": "World of Tanks 🛡️",
"price": "от 870 ₽ / занятие",
"description": "Тренировки по World of Tanks: позиционирование, сведение, тактика боёв. Разовое занятие 870 ₽, абонементы от 2 790 ₽."
}
}