diff --git a/auth.py b/auth.py index 20960890..21e193f2 100644 --- a/auth.py +++ b/auth.py @@ -1,15 +1,15 @@ import json - import requests -from config import API_URL, GET_INBOUNDS_URL +from config import SERVERS # Импортируем SERVERS из config.py session = None -def login_with_credentials(username, password): +def login_with_credentials(server_id: str, username: str, password: str): global session session = requests.Session() - auth_url = f"{API_URL}/login/" + api_url = SERVERS[server_id]['API_URL'] # Получаем API_URL для выбранного сервера + auth_url = f"{api_url}/login/" data = { "username": username, "password": password @@ -21,22 +21,23 @@ def login_with_credentials(username, password): else: raise Exception(f"Ошибка авторизации: {response.status_code}, {response.text}") - -def get_clients(session): - response = session.get(GET_INBOUNDS_URL) +def get_clients(session, server_id): + api_url = SERVERS[server_id]['API_URL'] # Получаем GET_INBOUNDS_URL для выбранного сервера + response = session.get(f'{api_url}/panel/api/inbounds/list/') if response.status_code == 200: return response.json() # Возвращает данные по инбаундам и клиентам else: raise Exception(f"Ошибка при получении клиентов: {response.status_code}, {response.text}") -def link(session, client_id: str, email: str): +def link(session, server_id: str, client_id: str, email: str): """ Получение ссылки для подключения по ID клиента. - :param session: requests.Session - авторизованная сессия + :param server_id: str - идентификатор сервера :param client_id: str - идентификатор клиента + :param email: str - электронная почта клиента :return: str - ссылка для подключения """ - response = get_clients(session) + response = get_clients(session, server_id) if 'obj' not in response or len(response['obj']) == 0: raise Exception("Не удалось получить данные клиентов.") @@ -51,6 +52,5 @@ def link(session, client_id: str, email: str): flow = stream_settings.get('flow', 'xtls-rprx-vision') # Создание ссылки для подключения VLESS - val = f"vless://{client_id}@solonet.pocomacho.ru:443?type={tcp}&security={reality}&pbk=Ik3cfHdM5RXYCuzTxhaFfo58Fzwjvw11AtKsz5-izxA&fp=chrome&sni=discord.com&sid=6fc9&spx=%2F&flow={flow}#SoloNet_DE-{email}" + val = f"vless://{client_id}@{SERVERS[server_id]['DOMEN']}?type={tcp}&security={reality}&pbk={SERVERS[server_id]['PBK']}&fp=chrome&sni={SERVERS[server_id]['SNI']}&sid={SERVERS[server_id]['SID']}=%2F&flow={flow}#{SERVERS[server_id]['PREFIX']}-{email}" return val - diff --git a/client.py b/client.py index 19fb12b3..d847e74c 100644 --- a/client.py +++ b/client.py @@ -1,10 +1,9 @@ import json +from config import SERVERS # Импортируем SERVERS из config.py -from config import API_URL - - -def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str): - url = f'{API_URL}/panel/api/inbounds/addClient' +def add_client(session, server_id: str, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str): + api_url = SERVERS[server_id]['API_URL'] # Получаем API_URL для выбранного сервера + url = f'{api_url}/panel/api/inbounds/addClient' email = email.lower() @@ -44,8 +43,9 @@ def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, t else: print(f"Ошибка при добавлении клиента: {response.status_code}, {response.text}") -def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: int) -> bool: - response = session.get(f"{API_URL}/panel/api/inbounds/getClientTraffics/{email}") +def extend_client_key(session, server_id: str, tg_id, client_id, email: str, new_expiry_time: int) -> bool: + api_url = SERVERS[server_id]['API_URL'] # Получаем API_URL для выбранного сервера + response = session.get(f"{api_url}/panel/api/inbounds/getClientTraffics/{email}") print(f"GET {response.url} Status: {response.status_code}") print(f"GET Response: {response.text}") @@ -93,7 +93,7 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in } try: - response = session.post(f"{API_URL}/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers) + response = session.post(f"{api_url}/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers) print(f"POST {response.url} Status: {response.status_code}") print(f"POST Request Data: {json.dumps(payload, indent=2)}") print(f"POST Response: {response.text}") @@ -107,8 +107,9 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in print(f"Ошибка запроса: {e}") return False -def delete_client(session, client_id: str) -> bool: - url = f"{API_URL}/panel/api/inbounds/1/delClient/{client_id}" +def delete_client(session, server_id: str, client_id: str) -> bool: + api_url = SERVERS[server_id]['API_URL'] # Получаем API_URL для выбранного сервера + url = f"{api_url}/panel/api/inbounds/1/delClient/{client_id}" headers = { 'Accept': 'application/json' } @@ -121,4 +122,4 @@ def delete_client(session, client_id: str) -> bool: return False except Exception as e: print(f"Ошибка запроса: {e}") - return False \ No newline at end of file + return False diff --git a/database.py b/database.py index 565a3157..dca8bd56 100644 --- a/database.py +++ b/database.py @@ -4,9 +4,9 @@ import asyncpg from config import DATABASE_URL - async def init_db(): conn = await asyncpg.connect(DATABASE_URL) + # Создаем таблицу connections, если она не существует await conn.execute(''' CREATE TABLE IF NOT EXISTS connections ( tg_id BIGINT PRIMARY KEY NOT NULL, @@ -14,6 +14,7 @@ async def init_db(): trial INTEGER NOT NULL DEFAULT 0 ) ''') + # Создаем таблицу keys, если она не существует await conn.execute(''' CREATE TABLE IF NOT EXISTS keys ( tg_id BIGINT NOT NULL, @@ -22,11 +23,22 @@ async def init_db(): created_at BIGINT NOT NULL, expiry_time BIGINT NOT NULL, key TEXT NOT NULL, + server_id TEXT NOT NULL DEFAULT 'server1', -- новое поле для идентификатора сервера PRIMARY KEY (tg_id, client_id) ) ''') + # Добавляем поле server_id в таблицу keys, если его нет + try: + await conn.execute(''' + ALTER TABLE keys + ADD COLUMN server_id TEXT NOT NULL DEFAULT 'server1' + ''') + except asyncpg.exceptions.DuplicateColumnError: + # Если поле уже существует, ничего не делаем + pass await conn.close() + async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0): conn = await asyncpg.connect(DATABASE_URL) await conn.execute(''' @@ -43,12 +55,12 @@ async def check_connection_exists(tg_id: int): await conn.close() return exists -async def store_key(tg_id: int, client_id: str, email: str, expiry_time: int, key: str): +async def store_key(tg_id: int, client_id: str, email: str, expiry_time: int, key: str, server_id: str): conn = await asyncpg.connect(DATABASE_URL) await conn.execute(''' - INSERT INTO keys (tg_id, client_id, email, created_at, expiry_time, key) - VALUES ($1, $2, $3, $4, $5, $6) - ''', tg_id, client_id, email, int(datetime.utcnow().timestamp() * 1000), expiry_time, key) + INSERT INTO keys (tg_id, client_id, email, created_at, expiry_time, key, server_id) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ''', tg_id, client_id, email, int(datetime.utcnow().timestamp() * 1000), expiry_time, key, server_id) await conn.close() async def get_keys(tg_id: int): @@ -61,6 +73,16 @@ async def get_keys(tg_id: int): await conn.close() return records +async def get_keys_by_server(tg_id: int, server_id: str): + conn = await asyncpg.connect(DATABASE_URL) + records = await conn.fetch(''' + SELECT client_id, email, created_at, key + FROM keys + WHERE tg_id = $1 AND server_id = $2 + ''', tg_id, server_id) + await conn.close() + return records + async def has_active_key(tg_id: int) -> bool: conn = await asyncpg.connect(DATABASE_URL) count = await conn.fetchval("SELECT COUNT(*) FROM keys WHERE tg_id = $1", tg_id) diff --git a/handlers/__pycache__/key_management.cpython-310.pyc b/handlers/__pycache__/key_management.cpython-310.pyc new file mode 100644 index 00000000..8d7f0407 Binary files /dev/null and b/handlers/__pycache__/key_management.cpython-310.pyc differ diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 38afa961..ff42bbce 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 e4475773..f8a62af4 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 47c19e57..1a6c3746 100644 Binary files a/handlers/__pycache__/pay.cpython-310.pyc and b/handlers/__pycache__/pay.cpython-310.pyc differ diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index 2fae3fe8..73873ab9 100644 Binary files a/handlers/__pycache__/profile.cpython-310.pyc and b/handlers/__pycache__/profile.cpython-310.pyc differ diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index e08ad019..0aabfdb4 100644 Binary files a/handlers/__pycache__/start.cpython-310.pyc and b/handlers/__pycache__/start.cpython-310.pyc differ diff --git a/handlers/key_management.py b/handlers/key_management.py index 82d3e530..3ce76967 100644 --- a/handlers/key_management.py +++ b/handlers/key_management.py @@ -10,11 +10,11 @@ from aiogram.types import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message) from auth import link, login_with_credentials -from bot import bot, dp +from bot import dp from client import add_client -from config import (ADMIN_ID, ADMIN_PASSWORD, ADMIN_USERNAME, API_TOKEN, - DATABASE_URL) -from database import (add_connection, get_balance, has_active_key, store_key, +from config import (ADMIN_PASSWORD, ADMIN_USERNAME, + DATABASE_URL, SERVERS) +from database import (add_connection, get_balance, store_key, update_balance) from handlers.instructions import send_instructions from handlers.profile import process_callback_view_profile @@ -28,6 +28,7 @@ def sanitize_key_name(key_name: str) -> str: return re.sub(r'[^a-z0-9@._-]', '', key_name.lower()) class Form(StatesGroup): + waiting_for_server_selection = State() waiting_for_key_name = State() viewing_profile = State() @@ -35,10 +36,38 @@ class Form(StatesGroup): async def process_callback_create_key(callback_query: CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id + # Получаем количество подключений для каждого сервера + server_buttons = [] + conn = await asyncpg.connect(DATABASE_URL) + try: + for server_id, server in SERVERS.items(): + # Получаем количество ключей на сервере + count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id) + percent_full = (count / 100) * 100 # Заполнение в процентах + server_name = f"{server['name']} ({percent_full:.1f}%)" + server_buttons.append([InlineKeyboardButton(text=server_name, callback_data=f'select_server|{server_id}')]) + finally: + await conn.close() + + await callback_query.message.edit_text( + "⚙️ Выберите сервер для создания ключа:", + parse_mode="HTML", + reply_markup=InlineKeyboardMarkup(inline_keyboard=server_buttons) + ) + + await state.set_state(Form.waiting_for_server_selection) + + await callback_query.answer() + +@dp.callback_query(F.data.startswith('select_server|')) +async def select_server(callback_query: CallbackQuery, state: FSMContext): + server_id = callback_query.data.split('|')[1] + await state.update_data(selected_server_id=server_id) + # Получаем данные о trial из базы данных conn = await asyncpg.connect(DATABASE_URL) try: - existing_connection = await conn.fetchrow('SELECT trial FROM connections WHERE tg_id = $1', tg_id) + existing_connection = await conn.fetchrow('SELECT trial FROM connections WHERE tg_id = $1', callback_query.from_user.id) finally: await conn.close() @@ -69,6 +98,8 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC @dp.callback_query(F.data == 'confirm_create_new_key') async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id + data = await state.get_data() + server_id = data.get('selected_server_id') # Проверяем баланс перед созданием нового ключа balance = await get_balance(tg_id) @@ -131,8 +162,9 @@ async def handle_key_name_input(message: Message, state: FSMContext): data = await state.get_data() creating_new_key = data.get('creating_new_key', False) + server_id = data.get('selected_server_id') - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) + session = login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) client_id = str(uuid.uuid4()) email = key_name.lower() current_time = datetime.utcnow() @@ -167,7 +199,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): try: # Попробуем добавить клиента - response = add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_timestamp, enable=True, flow="xtls-rprx-vision") + response = add_client(session, server_id, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_timestamp, enable=True, flow="xtls-rprx-vision") if not response.get("success", True): error_msg = response.get("msg", "Неизвестная ошибка.") @@ -178,7 +210,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): else: raise Exception(error_msg) - connection_link = link(session, client_id, email) + connection_link = link(session, server_id, client_id, email) conn = await asyncpg.connect(DATABASE_URL) try: @@ -191,7 +223,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): finally: await conn.close() - await store_key(tg_id, client_id, email, expiry_timestamp, connection_link) + await store_key(tg_id, client_id, email, expiry_timestamp, connection_link, server_id) # Рассчитываем оставшееся время до окончания действия ключа remaining_time = expiry_time - current_time @@ -230,4 +262,4 @@ async def handle_instructions(callback_query: CallbackQuery): @dp.callback_query(F.data == 'back_to_main') async def handle_back_to_main(callback_query: CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) - await callback_query.answer() \ No newline at end of file + await callback_query.answer() diff --git a/handlers/keys.py b/handlers/keys.py index 10a12eee..f88191d8 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -3,10 +3,10 @@ from datetime import datetime, timedelta import asyncpg from aiogram import Router, types -from auth import login_with_credentials +from auth import login_with_credentials, link from bot import bot -from client import delete_client, extend_client_key -from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL +from client import delete_client, extend_client_key, add_client +from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS from database import get_balance, update_balance router = Router() @@ -69,7 +69,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): conn = await asyncpg.connect(DATABASE_URL) try: record = await conn.fetchrow(''' - SELECT k.key, k.expiry_time + SELECT k.key, k.expiry_time, k.server_id FROM keys k WHERE k.tg_id = $1 AND k.email = $2 ''', tg_id, key_name) @@ -77,6 +77,11 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): if record: key = record['key'] expiry_time = record['expiry_time'] + server_id = record['server_id'] + + # Получаем название сервера по server_id + server_name = SERVERS.get(server_id, {}).get('name', 'Неизвестный сервер') + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) current_date = datetime.utcnow() time_left = expiry_date - current_date @@ -91,14 +96,24 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): response_message = (f"🔑 Ваш ключ:\n
{key}
\n" f"📅 Дата окончания: {expiry_date.strftime('%Y-%m-%d %H:%M:%S')}\n" - f"{days_left_message}") + f"{days_left_message}\n" + f"🌍 Сервер: {server_name}") # Кнопки для продления, инструкций и удаления renew_button = types.InlineKeyboardButton(text='⏳ Продлить ключ', callback_data=f'renew_key|{client_id}') instructions_button = types.InlineKeyboardButton(text='📘 Инструкции', callback_data='instructions') delete_button = types.InlineKeyboardButton(text='❌ Удалить ключ', callback_data=f'delete_key|{client_id}') + change_location_button = types.InlineKeyboardButton(text='🌍 Сменить локацию', callback_data=f'change_location|{client_id}') back_button = types.InlineKeyboardButton(text='🔙 Назад в профиль', callback_data='view_profile') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_button], [delete_button], [back_button]]) + + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=[ + [instructions_button], # Инструкции отдельной строкой + [renew_button, delete_button], # Продлить и Удалить в одном ряду + [change_location_button], # Сменить локацию отдельной строкой + [back_button] # Назад отдельной строкой + ] + ) await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML") else: @@ -112,6 +127,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): await callback_query.answer() + # Обработка запроса на удаление ключа @router.callback_query(lambda c: c.data.startswith('delete_key|')) async def process_callback_delete_key(callback_query: types.CallbackQuery): @@ -167,7 +183,6 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): await callback_query.answer() - @router.callback_query(lambda c: c.data.startswith('confirm_delete|')) async def process_callback_confirm_delete(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id @@ -176,13 +191,16 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): try: conn = await asyncpg.connect(DATABASE_URL) try: - record = await conn.fetchrow('SELECT email FROM keys WHERE client_id = $1', client_id) + # Извлекаем server_id и email из базы данных + record = await conn.fetchrow('SELECT email, server_id FROM keys WHERE client_id = $1', client_id) if record: email = record['email'] + server_id = record['server_id'] # Извлекаем server_id из записи - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) - success = delete_client(session, client_id) + # Используем server_id для авторизации + session = login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) + success = delete_client(session, server_id, client_id) if success: await conn.execute('DELETE FROM keys WHERE client_id = $1', client_id) @@ -215,11 +233,13 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): try: conn = await asyncpg.connect(DATABASE_URL) try: - record = await conn.fetchrow('SELECT email, expiry_time FROM keys WHERE client_id = $1', client_id) + # Извлекаем email, expiry_time и server_id из базы данных + record = await conn.fetchrow('SELECT email, expiry_time, server_id FROM keys WHERE client_id = $1', client_id) if record: email = record['email'] expiry_time = record['expiry_time'] + server_id = record['server_id'] # Извлекаем server_id из записи current_time = datetime.utcnow().timestamp() * 1000 # Текущее время в миллисекундах # Проверяем, если ключ истек, то продлеваем от текущей даты, иначе продлеваем от текущей даты истечения @@ -247,9 +267,9 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): await bot.edit_message_text("Недостаточно средств для продления ключа.", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) return - # Продлеваем ключ через API - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) - success = extend_client_key(session, tg_id, client_id, email, new_expiry_time) + # Продлеваем ключ через API, используя server_id + session = login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) + success = extend_client_key(session, server_id, tg_id, client_id, email, new_expiry_time) if success: # Обновляем баланс и ключ в базе данных @@ -272,6 +292,89 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): await callback_query.answer() - async def handle_error(tg_id, callback_query, message): - await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id) \ No newline at end of file + await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id) + +@router.callback_query(lambda c: c.data.startswith('change_location|')) +async def process_callback_change_location(callback_query: types.CallbackQuery): + tg_id = callback_query.from_user.id + client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта + + # Получаем количество подключений для каждого сервера + server_buttons = [] + conn = await asyncpg.connect(DATABASE_URL) + try: + for server_id, server in SERVERS.items(): + # Получаем количество ключей на сервере + count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id) + percent_full = (count / 100) * 100 # Заполнение в процентах + server_name = f"{server['name']} ({percent_full:.1f}%)" + server_buttons.append([types.InlineKeyboardButton(text=server_name, callback_data=f'select_server&{server_id}&{client_id}')]) + finally: + await conn.close() + + keyboard = types.InlineKeyboardMarkup(inline_keyboard=server_buttons) + + response_message = "Выберите новый сервер для вашего ключа:" + await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML") + await callback_query.answer() + +@router.callback_query(lambda c: c.data.startswith('select_server&')) +async def process_callback_select_server(callback_query: types.CallbackQuery): + tg_id = callback_query.from_user.id + server_id, client_id = callback_query.data.split('&')[1], callback_query.data.split('&')[2] + + try: + conn = await asyncpg.connect(DATABASE_URL) + try: + # Извлекаем email и expiry_time из базы данных + record = await conn.fetchrow('SELECT email, expiry_time, server_id FROM keys WHERE client_id = $1', client_id) + + if record: + email = record['email'] + expiry_time = record['expiry_time'] + current_server_id = record['server_id'] + + # Создаем сессию для нового сервера + session = login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) + + # Рассчитываем новое время окончания ключа + new_expiry_time = int(datetime.utcnow().timestamp() * 1000) + (expiry_time - datetime.utcnow().timestamp() * 1000) + + # Создаем нового клиента на новом сервере + new_client_data = add_client(session, server_id, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=new_expiry_time, enable=True, flow="xtls-rprx-vision") + + if new_client_data: + # Генерируем новый ключ + new_key = link(session, server_id, client_id, email) + + # Обновляем запись в базе данных, только ключ и сервер + await conn.execute('UPDATE keys SET server_id = $1, key = $2 WHERE client_id = $3', + server_id, new_key, client_id) + + # Удаляем клиента с текущего сервера + session = login_with_credentials(current_server_id, ADMIN_USERNAME, ADMIN_PASSWORD) + success_delete = delete_client(session, current_server_id, client_id) + if success_delete: + response_message = ("Ключ успешно перемещен на новый сервер.\n\n" + "Не забудьте удалить старый ключ из вашего приложения и установить новый.") + else: + response_message = "Ошибка при удалении ключа с текущего сервера." + else: + response_message = "Ошибка при создании клиента на новом сервере." + + else: + response_message = "Ключ не найден или уже удален." + + back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + + await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) + + finally: + await conn.close() + + except Exception as e: + await bot.edit_message_text(f"Ошибка при смене локации: {e}", chat_id=tg_id, message_id=callback_query.message.message_id) + + await callback_query.answer() diff --git a/handlers/notifications.py b/handlers/notifications.py index 55b4210b..4a7d408c 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -24,7 +24,7 @@ async def notify_expiring_keys(bot: Bot): # Получаем все ключи, которые истекают в течение следующих 10 часов threshold_time = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 # В миллисекундах records = await conn.fetch(''' - SELECT tg_id, email, expiry_time, client_id FROM keys + SELECT tg_id, email, expiry_time, client_id, server_id FROM keys WHERE expiry_time <= $1 AND expiry_time > $2 ''', threshold_time, datetime.utcnow().timestamp() * 1000) @@ -32,6 +32,7 @@ async def notify_expiring_keys(bot: Bot): tg_id = record['tg_id'] email = record['email'] expiry_time = record['expiry_time'] + server_id = record['server_id'] # Получаем server_id из записи # Рассчитываем оставшееся время и уменьшаем его на 3 часа time_left = (expiry_time / 1000) - datetime.utcnow().timestamp() @@ -55,7 +56,7 @@ async def notify_expiring_keys(bot: Bot): # Обрабатываем истекшие ключи expired_records = await conn.fetch(''' - SELECT tg_id, email, client_id FROM keys + SELECT tg_id, email, client_id, server_id FROM keys WHERE expiry_time <= $1 ''', datetime.utcnow().timestamp() * 1000) @@ -63,15 +64,16 @@ async def notify_expiring_keys(bot: Bot): tg_id = record['tg_id'] email = record['email'] client_id = record['client_id'] + server_id = record['server_id'] # Получаем server_id из записи # Удаляем ключ из базы данных await conn.execute('DELETE FROM keys WHERE client_id = $1', client_id) # Создаем сессию с использованием учетных данных - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) + session = login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) # Удаляем клиента из панели - delete_client(session, client_id) + delete_client(session, server_id, client_id) # Создаем клавиатуру с кнопкой "В профиль" keyboard = InlineKeyboardMarkup(inline_keyboard=[ @@ -90,6 +92,7 @@ async def notify_expiring_keys(bot: Bot): except Exception as e: print(f"Ошибка при отправке уведомлений: {e}") + @router.message(Command('send_to_all')) async def send_message_to_all_clients(message: types.Message): # Проверяем, является ли отправитель администратором diff --git a/handlers/pay.py b/handlers/pay.py index 901cf5b7..4fa376ea 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -1,7 +1,6 @@ import logging import uuid -import asyncpg from aiogram import Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup diff --git a/handlers/start.py b/handlers/start.py index 0b75fe2b..c1a1dad5 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -7,7 +7,7 @@ from aiogram.types import (BufferedInputFile, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message) from bot import bot -from config import (ADMIN_ID, CHANNEL_URL, +from config import (CHANNEL_URL, SUPPORT_CHAT_URL) router = Router()