From eac63031e9ff34aeb3cb86a648faab2c8324bf9a Mon Sep 17 00:00:00 2001 From: stevefoxru <53931856+stevefoxru@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:11:40 +0300 Subject: [PATCH] Update bot_manager.py --- awg/bot_manager.py | 791 ++++++++++++++------------------------------- 1 file changed, 246 insertions(+), 545 deletions(-) diff --git a/awg/bot_manager.py b/awg/bot_manager.py index 48354b8..d9f1757 100644 --- a/awg/bot_manager.py +++ b/awg/bot_manager.py @@ -6,13 +6,7 @@ import aiofiles import os import re import json -import subprocess import sys -import pytz -import zipfile -import ipaddress -import humanize -import shutil import uuid from aiogram import Bot, types from aiogram.dispatcher import Dispatcher @@ -21,7 +15,6 @@ from aiogram.utils import executor from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton from datetime import datetime, timedelta from apscheduler.schedulers.asyncio import AsyncIOScheduler -from apscheduler.triggers.interval import IntervalTrigger from yoomoney import Client, Quickpay logging.basicConfig(level=logging.INFO) @@ -66,7 +59,7 @@ class AdminMessageDeletionMiddleware(BaseMiddleware): asyncio.create_task(delete_message_after_delay(message.chat.id, message.message_id)) dp = Dispatcher(bot) -scheduler = AsyncIOScheduler(timezone=pytz.UTC) +scheduler = AsyncIOScheduler(timezone=pytz.utc) scheduler.start() dp.middleware.setup(AdminMessageDeletionMiddleware()) @@ -80,13 +73,12 @@ def get_main_menu_markup(user_id): ) markup.add( InlineKeyboardButton("πŸ”‘ ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠΎΠ½Ρ„ΠΈΠ³", callback_data="get_config"), - InlineKeyboardButton("ℹ️ Π˜Π½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡ", callback_data="instructions") + InlineKeyboardButton("🎟️ Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°ΠΌΠΈ", callback_data="manage_promocodes") ) markup.add( - InlineKeyboardButton("🎟️ Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π°ΠΌΠΈ", callback_data="manage_promocodes"), - InlineKeyboardButton("βš™οΈ Настройки", callback_data="settings") + InlineKeyboardButton("βš™οΈ Настройки", callback_data="settings"), + InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") ) - markup.add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) elif user_id in moderators: markup.add( InlineKeyboardButton("βž• Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ", callback_data="add_user"), @@ -94,35 +86,43 @@ def get_main_menu_markup(user_id): ) markup.add( InlineKeyboardButton("πŸ”‘ ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠΎΠ½Ρ„ΠΈΠ³", callback_data="get_config"), - InlineKeyboardButton("ℹ️ Π˜Π½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡ", callback_data="instructions") + InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") ) else: markup.add( InlineKeyboardButton("πŸ’³ ΠšΡƒΠΏΠΈΡ‚ΡŒ ΠΊΠ»ΡŽΡ‡", callback_data="buy_key"), - InlineKeyboardButton("🎟️ Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄", callback_data="use_promocode") + InlineKeyboardButton("🎟️ ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠ»ΡŽΡ‡ ΠΏΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρƒ", callback_data="use_promocode") ) return markup +# МСню ΠΏΠΎΠΊΡƒΠΏΠΊΠΈ ΠΊΠ»ΡŽΡ‡Π° +def get_buy_key_menu(user_id): + markup = InlineKeyboardMarkup(row_width=2) + markup.add( + InlineKeyboardButton("πŸ“… Π’Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄", callback_data="select_subscription_period"), + InlineKeyboardButton("🎟️ ВвСсти ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄", callback_data="enter_promocode_for_buy") + ) + markup.add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) + discount = user_main_messages.get(user_id, {}).get('promocode_discount', 0) + if discount > 0: + markup.add(InlineKeyboardButton(f"Π‘Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ скидку ({discount}%)", callback_data="reset_promocode")) + return markup + # МСню настроСк def get_settings_menu(): markup = InlineKeyboardMarkup(row_width=2) markup.add( - InlineKeyboardButton("πŸ”„ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ обновлСния", callback_data="check_updates"), - InlineKeyboardButton("πŸ”„ ΠŸΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ VPN", callback_data="restart_vpn") + InlineKeyboardButton("πŸ’Ύ Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ бэкап", callback_data="create_backup"), + InlineKeyboardButton("πŸ‘₯ Бписок Π°Π΄ΠΌΠΈΠ½ΠΎΠ²", callback_data="list_admins") ) markup.add( - InlineKeyboardButton("πŸ—‘οΈ ΠžΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ старыС ΠΊΠ»ΡŽΡ‡ΠΈ", callback_data="clear_old_keys"), - InlineKeyboardButton("πŸ’Ύ Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ бэкап", callback_data="create_backup") + InlineKeyboardButton("πŸ‘€ Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π°Π΄ΠΌΠΈΠ½Π°", callback_data="add_admin"), + InlineKeyboardButton("πŸ’Έ Настройки YooMoney", callback_data="yoomoney_settings") ) markup.add( - InlineKeyboardButton("πŸ‘₯ Бписок Π°Π΄ΠΌΠΈΠ½ΠΎΠ²", callback_data="list_admins"), - InlineKeyboardButton("πŸ‘€ Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π°Π΄ΠΌΠΈΠ½Π°", callback_data="add_admin") + InlineKeyboardButton("πŸ’° Настройки Ρ†Π΅Π½", callback_data="pricing_settings"), + InlineKeyboardButton("⬅️ Назад", callback_data="home") ) - markup.add( - InlineKeyboardButton("πŸ’Έ Настройки YooMoney", callback_data="yoomoney_settings"), - InlineKeyboardButton("πŸ’° Настройки Ρ†Π΅Π½", callback_data="pricing_settings") - ) - markup.add(InlineKeyboardButton("⬅️ Назад", callback_data="home")) return markup # МСню настроСк YooMoney @@ -159,65 +159,15 @@ def get_renewal_period_keyboard(username): ("1 мСсяц", "1_month"), ("3 мСсяца", "3_months"), ("6 мСсяцСв", "6_months"), - ("12 мСсяцСв", "12_months") + ("12 мСсяцСв", "12_months"), + ("ΠšΠ°ΡΡ‚ΠΎΠΌΠ½Π°Ρ Π΄Π°Ρ‚Π°", "custom_date") ] for period_name, period_key in periods: markup.add(InlineKeyboardButton(period_name, callback_data=f"renew_period_{username}_{period_key}")) markup.add(InlineKeyboardButton("ΠžΡ‚ΠΌΠ΅Π½Π°", callback_data="home")) return markup -# ΠšΠ»Π°Π²ΠΈΠ°Ρ‚ΡƒΡ€Π° для Π²Ρ‹Π±ΠΎΡ€Π° Π΄Π°Ρ‚Ρ‹ очистки ΠΊΠ»ΡŽΡ‡Π΅ΠΉ -def get_clear_keys_date_keyboard(): - markup = InlineKeyboardMarkup(row_width=2) - dates = [ - ("1 мСсяц Π½Π°Π·Π°Π΄", (datetime.now(pytz.UTC) - timedelta(days=30)).isoformat()), - ("3 мСсяца Π½Π°Π·Π°Π΄", (datetime.now(pytz.UTC) - timedelta(days=90)).isoformat()), - ("6 мСсяцСв Π½Π°Π·Π°Π΄", (datetime.now(pytz.UTC) - timedelta(days=180)).isoformat()), - ("1 Π³ΠΎΠ΄ Π½Π°Π·Π°Π΄", (datetime.now(pytz.UTC) - timedelta(days=365)).isoformat()) - ] - for date_name, date_iso in dates: - markup.add(InlineKeyboardButton(date_name, callback_data=f"clear_keys_date_{date_iso}")) - markup.add(InlineKeyboardButton("ΠžΡ‚ΠΌΠ΅Π½Π°", callback_data="home")) - return markup - user_main_messages = {} -isp_cache = {} -ISP_CACHE_FILE = 'files/isp_cache.json' -CACHE_TTL = 24 * 3600 - -def get_interface_name(): - return os.path.basename(WG_CONFIG_FILE).split('.')[0] - -async def load_isp_cache(): - global isp_cache - if os.path.exists(ISP_CACHE_FILE): - async with aiofiles.open(ISP_CACHE_FILE, 'r') as f: - isp_cache = json.loads(await f.read()) - -async def save_isp_cache(): - async with aiofiles.open(ISP_CACHE_FILE, 'w') as f: - await f.write(json.dumps(isp_cache)) - -async def get_isp_info(ip: str) -> str: - now = datetime.now(pytz.UTC).timestamp() - if ip in isp_cache and (now - isp_cache[ip]['timestamp']) < CACHE_TTL: - return isp_cache[ip]['isp'] - - try: - if ipaddress.ip_address(ip).is_private: - return "Private Range" - except: - return "Invalid IP" - - async with aiohttp.ClientSession() as session: - async with session.get(f"http://ip-api.com/json/{ip}?fields=isp") as resp: - if resp.status == 200: - data = await resp.json() - isp = data.get('isp', 'Unknown ISP') - isp_cache[ip] = {'isp': isp, 'timestamp': now} - await save_isp_cache() - return isp - return "Unknown ISP" async def delete_message_after_delay(chat_id: int, message_id: int, delay: int = 2): await asyncio.sleep(delay) @@ -226,40 +176,6 @@ async def delete_message_after_delay(chat_id: int, message_id: int, delay: int = except: pass -def parse_relative_time(relative_str: str) -> datetime: - if not isinstance(relative_str, str) or not relative_str.strip(): - logger.error(f"НСкоррСктный relative_str: {relative_str}") - return datetime.now(pytz.UTC) - try: - relative_str = relative_str.lower().replace(' ago', '') - delta = 0 - for part in relative_str.split(', '): - num, unit = part.split() - num = int(num) - if 'minute' in unit: - delta += num * 60 - elif 'hour' in unit: - delta += num * 3600 - elif 'day' in unit: - delta += num * 86400 - elif 'week' in unit: - delta += num * 604800 - elif 'month' in unit: - delta += num * 2592000 - return datetime.now(pytz.UTC) - timedelta(seconds=delta) - except Exception as e: - logger.error(f"Ошибка Π² parse_relative_time: {str(e)}") - return datetime.now(pytz.UTC) - -def parse_transfer(transfer_str: str) -> tuple: - try: - incoming, outgoing = transfer_str.split('/') - incoming_bytes = humanize.parse_bytes(incoming.strip()) - outgoing_bytes = humanize.parse_bytes(outgoing.strip()) - return incoming_bytes, outgoing_bytes - except: - return 0, 0 - async def generate_vpn_key(conf_path: str) -> str: process = await asyncio.create_subprocess_exec( 'python3.11', '/root/amnezia-bot/awg/awg-decode.py', '--encode', conf_path, @@ -276,8 +192,8 @@ async def issue_vpn_key(user_id: int, period: str) -> bool: username = f"user_{user_id}_{uuid.uuid4().hex[:8]}" success = db.root_add(username, ipv6=False) if success: - months = {'1_month': 1, '3_months': 3, '6_months': 6, '12_months': 12}[period] - expiration = datetime.now(pytz.UTC) + timedelta(days=30 * months) + months = {'1_month': 1, '3_months': 3, '6_months': 6, '12_months': 12}.get(period, 1) + expiration = datetime.now(pytz.utc) + timedelta(days=30 * months) db.set_user_expiration(username, expiration, "НСограничСнно") db.set_user_telegram_id(username, user_id) conf_path = os.path.join('users', username, f'{username}.conf') @@ -325,10 +241,10 @@ async def add_admin_command(message: types.Message): @dp.message_handler() async def handle_messages(message: types.Message): - global PRICING # ОбъявляСм PRICING глобальной Π² Π½Π°Ρ‡Π°Π»Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ + global PRICING user_id = message.from_user.id user_state = user_main_messages.get(user_id, {}).get('state') - + if user_state == 'waiting_for_user_name': user_name = message.text.strip() if not re.match(r'^[a-zA-Z0-9_-]+$', user_name): @@ -369,7 +285,6 @@ async def handle_messages(message: types.Message): promocode = message.text.strip() promocode_data = db.apply_promocode(promocode) if promocode_data: - discount = promocode_data.get('discount', 0) subscription_period = promocode_data.get('subscription_period') if subscription_period: success = await issue_vpn_key(user_id, subscription_period) @@ -378,8 +293,7 @@ async def handle_messages(message: types.Message): else: await message.reply("Ошибка ΠΏΡ€ΠΈ Π²Ρ‹Π΄Π°Ρ‡Π΅ ΠΊΠ»ΡŽΡ‡Π°. ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ΡΡŒ ΠΊ администратору.") else: - user_main_messages[user_id]['promocode_discount'] = discount - await message.reply(f"ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½! Π‘ΠΊΠΈΠ΄ΠΊΠ°: {discount}%") + await message.reply("ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π½Π΅ прСдоставляСт ΠΊΠ»ΡŽΡ‡.") else: await message.reply("НСвСрный ΠΈΠ»ΠΈ ΠΈΡΡ‚Ρ‘ΠΊΡˆΠΈΠΉ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄.") sent_message = await message.answer("Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", reply_markup=get_main_menu_markup(user_id)) @@ -388,6 +302,21 @@ async def handle_messages(message: types.Message): 'message_id': sent_message.message_id, 'state': None } + elif user_state == 'waiting_for_promocode_for_buy': + promocode = message.text.strip() + promocode_data = db.apply_promocode(promocode) + if promocode_data: + discount = promocode_data.get('discount', 0) + user_main_messages[user_id]['promocode_discount'] = discount + await message.reply(f"ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½! Π‘ΠΊΠΈΠ΄ΠΊΠ°: {discount}%") + else: + await message.reply("НСвСрный ΠΈΠ»ΠΈ ΠΈΡΡ‚Ρ‘ΠΊΡˆΠΈΠΉ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄.") + sent_message = await message.answer("Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", reply_markup=get_buy_key_menu(user_id)) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } elif user_state == 'waiting_for_new_promocode' and user_id in admins: try: parts = message.text.strip().split() @@ -397,20 +326,21 @@ async def handle_messages(message: types.Message): discount = float(discount) days_valid = int(days_valid) max_uses = int(max_uses) if max_uses.lower() != 'none' else None - if subscription_period not in PRICING and subscription_period.lower() != 'none': + if subscription_period not in ['none', '1_month', '3_months', '6_months', '12_months']: raise ValueError("НСвСрный ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ подписки") subscription_period = None if subscription_period.lower() == 'none' else subscription_period - expires_at = datetime.now(pytz.UTC) + timedelta(days=days_valid) if days_valid > 0 else None + expires_at = datetime.now(pytz.utc) + timedelta(days=days_valid) if days_valid > 0 else None if db.add_promocode(code, discount, expires_at, max_uses, subscription_period): await message.reply( f"ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ {code} Π΄ΠΎΠ±Π°Π²Π»Π΅Π½: скидка {discount}%, дСйствуСт {days_valid} Π΄Π½Π΅ΠΉ, " - f"макс. использований: {max_uses or 'Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ'}, подписка: {subscription_period or 'Π½Π΅Ρ‚'}" + f"макс. использований: {max_uses or 'Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ'}, ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ подписки: {subscription_period or 'Π½Π΅Ρ‚'}" ) else: await message.reply("ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ ΡƒΠΆΠ΅ сущСствуСт.") except: await message.reply( - "Π€ΠΎΡ€ΠΌΠ°Ρ‚: <ΠΊΠΎΠ΄> <скидка%> <Π΄Π½Π΅ΠΉ_дСйствия> <макс_использований|none> <ΠΏΠ΅Ρ€ΠΈΠΎΠ΄_подписки|none>" + "Π€ΠΎΡ€ΠΌΠ°Ρ‚: <ΠΊΠΎΠ΄> <скидка%> <Π΄Π½Π΅ΠΉ_дСйствия> <макс_использований|none> <ΠΏΠ΅Ρ€ΠΈΠΎΠ΄_подписки|none>\n" + "ΠŸΡ€ΠΈΠΌΠ΅Ρ€: PROMO1 10 30 none 1_month" ) sent_message = await message.answer("Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", reply_markup=get_main_menu_markup(user_id)) user_main_messages[user_id] = { @@ -464,6 +394,24 @@ async def handle_messages(message: types.Message): 'message_id': sent_message.message_id, 'state': None } + elif user_state.startswith('waiting_for_custom_date_') and user_id in admins: + username = user_state.split('waiting_for_custom_date_')[1] + try: + expiration = datetime.strptime(message.text.strip(), '%d-%m-%Y').replace(tzinfo=pytz.utc) + if expiration < datetime.now(pytz.utc): + await message.reply("Π”Π°Ρ‚Π° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ Π² Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ.") + return + db.set_user_expiration(username, expiration, "НСограничСнно") + await message.reply(f"Подписка для {username} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π΄ΠΎ {expiration.strftime('%d-%m-%Y')}.") + except: + await message.reply("Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Ρ‚Ρƒ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ Π”Π”-ММ-Π“Π“Π“Π“ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 31-12-2025).") + return + sent_message = await message.answer("Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", reply_markup=get_main_menu_markup(user_id)) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } @dp.callback_query_handler(lambda c: c.data == "settings") async def settings_menu_callback(callback_query: types.CallbackQuery): @@ -616,57 +564,6 @@ async def set_price_callback(callback_query: types.CallbackQuery): } await callback_query.answer() -@dp.callback_query_handler(lambda c: c.data == "clear_old_keys") -async def clear_old_keys_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Π΄Π°Ρ‚Ρƒ, Π΄ΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΠΊΠ»ΡŽΡ‡ΠΈ:", - reply_markup=get_clear_keys_date_keyboard() - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -@dp.callback_query_handler(lambda c: c.data.startswith('clear_keys_date_')) -async def clear_keys_date_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - before_date = callback_query.data.split('clear_keys_date_')[1] - try: - if db.clear_old_keys(before_date): - await bot.send_message(user_id, f"Π‘Ρ‚Π°Ρ€Ρ‹Π΅ ΠΊΠ»ΡŽΡ‡ΠΈ Π΄ΠΎ {before_date} ΡƒΠ΄Π°Π»Π΅Π½Ρ‹.", parse_mode="Markdown") - else: - await bot.send_message(user_id, "НС Π½Π°ΠΉΠ΄Π΅Π½ΠΎ ΠΊΠ»ΡŽΡ‡Π΅ΠΉ для удалСния.", parse_mode="Markdown") - except Exception as e: - await bot.send_message(user_id, f"Ошибка ΠΏΡ€ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΈ ΠΊΠ»ΡŽΡ‡Π΅ΠΉ: {str(e)}") - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", - reply_markup=get_main_menu_markup(user_id) - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - @dp.callback_query_handler(lambda c: c.data == "add_user") async def prompt_for_user_name(callback_query: types.CallbackQuery): user_id = callback_query.from_user.id @@ -723,7 +620,7 @@ async def client_selected_callback(callback_query: types.CallbackQuery): if user_id not in admins and user_id not in moderators: await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) return - + try: username = callback_query.data.split('client_')[1] clients = db.get_client_list() @@ -731,56 +628,33 @@ async def client_selected_callback(callback_query: types.CallbackQuery): if not client_info: await callback_query.answer("ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.", show_alert=True) return - + status = "πŸ”΄ ΠžΡ„Π»Π°ΠΉΠ½" - incoming_traffic = "↓—" - outgoing_traffic = "↑—" - ipv4_address = "β€”" expiration = db.get_user_expiration(username) expiration_text = expiration.strftime("%Y-%m-%d %H:%M UTC") if expiration else "НС установлСн" - telegram_id = db.get_user_telegram_id(username) or "НС ΡƒΠΊΠ°Π·Π°Π½" - if isinstance(client_info, (tuple, list)) and len(client_info) > 2 and client_info[2] is not None: - ip_match = re.search(r'(\d{1,3}\.){3}\d{1,3}/\d+', str(client_info[2])) - ipv4_address = ip_match.group(0) if ip_match else "β€”" - active_clients = db.get_active_list() active_info = next((ac for ac in active_clients if ac[0] == username), None) - - if active_info and isinstance(active_info, (tuple, list)) and len(active_info) > 2: - if active_info[1] and active_info[1].lower() not in ['never', 'Π½Π΅Ρ‚ Π΄Π°Π½Π½Ρ‹Ρ…', '-']: - try: - last_handshake = parse_relative_time(active_info[1]) - status = "🟒 Онлайн" if (datetime.now(pytz.UTC) - last_handshake).total_seconds() <= 60 else "❌ ΠžΡ„Π»Π°ΠΉΠ½" - except: - pass - if active_info[2]: - try: - incoming_bytes, outgoing_bytes = parse_transfer(active_info[2]) - incoming_traffic = f"↓{humanize.naturalsize(incoming_bytes)}" - outgoing_traffic = f"↑{humanize.naturalsize(outgoing_bytes)}" - except: - pass - + if active_info and active_info[1] and active_info[1].lower() not in ['never', 'Π½Π΅Ρ‚ Π΄Π°Π½Π½Ρ‹Ρ…', '-']: + try: + last_handshake = datetime.strptime(active_info[1], "%Y-%m-%d %H:%M:%S") + status = "🟒 Онлайн" if (datetime.now(pytz.utc) - last_handshake).total_seconds() <= 60 else "❌ ΠžΡ„Π»Π°ΠΉΠ½" + except: + pass + text = ( f"πŸ“§ *Имя:* {username}\n" - f"πŸ‘€ *ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ:* {telegram_id}\n" - f"🌐 *IPv4:* {ipv4_address}\n" f"🌐 *Бтатус:* {status}\n" - f"πŸ”Ό *Π˜ΡΡ…ΠΎΠ΄ΡΡ‰ΠΈΠΉ:* {incoming_traffic}\n" - f"πŸ”½ *Входящий:* {outgoing_traffic}\n" f"⏰ *Π‘Ρ€ΠΎΠΊ дСйствия:* {expiration_text}" ) - + keyboard = InlineKeyboardMarkup(row_width=2).add( - InlineKeyboardButton("ℹ️ IP info", callback_data=f"ip_info_{username}"), - InlineKeyboardButton("πŸ”— ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ", callback_data=f"connections_{username}"), InlineKeyboardButton("πŸ—‘οΈ Π£Π΄Π°Π»ΠΈΡ‚ΡŒ", callback_data=f"delete_user_{username}"), InlineKeyboardButton("πŸ”„ ΠŸΡ€ΠΎΠ΄Π»ΠΈΡ‚ΡŒ", callback_data=f"renew_user_{username}"), InlineKeyboardButton("⬅️ Назад", callback_data="list_users"), InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") ) - + try: await bot.delete_message( chat_id=callback_query.message.chat.id, @@ -800,7 +674,7 @@ async def client_selected_callback(callback_query: types.CallbackQuery): 'state': None } await callback_query.answer() - + except Exception as e: logger.error(f"Ошибка Π² client_selected_callback: {str(e)}") sent_message = await bot.send_message( @@ -824,7 +698,7 @@ async def list_users_callback(callback_query: types.CallbackQuery): if user_id not in admins and user_id not in moderators: await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) return - + try: clients = db.get_client_list() if not clients: @@ -849,7 +723,7 @@ async def list_users_callback(callback_query: types.CallbackQuery): } await callback_query.answer() return - + keyboard = InlineKeyboardMarkup(row_width=2) active_clients = {client[0]: client[1] for client in db.get_active_list()} for client in clients: @@ -858,9 +732,9 @@ async def list_users_callback(callback_query: types.CallbackQuery): status = "❌" if not last_handshake or last_handshake.lower() in ['never', 'Π½Π΅Ρ‚ Π΄Π°Π½Π½Ρ‹Ρ…', '-'] else "🟒" button_text = f"{status} {username}" keyboard.insert(InlineKeyboardButton(button_text, callback_data=f"client_{username}")) - + keyboard.add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) - + try: await bot.delete_message( chat_id=callback_query.message.chat.id, @@ -879,7 +753,7 @@ async def list_users_callback(callback_query: types.CallbackQuery): 'state': None } await callback_query.answer() - + except Exception as e: logger.error(f"Ошибка Π² list_users_callback: {str(e)}") sent_message = await bot.send_message( @@ -940,90 +814,6 @@ async def remove_admin_callback(callback_query: types.CallbackQuery): await bot.send_message(admin_id, "Π’Ρ‹ ΡƒΠ΄Π°Π»Π΅Π½Ρ‹ ΠΈΠ· администраторов.") await list_admins_callback(callback_query) -@dp.callback_query_handler(lambda c: c.data.startswith('connections_')) -async def client_connections_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins and user_id not in moderators: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - username = callback_query.data.split('connections_')[1] - file_path = os.path.join('files', 'connections', f'{username}_ip.json') - if not os.path.exists(file_path): - await callback_query.answer("НСт Π΄Π°Π½Π½Ρ‹Ρ… ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡΡ….", show_alert=True) - return - - async with aiofiles.open(file_path, 'r') as f: - data = json.loads(await f.read()) - last_connections = sorted(data.items(), key=lambda x: datetime.strptime(x[1], '%d.%m.%Y %H:%M'), reverse=True)[:5] - isp_results = await asyncio.gather(*(get_isp_info(ip) for ip, _ in last_connections)) - - text = f"*ПослСдниС ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ {username}:*\n" + "\n".join(f"{ip} ({isp}) - {time}" for (ip, time), isp in zip(last_connections, isp_results)) - keyboard = InlineKeyboardMarkup(row_width=2).add( - InlineKeyboardButton("⬅️ Назад", callback_data=f"client_{username}"), - InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") - ) - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text=text, - parse_mode="Markdown", - reply_markup=keyboard - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -@dp.callback_query_handler(lambda c: c.data.startswith('ip_info_')) -async def ip_info_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins and user_id not in moderators: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - username = callback_query.data.split('ip_info_')[1] - active_info = next((ac for ac in db.get_active_list() if ac[0] == username), None) - if not active_info: - await callback_query.answer("НСт Π΄Π°Π½Π½Ρ‹Ρ… ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ.", show_alert=True) - return - - ip_address = active_info[3].split(':')[0] - async with aiohttp.ClientSession() as session: - async with session.get(f"http://ip-api.com/json/{ip_address}") as resp: - data = await resp.json() if resp.status == 200 else {} - - text = f"*IP info {username}:*\n" + "\n".join(f"{k.capitalize()}: {v}" for k, v in data.items()) - keyboard = InlineKeyboardMarkup(row_width=2).add( - InlineKeyboardButton("⬅️ Назад", callback_data=f"client_{username}"), - InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") - ) - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text=text, - parse_mode="Markdown", - reply_markup=keyboard - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - @dp.callback_query_handler(lambda c: c.data.startswith('delete_user_')) async def client_delete_callback(callback_query: types.CallbackQuery): user_id = callback_query.from_user.id @@ -1080,7 +870,7 @@ async def renew_user_callback(callback_query: types.CallbackQuery): pass sent_message = await bot.send_message( chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ продлСния:", + text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ продлСния ΠΈΠ»ΠΈ ΡƒΠΊΠ°ΠΆΠΈΡ‚Π΅ Π΄Π°Ρ‚Ρƒ:", reply_markup=get_renewal_period_keyboard(username) ) user_main_messages[user_id] = { @@ -1097,34 +887,67 @@ async def renew_period_callback(callback_query: types.CallbackQuery): await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) return try: - username, period = callback_query.data.split('renew_period_')[1].split('_', 1) - months = {'1_month': 1, '3_months': 3, '6_months': 6, '12_months': 12}[period] - expiration = datetime.now(pytz.UTC) + timedelta(days=30 * months) - db.set_user_expiration(username, expiration, "НСограничСнно") - text = f"Подписка для {username} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π΄ΠΎ {expiration.strftime('%Y-%m-%d %H:%M UTC')}." - logger.info(f"Подписка для {username} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π½Π° {period} Π΄ΠΎ {expiration}.") + parts = callback_query.data.split('renew_period_')[1].split('_', 1) + username = parts[0] + period = parts[1] if len(parts) > 1 else 'custom_date' + if period == 'custom_date': + try: + await bot.delete_message( + chat_id=callback_query.message.chat.id, + message_id=callback_query.message.message_id + ) + except: + pass + sent_message = await bot.send_message( + chat_id=callback_query.message.chat.id, + text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π΄Π°Ρ‚Ρƒ продлСния Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ Π”Π”-ММ-Π“Π“Π“Π“ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 31-12-2025):", + reply_markup=InlineKeyboardMarkup().add(InlineKeyboardButton("ΠžΡ‚ΠΌΠ΅Π½Π°", callback_data="home")) + ) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': f'waiting_for_custom_date_{username}' + } + else: + months = {'1_month': 1, '3_months': 3, '6_months': 6, '12_months': 12}[period] + expiration = datetime.now(pytz.utc) + timedelta(days=30 * months) + db.set_user_expiration(username, expiration, "НСограничСнно") + text = f"Подписка для {username} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π΄ΠΎ {expiration.strftime('%Y-%m-%d %H:%M UTC')}." + logger.info(f"Подписка для {username} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π½Π° {period} Π΄ΠΎ {expiration}.") + try: + await bot.delete_message( + chat_id=callback_query.message.chat.id, + message_id=callback_query.message.message_id + ) + except: + pass + sent_message = await bot.send_message( + chat_id=callback_query.message.chat.id, + text=text, + parse_mode="Markdown", + reply_markup=get_main_menu_markup(user_id) + ) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } + await callback_query.answer() except Exception as e: text = f"Ошибка ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ΄Π»Π΅Π½ΠΈΠΈ: {str(e)}" logger.error(f"Ошибка ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ΄Π»Π΅Π½ΠΈΠΈ подписки для {username}: {str(e)}") - try: - await bot.delete_message( + sent_message = await bot.send_message( chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id + text=text, + parse_mode="Markdown", + reply_markup=get_main_menu_markup(user_id) ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text=text, - parse_mode="Markdown", - reply_markup=get_main_menu_markup(user_id) - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } + await callback_query.answer() @dp.callback_query_handler(lambda c: c.data == "home") async def return_home(callback_query: types.CallbackQuery): @@ -1158,7 +981,7 @@ async def list_users_for_config(callback_query: types.CallbackQuery): if not clients: await callback_query.answer("Бписок пуст.", show_alert=True) return - + keyboard = InlineKeyboardMarkup(row_width=2) for client in clients: keyboard.insert(InlineKeyboardButton(client[0], callback_data=f"send_config_{client[0]}")) @@ -1242,124 +1065,40 @@ async def create_backup_callback(callback_query: types.CallbackQuery): } await callback_query.answer() -@dp.callback_query_handler(lambda c: c.data == "instructions") -async def show_instructions(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins and user_id not in moderators: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - keyboard = InlineKeyboardMarkup(row_width=2).add( - InlineKeyboardButton("πŸ“± Для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ…", callback_data="mobile_instructions"), - InlineKeyboardButton("πŸ’» Для ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ²", callback_data="pc_instructions"), - InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") - ) - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ Ρ‚ΠΈΠΏ устройства для инструкции:", - reply_markup=keyboard - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -@dp.callback_query_handler(lambda c: c.data == "mobile_instructions") -async def mobile_instructions(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins and user_id not in moderators: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - instruction_text = ( - "πŸ“± *Π˜Π½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡ для ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройств:*\n\n" - "1. Π‘ΠΊΠ°Ρ‡Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ AmneziaVPN:\n" - " - [Google Play](https://play.google.com/store/apps/details?id=org.amnezia.vpn&hl=ru)\n" - " - Или Ρ‡Π΅Ρ€Π΅Π· [GitHub](https://github.com/amnezia-vpn/amnezia-client)\n" - "2. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈ Π²Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ 'Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ'.\n" - "3. Π‘ΠΊΠΎΠΏΠΈΡ€ΡƒΠΉΡ‚Π΅ VPN ΠΊΠ»ΡŽΡ‡ ΠΈΠ· сообщСния с Ρ„Π°ΠΉΠ»ΠΎΠΌ .conf.\n" - "4. Π’ΡΡ‚Π°Π²ΡŒΡ‚Π΅ ΠΊΠ»ΡŽΡ‡ Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈ Π½Π°ΠΆΠΌΠΈΡ‚Π΅ 'ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ'.\n" - "5. Π“ΠΎΡ‚ΠΎΠ²ΠΎ! Π’Ρ‹ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹ ΠΊ VPN." - ) - keyboard = InlineKeyboardMarkup().add( - InlineKeyboardButton("⬅️ Назад", callback_data="instructions"), - InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") - ) - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text=instruction_text, - parse_mode="Markdown", - reply_markup=keyboard - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -@dp.callback_query_handler(lambda c: c.data == "pc_instructions") -async def pc_instructions(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins and user_id not in moderators: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - instruction_text = ( - "πŸ’» *Π˜Π½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡ для ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€ΠΎΠ²:*\n\n" - "1. Π‘ΠΊΠ°Ρ‡Π°ΠΉΡ‚Π΅ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ AmneziaVPN с [GitHub](https://github.com/amnezia-vpn/amnezia-client).\n" - "2. УстановитС ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ Π½Π° ваш ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€.\n" - "3. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ AmneziaVPN ΠΈ Π²Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ 'Π˜ΠΌΠΏΠΎΡ€Ρ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ'.\n" - "4. Π£ΠΊΠ°ΠΆΠΈΡ‚Π΅ ΠΏΡƒΡ‚ΡŒ ΠΊ скачанному Ρ„Π°ΠΉΠ»Ρƒ .conf.\n" - "5. НаТмитС 'ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ' для Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ VPN.\n" - "6. Π“ΠΎΡ‚ΠΎΠ²ΠΎ! VPN Π°ΠΊΡ‚ΠΈΠ²Π΅Π½." - ) - keyboard = InlineKeyboardMarkup().add( - InlineKeyboardButton("⬅️ Назад", callback_data="instructions"), - InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") - ) - try: - await bot.delete_message( - chat_id=callback_query.message.chat.id, - message_id=callback_query.message.message_id - ) - except: - pass - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text=instruction_text, - parse_mode="Markdown", - reply_markup=keyboard - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - @dp.callback_query_handler(lambda c: c.data == "buy_key") async def buy_key_callback(callback_query: types.CallbackQuery): + user_id = callback_query.from_user.id + try: + await bot.delete_message( + chat_id=callback_query.message.chat.id, + message_id=callback_query.message.message_id + ) + except: + pass + sent_message = await bot.send_message( + chat_id=callback_query.message.chat.id, + text="МСню ΠΏΠΎΠΊΡƒΠΏΠΊΠΈ ΠΊΠ»ΡŽΡ‡Π°:", + reply_markup=get_buy_key_menu(user_id) + ) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } + await callback_query.answer() + +@dp.callback_query_handler(lambda c: c.data == "select_subscription_period") +async def select_period_callback(callback_query: types.CallbackQuery): user_id = callback_query.from_user.id keyboard = InlineKeyboardMarkup(row_width=2) + discount = user_main_messages.get(user_id, {}).get('promocode_discount', 0) for period, price in PRICING.items(): + final_price = price * (1 - discount / 100) keyboard.add(InlineKeyboardButton( - f"{period.replace('_', ' ')} - β‚½{price:.2f}", - callback_data=f"select_period_{period}" + f"{period.replace('_', ' ')} - β‚½{final_price:.2f}{' (-' + str(discount) + '%)' if discount > 0 else ''}", + callback_data=f"confirm_period_{period}" )) + keyboard.add(InlineKeyboardButton("⬅️ Назад", callback_data="buy_key")) keyboard.add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) try: await bot.delete_message( @@ -1380,21 +1119,21 @@ async def buy_key_callback(callback_query: types.CallbackQuery): } await callback_query.answer() -@dp.callback_query_handler(lambda c: c.data.startswith('select_period_')) -async def select_period_callback(callback_query: types.CallbackQuery): +@dp.callback_query_handler(lambda c: c.data.startswith('confirm_period_')) +async def confirm_period_callback(callback_query: types.CallbackQuery): user_id = callback_query.from_user.id - period = callback_query.data.split('select_period_')[1] + period = callback_query.data.split('confirm_period_')[1] price = PRICING[period] discount = user_main_messages.get(user_id, {}).get('promocode_discount', 0) final_price = price * (1 - discount / 100) - + if not yoomoney_client or not yoomoney_wallet: await callback_query.answer("ΠŸΠ»Π°Ρ‚Π΅ΠΆΠΈ нСдоступны. ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ΡΡŒ ΠΊ администратору.", show_alert=True) return - + payment_id = str(uuid.uuid4()) db.add_payment(user_id, payment_id, final_price, 'pending') - + quickpay = Quickpay( receiver=yoomoney_wallet, quickpay_form="shop", @@ -1404,10 +1143,10 @@ async def select_period_callback(callback_query: types.CallbackQuery): label=payment_id ) payment_url = quickpay.redirected_url - + keyboard = InlineKeyboardMarkup().add( InlineKeyboardButton("πŸ’³ ΠžΠΏΠ»Π°Ρ‚ΠΈΡ‚ΡŒ", url=payment_url), - InlineKeyboardButton("⬅️ Назад", callback_data="buy_key"), + InlineKeyboardButton("⬅️ Назад", callback_data="select_subscription_period"), InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home") ) try: @@ -1434,6 +1173,52 @@ async def select_period_callback(callback_query: types.CallbackQuery): } await callback_query.answer() +@dp.callback_query_handler(lambda c: c.data == "enter_promocode_for_buy") +async def enter_promocode_for_buy_callback(callback_query: types.CallbackQuery): + user_id = callback_query.from_user.id + try: + await bot.delete_message( + chat_id=callback_query.message.chat.id, + message_id=callback_query.message.message_id + ) + except: + pass + sent_message = await bot.send_message( + chat_id=callback_query.message.chat.id, + text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ для скидки:", + reply_markup=InlineKeyboardMarkup().add(InlineKeyboardButton("⬅️ Назад", callback_data="buy_key")) + ) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': 'waiting_for_promocode_for_buy' + } + await callback_query.answer() + +@dp.callback_query_handler(lambda c: c.data == "reset_promocode") +async def reset_promocode_callback(callback_query: types.CallbackQuery): + user_id = callback_query.from_user.id + if 'promocode_discount' in user_main_messages.get(user_id, {}): + del user_main_messages[user_id]['promocode_discount'] + try: + await bot.delete_message( + chat_id=callback_query.message.chat.id, + message_id=callback_query.message.message_id + ) + except: + pass + sent_message = await bot.send_message( + chat_id=callback_query.message.chat.id, + text="Π‘ΠΊΠΈΠ΄ΠΊΠ° ΡΠ±Ρ€ΠΎΡˆΠ΅Π½Π°. МСню ΠΏΠΎΠΊΡƒΠΏΠΊΠΈ ΠΊΠ»ΡŽΡ‡Π°:", + reply_markup=get_buy_key_menu(user_id) + ) + user_main_messages[user_id] = { + 'chat_id': sent_message.chat.id, + 'message_id': sent_message.message_id, + 'state': None + } + await callback_query.answer() + @dp.callback_query_handler(lambda c: c.data == "use_promocode") async def use_promocode_callback(callback_query: types.CallbackQuery): user_id = callback_query.from_user.id @@ -1446,7 +1231,7 @@ async def use_promocode_callback(callback_query: types.CallbackQuery): pass sent_message = await bot.send_message( chat_id=callback_query.message.chat.id, - text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄:", + text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ для получСния ΠΊΠ»ΡŽΡ‡Π°:", reply_markup=InlineKeyboardMarkup().add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) ) user_main_messages[user_id] = { @@ -1464,7 +1249,7 @@ async def manage_promocodes_callback(callback_query: types.CallbackQuery): return promocodes = db.get_promocodes() text = "ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹:\n" + "\n".join( - f"{code}: {info['discount']}% (использовано {info['uses']}/{info['max_uses'] or '∞'}, Π΄ΠΎ {info['expires_at'] or 'Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ'}, подписка: {info['subscription_period'] or 'Π½Π΅Ρ‚'})" + f"{code}: {info['discount']}% (использовано {info['uses']}/{info['max_uses'] or '∞'}, Π΄ΠΎ {info['expires_at'].strftime('%Y-%m-%d %H:%M UTC') if info['expires_at'] else 'Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ'}, ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ подписки: {info['subscription_period'] or 'Π½Π΅Ρ‚'})" for code, info in promocodes.items() ) if promocodes else "ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹ ΠΎΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚." keyboard = InlineKeyboardMarkup(row_width=2).add( @@ -1506,7 +1291,7 @@ async def add_promocode_callback(callback_query: types.CallbackQuery): pass sent_message = await bot.send_message( chat_id=callback_query.message.chat.id, - text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅: <ΠΊΠΎΠ΄> <скидка%> <Π΄Π½Π΅ΠΉ_дСйствия> <макс_использований|none> <ΠΏΠ΅Ρ€ΠΈΠΎΠ΄_подписки|none>", + text="Π’Π²Π΅Π΄ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅: <ΠΊΠΎΠ΄> <скидка%> <Π΄Π½Π΅ΠΉ_дСйствия> <макс_использований|none> <ΠΏΠ΅Ρ€ΠΈΠΎΠ΄_подписки|none>\nΠŸΡ€ΠΈΠΌΠ΅Ρ€: PROMO1 10 30 none 1_month", reply_markup=InlineKeyboardMarkup().add(InlineKeyboardButton("🏠 Π”ΠΎΠΌΠΎΠΉ", callback_data="home")) ) user_main_messages[user_id] = { @@ -1559,109 +1344,25 @@ async def remove_promocode_callback(callback_query: types.CallbackQuery): await callback_query.answer(f"ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ {code} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.", show_alert=True) await manage_promocodes_callback(callback_query) -@dp.callback_query_handler(lambda c: c.data == "check_updates") -async def check_updates_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - try: - process = await asyncio.create_subprocess_exec( - '/root/install.sh', '--check-update', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - output = stdout.decode().strip() + stderr.decode().strip() - if "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½" in output: - await bot.send_message(user_id, "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½, обновлСния Π½Π΅ Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‚ΡΡ.", parse_mode="Markdown") - elif "ОбновлСниС рСпозитория... Done!" in output: - await bot.send_message(user_id, "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Ρ‘Π½ ΠΈ слуТба ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡ‰Π΅Π½Π°.", parse_mode="Markdown") - else: - await bot.send_message(user_id, f"Ошибка ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ:\n```\n{output}\n```", parse_mode="Markdown") - except Exception as e: - await bot.send_message(user_id, f"Ошибка ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ΅ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ: {str(e)}") - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", - reply_markup=get_main_menu_markup(user_id) - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -@dp.callback_query_handler(lambda c: c.data == "restart_vpn") -async def restart_vpn_callback(callback_query: types.CallbackQuery): - user_id = callback_query.from_user.id - if user_id not in admins: - await callback_query.answer("НСт ΠΏΡ€Π°Π².", show_alert=True) - return - try: - process = await asyncio.create_subprocess_exec( - 'docker', 'ps', '-q', '-f', f'name={DOCKER_CONTAINER}', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - if not stdout.decode().strip(): - await bot.send_message(user_id, f"ΠšΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ {DOCKER_CONTAINER} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.", parse_mode="Markdown") - else: - process = await asyncio.create_subprocess_exec( - 'docker', 'restart', DOCKER_CONTAINER, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - if process.returncode == 0: - await bot.send_message(user_id, f"VPN-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ {DOCKER_CONTAINER} ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡ‰Π΅Π½.", parse_mode="Markdown") - else: - await bot.send_message(user_id, f"Ошибка ΠΏΡ€ΠΈ пСрСзапускС VPN:\n```\n{stderr.decode().strip()}\n```", parse_mode="Markdown") - except Exception as e: - await bot.send_message(user_id, f"Ошибка ΠΏΡ€ΠΈ пСрСзапускС VPN: {str(e)}") - sent_message = await bot.send_message( - chat_id=callback_query.message.chat.id, - text="Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ дСйствиС:", - reply_markup=get_main_menu_markup(user_id) - ) - user_main_messages[user_id] = { - 'chat_id': sent_message.chat.id, - 'message_id': sent_message.message_id, - 'state': None - } - await callback_query.answer() - -async def check_payment_status(): +async def check_pending_payments(): payments = db.get_pending_payments() - for user_id, payment_id, amount, _ in payments: + for payment in payments: + user_id, payment_id, amount, period = payment try: - if yoomoney_client: - operation = yoomoney_client.operation_history(label=payment_id) - for op in operation.operations: - if op.label == payment_id and op.status == "success": - db.update_payment_status(payment_id, 'completed') - pending_payment = user_main_messages.get(user_id, {}).get('pending_payment', {}) - if pending_payment and pending_payment['payment_id'] == payment_id: - period = pending_payment['period'] - success = await issue_vpn_key(user_id, period) - if success: - await bot.send_message(user_id, "ΠžΠΏΠ»Π°Ρ‚Π° ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½Π°! Π’Π°Ρˆ VPN ΠΊΠ»ΡŽΡ‡ ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½.") - else: - await bot.send_message(user_id, "Ошибка ΠΏΡ€ΠΈ создании ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ. ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ΡΡŒ ΠΊ администратору.") - user_main_messages[user_id].pop('pending_payment', None) - break + history = yoomoney_client.operation_history(label=payment_id) + for operation in history.operations: + if operation.status == "success" and operation.amount == amount: + db.update_payment_status(payment_id, 'completed') + success = await issue_vpn_key(user_id, period) + if success: + await bot.send_message(user_id, f"ΠžΠΏΠ»Π°Ρ‚Π° ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½Π°! VPN ΠΊΠ»ΡŽΡ‡ Π½Π° {period.replace('_', ' ')} Π²Ρ‹Π΄Π°Π½.") + else: + await bot.send_message(user_id, "Ошибка ΠΏΡ€ΠΈ Π²Ρ‹Π΄Π°Ρ‡Π΅ ΠΊΠ»ΡŽΡ‡Π°. ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ΡΡŒ ΠΊ администратору.") + break except Exception as e: - logger.error(f"Ошибка ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ ΠΏΠ»Π°Ρ‚Π΅ΠΆΠ° {payment_id}: {str(e)}") + logger.error(f"Ошибка ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ΅ ΠΏΠ»Π°Ρ‚Π΅ΠΆΠ° {payment_id}: {str(e)}") + +scheduler.add_job(check_pending_payments, IntervalTrigger(minutes=5)) if __name__ == '__main__': - import asyncio - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - loop.run_until_complete(load_isp_cache()) - scheduler.add_job(check_payment_status, IntervalTrigger(minutes=5)) - executor.start_polling(dp, skip_updates=True, loop=loop) - finally: - loop.close() + executor.start_polling(dp, skip_updates=True)