Доработка VK-бота ИКП: контент, новые функции, защита и визуал

Контент и брендинг:
- Реальные данные ИК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>
This commit is contained in:
Kenik
2026-06-21 21:54:31 +03:00
parent f38324645e
commit 79cc1ba38d
13 changed files with 1860 additions and 269 deletions
+61
View File
@@ -0,0 +1,61 @@
"""Отправка email-уведомлений о заявках на корпоративную почту.
Доступы берутся из .env (см. .env.example). Если SMTP не настроен,
функция тихо пропускает отправку и возвращает False, не ломая основной поток.
"""
import smtplib
import logging
from typing import Optional
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
import config
logger = logging.getLogger("VK_Bot_IKP.mailer")
def is_configured() -> bool:
"""Проверяет, заданы ли минимальные параметры SMTP для отправки писем."""
return bool(
getattr(config, "SMTP_HOST", "")
and getattr(config, "SMTP_USER", "")
and getattr(config, "SMTP_PASSWORD", "")
and getattr(config, "MAIL_TO", "")
)
def send_email(subject: str, body: str, mail_to: Optional[str] = None) -> bool:
"""Отправляет письмо. По умолчанию на config.MAIL_TO; можно указать другой адрес (например, HR)."""
if not is_configured():
logger.info("[MAIL] Пропущено: SMTP не настроен в .env (SMTP_HOST/SMTP_USER/SMTP_PASSWORD/MAIL_TO).")
return False
host = config.SMTP_HOST
port = int(getattr(config, "SMTP_PORT", 465))
user = config.SMTP_USER
password = config.SMTP_PASSWORD
mail_from = getattr(config, "MAIL_FROM", "") or user
mail_to = mail_to or config.MAIL_TO
use_ssl = bool(getattr(config, "SMTP_USE_SSL", True))
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = Header(subject, "utf-8")
msg["From"] = formataddr((str(Header("Бот ИКП", "utf-8")), mail_from))
msg["To"] = mail_to
try:
if use_ssl:
with smtplib.SMTP_SSL(host, port, timeout=15) as server:
server.login(user, password)
server.sendmail(mail_from, [a.strip() for a in mail_to.split(",")], msg.as_string())
else:
with smtplib.SMTP(host, port, timeout=15) as server:
server.starttls()
server.login(user, password)
server.sendmail(mail_from, [a.strip() for a in mail_to.split(",")], msg.as_string())
logger.info(f"[MAIL] Письмо отправлено на {mail_to}: {subject}")
return True
except Exception as e:
logger.error(f"[MAIL] Ошибка отправки письма: {e}")
return False