From e5abc1fe99340b979b18864d978179f71fe8f5c5 Mon Sep 17 00:00:00 2001 From: vlapa Date: Sat, 13 Jun 2026 20:33:26 +0300 Subject: First --- files/TG_bot_VPN-main/handlers/__init__.py | 2 + .../handlers/custom_heandlers/__init__.py | 3 + .../handlers/custom_heandlers/admin_handlers.py | 405 +++++++++++++++++++++ .../custom_heandlers/instruction_handlers.py | 26 ++ .../handlers/custom_heandlers/location_handlers.py | 135 +++++++ .../handlers/default_heandlers/__init__.py | 6 + .../handlers/default_heandlers/echo.py | 21 ++ .../handlers/default_heandlers/help.py | 13 + .../handlers/default_heandlers/start.py | 99 +++++ 9 files changed, 710 insertions(+) create mode 100644 files/TG_bot_VPN-main/handlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/admin_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/instruction_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/location_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/echo.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/help.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/start.py (limited to 'files/TG_bot_VPN-main/handlers') diff --git a/files/TG_bot_VPN-main/handlers/__init__.py b/files/TG_bot_VPN-main/handlers/__init__.py new file mode 100644 index 0000000..3fb88a6 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/__init__.py @@ -0,0 +1,2 @@ +from . import custom_heandlers +from . import default_heandlers diff --git a/files/TG_bot_VPN-main/handlers/custom_heandlers/__init__.py b/files/TG_bot_VPN-main/handlers/custom_heandlers/__init__.py new file mode 100644 index 0000000..f34731f --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/custom_heandlers/__init__.py @@ -0,0 +1,3 @@ +from . import admin_handlers +from. import location_handlers +from. import instruction_handlers diff --git a/files/TG_bot_VPN-main/handlers/custom_heandlers/admin_handlers.py b/files/TG_bot_VPN-main/handlers/custom_heandlers/admin_handlers.py new file mode 100644 index 0000000..51fe601 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/custom_heandlers/admin_handlers.py @@ -0,0 +1,405 @@ +import os + +import peewee +import qrcode +from telebot.types import Message +from config_data.config import ALLOWED_USERS, DEFAULT_COMMANDS, ADMIN_COMMANDS, QR_CODE_DIR +from database.models import User, Server, VPNKey +from keyboards.inline.admin_buttons import ( + users_markup, + admin_markup, + get_vpn_markup, + get_servers_markup, + delete_vpn_markup, + key_actions_markup +) +from loader import bot, app_logger +from states.states import AdminPanel +from utils.functions import valid_ip, convert_amnezia_xray_json_to_vless_str, get_all_commands_bot +from utils.generate_vpn_keys import setup_server, generate_key +from utils.work_vpn_keys import suspend_key, resume_key, revoke_key, cleanup_server + + +@bot.message_handler(commands=["admin_panel"]) +def admin_panel(message: Message): + """ Хендлер для админ панели """ + if message.from_user.id in ALLOWED_USERS: + app_logger.info(f"Администратор {message.from_user.full_name} зашел в админ панель.") + bot.send_message(message.from_user.id, "🔧 Добро пожаловать в админ панель!\n\nВыберите нужную опцию ниже 👇", + reply_markup=admin_markup()) + bot.set_state(message.from_user.id, AdminPanel.get_option) + else: + bot.send_message(message.from_user.id, "❌ У вас недостаточно прав") + app_logger.warning(f"Пользователь {message.from_user.full_name} пытался войти в админ панель") + + +@bot.callback_query_handler(func=None, state=AdminPanel.get_option) +def admin_panel_handler(call): + """ Callback handler для выбора раздела админ панели """ + bot.answer_callback_query(callback_query_id=call.id) + + if call.data == "Exit": + bot.send_message(call.message.chat.id, "🚪 Вы успешно вышли из админ панели. До встречи!") + bot.set_state(call.message.chat.id, None) + app_logger.info(f"Администратор {call.from_user.full_name} вышел из админ панели.") + elif call.data == "users": + app_logger.info(f"Администратор {call.from_user.full_name} зашел в юзер панель.") + bot.send_message(call.message.chat.id, "👥 Список всех пользователей базы данных:", reply_markup=users_markup()) + bot.set_state(call.message.chat.id, AdminPanel.get_users) + elif call.data == "servers": + app_logger.info(f"Администратор {call.from_user.full_name} зашел в панель управления серверами.") + bot.send_message(call.message.chat.id, "🖥 Панель управления серверами:", reply_markup=get_servers_markup()) + bot.set_state(call.message.chat.id, AdminPanel.get_servers) + + +@bot.callback_query_handler(func=None, state=AdminPanel.get_users) +def get_user(call): + """ Хендлер для работы с юзерами из админ панели """ + bot.answer_callback_query(callback_query_id=call.id) + if call.data == "Exit": + + bot.send_message(call.message.chat.id, "Выберите опцию", reply_markup=admin_markup()) + bot.set_state(call.message.chat.id, AdminPanel.get_option) + app_logger.info(f"Администратор {call.from_user.full_name} вернулся обратно к выбору опций.") + else: + user_obj: User = User.get_by_id(call.data) + try: + vpn_key: VPNKey = user_obj.vpn_key + vpn_key = vpn_key.name + except Exception: + vpn_key = "отсутствует" + app_logger.info(f"Администратор {call.from_user.full_name} запросил " + f"информацию о пользователе {user_obj.full_name}") + bot.send_message(call.message.chat.id, + f"👤 Имя: {user_obj.full_name}\n" + f"📱 Телеграм: @{user_obj.username}\n" + f"📢 Подписан на канал: {user_obj.is_subscribed}\n" + f"🔑 VPN ключ: {vpn_key}" + ) + + +@bot.callback_query_handler(func=None, state=AdminPanel.get_servers) +def server_panel_handler(call): + """ Хендлер для управления серверами """ + bot.answer_callback_query(callback_query_id=call.id) + + if call.data == "Add": + bot.send_message(call.message.chat.id, + "📄 Введите данные сервера в следующем формате:\n" + "🏙 Location (например, США)\n" + "👤 Username (например, root)\n" + "🔒 Password (пароль от root)\n" + "🌐 IP address" + ) + bot.set_state(call.message.chat.id, AdminPanel.add_server) + return + + server_id = call.data + server_obj: Server = Server.get_by_id(server_id) + + app_logger.info(f"Администратор {call.from_user.full_name} запросил информацию о сервере {server_obj.location}") + + # Проверка настроен ли сервер + status = "✅ Настроен" if server_obj.public_key else "❌ Требует настройки" + + bot.send_message(call.message.chat.id, + f"🖥 Сервер: {server_obj.location}\n" + f"🔰 Статус: {status}\n" + f"🌐 IP адрес: {server_obj.ip_address}\n" + f"🔑 Количество ключей: {server_obj.keys.count()}", + reply_markup=get_vpn_markup(server_id)) + bot.set_state(call.message.chat.id, AdminPanel.get_vpn_keys) + + +@bot.message_handler(state=AdminPanel.add_server) +def add_server(message: Message): + """ Добавление нового сервера """ + try: + server_data = [item.strip() for item in message.text.split("\n")] + if len(server_data) != 4: # Убрали порт X-UI + raise ValueError("❌ Неверное количество полей!") + elif valid_ip(server_data[3]) is False: + raise ValueError("❌ Неверный формат IP адреса!") + + # Создаем сервер и настраиваем + server = Server.create( + location=server_data[0], + username=server_data[1], + password=server_data[2], + ip_address=server_data[3] + ) + app_logger.info(f"Администратор {message.from_user.full_name} начинает настройку сервера {server.location}") + bot.send_message(message.from_user.id, "Начинаю настройку сервера, подождите...") + # Автоматическая настройка сервера + if setup_server(server): + bot.send_message(message.from_user.id, "✅ Сервер успешно настроен!") + else: + bot.send_message(message.from_user.id, "❌ Ошибка настройки сервера!") + server.delete_instance() + bot.set_state(message.from_user.id, None) + return + + bot.set_state(message.from_user.id, None) + app_logger.info(f"Администратор {message.from_user.full_name} добавил сервер {server.location}") + + except Exception as ex: + bot.send_message(message.from_user.id, f"❌ Некорректные данные!\n{ex}") + app_logger.error(f"Ошибка при добавлении сервера {ex}") + bot.set_state(message.from_user.id, None) + + +@bot.callback_query_handler(func=None, state=AdminPanel.get_vpn_keys) +def vpn_panel_handler(call): + """ Хендлер для управления всеми привязанными к серверу VPN ключами """ + bot.answer_callback_query(callback_query_id=call.id) + + if "Generate" in call.data: + # Генерация нового ключа + server_id = call.data.split()[1] + server = Server.get_by_id(server_id) + app_logger.info(f"Администратор {call.from_user.full_name} запросил генерацию" + f" VPN ключа для сервера {server.location}") + bot.send_message(call.message.chat.id, f"⌛ Пожалуйста, подождите... Идет генерация нового VPN ключа...") + key = generate_key(server) + if key: + bot.send_message(call.message.chat.id, f"✅ Отлично! VPN ключ «{key.name}» успешно создан!") + app_logger.info(f"Администратор {call.from_user.full_name} успешно создал " + f"VPN ключ {key.name} для сервера {server.location}") + else: + bot.send_message(call.message.chat.id, "❌ Ошибка генерации ключа!") + + bot.set_state(call.message.chat.id, AdminPanel.get_vpn_keys) + return + + if "Delete" in call.data: + server_id = call.data.split()[1] + server_obj: Server = Server.get_by_id(server_id) + bot.send_message(call.message.chat.id, f"🗑 Начинается полная очистка сервера. Пожалуйста, подождите...") + if cleanup_server(server_obj): + app_logger.info(f"Администратор {call.from_user.full_name} удалил сервер {server_obj.location}") + bot.send_message(call.message.chat.id, f"✅ Сервер {server_obj.location} успешно удалён вместе " + f"с привязанными VPN ключами!") + bot.set_state(call.message.chat.id, AdminPanel.get_servers) + else: + bot.send_message(call.message.chat.id, "❌ Ошибка удаления сервера!") + bot.set_state(call.message.chat.id, AdminPanel.get_servers) + return + + if "VPN - " in call.data: + # Выдача всей информации по VPN ключу + vpn_obj: VPNKey = VPNKey.get_by_id(call.data.split("VPN - ")[1]) + app_logger.info(f"Администратор {call.from_user.full_name} запросил информацию о VPN ключе {vpn_obj.name}") + status = "✅ Активен" if vpn_obj.is_valid else "⏸ Приостановлен / Занят" + users = ", ".join([user.full_name for user in vpn_obj.users]) if vpn_obj.users else "Нет пользователей" + text = ( + f"🔑 Ключ: {vpn_obj.name}\n" + f"📍 Сервер: {vpn_obj.server.location}\n" + f"📡 Статус: {status}\n" + f"👤 Пользователи: {users}\n" + f"🕒 Создан: {vpn_obj.created_at.strftime('%d.%m.%Y %H:%M')}" + ) + + # Отправляем QR-код если есть + if os.path.exists(vpn_obj.qr_code): + with open(vpn_obj.qr_code, 'rb') as qr_file: + bot.send_photo(call.message.chat.id, qr_file, caption=text, + reply_markup=key_actions_markup(vpn_obj.id)) + else: + bot.send_message(call.message.chat.id, text, + reply_markup=key_actions_markup(vpn_obj.id)) + bot.set_state(call.message.chat.id, AdminPanel.delete_vpn) + elif "Cancel" in call.data: + # Возврат в меню серверов + bot.send_message(call.message.chat.id, "Вы вернулись в меню серверов.", + reply_markup=get_servers_markup()) + app_logger.info(f"Администратор {call.from_user.full_name} вернулся в меню серверов") + bot.set_state(call.message.chat.id, AdminPanel.get_servers) + else: + bot.set_state(call.message.chat.id, AdminPanel.get_vpn_keys) + +@bot.callback_query_handler(func=None, state=AdminPanel.delete_vpn) +def vpn_panel_handler(call): + """ Хендлер для удаления VPN ключа """ + bot.answer_callback_query(callback_query_id=call.id) + + if "action_" in call.data: + action, key_id = call.data.split("_")[1], call.data.split("_")[2] + vpn_key = VPNKey.get_by_id(key_id) + + if action == "suspend": + app_logger.info(f"Администратор {call.message.from_user.full_name} запросил остановку " + f"VPN ключа {vpn_key.name}") + if suspend_key(vpn_key): + bot.send_message(call.message.chat.id, f"⏸ Ключ {vpn_key.name} приостановлен") + else: + bot.send_message(call.message.chat.id, "❌ Ошибка приостановки ключа!") + + elif action == "resume": + app_logger.info(f"Администратор {call.message.from_user.full_name} запросил возобновление работы " + f"VPN ключа {vpn_key.name}") + if resume_key(vpn_key): + bot.send_message(call.message.chat.id, f"▶️ Ключ {vpn_key.name} возобновлен") + else: + bot.send_message(call.message.chat.id, "❌ Ошибка возобновления ключа!") + + elif action == "revoke": + app_logger.info(f"Администратор {call.message.from_user.full_name} запросил отзыв " + f"VPN ключа {vpn_key.name}") + if revoke_key(vpn_key): + for user in User.select().where(User.vpn_key == vpn_key): + user.vpn_key = None + user.save() + bot.send_message(call.message.chat.id, f"🗑 Ключ {vpn_key.name} отозван") + else: + bot.send_message(call.message.chat.id, "❌ Ошибка отзыва ключа!") + + bot.set_state(call.message.chat.id, AdminPanel.get_servers) + return + + if "Cancel" in call.data: + bot.send_message(call.message.chat.id, "Вы вернулись в админку.", + reply_markup=admin_markup()) + app_logger.info(f"Администратор {call.from_user.full_name} вернулся в админку") + bot.set_state(call.message.chat.id, AdminPanel.get_option) + return + else: + # Скорее всего, пользователь выбрал другой VPN ключ + bot.set_state(call.message.chat.id, AdminPanel.get_vpn_keys) + vpn_panel_handler(call) + + +@bot.message_handler(commands=["message_sending"]) +def message_sending_handler(message: Message): + """ Хендлер рассылки сообщений юзерам """ + if message.from_user.id in ALLOWED_USERS: + app_logger.info(f"Администратор {message.from_user.full_name} вызвал команду /message_sending.") + bot.send_message(message.from_user.id, "✉️ Введите текст сообщения для рассылки пользователям:") + bot.set_state(message.from_user.id, AdminPanel.send_message) + else: + bot.send_message(message.from_user.id, "❌ У вас недостаточно прав для выполнения этой команды!") + app_logger.warning(f"Попытка доступа к /message_sending без прав администратора {message.from_user.full_name}") + + +@bot.message_handler(state=AdminPanel.send_message) +def send_message_to_users_handler(message: Message): + """ Отправка сообщений пользователям """ + # Проверка, что сообщение для рассылки - не одна из команд бота + + if message.text in get_all_commands_bot(): + bot.send_message(message.from_user.id, "Это одна из команд бота") + bot.set_state(message.from_user.id, None) + return + + # Проверка, что сообщение не пустое + if not message.text: + bot.send_message(message.from_user.id, "Сообщение не может быть пустым.") + return + app_logger.info(f"Администратор {message.from_user.full_name} начал рассылку сообщений: {message.text}") + + bot.send_message(message.chat.id, "Начинаю рассылку...") + bot.send_chat_action(message.chat.id, "typing") + for user_obj in User.select(): + if int(user_obj.user_id) not in ALLOWED_USERS: + bot.send_message(user_obj.user_id, message.text) + bot.send_message(message.chat.id, "✅ Рассылка сообщений завершена!") + bot.set_state(message.from_user.id, None) + + +@bot.message_handler(commands=["add_vpn_key"]) +def add_vpn_key_handler(message: Message): + """ Хендлер для ручного добавления VPN ключа """ + if message.from_user.id in ALLOWED_USERS: + app_logger.info(f"Администратор {message.from_user.full_name} вызвал команду /add_vpn_key.") + bot.send_message(message.from_user.id, "🔑 Введите название нового VPN ключа:") + bot.set_state(message.from_user.id, AdminPanel.add_vpn_key_name) + else: + bot.send_message(message.from_user.id, "У вас недостаточно прав!") + + +@bot.message_handler(state=AdminPanel.add_vpn_key_name) +def add_vpn_key_name_handler(message: Message): + """ Обработка ввода названия VPN ключа """ + app_logger.info(f"Администратор {message.from_user.full_name} ввел название VPN ключа: {message.text}") + with bot.retrieve_data(message.from_user.id, message.chat.id) as data: + data["vpn_key_name"] = message.text + bot.send_message(message.from_user.id, "🔑 Введите VPN KEY в формате JSON или " + "VLESS-ссылку (например, vless://...) :") + bot.set_state(message.from_user.id, AdminPanel.add_vpn_key_key) + + +@bot.message_handler(state=AdminPanel.add_vpn_key_key) +def add_vpn_key_key_handler(message: Message): + """ Обработка ввода VPN ключа """ + + if message.text in get_all_commands_bot(): + bot.send_message(message.from_user.id, "Это одна из команд бота") + bot.set_state(message.from_user.id, None) + return + + app_logger.info(f"Администратор {message.from_user.full_name} ввел VPN ключ") + if "vless://" not in message.text: + vless_str = convert_amnezia_xray_json_to_vless_str(message.text) + if vless_str is None: + bot.send_message(message.from_user.id, "Не удалось преобразовать JSON в VLESS строку. " + "Проверьте правильность ввода.") + app_logger.warning("Не удалось преобразовать json конфиг в vless строку!") + return + else: + vless_str = message.text + with bot.retrieve_data(message.from_user.id, message.chat.id) as data: + data["vpn_key_key"] = vless_str + bot.send_message(message.from_user.id, "Выберите сервер, к которому принадлежит ключ", + reply_markup=get_servers_markup()) + bot.set_state(message.from_user.id, AdminPanel.save_vpn_key) + + +@bot.callback_query_handler(func=None, state=AdminPanel.save_vpn_key) +def save_vpn_handler(call): + """ Хендлер для сохранения в БД VPN ключа """ + bot.answer_callback_query(callback_query_id=call.id) + + if call.data == "Add": + bot.send_message(call.message.chat.id, + "📄 Введите данные сервера в следующем формате:\n" + "🏙 Location (например, США)\n" + "👤 Username (например, root)\n" + "🔒 Password (пароль от root)\n" + "🌐 IP address" + ) + bot.set_state(call.message.chat.id, AdminPanel.add_server) + return + + server_id = call.data + server_obj: Server = Server.get_by_id(server_id) + with bot.retrieve_data(call.from_user.id, call.from_user.id) as data: + try: + key_number = len(server_obj.keys) + 1 if hasattr(server_obj, "keys") else 1 + qr_code_filename = f"vpn_key_{server_obj.id}_{key_number}.png" + qr_code_path = os.path.join(QR_CODE_DIR, qr_code_filename) + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_H, + box_size=10, + border=4 + ) + qr.add_data(data["vpn_key_key"]) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white") + img.save(qr_code_path) + app_logger.info(f"QR-код сгенерирован и сохранён по пути: {qr_code_path}") + vpn_key = VPNKey.create( + name=data["vpn_key_name"], + key=data["vpn_key_key"], + qr_code=qr_code_path, + server=server_obj, + ) + except peewee.IntegrityError: + bot.send_message(call.message.chat.id, "Такой ключ уже существует!") + bot.send_message(call.message.chat.id, "Вы вышли в главное меню") + app_logger.warning(f"Попытка создания дубликата VPN ключа {data['vpn_key_name']}!") + + else: + app_logger.info(f"Администратор {call.message.from_user.full_name} добавил VPN ключ {vpn_key.name} " + f"к серверу {server_obj.location}") + bot.send_message(call.message.chat.id, f"VPN ключ {vpn_key.name} добавлен к серверу {server_obj.location}.") + bot.set_state(call.message.chat.id, None) diff --git a/files/TG_bot_VPN-main/handlers/custom_heandlers/instruction_handlers.py b/files/TG_bot_VPN-main/handlers/custom_heandlers/instruction_handlers.py new file mode 100644 index 0000000..4702212 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/custom_heandlers/instruction_handlers.py @@ -0,0 +1,26 @@ +from telebot.types import Message + +from config_data.config import CHANNEL_ID +from database.models import User +from loader import bot, app_logger +from keyboards.inline.app_buttons import get_apps_murkup + + +@bot.message_handler(commands=["instruction"]) +def instruction_handler(message: Message): + """ Хендлер для выбора сервера подключения """ + app_logger.info(f"Пользователь {message.from_user.full_name} вызвал команду /location") + cur_user = User.get(User.user_id == message.from_user.id) + + if cur_user.is_subscribed: + instruction_text = ( + "📖 Подробная инструкция по подключению доступна по [ссылке]" + "(https://telegra.ph/Kak-ispolzovat-VPN-servis-Guard-Tunnel-01-16).\n\n" + "💡 Для использования VPN, пожалуйста, скачайте приложение **Hiddify**.\n\n" + "Желаем вам безопасного и комфортного подключения! 🚀" + ) + bot.send_message(message.chat.id, instruction_text, parse_mode="Markdown", reply_markup=get_apps_murkup()) + else: + bot.send_message(message.chat.id, f"🚫 Вы не подписаны на [наш канал](https://t.me/{CHANNEL_ID[1:]})!\n" + f"Подпишитесь, чтобы получить доступ ко всему функционалу.", + parse_mode="Markdown") diff --git a/files/TG_bot_VPN-main/handlers/custom_heandlers/location_handlers.py b/files/TG_bot_VPN-main/handlers/custom_heandlers/location_handlers.py new file mode 100644 index 0000000..45490e4 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/custom_heandlers/location_handlers.py @@ -0,0 +1,135 @@ +from telebot.types import Message + +from config_data.config import CHANNEL_ID +from database.models import User, Server, VPNKey +from loader import bot, app_logger, scheduler +from keyboards.inline.servers import get_locations_markup, get_instruction_markup +from states.states import GetVPNKey +from utils.functions import is_subscribed +from utils.generate_vpn_keys import generate_key +from utils.tasks import cancel_key_revocation +from utils.work_vpn_keys import revoke_key + + +@bot.message_handler(commands=["location"]) +def location_handler(message: Message): + """ Хендлер для выбора сервера подключения """ + app_logger.info(f"Пользователь {message.from_user.full_name} вызвал команду /location") + cur_user = User.get(User.user_id == message.from_user.id) + + if is_subscribed(CHANNEL_ID, message.from_user.id): + cur_user.is_subscribed = True + bot.send_message(message.chat.id, "🌍 Пожалуйста, выберите сервер для подключения:", reply_markup=get_locations_markup()) + bot.set_state(message.chat.id, GetVPNKey.get_server) + else: + bot.send_message(message.chat.id, f"🚫 Вы не подписаны на [наш канал](https://t.me/{CHANNEL_ID[1:]})!\n" + f"Подпишитесь, чтобы получить доступ ко всему функционалу.", + parse_mode="Markdown") + cur_user.is_subscribed = False + cur_user.save() + + +@bot.callback_query_handler(func=None, state=GetVPNKey.get_server) +def get_server_handler(call): + """ Хендлер для получения сервера подключения """ + bot.answer_callback_query(callback_query_id=call.id) + + server_id = call.data + + cur_user: User = User.get(User.user_id == call.from_user.id) + cur_server: Server = Server.get_by_id(server_id) + + # Проверка на то, что текущий VPN ключ пользователя удален. + try: + VPNKey.get_by_id(cur_user.vpn_key) + except Exception: + app_logger.warning(f"Внимание! У пользователя {cur_user.full_name} не нашлось ключа VPN!") + cur_user.vpn_key = None + cur_user.save() + + app_logger.info(f"Пользователь {cur_user.full_name} выбрал сервер {cur_server.location}") + + # Получение всех vpn ключей данного сервера и выбор одного + for vpn_key_obj in cur_server.keys: + if vpn_key_obj.is_valid: + if cur_user.vpn_key is not None: + users_vpn = VPNKey.get_by_id(cur_user.vpn_key) + users_vpn.is_valid = True + users_vpn.save() + app_logger.info(f"VPN ключ {users_vpn.name} теперь свободен.") + cur_user.vpn_key = vpn_key_obj + cur_user.save() + vpn_key_obj.is_valid = False + vpn_key_obj.save() + app_logger.info(f"Пользователь {cur_user.full_name} зарезервировал ключ {vpn_key_obj.name}") + with open(vpn_key_obj.qr_code, "rb") as qr_code: + bot.send_photo( + call.message.chat.id, + qr_code, + caption=( + "🔒 Мы не храним информацию о ваших подключениях!\n\n" + f"🔑 Имя ключа: *{vpn_key_obj.name}*\n" + f"🌍 Сервер: *{cur_server.location}*\n" + f"🔗 URL для подключения:\n\n`{vpn_key_obj.key}`" + ), + parse_mode="Markdown", + reply_markup=get_instruction_markup() + ) + bot.set_state(call.message.chat.id, None) + return + + # Если нет свободных ключей, генерируем новый + app_logger.warning(f"Внимание! Для сервера {cur_server.location} не нашлось свободных VPN ключей! " + f"Генерирую новый...") + bot.send_message(call.message.chat.id, "⌛ Пожалуйста, подождите... Идет генерация нового VPN ключа...") + + new_key: VPNKey = generate_key(cur_server) + if new_key is None: + app_logger.error("Внимание! Не удалось сгенерировать новый ключ!") + bot.send_message(call.message.chat.id, "❌ Ведутся технические работы на стороне сервера, поэтому " + "генерация нового ключа пока невозможна!\n" + "Мы приносим свои извинения, попробуйте позже 😊") + bot.set_state(call.message.chat.id, None) + return + app_logger.info(f"Сгенерирован новый ключ {new_key.name}!") + + if cur_user.vpn_key is not None: + users_vpn = VPNKey.get_by_id(cur_user.vpn_key) + revoke_key(users_vpn) + + cur_user.vpn_key = new_key + cur_user.save() + + new_key.is_valid = False + new_key.save() + app_logger.info(f"Пользователь {cur_user.full_name} зарезервировал новый ключ {new_key.name}") + with open(new_key.qr_code, "rb") as qr_code: + bot.send_photo( + call.message.chat.id, + qr_code, + caption=( + "🔒 Мы не храним информацию о ваших подключениях!\n\n" + f"🔑 Имя ключа: *{new_key.name}*\n" + f"🌍 Сервер: *{cur_server.location}*\n" + f"🔗 URL для подключения:\n\n`{new_key.key}`" + ), + parse_mode="Markdown", + reply_markup=get_instruction_markup() + ) + bot.set_state(call.message.chat.id, None) + + +@bot.callback_query_handler(func=lambda call: call.data.startswith("renew_")) +def renew_key_handler(call): + bot.answer_callback_query(callback_query_id=call.id) + try: + vpn_key_id = int(call.data.split("_")[1]) + vpn_key = VPNKey.get_by_id(vpn_key_id) + # Отменяем запланированную задачу отзыва + cancel_key_revocation(vpn_key, scheduler) + bot.send_message(call.message.chat.id, + f"✅ Ваш VPN ключ «{vpn_key.name}» успешно продлён!") + app_logger.info(f"VPN ключ {vpn_key.name} продлён пользователем {call.from_user.full_name}.") + except Exception as e: + app_logger.error(f"Ошибка в обработчике продления ключа: {e}") + bot.send_message(call.message.chat.id, "❌ Произошла ошибка при продлении ключа.") diff --git a/files/TG_bot_VPN-main/handlers/default_heandlers/__init__.py b/files/TG_bot_VPN-main/handlers/default_heandlers/__init__.py new file mode 100644 index 0000000..4e4b225 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/default_heandlers/__init__.py @@ -0,0 +1,6 @@ +from . import start +from . import help +from . import echo + + +__all__ = ['start', 'help', 'echo'] diff --git a/files/TG_bot_VPN-main/handlers/default_heandlers/echo.py b/files/TG_bot_VPN-main/handlers/default_heandlers/echo.py new file mode 100644 index 0000000..f8f5904 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/default_heandlers/echo.py @@ -0,0 +1,21 @@ +from telebot.types import Message +from loader import bot +from handlers.custom_heandlers.location_handlers import location_handler +from handlers.default_heandlers.help import bot_help +from handlers.custom_heandlers.instruction_handlers import instruction_handler + + +# Эхо хендлер, куда летят текстовые сообщения без указанного состояния + +@bot.message_handler(state=None) +def bot_echo(message: Message): + if message.text == "🌍 Серверы": + location_handler(message) + elif message.text == "❓ Справка": + bot_help(message) + elif message.text == "📖 Инструкция": + instruction_handler(message) + else: + bot.reply_to(message, f"Введите любую команду из меню, чтобы я начал работать\n" + f"Либо выберите одну из кнопок, которые я вам прислал 👇") + diff --git a/files/TG_bot_VPN-main/handlers/default_heandlers/help.py b/files/TG_bot_VPN-main/handlers/default_heandlers/help.py new file mode 100644 index 0000000..fc77dd4 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/default_heandlers/help.py @@ -0,0 +1,13 @@ +from telebot.types import Message + +from config_data.config import DEFAULT_COMMANDS, ADMIN_COMMANDS, ALLOWED_USERS +from loader import bot + + +@bot.message_handler(commands=['help']) +def bot_help(message: Message): + commands = [f"/{command} - {description}" for command, description in DEFAULT_COMMANDS] + if message.from_user.id in ALLOWED_USERS: + commands.extend([f"/{command} - {description}" for command, description in ADMIN_COMMANDS]) + bot.reply_to(message, "📋 Доступные команды:\n" + "\n".join(commands)) + bot.send_message(message.chat.id, "🤝 Для поддержки обращайтесь: @guardtunnel_support") diff --git a/files/TG_bot_VPN-main/handlers/default_heandlers/start.py b/files/TG_bot_VPN-main/handlers/default_heandlers/start.py new file mode 100644 index 0000000..32beca8 --- /dev/null +++ b/files/TG_bot_VPN-main/handlers/default_heandlers/start.py @@ -0,0 +1,99 @@ +from telebot.types import Message +from loader import bot, app_logger +from config_data.config import DEFAULT_COMMANDS, ADMIN_COMMANDS, ALLOWED_USERS, CHANNEL_ID +from database.models import User, Group +from utils.functions import is_subscribed +from keyboards.inline.subscribed import is_subscribed_markup +from keyboards.reply.handlers_reply import handlers_reply +from states.states import SubscribedState + + +start_text = """*Вы не подписались на канал!* + +☺️ Для доступа к полному функционалу бота вам необходимо подписаться на наш новостной канал [Guard Tunnel VPN](https://t.me/{channel_id}) + +🎁 В подарок вы получите *бесплатный доступ* на использование нашего сервиса + +*С Уважением, команда Guard Tunnel VPN* + +_После подписки на наш канал нажмите ниже на кнопку: ✅ Я подписался_""" + + +@bot.message_handler(commands=['start']) +def bot_start(message: Message): + if message.chat.type == "private": + is_sub = True if message.from_user.id in ALLOWED_USERS else False + if User.get_or_none(user_id=message.from_user.id) is None: + app_logger.info(f"Внимание! Новый юзер: {message.from_user.full_name} - {message.from_user.username}") + User.create(user_id=message.from_user.id, + full_name=message.from_user.full_name, + username=message.from_user.username if message.from_user.username is not None else "None", + is_premium=message.from_user.is_premium, + is_subscribed=is_sub) + commands = [f"/{command} - {description}" for command, description in DEFAULT_COMMANDS] + if message.from_user.id in ALLOWED_USERS: + commands.extend([f"/{command} - {description}" for command, description in ADMIN_COMMANDS]) + bot.send_message( + message.from_user.id, + f"Здравствуйте, {message.from_user.full_name}! 👋\n" + f"Вы вошли как администратор. Доступны следующие команды:\n{'\n'.join(commands)}", + reply_markup=handlers_reply() + ) + else: + if is_subscribed(CHANNEL_ID, message.from_user.id): + # Если пользователь подписан на канал, тогда ему можно пользоваться ботом. + bot.send_message(message.from_user.id, f"Приветствуем, {message.from_user.full_name}.\n" + f"Рады приветствовать вас на нашем сервисе!\n" + f"Что бы использовать наш VPN сервис, " + f"следуйте инструкциям ниже 👇\n" + f"{'\n'.join(commands)}", + reply_markup=handlers_reply()) + cur_user = User.get(User.user_id == message.from_user.id) + cur_user.is_subscribed = True + cur_user.save() + else: + bot.send_message(message.from_user.id, start_text.format(channel_id=CHANNEL_ID[1:]), + reply_markup=is_subscribed_markup(), parse_mode='Markdown', + disable_web_page_preview=True) + bot.set_state(message.from_user.id, SubscribedState.subscribe) + + else: + bot.send_message(message.chat.id, "Здравствуйте! Я - телеграм бот, модератор каналов и групп. " + "Чтобы получить больше информации, " + "обратитесь к администратору, или напишите мне в личку)") + if Group.get_or_none(group_id=message.chat.id) is None: + Group.create(group_id=message.chat.id, + title=message.chat.title, + description=message.chat.description, + bio=message.chat.bio, + invite_link=message.chat.invite_link, + location=message.chat.location, + username=message.chat.username) + app_logger.info(f"Внимание! Новая группа: {message.chat.title} - {message.chat.invite_link}") + if User.get_or_none(user_id=message.from_user.id) is None: + User.create(user_id=message.from_user.id, + full_name=message.from_user.full_name, + username=message.from_user.username, + is_premium=message.from_user.is_premium) + app_logger.info(f"Внимание! Новый юзер: {message.from_user.full_name} - {message.from_user.username}") + +@bot.callback_query_handler(func=None, state=SubscribedState.subscribe) +def is_subscribed_handler(call): + if is_subscribed(CHANNEL_ID, call.from_user.id): + commands = [f"/{command} - {description}" for command, description in DEFAULT_COMMANDS] + app_logger.info(f"Пользователь {call.from_user.full_name} подписался на канал!") + + cur_user = User.get(User.user_id == call.from_user.id) + cur_user.is_subscribed = True + cur_user.save() + + bot.answer_callback_query(callback_query_id=call.id) + bot.send_message(call.message.chat.id, "Спасибо, что выбрали наш сервис, приятного использования!\n" + f"Вам доступны следующие команды:\n" + f"{'\n'.join(commands)}") + bot.set_state(call.message.chat.id, None) + else: + bot.answer_callback_query(callback_query_id=call.id) + bot.send_message(call.message.chat.id, start_text.format(channel_id=CHANNEL_ID[1:]), + reply_markup=is_subscribed_markup(), parse_mode='Markdown', + disable_web_page_preview=True) -- cgit v1.2.3