From c8d769ee1a4e3c75382c5bf797210526ce10988c Mon Sep 17 00:00:00 2001 From: "a.gruzdev" Date: Mon, 15 Jun 2026 04:56:06 +0000 Subject: [PATCH] main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Начальный проект чат бота (Для тестирования и исправления) --- .env | 12 + .env.example | 12 + .gitignore | 18 + bot.py | 1036 +++++++++++++++++++++++++++++++++++++++++++++++ bot_database.db | Bin 0 -> 385024 bytes 5 files changed, 1078 insertions(+) create mode 100644 .env create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 bot.py create mode 100644 bot_database.db diff --git a/.env b/.env new file mode 100644 index 0000000..e7cda77 --- /dev/null +++ b/.env @@ -0,0 +1,12 @@ +# VK API Configuration +# Токен сообщества ВКонтакте (с правами на управление сообщениями) +VK_TOKEN=your_vk_community_token_here + +# Идентификатор (ID) сообщества ВКонтакте (только цифры) +VK_GROUP_ID=0 + +# ID оператора / администратора ВКонтакте для отправки лидов и оповещений (только цифры) +OPERATOR_PEER_ID=12345678 + +# Список ID администраторов (через запятую) для управления услугами через чат +ADMIN_IDS=12345678 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e7cda77 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# VK API Configuration +# Токен сообщества ВКонтакте (с правами на управление сообщениями) +VK_TOKEN=your_vk_community_token_here + +# Идентификатор (ID) сообщества ВКонтакте (только цифры) +VK_GROUP_ID=0 + +# ID оператора / администратора ВКонтакте для отправки лидов и оповещений (только цифры) +OPERATOR_PEER_ID=12345678 + +# Список ID администраторов (через запятую) для управления услугами через чат +ADMIN_IDS=12345678 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0cbd5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Configuration files containing credentials +.env + +# Python byte-compiled files +__pycache__/ +*.py[cod] +*$py.class + +# Local Databases and dynamic data files +*.db +*.xlsx + +# Environment/IDE configurations +.idea/ +.vscode/ + +# Logs +*.log diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..997d552 --- /dev/null +++ b/bot.py @@ -0,0 +1,1036 @@ +import json +import re +import logging +from typing import Dict, Any, Optional + +# Нам понадобятся библиотеки vk_api. +try: + import vk_api + from vk_api.bot_longpoll import VkBotLongPoll, VkBotEventType + from vk_api.keyboard import VkKeyboard, VkKeyboardColor + from vk_api.utils import get_random_id +except ImportError: + vk_api = None + +from database import DatabaseManager +import config + +# Настройка логирования +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - [%(levelname)s] - %(name)s - %(message)s" +) +logger = logging.getLogger("VK_Bot_IKP") + +# Инициализация базы данных сессий +db = DatabaseManager() + +# Регулярное выражение для базовой валидации номера телефона +PHONE_REGEX = re.compile(r"^\+?[78]\d{10}$") + + +# ========================================== +# КЛАВИАТУРЫ ДЛЯ ИНТЕРФЕЙСА (UX/UI ВКонтакте) +# ========================================== + +def get_main_keyboard() -> str: + """Главное меню бота.""" + keyboard = VkKeyboard(one_time=False) + # Кнопка начать опрос (зеленая) + keyboard.add_button("Заполнить заявку 📝", color=VkKeyboardColor.PRIMARY) + keyboard.add_line() + # Кнопки FAQ и оператора + keyboard.add_button("Частые вопросы ❓", color=VkKeyboardColor.SECONDARY) + keyboard.add_button("Позвать оператора 🧑‍💼", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_faq_keyboard() -> str: + """Клавиатура раздела Частых Вопросов (FAQ).""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Цены 💰", color=VkKeyboardColor.SECONDARY) + keyboard.add_button("Услуги ⚙️", color=VkKeyboardColor.SECONDARY) + keyboard.add_line() + keyboard.add_button("Контакты 📞", color=VkKeyboardColor.SECONDARY) + keyboard.add_button("О компании 🏢", color=VkKeyboardColor.SECONDARY) + keyboard.add_line() + keyboard.add_button("Назад в меню ↩️", color=VkKeyboardColor.PRIMARY) + return keyboard.get_keyboard() + + +def get_cancel_keyboard() -> str: + """Клавиатура отмены заполнения формы.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_operator_keyboard() -> str: + """Клавиатура при активном диалоге с оператором.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Завершить диалог 🚪", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_binary_keyboard(show_skip: bool = False) -> str: + """Клавиатура с кнопками Согласен / Не согласен.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Согласен(а) и подтверждаю ✅", color=VkKeyboardColor.PRIMARY) + keyboard.add_button("Не согласен(а) ❌", color=VkKeyboardColor.NEGATIVE) + if show_skip: + keyboard.add_line() + keyboard.add_button("Пропустить ➡️", color=VkKeyboardColor.SECONDARY) + keyboard.add_line() + keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_services_keyboard() -> str: + """Клавиатура со списком доступных услуг.""" + keyboard = VkKeyboard(one_time=False) + first = True + for key, svc in config.SERVICES.items(): + if not first: + keyboard.add_line() + keyboard.add_button(svc["name"], color=VkKeyboardColor.SECONDARY) + first = False + keyboard.add_line() + keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_confirm_service_keyboard() -> str: + """Клавиатура для подтверждения выбранной услуги.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Да, подтверждаю ✅", color=VkKeyboardColor.PRIMARY) + keyboard.add_button("Нет, выбрать другую 🔄", color=VkKeyboardColor.NEGATIVE) + keyboard.add_line() + keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_review_keyboard() -> str: + """Клавиатура для финальной проверки данных пользователем.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Да, всё верно! ✅", color=VkKeyboardColor.PRIMARY) + keyboard.add_button("Изменить данные 🔄", color=VkKeyboardColor.SECONDARY) + keyboard.add_line() + keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE) + return keyboard.get_keyboard() + + +def get_edit_menu_keyboard() -> str: + """Клавиатура меню изменения полей.""" + keyboard = VkKeyboard(one_time=False) + keyboard.add_button("Изменить Имя 👤", color=VkKeyboardColor.SECONDARY) + keyboard.add_button("Изменить Телефон 📞", color=VkKeyboardColor.SECONDARY) + keyboard.add_line() + keyboard.add_button("Изменить Услугу 🎮", color=VkKeyboardColor.SECONDARY) + keyboard.add_button("Назад к проверке ↩️", color=VkKeyboardColor.PRIMARY) + return keyboard.get_keyboard() + + + +# ========================================== +# КЛАСС БОТА И ЛОГИКА ОБРАБОТКИ +# ========================================== + +class VKBotApplication: + def __init__(self): + self.vk_session = None + self.vk = None + self.longpoll = None + + # Инициализируем динамические услуги из базы данных + config.SERVICES = db.get_services() + + if vk_api and config.VK_TOKEN != "your_vk_community_token_here": + try: + self.vk_session = vk_api.VkApi(token=config.VK_TOKEN) + self.vk = self.vk_session.get_api() + self.longpoll = VkBotLongPoll(self.vk_session, config.GROUP_ID) + logger.info("Успешное подключение к VK API!") + except Exception as e: + logger.error(f"Ошибка авторизации в VK API: {e}") + + def send_message(self, peer_id: int, message: str, keyboard: Optional[str] = None, attachment: Optional[str] = None): + """Отправка сообщения пользователю с сохранением в лог.""" + db.log_message(peer_id, "bot", message) + + if self.vk: + try: + params = { + "peer_id": peer_id, + "message": message, + "random_id": get_random_id() + } + if keyboard: + params["keyboard"] = keyboard + if attachment: + params["attachment"] = attachment + self.vk.messages.send(**params) + except Exception as e: + logger.error(f"Не удалось отправить сообщение пользователю {peer_id}: {e}") + else: + attachment_str = f", Attachment: {attachment}" if attachment else "" + logger.info(f"[MOCK SEND] to {peer_id}: {message} (Keyboard: {'Да' if keyboard else 'Нет'}{attachment_str})") + + def handle_message(self, user_id: int, text: str): + """Маршрутизатор входящих сообщений на основе состояний пользователя.""" + text_clean = text.strip() + text_lower = text_clean.lower() + + # Логируем входящее сообщение от пользователя + db.log_message(user_id, "user", text_clean) + + # Получаем текущее состояние и заполненные данные + user_data = db.get_user_data(user_id) + state = user_data["state"] + + # Глобальная обработка админ-команд (доступна только пользователям из ADMIN_IDS) + if hasattr(config, "ADMIN_IDS") and user_id in config.ADMIN_IDS: + if text_lower in ["админка", "/admin", "услуги"]: + self._send_admin_menu(user_id) + return + elif text_lower.startswith("ред "): + self._handle_admin_edit(user_id, text_clean) + return + elif text_lower.startswith("доб "): + self._handle_admin_add(user_id, text_clean) + return + elif text_lower.startswith("удал "): + self._handle_admin_delete(user_id, text_clean) + return + + logger.info(f"User {user_id} in state {state} sent: {text_clean}") + + # Глобальная обработка отмены формы в любых состояниях заполнения опроса + if state.startswith("FORM_") and text_lower in ["отменить заполнение ❌", "отмена", "отменить"]: + db.reset_form(user_id) + self.send_message( + user_id, + "Заполнение заявки отменено. Мы вернулись в главное меню. Чем я могу помочь?", + keyboard=get_main_keyboard() + ) + return + + # Маршрутизация по состояниям (FSM) + if state == "FREE_CHAT": + self._handle_free_chat(user_id, text_clean, text_lower) + elif state == "FORM_NAME": + self._handle_form_name(user_id, text_clean) + elif state == "FORM_PHONE": + self._handle_form_phone(user_id, text_clean) + elif state == "FORM_QUESTION": + self._handle_form_question(user_id, text_clean, user_data) + elif state == "FORM_CONFIRM_QUESTION": + self._handle_form_confirm_question(user_id, text_clean, user_data) + elif state == "FORM_REVIEW": + self._handle_form_review(user_id, text_clean, user_data) + elif state == "FORM_EDIT_MENU": + self._handle_form_edit_menu(user_id, text_clean, user_data) + elif state == "FORM_EDIT_NAME": + self._handle_form_edit_name(user_id, text_clean) + elif state == "FORM_EDIT_PHONE": + self._handle_form_edit_phone(user_id, text_clean) + elif state == "FORM_EDIT_QUESTION": + self._handle_form_edit_question(user_id, text_clean) + elif state == "FORM_CONSENT_1": + self._handle_form_consent_1(user_id, text_clean, text_lower) + elif state == "FORM_CONSENT_2": + self._handle_form_consent_2(user_id, text_clean, text_lower) + elif state == "FORM_CONSENT_3": + self._handle_form_consent_3(user_id, text_clean, text_lower, user_data) + elif state == "OPERATOR_CHAT": + self._handle_operator_chat(user_id, text_clean, text_lower) + else: + # Предотвращение неопределенного состояния + db.update_user_state(user_id, "FREE_CHAT") + self.send_message( + user_id, + "Произошла непредвиденная ошибка в системе. Возвращаю вас в главное меню.", + keyboard=get_main_keyboard() + ) + + # ------------------------------------------ + # ОБРАБОТЧИКИ СОСТОЯНИЙ (FSM) + # ------------------------------------------ + + def _handle_free_chat(self, user_id: int, text: str, text_lower: str): + """Логика Свободного Чата (Главное меню, FAQ, переключение на форму).""" + + # 1. Запуск формы опроса + if text_lower in ["заполнить заявку 📝", "заявка", "начать опрос", "опрос"]: + db.update_user_state(user_id, "FORM_NAME") + self.send_message( + user_id, + "📝 Начинаем заполнение заявки!\n\n" + "Шаг 1 из 3: Как к вам обращаться? Пожалуйста, введите ваше имя.", + keyboard=get_cancel_keyboard() + ) + return + + # 2. Переход к списку частых вопросов (FAQ) + if text_lower in ["частые вопросы ❓", "faq", "вопросы", "помощь"]: + self.send_message( + user_id, + "❓ Выберите тему, которая вас интересует, на клавиатуре ниже:", + keyboard=get_faq_keyboard() + ) + return + + # 3. Возврат в главное меню + if text_lower in ["назад в меню ↩️", "меню", "назад"]: + self.send_message( + user_id, + "🏠 Вы вернулись в главное меню. Выберите интересующее действие:", + keyboard=get_main_keyboard() + ) + return + + # 4. Вызов оператора + if text_lower in ["позвать оператора 🧑‍💼", "позвать оператора", "оператор", "менеджер", "человек", "позови оператора", "связь с оператором"]: + self._handoff_to_operator(user_id) + return + + # 5. Обработка конкретных ответов FAQ + for key, answer in config.FAQ_ANSWERS.items(): + if key in text_lower: + self.send_message(user_id, answer, keyboard=get_faq_keyboard()) + return + + # 6. Приветственные сообщения + if text_lower in ["начать", "привет", "здравствуйте", "ку", "hi", "hello"]: + self.send_message( + user_id, + "👋 Здравствуйте! Я умный чат-бот школы киберспорта 'Cyber Pro'.\n\n" + "Я могу помочь вам быстро оформить заявку на наши услуги или ответить на частые вопросы. " + "Выберите действие на кнопках ниже или просто напишите ваш вопрос!", + keyboard=get_main_keyboard() + ) + return + + # 7. Fallback (Бот не понял, но пытается помочь, предлагая клавиатуру) + self.send_message( + user_id, + "🤔 Я пока только учусь и не смог полностью распознать ваш запрос.\n\n" + "Пожалуйста, воспользуйтесь кнопками меню ниже или напишите 'оператор', чтобы я пригласил специалиста.", + keyboard=get_main_keyboard() + ) + + def _handle_form_name(self, user_id: int, text: str): + """Сохранение имени и переход к номеру телефона.""" + # Валидация имени: длина от 2 до 50 символов + if len(text) < 2 or len(text) > 50: + self.send_message( + user_id, + "⚠️ Имя выглядит слишком коротким или длинным. Пожалуйста, введите корректное имя.", + keyboard=get_cancel_keyboard() + ) + return + + # Сохраняем имя в БД + db.update_user_field(user_id, "name", text) + db.update_user_state(user_id, "FORM_PHONE") + + self.send_message( + user_id, + f"👍 Приятно познакомиться, {text}!\n\n" + "Шаг 2 из 3: Введите ваш контактный номер телефона (например, +79991234567) для связи с менеджером.", + keyboard=get_cancel_keyboard() + ) + + def _handle_form_phone(self, user_id: int, text: str): + """Сохранение телефона и переход к выбору услуги.""" + # Очищаем телефон от пробелов, скобок и дефисов для валидации + phone_cleaned = re.sub(r"[\s\-\(\)]", "", text) + + # Если телефон начинается с 8, заменяем на +7 для стандартизации + if phone_cleaned.startswith("8") and len(phone_cleaned) == 11: + phone_cleaned = "+7" + phone_cleaned[1:] + elif phone_cleaned.startswith("7") and len(phone_cleaned) == 11: + phone_cleaned = "+" + phone_cleaned + + # Валидация регулярным выражением + if not PHONE_REGEX.match(phone_cleaned): + self.send_message( + user_id, + "⚠️ Некорректный формат телефона. Пожалуйста, отправьте номер в формате +79991234567.", + keyboard=get_cancel_keyboard() + ) + return + + # Сохраняем телефон в БД + db.update_user_field(user_id, "phone", phone_cleaned) + db.update_user_state(user_id, "FORM_QUESTION") + + # Формируем список услуг с ценами + services_text = ( + "✅ Телефон принят!\n\n" + "Шаг 3 из 3: Какая услуга вас интересует?\n\n" + "📋 Доступные услуги и расценки:\n\n" + ) + for key, svc in config.SERVICES.items(): + services_text += f"{key}️⃣ **{svc['name']}**\n" + services_text += f" 💰 Стоимость: {svc['price']}\n" + services_text += f" 📝 {svc['description']}\n\n" + services_text += "Пожалуйста, выберите нужную услугу, нажав на кнопку на клавиатуре или отправив её номер/название." + + self.send_message( + user_id, + services_text, + keyboard=get_services_keyboard() + ) + + def _handle_form_question(self, user_id: int, text: str, user_data: Dict[str, Any]): + """Обработка выбора услуги с прямым переходом к проверке данных.""" + selected_service = None + text_lower = text.lower().strip() + + # 1. Прямое сопоставление синонимов дисциплин для надежности + synonyms = { + "1": ["дота", "dota", "дотку", "дотка", "dota2"], + "2": ["кс", "cs", "ксго", "кс2", "csgo", "cs2", "контра", "кондра", "counter"] + } + for key_opt, words in synonyms.items(): + if any(w in text_lower for w in words): + selected_service = config.SERVICES.get(key_opt) + break + + # 2. Поиск по названию или ключу (если синонимы не сработали) + if not selected_service: + for key, svc in config.SERVICES.items(): + name_clean = svc["name"].lower().strip() + # Убираем эмодзи и спецсимволы для гибкого сравнения + name_no_emoji = re.sub(r"[^\w\s\d]", "", name_clean).strip() + text_no_emoji = re.sub(r"[^\w\s\d]", "", text_lower).strip() + + if (text_lower == key or + text_lower == f"{key}." or + text_lower == f"{key}️⃣" or + name_clean in text_lower or + text_lower in name_clean or + (name_no_emoji and text_no_emoji and (text_no_emoji in name_no_emoji or name_no_emoji in text_no_emoji))): + selected_service = svc + break + + if selected_service: + # Сохраняем временный выбор в поле question + db.update_user_field(user_id, "question", f"{selected_service['name']} ({selected_service['price']})") + db.update_user_state(user_id, "FORM_REVIEW") + + updated_data = db.get_user_data(user_id) + self._send_review_message(user_id, updated_data) + else: + # Формируем список услуг с ценами для повторного показа + services_text = ( + "⚠️ К сожалению, я не смог распознать выбранную услугу.\n\n" + "Пожалуйста, выберите одну из доступных услуг на клавиатуре или отправьте её номер/название:\n\n" + ) + for key, svc in config.SERVICES.items(): + services_text += f"{key}️⃣ **{svc['name']}**\n" + services_text += f" 💰 Стоимость: {svc['price']}\n" + services_text += f" 📝 {svc['description']}\n\n" + + self.send_message( + user_id, + services_text, + keyboard=get_services_keyboard() + ) + + def _handle_form_confirm_question(self, user_id: int, text: str, user_data: Dict[str, Any]): + """Резервный обработчик для совместимости с устаревшим шагом подтверждения.""" + db.update_user_state(user_id, "FORM_REVIEW") + self._send_review_message(user_id, user_data) + + def _send_review_message(self, user_id: int, user_data: Dict[str, Any]): + """Отправляет сводную карточку проверки данных перед финальным шагом.""" + name = user_data.get("name", "Не указано") + phone = user_data.get("phone", "Не указано") + question = user_data.get("question", "Не указано") + + review_text = ( + "🔍 **Пожалуйста, проверьте правильность заполненных данных:**\n\n" + f"👤 **Имя:** {name}\n" + f"📞 **Телефон:** {phone}\n" + f"🎮 **Выбранная услуга:** {question}\n\n" + "Если всё верно, нажмите «Да, всё верно! ✅» для продолжения. Если хотите исправить — нажмите «Изменить данные 🔄»." + ) + self.send_message(user_id, review_text, keyboard=get_review_keyboard()) + + def _handle_form_review(self, user_id: int, text: str, user_data: Dict[str, Any]): + """Обработка решений на экране финальной проверки данных.""" + text_lower = text.lower().strip() + + if text_lower in ["да, всё верно! ✅", "да, всё верно", "все верно", "да", "верно"]: + # Переходим к шагу оферты и согласий + db.update_user_state(user_id, "FORM_CONSENT_1") + + # Загружаем документ оферты для ознакомления + doc_attachment = self.get_offer_document_attachment(user_id) + + # Отправляем значительно сокращенное и дружелюбное сообщение-согласие + self.send_message( + user_id, + "📄 **Согласие с условиями и офертой**\n\n" + "Для завершения отправки заявки подтвердите, пожалуйста, ваше согласие с условиями Публичной оферты, " + "Политикой обработки персональных данных и записью разговора.\n\n" + "«Я подтверждаю, что ознакомлен(а) с договором оферты (прикреплен ниже), принимаю его условия, " + "соглашаюсь на обработку персональных данных и запись консультации.»", + keyboard=get_binary_keyboard(show_skip=False), + attachment=doc_attachment + ) + + elif text_lower in ["изменить данные 🔄", "изменить данные", "изменить", "редактировать", "поменять"]: + # Переходим в меню изменения полей + db.update_user_state(user_id, "FORM_EDIT_MENU") + self.send_message( + user_id, + "🔄 **Меню изменения данных**\n\n" + "Пожалуйста, выберите на клавиатуре ниже, какое поле вы хотите изменить:", + keyboard=get_edit_menu_keyboard() + ) + + else: + self.send_message( + user_id, + "❓ Пожалуйста, подтвердите ваши данные кнопкой «Да, всё верно! ✅» или нажмите «Изменить данные 🔄» для редактирования.", + keyboard=get_review_keyboard() + ) + + def _handle_form_edit_menu(self, user_id: int, text: str, user_data: Dict[str, Any]): + """Обработка меню выбора поля для редактирования.""" + text_lower = text.lower().strip() + + if text_lower in ["изменить имя 👤", "изменить имя", "имя", "1"]: + db.update_user_state(user_id, "FORM_EDIT_NAME") + self.send_message( + user_id, + "👤 Введите ваше новое имя:", + keyboard=get_cancel_keyboard() + ) + elif text_lower in ["изменить телефон 📞", "изменить телефон", "телефон", "2"]: + db.update_user_state(user_id, "FORM_EDIT_PHONE") + self.send_message( + user_id, + "📞 Введите ваш новый номер телефона в формате +79991234567:", + keyboard=get_cancel_keyboard() + ) + elif text_lower in ["изменить услугу 🎮", "изменить услугу", "услугу", "услуга", "3"]: + db.update_user_state(user_id, "FORM_EDIT_QUESTION") + + services_text = ( + "🎮 **Выберите новую услугу из списка:**\n\n" + ) + for key, svc in config.SERVICES.items(): + services_text += f"{key}️⃣ **{svc['name']}**\n" + services_text += f" 💰 Стоимость: {svc['price']}\n\n" + + self.send_message( + user_id, + services_text, + keyboard=get_services_keyboard() + ) + elif text_lower in ["назад к проверке ↩️", "назад к проверке", "назад", "вернуть", "4"]: + db.update_user_state(user_id, "FORM_REVIEW") + self._send_review_message(user_id, user_data) + else: + self.send_message( + user_id, + "❓ Выберите нужный пункт меню на клавиатуре или нажмите «Назад к проверке ↩️» для возврата к карточке данных.", + keyboard=get_edit_menu_keyboard() + ) + + def _handle_form_edit_name(self, user_id: int, text: str): + """Сохранение нового имени и возврат к проверке.""" + if len(text) < 2 or len(text) > 50: + self.send_message( + user_id, + "⚠️ Имя выглядит слишком коротким или длинным. Пожалуйста, введите корректное имя.", + keyboard=get_cancel_keyboard() + ) + return + + db.update_user_field(user_id, "name", text) + db.update_user_state(user_id, "FORM_REVIEW") + + updated_data = db.get_user_data(user_id) + self.send_message(user_id, "✅ Имя успешно обновлено!") + self._send_review_message(user_id, updated_data) + + def _handle_form_edit_phone(self, user_id: int, text: str): + """Сохранение нового телефона и возврат к проверке.""" + phone_cleaned = re.sub(r"[\s\-\(\)]", "", text) + + if phone_cleaned.startswith("8") and len(phone_cleaned) == 11: + phone_cleaned = "+7" + phone_cleaned[1:] + elif phone_cleaned.startswith("7") and len(phone_cleaned) == 11: + phone_cleaned = "+" + phone_cleaned + + if not PHONE_REGEX.match(phone_cleaned): + self.send_message( + user_id, + "⚠️ Некорректный формат телефона. Пожалуйста, отправьте номер в формате +79991234567.", + keyboard=get_cancel_keyboard() + ) + return + + db.update_user_field(user_id, "phone", phone_cleaned) + db.update_user_state(user_id, "FORM_REVIEW") + + updated_data = db.get_user_data(user_id) + self.send_message(user_id, "✅ Номер телефона успешно обновлен!") + self._send_review_message(user_id, updated_data) + + def _handle_form_edit_question(self, user_id: int, text: str): + """Сохранение новой услуги и возврат к проверке.""" + selected_service = None + text_lower = text.lower().strip() + + # 1. Прямое сопоставление синонимов дисциплин для надежности + synonyms = { + "1": ["дота", "dota", "дотку", "дотка", "dota2"], + "2": ["кс", "cs", "ксго", "кс2", "csgo", "cs2", "контра", "кондра", "counter"] + } + for key_opt, words in synonyms.items(): + if any(w in text_lower for w in words): + selected_service = config.SERVICES.get(key_opt) + break + + # 2. Поиск по названию или ключу (если синонимы не сработали) + if not selected_service: + for key, svc in config.SERVICES.items(): + name_clean = svc["name"].lower().strip() + name_no_emoji = re.sub(r"[^\w\s\d]", "", name_clean).strip() + text_no_emoji = re.sub(r"[^\w\s\d]", "", text_lower).strip() + + if (text_lower == key or + text_lower == f"{key}." or + text_lower == f"{key}️⃣" or + name_clean in text_lower or + text_lower in name_clean or + (name_no_emoji and text_no_emoji and (text_no_emoji in name_no_emoji or name_no_emoji in text_no_emoji))): + selected_service = svc + break + + if selected_service: + db.update_user_field(user_id, "question", f"{selected_service['name']} ({selected_service['price']})") + db.update_user_state(user_id, "FORM_REVIEW") + + updated_data = db.get_user_data(user_id) + self.send_message(user_id, "✅ Услуга успешно обновлена!") + self._send_review_message(user_id, updated_data) + else: + services_text = ( + "⚠️ К сожалению, я не смог распознать выбранную услугу.\n\n" + "Пожалуйста, выберите одну из доступных услуг на клавиатуре:\n\n" + ) + for key, svc in config.SERVICES.items(): + services_text += f"{key}️⃣ **{svc['name']}**\n" + services_text += f" 💰 Стоимость: {svc['price']}\n\n" + + self.send_message( + user_id, + services_text, + keyboard=get_services_keyboard() + ) + + + def get_offer_document_attachment(self, peer_id: int) -> Optional[str]: + """Загружает документ оферты на сервер VK и возвращает attachment-строку.""" + if hasattr(self, "_cached_doc_attachment") and self._cached_doc_attachment: + return self._cached_doc_attachment + + import os + doc_path = os.path.join(os.path.dirname(__file__), "Dogovor_oferty_kibersport_versia_izmenennaya.docx") + if not os.path.exists(doc_path): + logger.error(f"Файл оферты не найден по пути: {doc_path}") + return None + + if not self.vk: + # Режим эмуляции (консольный) + logger.info(f"[EMULATOR] Файл {os.path.basename(doc_path)} прикреплен к сообщению.") + return None + + try: + import requests + logger.info("Загрузка документа оферты в VK API...") + upload_server = self.vk.docs.getMessagesUploadServer(peer_id=peer_id, type="doc") + upload_url = upload_server["upload_url"] + + title = "Dogovor_oferty_kibersport.docx" + with open(doc_path, "rb") as f: + response = requests.post(upload_url, files={"file": (title, f)}) + + response_json = response.json() + if "file" not in response_json: + logger.error(f"Ошибка загрузки файла в VK: {response_json}") + return None + + saved_doc = self.vk.docs.save(file=response_json["file"], title=title) + doc_info = saved_doc[0] if isinstance(saved_doc, list) else saved_doc + + attachment = f"doc{doc_info['owner_id']}_{doc_info['id']}" + self._cached_doc_attachment = attachment + logger.info(f"Документ загружен успешно: {attachment}") + return attachment + except Exception as e: + logger.error(f"Ошибка при загрузке документа в VK API: {e}") + return None + + def _handle_form_consent_1(self, user_id: int, text: str, text_lower: str): + """Обработка объединенного шага согласий.""" + is_agree = text_lower in ["согласен(а) и подтверждаю ✅", "согласен и подтверждаю", "согласен(а) и подтверждаю", "согласен", "согласна", "да", "принимаю", "согласен(а) ✅"] + is_disagree = text_lower in ["не согласен(а) ❌", "не согласен", "не согласна", "нет", "не принимаю"] + + if is_agree: + # Записываем все согласия как "Согласен" в базу данных для полной совместимости + db.update_user_field(user_id, "consent_1", "Согласен") + db.update_user_field(user_id, "consent_2", "Согласен") + db.update_user_field(user_id, "consent_3", "Согласен") + + # Получаем актуальные данные пользователя + updated_data = db.get_user_data(user_id) + phone = updated_data["phone"] + + # Возвращаем пользователя в главное меню + db.update_user_state(user_id, "FREE_CHAT") + + # Динамически определяем время ответа в зависимости от рабочего времени (с 9:00 до 21:00) + from datetime import datetime + now = datetime.now() + if 9 <= now.hour < 21: + callback_time = "в течение 15 минут" + else: + callback_time = "сегодня утром, начиная с 9:00" + + # Отправляем подтверждение пользователю + self.send_message( + user_id, + "🎉 Заявка и все необходимые согласия успешно оформлены!\n\n" + f"Наш менеджер уже изучает детали и свяжется с вами {callback_time} по номеру " + f"{phone}. Спасибо за обращение!\n\n" + "Вы вернулись в главное меню бота.", + keyboard=get_main_keyboard() + ) + + # Уведомляем оператора о новой заявке со всеми согласиями + self._notify_operator_about_lead(user_id, updated_data) + elif is_disagree: + self.send_message( + user_id, + "⚠️ **Для продолжения оформления заявки необходимо принять условия Публичной оферты и дать согласие на обработку данных и запись разговора.**\n\n" + "Пожалуйста, подтвердите согласие кнопкой «Согласен(а) и подтверждаю ✅» или нажмите «Отменить заполнение ❌» для выхода.", + keyboard=get_binary_keyboard(show_skip=False) + ) + else: + self.send_message( + user_id, + "❓ Пожалуйста, подтвердите согласие кнопкой «Согласен(а) и подтверждаю ✅» или выберите «Не согласен(а) ❌» / «Отменить заполнение ❌».", + keyboard=get_binary_keyboard(show_skip=False) + ) + + def _handle_form_consent_2(self, user_id: int, text: str, text_lower: str): + """Резервный обработчик для совместимости с устаревшими состояниями в БД.""" + db.update_user_field(user_id, "consent_2", "Согласен") + db.update_user_field(user_id, "consent_3", "Согласен") + + updated_data = db.get_user_data(user_id) + phone = updated_data["phone"] + db.update_user_state(user_id, "FREE_CHAT") + + self.send_message( + user_id, + "🎉 Заявка успешно оформлена!\n\n" + f"Наш менеджер свяжется с вами по номеру {phone}. Спасибо за обращение!", + keyboard=get_main_keyboard() + ) + self._notify_operator_about_lead(user_id, updated_data) + + def _handle_form_consent_3(self, user_id: int, text: str, text_lower: str, user_data: Dict[str, Any]): + """Резервный обработчик для совместимости с устаревшими состояниями в БД.""" + db.update_user_field(user_id, "consent_3", "Согласен") + + updated_data = db.get_user_data(user_id) + phone = updated_data["phone"] + db.update_user_state(user_id, "FREE_CHAT") + + self.send_message( + user_id, + "🎉 Заявка успешно оформлена!\n\n" + f"Наш менеджер свяжется с вами по номеру {phone}. Спасибо за обращение!", + keyboard=get_main_keyboard() + ) + self._notify_operator_about_lead(user_id, updated_data) + + def _handle_operator_chat(self, user_id: int, text: str, text_lower: str): + """Поведение бота в режиме активного диалога с живым человеком.""" + # Пользователь хочет выйти из диалога с оператором + if text_lower in ["завершить диалог 🚪", "завершить", "/exit", "выход"]: + db.update_user_state(user_id, "FREE_CHAT") + self.send_message( + user_id, + "🚪 Диалог с оператором завершен. Вы вернулись к автоматическому ассистенту.", + keyboard=get_main_keyboard() + ) + # Уведомляем оператора о закрытии диалога + self.send_message( + config.OPERATOR_PEER_ID, + f"ℹ️ Пользователь vk.com/id{user_id} завершил сессию общения с оператором." + ) + return + + # В остальное время бот МОЛЧИТ в диалог с пользователем. + logger.info(f"[OPERATOR MODE] Message from {user_id} bypasses bot: {text}") + self._forward_to_operator(user_id, text) + + # ------------------------------------------ + # СЦЕНАРИИ ПЕРЕДАЧИ ОПЕРАТОРУ И СВЯЗИ + # ------------------------------------------ + + def _handoff_to_operator(self, user_id: int): + """Переводит пользователя в режим оператора и уведомляет команду поддержки.""" + db.update_user_state(user_id, "OPERATOR_CHAT") + + self.send_message( + user_id, + "🧑‍💼 Переключаю вас на оператора...\n\n" + "Я отправил уведомление специалисту поддержки. Он ответит вам прямо в этом диалоге в ближайшее время.\n" + "Все ваши сообщения сейчас направляются напрямую человеку. Чтобы вернуться к боту, нажмите кнопку ниже.", + keyboard=get_operator_keyboard() + ) + + # Получаем историю последних 10 сообщений для контекста оператора + history = db.get_dialogue_history(user_id, limit=10) + formatted_history = "" + for sender, msg, ts in history: + role = "Вы" if sender == "user" else "Бот" + formatted_history += f"[{ts}] {role}: {msg}\n" + + # Отправляем алерт оператору с историей диалога + operator_msg = ( + f"🚨 **Запрос оператора!**\n" + f"Пользователь: vk.com/id{user_id}\n" + f"Тип запроса: Прямой вызов из меню\n\n" + f"📋 **Последние сообщения для контекста:**\n{formatted_history}" + ) + self.send_message(config.OPERATOR_PEER_ID, operator_msg) + + def _notify_operator_about_lead(self, user_id: int, user_data: Dict[str, Any]): + """Отправляет структурированный лид оператору после заполнения формы с указанием всех согласий.""" + name = user_data["name"] + phone = user_data["phone"] + question = user_data["question"] + consent_1 = user_data.get("consent_1", "Согласен") + consent_2 = user_data.get("consent_2", "Согласен") + consent_3 = user_data.get("consent_3", "Согласен") + + operator_msg = ( + f"⚡ **Получена новая заявка!**\n\n" + f"👤 **Имя:** {name}\n" + f"📞 **Телефон:** {phone}\n" + f"🎯 **Выбранная услуга:** {question}\n\n" + f"⚖️ **Согласия пользователя:**\n" + f"1. Оферта и обработка ПД (Обяз.): {consent_1}\n" + f"2. Маркетинговые рассылки (Необяз.): {consent_2}\n" + f"3. Запись консультации (Обяз.): {consent_3}\n\n" + f"🔗 Профиль клиента: vk.com/id{user_id}\n" + f"Переписка сохранена в базе данных." + ) + self.send_message(config.OPERATOR_PEER_ID, operator_msg) + + def _forward_to_operator(self, user_id: int, text: str): + """Пересылка сообщений от клиента в чат оператора.""" + forward_msg = ( + f"💬 **Сообщение от vk.com/id{user_id}:**\n{text}" + ) + self.send_message(config.OPERATOR_PEER_ID, forward_msg) + + # ------------------------------------------ + # ЗАПУСК И ОБРАБОТКА LONGPOLL + # ------------------------------------------ + + def _send_admin_menu(self, user_id: int): + """Отправляет администратору панель управления услугами с текущим списком.""" + services = db.get_services() + + services_list_text = "" + for key, svc in services.items(): + services_list_text += f"🔹 **[{key}] {svc['name']}**\n" + services_list_text += f" 💰 Стоимость: {svc['price']}\n" + services_list_text += f" 📝 Описание: {svc['description']}\n\n" + + admin_text = ( + "⚙️ **Панель управления услугами (Админ-меню)**\n\n" + "Здесь вы можете редактировать существующие услуги или добавлять новые.\n\n" + "📋 **Текущий список активных услуг:**\n\n" + f"{services_list_text}" + "🔧 **Доступные команды управления:**\n\n" + "1. **Редактирование услуги**:\n" + " 👉 Отправьте: `ред [номер] [поле] [новое значение]`\n" + " Доступные поля: `имя`, `цена`, `описание`.\n" + " *Пример:* `ред 1 цена 1 800 ₽/час`\n\n" + "2. **Добавление услуги**:\n" + " 👉 Отправьте: `доб [Имя] | [Цена] | [Описание]`\n" + " *Пример:* `доб Тренировка по Valorant 🎯 | 1 500 ₽/час | Разбор стрельбы и aim.`\n\n" + "3. **Удаление услуги**:\n" + " 👉 Отправьте: `удал [номер]`\n" + " *Пример:* `удал 4`" + ) + self.send_message(user_id, admin_text) + + def _handle_admin_edit(self, user_id: int, text: str): + """Обрабатывает редактирование услуги администратором.""" + parts = text.split(" ", 3) + if len(parts) < 4: + self.send_message( + user_id, + "⚠️ Некорректный формат команды редактирования.\n" + "Правильный формат: `ред [номер] [поле] [новое значение]`\n" + "Пример: `ред 1 цена 1 800 ₽/час`" + ) + return + + _, service_id, field_name, new_value = parts + field_name = field_name.lower().strip() + new_value = new_value.strip() + + field_mapping = { + "имя": "name", + "название": "name", + "цена": "price", + "стоимость": "price", + "описание": "description" + } + + if field_name not in field_mapping: + self.send_message( + user_id, + "⚠️ Неизвестное поле для редактирования. Допустимые поля: `имя`, `цена`, `описание`." + ) + return + + db_field = field_mapping[field_name] + services = db.get_services() + + if service_id not in services: + self.send_message( + user_id, + f"⚠️ Услуга с номером '{service_id}' не найдена в списке активных услуг." + ) + return + + try: + db.update_service(service_id, db_field, new_value) + config.SERVICES = db.get_services() + + self.send_message( + user_id, + f"✅ Услуга [{service_id}] успешно обновлена!\n" + f"Поле `{field_name}` изменено на: `{new_value}`." + ) + self._send_admin_menu(user_id) + except Exception as e: + logger.error(f"Ошибка при редактировании услуги в БД: {e}") + self.send_message(user_id, f"⚠️ Произошла ошибка при обновлении базы данных: {e}") + + def _handle_admin_add(self, user_id: int, text: str): + """Обрабатывает добавление новой услуги администратором.""" + content = text[4:].strip() + parts = [p.strip() for p in content.split("|")] + + if len(parts) < 3: + self.send_message( + user_id, + "⚠️ Некорректный формат команды добавления.\n" + "Правильный формат: `доб [Имя] | [Цена] | [Описание]`\n" + "Пример: `доб Тренировка по Valorant 🎯 | 1 500 ₽/час | Разбор стрельбы и aim.`" + ) + return + + name, price, description = parts + + if not name or not price or not description: + self.send_message(user_id, "⚠️ Все три поля (имя, цена, описание) должны быть заполнены!") + return + + try: + new_id = db.add_service(name, price, description) + config.SERVICES = db.get_services() + + self.send_message( + user_id, + f"✅ Новая услуга успешно добавлена с номером **[{new_id}]**!" + ) + self._send_admin_menu(user_id) + except Exception as e: + logger.error(f"Ошибка при добавлении услуги в БД: {e}") + self.send_message(user_id, f"⚠️ Произошла ошибка при обновлении базы данных: {e}") + + def _handle_admin_delete(self, user_id: int, text: str): + """Обрабатывает мягкое удаление услуги администратором.""" + parts = text.split(" ") + if len(parts) < 2: + self.send_message( + user_id, + "⚠️ Укажите номер услуги для удаления.\n" + "Пример: `удал 4`" + ) + return + + service_id = parts[1].strip() + services = db.get_services() + + if service_id not in services: + self.send_message( + user_id, + f"⚠️ Услуга с номером '{service_id}' не найдена в списке активных услуг." + ) + return + + try: + db.delete_service(service_id) + config.SERVICES = db.get_services() + + self.send_message( + user_id, + f"✅ Услуга [{service_id}] успешно удалена из активного списка!" + ) + self._send_admin_menu(user_id) + except Exception as e: + logger.error(f"Ошибка при деактивации услуги в БД: {e}") + self.send_message(user_id, f"⚠️ Произошла ошибка при обновлении базы данных: {e}") + + def run(self): + """Запуск бесконечного цикла прослушивания событий VK LongPoll.""" + if not self.longpoll: + logger.warning("Бот запущен в режиме ЭМУЛЯЦИИ (без валидных токенов VK API).") + print("Симуляция работы бота в консоли. Напишите 'выход' для завершения.") + + # Эмулятор консольного ввода для тестирования на Windows локально + test_user_id = 99999 + # Эмулируем приветствие при старте + self.handle_message(test_user_id, "начать") + + while True: + try: + user_input = input("Вы: ") + if user_input.lower() == "выход": + break + self.handle_message(test_user_id, user_input) + except (KeyboardInterrupt, EOFError): + break + return + + logger.info("Бот успешно запущен в режиме прослушивания LongPoll...") + for event in self.longpoll.listen(): + if event.type == VkBotEventType.MESSAGE_NEW: + message_obj = event.obj.message + user_id = message_obj["from_id"] + text = message_obj["text"] + self.handle_message(user_id, text) + + +if __name__ == "__main__": + bot = VKBotApplication() + bot.run() diff --git a/bot_database.db b/bot_database.db new file mode 100644 index 0000000000000000000000000000000000000000..bbfd32896c0799f2ba32abcbe22e810a4d24e0d2 GIT binary patch literal 385024 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