diff --git a/backup.py b/backup.py index 196e024a..51c6636b 100644 --- a/backup.py +++ b/backup.py @@ -2,7 +2,6 @@ import os import subprocess from datetime import datetime -import aiofiles from aiogram.types import BufferedInputFile from config import ADMIN_ID, BACK_DIR, DB_NAME, DB_PASSWORD, DB_USER, PG_HOST, PG_PORT @@ -99,9 +98,11 @@ async def create_backup_and_send_to_admins(xui) -> None: async def _send_backup_to_admins(backup_file_path: str) -> None: try: from bot import bot + import aiofiles - with open(backup_file_path, "rb") as backup_file: - backup_input_file = BufferedInputFile(file=backup_file.read(), filename=os.path.basename(backup_file_path)) + async with aiofiles.open(backup_file_path, "rb") as backup_file: + backup_data = await backup_file.read() + backup_input_file = BufferedInputFile(file=backup_data, filename=os.path.basename(backup_file_path)) admin_ids = ADMIN_ID if isinstance(ADMIN_ID, list) else [ADMIN_ID] for admin_id in admin_ids: await bot.send_document(chat_id=admin_id, document=backup_input_file) diff --git a/client.py b/client.py index 8c4d452c..984a662a 100644 --- a/client.py +++ b/client.py @@ -1,5 +1,6 @@ from dataclasses import dataclass -from typing import Optional, Dict, Any, List +from typing import Any + import py3xui from config import LIMIT_IP, SUPERNODE @@ -22,7 +23,7 @@ class ClientConfig: sub_id: str -async def add_client(xui: py3xui.API, config: ClientConfig) -> Dict[str, Any]: +async def add_client(xui: py3xui.API, config: ClientConfig) -> dict[str, Any]: """ Добавляет клиента на сервер через 3x-ui. @@ -66,7 +67,7 @@ async def add_client(xui: py3xui.API, config: ClientConfig) -> Dict[str, Any]: async def extend_client_key( xui: py3xui.API, inbound_id: int, email: str, new_expiry_time: int, client_id: str, total_gb: int, sub_id: str -) -> Optional[bool]: +) -> bool | None: """ Обновляет срок действия ключа клиента. diff --git a/database.py b/database.py index b526d510..2492ddc6 100644 --- a/database.py +++ b/database.py @@ -1567,1447 +1567,4 @@ async def get_all_keys(session: Any = None): raise finally: if conn is not None and session is None: - await conn.close() - - -import json -from datetime import datetime -from typing import Any - -import asyncpg -import pytz - -from config import DATABASE_URL, REFERRAL_BONUS_PERCENTAGES -from logger import logger - - -async def create_temporary_data(session, tg_id: int, state: str, data: dict): - """Сохраняет временные данные пользователя.""" - await session.execute( - """ - INSERT INTO temporary_data (tg_id, state, data, updated_at) - VALUES ($1, $2, $3, $4) - ON CONFLICT (tg_id) - DO UPDATE SET state = $2, data = $3, updated_at = $4 - """, - tg_id, - state, - json.dumps(data), - datetime.utcnow(), - ) - - -async def get_temporary_data(session, tg_id: int) -> dict | None: - """Извлекает временные данные пользователя.""" - result = await session.fetchrow("SELECT state, data FROM temporary_data WHERE tg_id = $1", tg_id) - if result: - return {"state": result["state"], "data": json.loads(result["data"])} - return None - - -async def clear_temporary_data(session, tg_id: int): - # TODO rename delete_temporary_data - await session.execute("DELETE FROM temporary_data WHERE tg_id = $1", tg_id) - - -async def create_blocked_user(tg_id: int, conn: asyncpg.Connection): - await conn.execute( - "INSERT INTO blocked_users (tg_id) VALUES ($1) ON CONFLICT (tg_id) DO NOTHING", - tg_id, - ) - - -async def delete_blocked_user(tg_id: int | list[int], conn: asyncpg.Connection): - """ - Удаляет пользователя или список пользователей из списка заблокированных. - - :param tg_id: ID пользователя Telegram или список ID - :param conn: Подключение к базе данных - """ - if isinstance(tg_id, list): - await conn.execute("DELETE FROM blocked_users WHERE tg_id = ANY($1)", tg_id) - else: - await conn.execute("DELETE FROM blocked_users WHERE tg_id = $1", tg_id) - - -async def init_db(file_path: str = "assets/schema.sql"): - with open(file_path) as file: - sql_content = file.read() - - statements = [stmt.strip() for stmt in sql_content.split(";") if stmt.strip()] - conn = await asyncpg.connect(DATABASE_URL) - - try: - for statement in statements: - await conn.execute(statement) - except Exception as e: - logger.error(f"Error while executing SQL statement: {e}") - finally: - logger.info("Tables created successfully") - await conn.close() - - -async def check_unique_server_name(server_name: str, session: Any, cluster_name: str | None = None) -> bool: - """ - Проверяет уникальность имени сервера. - - :param server_name: Имя сервера. - :param session: Сессия базы данных. - :param cluster_name: Имя кластера (опционально). - :return: True, если имя сервера уникально, False, если уже существует. - """ - if cluster_name: - result = await session.fetchrow( - "SELECT 1 FROM servers WHERE server_name = $1 AND cluster_name = $2 LIMIT 1", server_name, cluster_name - ) - else: - result = await session.fetchrow("SELECT 1 FROM servers WHERE server_name = $1 LIMIT 1", server_name) - - return result is None - - -async def create_coupon(coupon_code: str, amount: float, usage_limit: int, session: Any): - """ - Создает новый купон в базе данных. - - Args: - coupon_code (str): Уникальный код купона. - amount (float): Сумма, которую дает купон. - usage_limit (int): Максимальное количество использований купона. - session (Any): Сессия базы данных для выполнения запроса. - - Raises: - Exception: В случае ошибки при создании купона. - - Example: - await create_coupon('SALE50', 50.0, 5, session) - """ - try: - await session.execute( - """ - INSERT INTO coupons (code, amount, usage_limit, usage_count, is_used) - VALUES ($1, $2, $3, 0, FALSE) - """, - coupon_code, - amount, - usage_limit, - ) - logger.info(f"Успешно создан купон с кодом {coupon_code} на сумму {amount}") - except Exception as e: - logger.error(f"Ошибка при создании купона {coupon_code}: {e}") - raise - - -async def get_coupon_by_code(coupon_code: str, session: Any) -> dict | None: - """ - Получает информацию о купоне по его коду. - - Args: - coupon_code (str): Код купона для поиска - session (Any): Сессия базы данных - - Returns: - dict | None: Словарь с информацией о купоне или None, если купон не найден - - id (int): ID купона - - usage_limit (int): Лимит использований - - usage_count (int): Текущее количество использований - - is_used (bool): Флаг использования - - amount (float): Сумма купона - - Raises: - Exception: В случае ошибки при выполнении запроса - """ - try: - result = await session.fetchrow( - """ - SELECT id, usage_limit, usage_count, is_used, amount - FROM coupons - WHERE code = $1 AND (usage_count < usage_limit OR usage_limit = 0) AND is_used = FALSE - """, - coupon_code, - ) - return dict(result) if result else None - except Exception as e: - logger.error(f"Ошибка при получении купона {coupon_code}: {e}") - raise - - -async def get_all_coupons(session: Any, page: int = 1, per_page: int = 10): - """ - Получает список купонов из базы данных с пагинацией. - - Args: - session (Any): Сессия базы данных для выполнения запроса - page (int): Номер страницы (по умолчанию 1) - per_page (int): Количество купонов на странице (по умолчанию 10) - - Returns: - dict: Словарь с информацией о купонах и пагинации: - - coupons (list): Список словарей с информацией о купонах - - total (int): Общее количество купонов - - pages (int): Общее количество страниц - - current_page (int): Текущая страница - - Raises: - Exception: В случае ошибки при получении данных из базы - """ - try: - offset = (page - 1) * per_page - coupons = await session.fetch( - """ - SELECT code, amount, usage_limit, usage_count - FROM coupons - ORDER BY id - LIMIT $1 OFFSET $2 - """, - per_page, - offset, - ) - - total_count = await session.fetchval("SELECT COUNT(*) FROM coupons") - total_pages = -(-total_count // per_page) # Округление вверх - - logger.info(f"Успешно получено {len(coupons)} купонов из базы данных (страница {page})") - - return {"coupons": coupons, "total": total_count, "pages": total_pages, "current_page": page} - except Exception as e: - logger.error(f"Критическая ошибка при получении списка купонов: {e}") - logger.exception("Трассировка стека ошибки получения купонов") - return {"coupons": [], "total": 0, "pages": 0, "current_page": page} - - -async def delete_coupon(coupon_code: str, session: Any): - """ - Удаляет купон из базы данных по его коду. - - Args: - coupon_code (str): Код купона для удаления - session (Any): Сессия базы данных для выполнения запроса - - Returns: - bool: True, если купон успешно удален, False если купон не найден или произошла ошибка - - Raises: - Exception: В случае ошибки при выполнении запроса к базе данных - - Example: - result = await delete_coupon('SALE50', session) - """ - try: - coupon_record = await session.fetchrow( - """ - SELECT id FROM coupons WHERE code = $1 - """, - coupon_code, - ) - - if not coupon_record: - logger.info(f"Купон {coupon_code} не найден в базе данных") - return False - - await session.execute( - """ - DELETE FROM coupons WHERE code = $1 - """, - coupon_code, - ) - - logger.info(f"Купон {coupon_code} успешно удален из базы данных") - return True - - except Exception as e: - logger.error(f"Произошла ошибка при удалении купона {coupon_code}: {e}") - return False - - -async def set_trial(tg_id: int, status: int, session: Any): - """ - Устанавливает статус триального периода для пользователя. - - Args: - tg_id (int): Telegram ID пользователя - status (int): Статус триального периода (0 - доступен, 1 - использован) - session (Any): Сессия базы данных - - Returns: - bool: True, если статус успешно установлен, False в случае ошибки - """ - try: - await session.execute( - """ - INSERT INTO connections (tg_id, trial) - VALUES ($1, $2) - ON CONFLICT (tg_id) - DO UPDATE SET trial = $2 - """, - tg_id, - status, - ) - status_text = "восстановлен" if status == 0 else "использован" - logger.info(f"Триальный период успешно {status_text} для пользователя {tg_id}") - return True - except Exception as e: - logger.error(f"Ошибка при установке статуса триального периода для пользователя {tg_id}: {e}") - return False - - -async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0, session: Any = None): - """ - Добавляет новое подключение для пользователя в базу данных. - - Args: - tg_id (int): Telegram ID пользователя - balance (float, optional): Начальный баланс пользователя. По умолчанию 0.0. - trial (int, optional): Статус триального периода. По умолчанию 0. - session (Any, optional): Сессия базы данных. - - Raises: - Exception: Если возникает ошибка при добавлении подключения в базу данных. - """ - try: - await session.execute( - """ - INSERT INTO connections (tg_id, balance, trial) - VALUES ($1, $2, $3) - """, - tg_id, - balance, - trial, - ) - logger.info( - f"Успешно добавлено новое подключение для пользователя {tg_id} с балансом {balance} и статусом триала {trial}" - ) - except Exception as e: - logger.error(f"Не удалось добавить подключение для пользователя {tg_id}. Причина: {e}") - raise - - -async def check_connection_exists(tg_id: int): - """ - Проверяет существование подключения для указанного пользователя в базе данных. - - Args: - tg_id (int): Telegram ID пользователя для проверки. - - Returns: - bool: True, если подключение существует, иначе False. - - Raises: - Exception: В случае ошибки при подключении к базе данных. - """ - try: - conn = await asyncpg.connect(DATABASE_URL) - exists = await conn.fetchval( - """ - SELECT EXISTS(SELECT 1 FROM connections WHERE tg_id = $1) - """, - tg_id, - ) - logger.info( - f"Проверка существования подключения для пользователя {tg_id}: {'найдено' if exists else 'не найдено'}" - ) - return exists - except Exception as e: - logger.error(f"Ошибка при проверке подключения для пользователя {tg_id}: {e}") - raise - finally: - if conn: - await conn.close() - - -async def store_key( - tg_id: int, - client_id: str, - email: str, - expiry_time: int, - key: str, - server_id: str, - session: Any, -): - """ - Сохраняет информацию о ключе в базу данных. - - Args: - tg_id (int): Telegram ID пользователя - client_id (str): Уникальный идентификатор клиента - email (str): Электронная почта или имя устройства - expiry_time (int): Время истечения ключа в миллисекундах - key (str): Ключ доступа - server_id (str): Идентификатор сервера - - Raises: - Exception: Если возникает ошибка при сохранении ключа в базу данных - """ - try: - await session.execute( - """ - 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, - ) - logger.info(f"Ключ успешно сохранен для пользователя {tg_id} на сервере {server_id}") - except Exception as e: - logger.error(f"Ошибка при сохранении ключа для пользователя {tg_id}: {e}") - raise - - -async def get_keys(tg_id: int, session: Any): - """ - Получает список ключей для указанного пользователя. - - Args: - tg_id (int): Telegram ID пользователя - - Returns: - list: Список записей ключей с информацией о клиенте, электронной почте, времени создания и ключе - - Raises: - Exception: В случае ошибки при подключении к базе данных или выполнении запроса - """ - try: - records = await session.fetch( - """ - SELECT client_id, email, created_at, key - FROM keys - WHERE tg_id = $1 - """, - tg_id, - ) - logger.info(f"Успешно получено {len(records)} ключей для пользователя {tg_id}") - return records - except Exception as e: - logger.error(f"Ошибка при получении ключей для пользователя {tg_id}: {e}") - raise - - -async def get_keys_by_server(tg_id: int | None, server_id: str, session: Any): - """ - Получает список ключей на определенном сервере. Если tg_id=None, возвращает все ключи на сервере. - - Args: - tg_id (int | None): Telegram ID пользователя или None для всех пользователей - server_id (str): Идентификатор сервера - - Returns: - list: Список записей ключей с информацией о клиенте, электронной почте, времени создания и ключе - - Raises: - Exception: В случае ошибки при подключении к базе данных или выполнении запроса - """ - try: - if tg_id is not None: - records = await session.fetch( - """ - SELECT * - FROM keys - WHERE tg_id = $1 AND server_id = $2 - """, - tg_id, - server_id, - ) - logger.info(f"Успешно получено {len(records)} ключей для пользователя {tg_id} на сервере {server_id}") - else: - records = await session.fetch( - """ - SELECT * - FROM keys - WHERE server_id = $1 - """, - server_id, - ) - logger.info(f"Успешно получено {len(records)} ключей на сервере {server_id}") - - return records - except Exception as e: - error_msg = f"Ошибка при получении ключей на сервере {server_id}" - if tg_id is not None: - error_msg += f" для пользователя {tg_id}" - logger.error(f"{error_msg}: {e}") - raise - - -async def get_balance(tg_id: int) -> float: - """ - Получает баланс пользователя из базы данных. - - Args: - tg_id (int): Telegram ID пользователя - - Returns: - float: Баланс пользователя, 0.0 если баланс не найден - - Raises: - Exception: В случае ошибки при подключении к базе данных или выполнении запроса - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) - logger.info(f"Получен баланс для пользователя {tg_id}: {balance}") - return round(balance, 1) if balance is not None else 0.0 - except Exception as e: - logger.error(f"Ошибка при получении баланса для пользователя {tg_id}: {e}") - return 0.0 - finally: - if conn: - await conn.close() - - -async def update_balance(tg_id: int, amount: float, session: Any = None): - """ - Обновляет баланс пользователя в базе данных. - - Args: - tg_id (int): Telegram ID пользователя - amount (float): Сумма для обновления баланса - session (Any, optional): Сессия базы данных. Если не передана, создается новая. - - Raises: - Exception: В случае ошибки при подключении к базе данных или обновлении баланса - """ - conn = None - try: - if session is None: - conn = await asyncpg.connect(DATABASE_URL) - session = conn - - await session.execute( - """ - UPDATE connections - SET balance = balance + $1 - WHERE tg_id = $2 - """, - amount, - tg_id, - ) - logger.info(f"Баланс пользователя {tg_id} обновлен на сумму {amount}") - - await handle_referral_on_balance_update(tg_id, amount) - - except Exception as e: - logger.error(f"Ошибка при обновлении баланса для пользователя {tg_id}: {e}") - raise - finally: - if conn is not None: - await conn.close() - - -async def get_trial(tg_id: int, session: Any) -> int: - """ - Получает статус триала для пользователя из базы данных. - - Args: - tg_id (int): Telegram ID пользователя - session (Any): Сессия базы данных - - Returns: - int: Статус триала (0 - не использован, 1 - использован) - """ - try: - trial = await session.fetchval("SELECT trial FROM connections WHERE tg_id = $1", tg_id) - logger.info(f"Получен статус триала для пользователя {tg_id}: {trial}") - return trial if trial is not None else 0 - except Exception as e: - logger.error(f"Ошибка при получении статуса триала для пользователя {tg_id}: {e}") - return 0 - - -async def get_key_count(tg_id: int) -> int: - """ - Получает количество ключей для указанного пользователя. - - Args: - tg_id (int): Telegram ID пользователя - - Returns: - int: Количество ключей пользователя, 0 если ключей нет - - Raises: - Exception: В случае ошибки при подключении к базе данных - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - count = await conn.fetchval("SELECT COUNT(*) FROM keys WHERE tg_id = $1", tg_id) - logger.info(f"Получено количество ключей для пользователя {tg_id}: {count}") - return count if count is not None else 0 - except Exception as e: - logger.error(f"Ошибка при получении количества ключей для пользователя {tg_id}: {e}") - return 0 - finally: - if conn: - await conn.close() - - -async def add_referral(referred_tg_id: int, referrer_tg_id: int, session: Any): - try: - if referred_tg_id == referrer_tg_id: - logger.warning(f"Пользователь {referred_tg_id} попытался использовать свою собственную реферальную ссылку.") - return - - await session.execute( - """ - INSERT INTO referrals (referred_tg_id, referrer_tg_id) - VALUES ($1, $2) - """, - referred_tg_id, - referrer_tg_id, - ) - logger.info(f"Добавлена реферальная связь: приглашенный {referred_tg_id}, пригласивший {referrer_tg_id}") - except Exception as e: - logger.error(f"Ошибка при добавлении реферала: {e}") - raise - - -async def handle_referral_on_balance_update(tg_id: int, amount: float): - """ - Обработка многоуровневой реферальной системы при обновлении баланса пользователя. - - Метод анализирует цепочку рефералов для указанного пользователя и начисляет - бонусы реферерам на разных уровнях согласно настроенным процентам. - - Args: - tg_id (int): Идентификатор Telegram пользователя, пополнившего баланс - amount (float): Сумма пополнения баланса - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Начало обработки реферальной системы для пользователя {tg_id}") - - MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys()) - if MAX_REFERRAL_LEVELS == 0: - logger.warning("Реферальные бонусы отключены.") - return - - visited_tg_ids = set() - current_tg_id = tg_id - referral_chain = [] - - for level in range(1, MAX_REFERRAL_LEVELS + 1): - if current_tg_id in visited_tg_ids: - logger.warning(f"Обнаружен цикл в реферальной цепочке для пользователя {current_tg_id}. Прекращение.") - break - - visited_tg_ids.add(current_tg_id) - - referral = await conn.fetchrow( - """ - SELECT referrer_tg_id - FROM referrals - WHERE referred_tg_id = $1 - """, - current_tg_id, - ) - - if not referral: - logger.info(f"Цепочка рефералов завершена на уровне {level}.") - break - - referrer_tg_id = referral["referrer_tg_id"] - - if referrer_tg_id in visited_tg_ids: - logger.warning(f"Реферер {referrer_tg_id} уже обработан. Пропуск.") - break - - referral_chain.append({"tg_id": referrer_tg_id, "level": level}) - current_tg_id = referrer_tg_id - - for referral in referral_chain: - referrer_tg_id = referral["tg_id"] - level = referral["level"] - - bonus_percent = REFERRAL_BONUS_PERCENTAGES.get(level, 0) - if bonus_percent <= 0: - logger.warning(f"Процент бонуса для уровня {level} равен 0. Пропуск.") - continue - - bonus = round(amount * bonus_percent, 2) - - if bonus > 0: - logger.info(f"Начисление бонуса {bonus} рублей рефереру {referrer_tg_id} на уровне {level}.") - await update_balance(referrer_tg_id, bonus) - - except Exception as e: - logger.error(f"Ошибка при обработке многоуровневой реферальной системы для {tg_id}: {e}") - finally: - if conn: - await conn.close() - - -async def get_referral_stats(referrer_tg_id: int): - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info( - f"Установлено подключение к базе данных для получения статистики рефералов пользователя {referrer_tg_id}" - ) - - total_referrals = await conn.fetchval( - """ - SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1 - """, - referrer_tg_id, - ) - logger.debug(f"Получено общее количество рефералов: {total_referrals}") - - active_referrals = await conn.fetchval( - """ - SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1 AND reward_issued = TRUE - """, - referrer_tg_id, - ) - logger.debug(f"Получено количество активных рефералов: {active_referrals}") - - MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys()) - - referrals_by_level_records = await conn.fetch( - f""" - WITH RECURSIVE referral_levels AS ( - SELECT referred_tg_id, referrer_tg_id, 1 AS level - FROM referrals - WHERE referrer_tg_id = $1 - - UNION - - SELECT r.referred_tg_id, r.referrer_tg_id, rl.level + 1 - FROM referrals r - JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id - WHERE rl.level < {MAX_REFERRAL_LEVELS} - ) - SELECT level, - COUNT(*) AS level_count, - COUNT(CASE WHEN reward_issued = TRUE THEN 1 END) AS active_level_count - FROM referral_levels rl - JOIN referrals r ON rl.referred_tg_id = r.referred_tg_id - GROUP BY level - ORDER BY level - """, - referrer_tg_id, - ) - - referrals_by_level = { - record["level"]: { - "total": record["level_count"], - "active": record["active_level_count"], - } - for record in referrals_by_level_records - } - logger.debug(f"Получена статистика рефералов по уровням: {referrals_by_level}") - - total_referral_bonus = await conn.fetchval( - f""" - WITH RECURSIVE referral_levels AS ( - SELECT - referred_tg_id, - referrer_tg_id, - 1 AS level - FROM referrals - WHERE referrer_tg_id = $1 - - UNION - - SELECT - r.referred_tg_id, - r.referrer_tg_id, - rl.level + 1 - FROM referrals r - JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id - WHERE rl.level < {MAX_REFERRAL_LEVELS} - ) - SELECT - COALESCE(SUM(p.amount * ( - CASE - {" ".join([f"WHEN rl.level = {level} THEN {REFERRAL_BONUS_PERCENTAGES[level]}" for level in REFERRAL_BONUS_PERCENTAGES])} - ELSE 0 - END)), 0) AS total_bonus - FROM referral_levels rl - JOIN payments p ON rl.referred_tg_id = p.tg_id - WHERE p.status = 'success' AND rl.level <= {MAX_REFERRAL_LEVELS} - """, - referrer_tg_id, - ) - - logger.debug(f"Получена общая сумма бонусов от рефералов: {total_referral_bonus}") - - return { - "total_referrals": total_referrals, - "active_referrals": active_referrals, - "referrals_by_level": referrals_by_level, - "total_referral_bonus": total_referral_bonus, - } - - except Exception as e: - logger.error(f"Ошибка при получении статистики рефералов для пользователя {referrer_tg_id}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных") - - -async def update_key_expiry(client_id: str, new_expiry_time: int, session: Any): - """ - Обновление времени истечения ключа для указанного клиента. - - Args: - client_id (str): Уникальный идентификатор клиента - new_expiry_time (int): Новое время истечения ключа - - Raises: - Exception: В случае ошибки при подключении к базе данных или обновлении ключа - """ - try: - await session.execute( - """ - UPDATE keys - SET expiry_time = $1, notified = FALSE, notified_24h = FALSE - WHERE client_id = $2 - """, - new_expiry_time, - client_id, - ) - logger.info(f"Успешно обновлено время истечения ключа для клиента {client_id}") - - except Exception as e: - logger.error(f"Ошибка при обновлении времени истечения ключа для клиента {client_id}: {e}") - raise - - -async def add_balance_to_client(client_id: str, amount: float): - """ - Добавление баланса клиенту по его идентификатору Telegram. - - Args: - client_id (str): Идентификатор клиента в Telegram - amount (float): Сумма для пополнения баланса - - Raises: - Exception: В случае ошибки при подключении к базе данных или обновлении баланса - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для пополнения баланса клиента {client_id}") - - await conn.execute( - """ - UPDATE connections - SET balance = balance + $1 - WHERE tg_id = $2 - """, - amount, - client_id, - ) - logger.info(f"Успешно пополнен баланс клиента {client_id} на сумму {amount}") - - except Exception as e: - logger.error(f"Ошибка при пополнении баланса для клиента {client_id}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных") - - -async def get_client_id_by_email(email: str): - """ - Получение идентификатора клиента по электронной почте. - - Args: - email (str): Электронная почта клиента - - Returns: - str: Идентификатор клиента или None, если клиент не найден - - Raises: - Exception: В случае ошибки при подключении к базе данных или выполнении запроса - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для поиска client_id по email: {email}") - - client_id = await conn.fetchval( - """ - SELECT client_id FROM keys WHERE email = $1 - """, - email, - ) - - if client_id: - logger.info(f"Найден client_id для email: {email}") - else: - logger.warning(f"Не найден client_id для email: {email}") - - return client_id - - except Exception as e: - logger.error(f"Ошибка при получении client_id для email {email}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных") - - -async def get_tg_id_by_client_id(client_id: str): - """ - Получение Telegram ID по идентификатору клиента. - - Args: - client_id (str): Идентификатор клиента - - Returns: - int или None: Telegram ID клиента, если найден, иначе None - - Raises: - Exception: В случае ошибки при подключении к базе данных или выполнении запроса - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для поиска Telegram ID по client_id: {client_id}") - - result = await conn.fetchrow("SELECT tg_id FROM keys WHERE client_id = $1", client_id) - - if result: - logger.info(f"Найден Telegram ID для client_id: {client_id}") - return result["tg_id"] - else: - logger.warning(f"Не найден Telegram ID для client_id: {client_id}") - return None - - except Exception as e: - logger.error(f"Ошибка при получении Telegram ID для client_id {client_id}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных") - - -async def upsert_user( - tg_id: int, - username: str = None, - first_name: str = None, - last_name: str = None, - language_code: str = None, - is_bot: bool = False, -): - """ - Обновляет или вставляет информацию о пользователе в базу данных. - - Args: - tg_id (int): Идентификатор пользователя в Telegram - username (str, optional): Имя пользователя в Telegram - first_name (str, optional): Имя пользователя - last_name (str, optional): Фамилия пользователя - language_code (str, optional): Код языка пользователя - is_bot (bool, optional): Флаг, указывающий является ли пользователь ботом - - Raises: - Exception: В случае ошибки при работе с базой данных - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для обновления пользователя {tg_id}") - - await conn.execute( - """ - INSERT INTO users (tg_id, username, first_name, last_name, language_code, is_bot, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) - ON CONFLICT (tg_id) DO UPDATE - SET - username = COALESCE(EXCLUDED.username, users.username), - first_name = COALESCE(EXCLUDED.first_name, users.first_name), - last_name = COALESCE(EXCLUDED.last_name, users.last_name), - language_code = COALESCE(EXCLUDED.language_code, users.language_code), - is_bot = EXCLUDED.is_bot, - updated_at = CURRENT_TIMESTAMP - """, - tg_id, - username, - first_name, - last_name, - language_code, - is_bot, - ) - logger.info(f"Успешно обновлена информация о пользователе {tg_id}") - except Exception as e: - logger.error(f"Ошибка при обновлении информации о пользователе {tg_id}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных") - - -async def add_payment(tg_id: int, amount: float, payment_system: str): - """ - Добавляет информацию о платеже в базу данных. - - Args: - tg_id (int): Идентификатор пользователя в Telegram - amount (float): Сумма платежа - payment_system (str): Система оплаты - - Raises: - Exception: В случае ошибки при добавлении платежа - """ - conn = None - try: - conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для добавления платежа пользователя {tg_id}") - - await conn.execute( - """ - INSERT INTO payments (tg_id, amount, payment_system, status) - VALUES ($1, $2, $3, 'success') - """, - tg_id, - amount, - payment_system, - ) - logger.info(f"Успешно добавлен платеж для пользователя {tg_id} на сумму {amount}") - except Exception as e: - logger.error(f"Ошибка при добавлении платежа для пользователя {tg_id}: {e}") - raise - finally: - if conn: - await conn.close() - logger.info("Закрытие подключения к базе данных после добавления платежа") - - -async def add_notification(tg_id: int, notification_type: str, session: Any): - """ - Добавляет запись о notification в базу данных. - - Args: - tg_id (int): Идентификатор пользователя в Telegram - notification_type (str): Тип уведомления - session (Any): Сессия базы данных для выполнения запроса - - Raises: - Exception: В случае ошибки при добавлении notification - """ - try: - await session.execute( - """ - INSERT INTO notifications (tg_id, notification_type) - VALUES ($1, $2) - ON CONFLICT (tg_id, notification_type) - DO UPDATE SET last_notification_time = NOW() - """, - tg_id, - notification_type, - ) - logger.info(f"Успешно добавлено уведомление типа {notification_type} для пользователя {tg_id}") - except Exception as e: - logger.error(f"Ошибка при добавлении notification для пользователя {tg_id}: {e}") - raise - - -async def check_notification_time(tg_id: int, notification_type: str, hours: int = 12, session: Any = None) -> bool: - """ - Проверяет, прошло ли указанное количество часов с момента последнего уведомления. - - Args: - tg_id (int): Идентификатор пользователя в Telegram - notification_type (str): Тип уведомления - hours (int, optional): Количество часов для проверки. По умолчанию 12. - session (Any): Сессия базы данных для выполнения запроса - - Returns: - bool: True, если с момента последнего уведомления прошло больше указанного времени, иначе False - - Raises: - Exception: В случае ошибки при проверке времени уведомления - """ - conn = None - try: - conn = session if session is not None else await asyncpg.connect(DATABASE_URL) - - result = await conn.fetchval( - """ - SELECT - CASE - WHEN MAX(last_notification_time) IS NULL THEN TRUE - WHEN NOW() - MAX(last_notification_time) > ($1 * INTERVAL '1 hour') THEN TRUE - ELSE FALSE - END AS can_notify - FROM notifications - WHERE tg_id = $2 AND notification_type = $3 - """, - hours, - tg_id, - notification_type, - ) - - can_notify = result if result is not None else True - - logger.info( - f"Проверка уведомления типа {notification_type} для пользователя {tg_id}: {'можно отправить' if can_notify else 'слишком рано'}" - ) - - return can_notify - - except Exception as e: - logger.error(f"Ошибка при проверке времени уведомления для пользователя {tg_id}: {e}") - return False - - finally: - if conn is not None and session is None: - await conn.close() - - -async def get_servers(session: Any = None): - conn = None - try: - conn = session if session is not None else await asyncpg.connect(DATABASE_URL) - - result = await conn.fetch( - """ - SELECT cluster_name, server_name, api_url, subscription_url, inbound_id - FROM servers - """ - ) - servers = {} - for row in result: - cluster_name = row["cluster_name"] - if cluster_name not in servers: - servers[cluster_name] = [] - - servers[cluster_name].append( - { - "server_name": row["server_name"], - "api_url": row["api_url"], - "subscription_url": row["subscription_url"], - "inbound_id": row["inbound_id"], - } - ) - - return servers - - finally: - if conn is not None and session is None: - await conn.close() - - -async def delete_user_data(session: Any, tg_id: int): - try: - await session.execute("DELETE FROM gifts WHERE sender_tg_id = $1 OR recipient_tg_id = $1", tg_id) - except Exception as e: - logger.warning(f"У Вас версия без подарков для {tg_id}: {e}") - await session.execute("DELETE FROM payments WHERE tg_id = $1", tg_id) - await session.execute("DELETE FROM users WHERE tg_id = $1", tg_id) - await session.execute("DELETE FROM connections WHERE tg_id = $1", tg_id) - await delete_key(tg_id, session) - await session.execute("DELETE FROM referrals WHERE referrer_tg_id = $1", tg_id) - - -async def store_gift_link( - gift_id: str, - sender_tg_id: int, - selected_months: int, - expiry_time: datetime, - gift_link: str, - session: Any = None, -): - """ - Добавляет информацию о подарке в базу данных. - - Args: - gift_id (str): Уникальный идентификатор подарка - sender_tg_id (int): Идентификатор пользователя, который отправил подарок - selected_months (int): Количество месяцев подписки - expiry_time (datetime): Время окончания подписки - gift_link (str): Ссылка для активации подарка - session (Any): Сессия базы данных для выполнения запроса - - Returns: - bool: True, если информация о подарке успешно добавлена, иначе False - - Raises: - Exception: В случае ошибки при сохранении информации о подарке - """ - conn = None - try: - conn = session if session is not None else await asyncpg.connect(DATABASE_URL) - - result = await conn.execute( - """ - INSERT INTO gifts (gift_id, sender_tg_id, recipient_tg_id, selected_months, expiry_time, gift_link, created_at, is_used) - VALUES ($1, $2, NULL, $3, $4, $5, $6, FALSE) - """, - gift_id, - sender_tg_id, - selected_months, - expiry_time, - gift_link, - datetime.utcnow(), - ) - - if result: - logger.info(f"Подарок с ID {gift_id} успешно добавлен в базу данных.") - return True - else: - logger.error(f"Не удалось добавить подарок с ID {gift_id} в базу данных.") - return False - except Exception as e: - logger.error(f"Ошибка при сохранении подарка с ID {gift_id} в базе данных: {e}") - return False - - finally: - if conn is not None and session is None: - await conn.close() - - -async def get_key_details(email, session): - record = await session.fetchrow( - """ - SELECT k.key, k.email, k.expiry_time, k.server_id,k, k.client_id, k.created_at, c.tg_id, c.balance - FROM keys k - JOIN connections c ON k.tg_id = c.tg_id - WHERE k.email = $1 - """, - email, - ) - - if not record: - return None - - cluster_name = record["server_id"] - - moscow_tz = pytz.timezone("Europe/Moscow") - expiry_date = datetime.fromtimestamp(record["expiry_time"] / 1000, tz=moscow_tz) - current_date = datetime.now(moscow_tz) - 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}" - - return { - "key": record["key"], - "created_at": record["created_at"], - "expiry_time": record["expiry_time"], - "client_id": record["client_id"], - "expiry_date": expiry_date.strftime("%d %B %Y года %H:%M"), - "days_left_message": days_left_message, - "server_name": cluster_name, - "balance": record["balance"], - "tg_id": record["tg_id"], - "email": record["email"], - } - - -async def delete_key(identifier, session): - """ - Удаляет ключ из базы данных по client_id или tg_id - - Args: - identifier (str): client_id или tg_id для удаления - session: Сессия базы данных - - Raises: - Exception: В случае ошибки при удалении ключа - """ - try: - # Проверяем, является ли идентификатор числом (tg_id) или строкой (client_id) - if identifier.isdigit(): - query = "DELETE FROM keys WHERE tg_id = $1" - else: - query = "DELETE FROM keys WHERE client_id = $1" - - await session.execute(query, identifier) - logger.info(f"Ключ с идентификатором {identifier} успешно удалён") - except Exception as e: - logger.error(f"Ошибка при удалении ключа с идентификатором {identifier} из базы данных: {e}") - - -async def create_server( - cluster_name: str, server_name: str, api_url: str, subscription_url: str, inbound_id: int, session: Any -): - """ - Добавляет новый сервер в базу данных. - - Args: - cluster_name (str): Название кластера - server_name (str): Название сервера - api_url (str): URL API сервера - subscription_url (str): URL подписки - inbound_id (int): ID входящего подключения - session (Any): Сессия базы данных - - Raises: - Exception: В случае ошибки при добавлении сервера - """ - try: - await session.execute( - """ - INSERT INTO servers (cluster_name, server_name, api_url, subscription_url, inbound_id) - VALUES ($1, $2, $3, $4, $5) - """, - cluster_name, - server_name, - api_url, - subscription_url, - inbound_id, - ) - logger.info(f"Сервер {server_name} успешно добавлен в кластер {cluster_name}") - except Exception as e: - logger.error(f"Ошибка при добавлении сервера {server_name} в кластер {cluster_name}: {e}") - raise - - -async def delete_server(server_name: str, session: Any): - """ - Удаляет сервер из базы данных по его названию. - - Args: - server_name (str): Название сервера для удаления - session (Any): Сессия базы данных - - Raises: - Exception: В случае ошибки при удалении сервера - """ - try: - await session.execute( - """ - DELETE FROM servers WHERE server_name = $1 - """, - server_name, - ) - logger.info(f"Сервер {server_name} успешно удалён из базы данных") - except Exception as e: - logger.error(f"Ошибка при удалении сервера {server_name} из базы данных: {e}") - raise - - -async def create_coupon_usage(coupon_id: int, user_id: int, session: Any): - """ - Создаёт запись об использовании купона в базе данных. - - Args: - coupon_id (int): ID купона - user_id (int): ID пользователя - session (Any): Сессия базы данных - - Raises: - Exception: В случае ошибки при создании записи - """ - try: - await session.execute( - """ - INSERT INTO coupon_usages (coupon_id, user_id, used_at) - VALUES ($1, $2, $3) - """, - coupon_id, - user_id, - datetime.utcnow(), - ) - logger.info(f"Создана запись об использовании купона {coupon_id} пользователем {user_id}") - except Exception as e: - logger.error(f"Ошибка при создании записи об использовании купона {coupon_id} пользователем {user_id}: {e}") - raise - - -async def check_coupon_usage(coupon_id: int, user_id: int, session: Any) -> bool: - """ - Проверяет, использовал ли пользователь данный купон. - - Args: - coupon_id (int): ID купона для проверки - user_id (int): ID пользователя для проверки - session (Any): Сессия базы данных - - Returns: - bool: True если купон уже использован, False если нет - - Raises: - Exception: В случае ошибки при выполнении запроса - """ - try: - result = await session.fetchrow( - """ - SELECT 1 FROM coupon_usages WHERE coupon_id = $1 AND user_id = $2 - """, - coupon_id, - user_id, - ) - return result is not None - except Exception as e: - logger.error(f"Ошибка при проверке использования купона {coupon_id} пользователем {user_id}: {e}") - raise - - -async def update_coupon_usage_count(coupon_id: int, session: Any): - """ - Обновляет счетчик использования купона и его статус. - - Args: - coupon_id (int): ID купона для обновления - session (Any): Сессия базы данных - - Raises: - Exception: В случае ошибки при обновлении данных купона - """ - try: - await session.execute( - """ - UPDATE coupons - SET usage_count = usage_count + 1, - is_used = CASE WHEN usage_count + 1 >= usage_limit AND usage_limit > 0 THEN TRUE ELSE FALSE END - WHERE id = $1 - """, - coupon_id, - ) - logger.info(f"Успешно обновлен счетчик использования купона {coupon_id}") - except Exception as e: - logger.error(f"Ошибка при обновлении счетчика использования купона {coupon_id}: {e}") - raise - - -async def get_last_payments(tg_id: int, session: Any): - """ - Получает последние 3 платежа пользователя. - - Args: - tg_id (int): Telegram ID пользователя - session (Any): Сессия базы данных - - Returns: - list: Список последних платежей пользователя - - Raises: - Exception: В случае ошибки при выполнении запроса - """ - try: - records = await session.fetch( - """ - SELECT amount, payment_system, status, created_at - FROM payments - WHERE tg_id = $1 - ORDER BY created_at DESC - LIMIT 3 - """, - tg_id, - ) - logger.info(f"Успешно получены последние платежи для пользователя {tg_id}") - return records - except Exception as e: - logger.error(f"Ошибка при получении последних платежей для пользователя {tg_id}: {e}") - raise + await conn.close() \ No newline at end of file diff --git a/handlers/admin/admin_bans.py b/handlers/admin/admin_bans.py index 9ab8e7cf..833f3084 100644 --- a/handlers/admin/admin_bans.py +++ b/handlers/admin/admin_bans.py @@ -1,7 +1,7 @@ from typing import Any from aiogram import F, Router -from aiogram.types import CallbackQuery, BufferedInputFile +from aiogram.types import BufferedInputFile, CallbackQuery from database import delete_user_data from filters.admin import IsAdminFilter diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index b65ea5e4..52c10a4e 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -4,9 +4,9 @@ from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from database import delete_coupon, get_all_coupons, create_coupon +from database import create_coupon, delete_coupon, get_all_coupons from filters.admin import IsAdminFilter -from keyboards.admin.coupons_kb import build_coupons_kb, build_coupons_list_kb, AdminCouponDeleteCallback +from keyboards.admin.coupons_kb import AdminCouponDeleteCallback, build_coupons_kb, build_coupons_list_kb from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb from logger import logger diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index eb9eb744..f3d76f87 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -5,7 +5,7 @@ from aiogram.types import CallbackQuery from bot import version from filters.admin import IsAdminFilter -from keyboards.admin.panel_kb import build_panel_kb, AdminPanelCallback, build_management_kb +from keyboards.admin.panel_kb import AdminPanelCallback, build_management_kb, build_panel_kb router = Router() diff --git a/handlers/admin/admin_restart.py b/handlers/admin/admin_restart.py index 58c1a115..d2501758 100644 --- a/handlers/admin/admin_restart.py +++ b/handlers/admin/admin_restart.py @@ -4,7 +4,7 @@ from aiogram import F, Router from aiogram.types import CallbackQuery from filters.admin import IsAdminFilter -from keyboards.admin.panel_kb import build_restart_kb, AdminPanelCallback, build_admin_back_kb +from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb, build_restart_kb router = Router() diff --git a/handlers/admin/admin_sender.py b/handlers/admin/admin_sender.py index 70563437..0be8e6e8 100644 --- a/handlers/admin/admin_sender.py +++ b/handlers/admin/admin_sender.py @@ -8,7 +8,7 @@ from aiogram.types import CallbackQuery from filters.admin import IsAdminFilter from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb -from keyboards.admin.sender_kb import build_sender_kb, AdminSenderCallback +from keyboards.admin.sender_kb import AdminSenderCallback, build_sender_kb from logger import logger router = Router() diff --git a/handlers/admin/admin_servers.py b/handlers/admin/admin_servers.py index 51257e9a..2969e83d 100644 --- a/handlers/admin/admin_servers.py +++ b/handlers/admin/admin_servers.py @@ -6,15 +6,15 @@ from py3xui import AsyncApi from backup import create_backup_and_send_to_admins from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL -from database import create_server, check_unique_server_name, delete_server, get_keys_by_server, get_servers +from database import check_unique_server_name, delete_server, get_servers from filters.admin import IsAdminFilter from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb from keyboards.admin.servers_kb import ( - build_manage_server_kb, + AdminServerEditorCallback, + build_clusters_editor_kb, build_delete_server_kb, build_manage_cluster_kb, - build_clusters_editor_kb, - AdminServerEditorCallback, + build_manage_server_kb, ) router = Router() diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index 2d470b56..0538d245 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -16,8 +16,8 @@ from database import ( get_key_details, get_keys, get_servers, - update_trial, update_key_expiry, + update_trial, ) from filters.admin import IsAdminFilter from handlers.keys.key_utils import ( diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py index 4601d646..fe76bec5 100644 --- a/handlers/admin/admin_users.py +++ b/handlers/admin/admin_users.py @@ -21,17 +21,17 @@ from handlers.keys.key_utils import ( from handlers.utils import sanitize_key_name from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb from keyboards.admin.users_kb import ( - build_user_edit_kb, - build_key_edit_kb, - build_key_delete_kb, - build_user_delete_kb, AdminUserEditorCallback, - build_editor_kb, - build_users_balance_kb, - build_users_balance_change_kb, - build_user_key_kb, - build_users_key_expiry_kb, AdminUserKeyEditorCallback, + build_editor_kb, + build_key_delete_kb, + build_key_edit_kb, + build_user_delete_kb, + build_user_edit_kb, + build_user_key_kb, + build_users_balance_change_kb, + build_users_balance_kb, + build_users_key_expiry_kb, build_users_key_show_kb, ) from logger import logger @@ -428,7 +428,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext, se text = f"✅ Время действия ключа изменено на {message.text}" await change_expiry_time(expiry_time, email, session) except ValueError: - text = f"🚫 Пожалуйста, используйте корректный формат даты!" + text = "🚫 Пожалуйста, используйте корректный формат даты!" except Exception as e: text = f"❗ Произошла ошибка во время изменения времени действия ключа: {e}" diff --git a/handlers/coupons.py b/handlers/coupons.py index b652cf1c..0a125da2 100644 --- a/handlers/coupons.py +++ b/handlers/coupons.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import Any from aiogram import F, Router, types diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 8264ee79..3330f2da 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -24,10 +24,10 @@ from config import ( USE_NEW_PAYMENT_FLOW, ) from database import ( + create_temporary_data, get_balance, get_key_details, get_trial, - create_temporary_data, store_key, update_balance, ) diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py index 80461348..8de0861b 100644 --- a/handlers/keys/key_utils.py +++ b/handlers/keys/key_utils.py @@ -4,7 +4,7 @@ from typing import Any from py3xui import AsyncApi from client import ClientConfig, add_client, delete_client, extend_client_key -from config import ADMIN_PASSWORD, ADMIN_USERNAME, LIMIT_IP, SUPERNODE, TOTAL_GB, PUBLIC_LINK +from config import ADMIN_PASSWORD, ADMIN_USERNAME, LIMIT_IP, PUBLIC_LINK, SUPERNODE, TOTAL_GB from database import get_servers, store_key from handlers.utils import get_least_loaded_cluster from logger import logger diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 7bbf3eb8..5586c164 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -10,7 +10,6 @@ import pytz from aiogram import F, Router, types from aiogram.types import BufferedInputFile, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder -from handlers.payments.yookassa_pay import process_custom_amount_input from bot import bot from config import ( @@ -29,14 +28,13 @@ from config import ( ) from database import ( check_server_name_by_cluster, + create_temporary_data, delete_key, get_balance, get_key_details, get_keys, get_keys_by_server, get_servers, - create_temporary_data, - store_key, update_balance, update_key_expiry, ) @@ -55,6 +53,7 @@ from handlers.keys.key_utils import ( update_subscription, ) from handlers.payments.robokassa_pay import handle_custom_amount_input +from handlers.payments.yookassa_pay import process_custom_amount_input from handlers.texts import ( DISCOUNTS, KEY_NOT_FOUND_MSG, diff --git a/handlers/notifications.py b/handlers/notifications.py index 35c7c2c6..614ee497 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -23,9 +23,9 @@ from config import ( TRIAL_TIME, ) from database import ( - create_blocked_user, add_notification, check_notification_time, + create_blocked_user, delete_key, get_balance, get_servers, diff --git a/keyboards/admin/bans_kb.py b/keyboards/admin/bans_kb.py index 0c5554d4..3c5c5145 100644 --- a/keyboards/admin/bans_kb.py +++ b/keyboards/admin/bans_kb.py @@ -1,7 +1,7 @@ from aiogram.types import InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder -from keyboards.admin.panel_kb import build_admin_back_btn, AdminPanelCallback +from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_btn def build_bans_kb() -> InlineKeyboardMarkup: diff --git a/keyboards/admin/panel_kb.py b/keyboards/admin/panel_kb.py index 6b16151f..2c23381d 100644 --- a/keyboards/admin/panel_kb.py +++ b/keyboards/admin/panel_kb.py @@ -1,5 +1,5 @@ from aiogram.filters.callback_data import CallbackData -from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder diff --git a/keyboards/admin/stats_kb.py b/keyboards/admin/stats_kb.py index 3a6e4092..b8046593 100644 --- a/keyboards/admin/stats_kb.py +++ b/keyboards/admin/stats_kb.py @@ -1,7 +1,7 @@ from aiogram.types import InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder -from keyboards.admin.panel_kb import build_admin_back_btn, AdminPanelCallback +from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_btn def build_stats_kb() -> InlineKeyboardMarkup: diff --git a/keyboards/admin/users_kb.py b/keyboards/admin/users_kb.py index 78378f9d..54d056a9 100644 --- a/keyboards/admin/users_kb.py +++ b/keyboards/admin/users_kb.py @@ -143,7 +143,7 @@ def build_user_delete_kb(tg_id: int): def build_user_key_kb(tg_id: int, email: str) -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() builder.button( - text=f"🔙 Назад", callback_data=AdminUserEditorCallback(action="users_key_edit", tg_id=tg_id, data=email).pack() + text="🔙 Назад", callback_data=AdminUserEditorCallback(action="users_key_edit", tg_id=tg_id, data=email).pack() ) builder.adjust(1) return builder.as_markup() diff --git a/servers.py b/servers.py index 521b55f1..6d29431c 100644 --- a/servers.py +++ b/servers.py @@ -9,7 +9,7 @@ from ping3 import ping from bot import bot from config import ADMIN_ID, DATABASE_URL, PING_TIME -from database import create_server, check_unique_server_name, get_servers +from database import check_unique_server_name, create_server, get_servers from logger import logger try: