diff --git a/bot.py b/bot.py index b22f25db..622fc347 100644 --- a/bot.py +++ b/bot.py @@ -9,7 +9,7 @@ storage = MemoryStorage() dp = Dispatcher(bot=bot, storage=storage) router = Router() -from handlers import balance, keys, pay, profile, start +from handlers import balance, keys, pay, profile, start, notifications from key_management import router as key_management_router # Регистрация обработчиков @@ -19,6 +19,7 @@ dp.include_router(keys.router) dp.include_router(balance.router) dp.include_router(key_management_router) dp.include_router(pay.router) +dp.include_router(notifications.router) diff --git a/client.py b/client.py index 87587bab..19fb12b3 100644 --- a/client.py +++ b/client.py @@ -40,6 +40,7 @@ def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, t if response.status_code == 200: print(f"Клиент добавлен: email={email}") + return response.json() else: print(f"Ошибка при добавлении клиента: {response.status_code}, {response.text}") diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 028d3eb0..0500dff1 100644 Binary files a/handlers/__pycache__/keys.cpython-310.pyc and b/handlers/__pycache__/keys.cpython-310.pyc differ diff --git a/handlers/__pycache__/notifications.cpython-310.pyc b/handlers/__pycache__/notifications.cpython-310.pyc index 80b1e136..c354ebf2 100644 Binary files a/handlers/__pycache__/notifications.cpython-310.pyc and b/handlers/__pycache__/notifications.cpython-310.pyc differ diff --git a/handlers/__pycache__/pay.cpython-310.pyc b/handlers/__pycache__/pay.cpython-310.pyc index 65cf0116..97a39129 100644 Binary files a/handlers/__pycache__/pay.cpython-310.pyc and b/handlers/__pycache__/pay.cpython-310.pyc differ diff --git a/handlers/keys.py b/handlers/keys.py index 064d3517..ab89bb5e 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -79,9 +79,16 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): expiry_time = record['expiry_time'] expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) current_date = datetime.utcnow() - days_left = (expiry_date - current_date).days + time_left = expiry_date - current_date + + if time_left.total_seconds() <= 0: + days_left_message = "Ключ истек." + elif time_left.days > 0: + days_left_message = f"Осталось дней: {time_left.days}" + else: + hours_left = time_left.seconds // 3600 + days_left_message = f"Осталось часов: {hours_left}" - days_left_message = f"Осталось дней: {days_left}" if days_left > 0 else "Ключ истек." response_message = (f"Ваш ключ:\n
{key}
\n" f"Дата окончания: {expiry_date.strftime('%Y-%m-%d %H:%M:%S')}\n" f"{days_left_message}") diff --git a/handlers/notifications.py b/handlers/notifications.py index 42c57a76..6ddce0db 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -1,6 +1,11 @@ import asyncpg from datetime import datetime, timedelta from aiogram import Bot +from aiogram import Router, types +from bot import bot +from aiogram.filters import Command + +router = Router() from config import DATABASE_URL @@ -39,3 +44,24 @@ async def notify_expiring_keys(bot: Bot): await conn.close() except Exception as e: print(f"Ошибка при отправке уведомлений: {e}") + +@router.message(Command(commands=['notify'])) +async def notify_command(message: types.Message): + # Запрашиваем у администратора ID пользователя и текст уведомления + await message.answer("Введите ID пользователя и текст уведомления в формате:\n/notify user_id текст", parse_mode="HTML") + +# Обработка команды уведомления +@router.message() +async def process_notification(message: types.Message): + if message.text.startswith("/notify"): + try: + # Парсим команду + command, user_id, *text = message.text.split() + text = ' '.join(text) + + # Отправляем сообщение пользователю + await bot.send_message(chat_id=user_id, text=text) + await message.answer(f"Уведомление успешно отправлено пользователю {user_id}.") + + except Exception as e: + await message.answer(f"Ошибка при отправке уведомления: {e}") diff --git a/handlers/pay.py b/handlers/pay.py index 1828e21f..826f3d6f 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -232,4 +232,4 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: finally: await state.clear() - await callback_query.answer() + await callback_query.answer() \ No newline at end of file diff --git a/key_management.py b/key_management.py index 9cd058ec..2336a92e 100644 --- a/key_management.py +++ b/key_management.py @@ -9,6 +9,7 @@ from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message) +from pytz import timezone from auth import link, login_with_credentials from bot import bot, dp @@ -139,7 +140,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): if trial_status == 0: # Создаем пробный ключ на 1 день - expiry_time = int((current_time + timedelta(days=1)).timestamp() * 1000) + expiry_time = int((current_time + timedelta(days=1, hours=3)).timestamp() * 1000) else: # Проверяем баланс перед созданием нового ключа balance = await get_balance(tg_id) @@ -151,10 +152,23 @@ async def handle_key_name_input(message: Message, state: FSMContext): return await update_balance(tg_id, -100) - expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000) + expiry_time = int((current_time + timedelta(days=30, hours=3)).timestamp() * 1000) try: - add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_time, enable=True, flow="xtls-rprx-vision") + # Попробуем добавить клиента + response = add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_time, enable=True, flow="xtls-rprx-vision") + + # Проверяем статус ответа от сервера + if not response.get("success", True): + error_msg = response.get("msg", "Неизвестная ошибка.") + if "Duplicate email" in error_msg: + await message.bot.send_message(tg_id, "❌ Этот email уже используется. Пожалуйста, выберите другое имя для ключа.") + await state.set_state(Form.waiting_for_key_name) # Возвращаем пользователя к вводу имени ключа + return + else: + raise Exception(error_msg) + + # Если добавление клиента прошло успешно, получаем ссылку connection_link = link(session, client_id, email) # Проверка существующей записи @@ -182,6 +196,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): f"
{connection_link}
" ) await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard) + except Exception as e: await message.bot.send_message(tg_id, f"❌ Ошибка при создании ключа: {e}") @@ -189,6 +204,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): + @dp.callback_query(F.data == 'instructions') async def handle_instructions(callback_query: CallbackQuery): instructions_message = (