diff --git a/backup.py b/backup.py index 8550e619..eeeec50a 100644 --- a/backup.py +++ b/backup.py @@ -3,8 +3,8 @@ import subprocess from datetime import datetime from aiogram.types import BufferedInputFile - from config import ADMIN_ID, BACK_DIR, DB_NAME, DB_PASSWORD, DB_USER, PG_HOST, PG_PORT + from logger import logger diff --git a/bot.py b/bot.py index 0ab0ea90..0d21ffde 100644 --- a/bot.py +++ b/bot.py @@ -8,8 +8,8 @@ from aiogram.filters import ExceptionTypeFilter from aiogram.fsm.storage.memory import MemoryStorage from aiogram.types import BufferedInputFile, ErrorEvent from aiogram.utils.markdown import hbold - from config import ADMIN_ID, API_TOKEN + from filters.private import IsPrivateFilter from logger import logger from middlewares import register_middleware diff --git a/client.py b/client.py index 219ccca5..5f680f9f 100644 --- a/client.py +++ b/client.py @@ -2,8 +2,8 @@ from dataclasses import dataclass from typing import Any import py3xui - from config import LIMIT_IP, SUPERNODE + from logger import logger diff --git a/database.py b/database.py index 25222bf9..9ed0a47d 100644 --- a/database.py +++ b/database.py @@ -4,8 +4,8 @@ from typing import Any import asyncpg import pytz - from config import CASHBACK, DATABASE_URL, REFERRAL_BONUS_PERCENTAGES + from logger import logger @@ -556,7 +556,7 @@ async def update_balance(tg_id: int, amount: float, session: Any = None): session = conn extra = amount * (CASHBACK / 100.0) if (CASHBACK > 0 and amount > 0) else 0 - total_amount = amount + extra + total_amount = int(amount + extra) await session.execute( """ diff --git a/filters/admin.py b/filters/admin.py index 5a392217..ee324169 100644 --- a/filters/admin.py +++ b/filters/admin.py @@ -1,6 +1,5 @@ from aiogram.filters import BaseFilter from aiogram.types import Message - from config import ADMIN_ID diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index 64510d48..01fa8c1c 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -4,8 +4,8 @@ from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message - from config import USERNAME_BOT + from database import create_coupon, delete_coupon, get_all_coupons from filters.admin import IsAdminFilter from keyboards.admin.coupons_kb import AdminCouponDeleteCallback, build_coupons_kb, build_coupons_list_kb diff --git a/handlers/admin/admin_servers.py b/handlers/admin/admin_servers.py index 3a347c3f..4afa1dbc 100644 --- a/handlers/admin/admin_servers.py +++ b/handlers/admin/admin_servers.py @@ -6,10 +6,10 @@ from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message +from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL 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 check_unique_server_name, delete_server, get_servers from filters.admin import IsAdminFilter from handlers.keys.key_utils import create_key_on_cluster @@ -264,7 +264,7 @@ async def handle_servers_availability( cluster_servers = servers.get(cluster_name, []) if not cluster_servers: - await callback_query.message.answer(text=f"Кластер '{cluster_name}' не содержит серверов.") + await callback_query.message.edit_text(text=f"Кластер '{cluster_name}' не содержит серверов.") return text = ( @@ -272,23 +272,22 @@ async def handle_servers_availability( "Это может занять до 1 минуты, пожалуйста, подождите..." ) - in_progress_message = await callback_query.message.answer(text=text) + await callback_query.message.edit_text(text=text) - text = f"🖥️ Проверка доступности серверов для кластера {cluster_name} завершена:\n\n" + result_text = f"🖥️ Проверка доступности серверов для кластера {cluster_name} завершена:\n\n" for server in cluster_servers: xui = AsyncApi(server["api_url"], username=ADMIN_USERNAME, password=ADMIN_PASSWORD) try: await xui.login() - online_users = len(await xui.client.online()) - text += f"🌍 {server['server_name']}: {online_users} активных пользователей.\n" + result_text += f"🌍 {server['server_name']}: {online_users} активных пользователей.\n" except Exception as e: - text += f"❌ {server['server_name']}: Не удалось получить информацию. Ошибка: {e}\n" + result_text += f"❌ {server['server_name']}: Не удалось получить информацию. Ошибка: {e}\n" - await in_progress_message.edit_text(text=text, reply_markup=build_admin_back_kb("servers")) + await callback_query.message.edit_text(text=result_text, reply_markup=build_admin_back_kb("servers")) @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_manage"), IsAdminFilter()) diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py index 0dc0083e..9fe62c24 100644 --- a/handlers/admin/admin_users.py +++ b/handlers/admin/admin_users.py @@ -7,8 +7,8 @@ from aiogram.exceptions import TelegramBadRequest from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message - from config import TOTAL_GB + from database import ( delete_key, delete_user_data, diff --git a/handlers/captcha.py b/handlers/captcha.py index ef3bbc30..27ee4f72 100644 --- a/handlers/captcha.py +++ b/handlers/captcha.py @@ -6,8 +6,8 @@ from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.types import CallbackQuery, Message from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import CAPTCHA_EMOJIS + from logger import logger router = Router() diff --git a/handlers/donate.py b/handlers/donate.py index 73d6f76e..66c1c392 100644 --- a/handlers/donate.py +++ b/handlers/donate.py @@ -3,8 +3,8 @@ from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, InlineKeyboardButton, LabeledPrice, Message, PreCheckoutQuery from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import RUB_TO_XTR + from logger import logger diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index db8ec324..f08bf817 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -5,8 +5,8 @@ import aiofiles from aiogram import F, Router, types from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import CONNECT_MACOS, CONNECT_WINDOWS, SUPPORT_CHAT_URL + from database import get_key_details from handlers.texts import ( CONNECT_TV_TEXT, diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 9344f88f..18dfc8c4 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -8,10 +8,6 @@ from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.types import CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder -from py3xui import AsyncApi - -from bot import bot -from client import delete_client from config import ( ADMIN_PASSWORD, ADMIN_USERNAME, @@ -26,6 +22,10 @@ from config import ( USE_COUNTRY_SELECTION, USE_NEW_PAYMENT_FLOW, ) +from py3xui import AsyncApi + +from bot import bot +from client import delete_client from database import ( create_temporary_data, delete_key, diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py index 46c984aa..43f09005 100644 --- a/handlers/keys/key_utils.py +++ b/handlers/keys/key_utils.py @@ -1,10 +1,10 @@ import asyncio from typing import Any +from config import ADMIN_PASSWORD, ADMIN_USERNAME, LIMIT_IP, PUBLIC_LINK, SUPERNODE, TOTAL_GB from py3xui import AsyncApi from client import ClientConfig, add_client, delete_client, extend_client_key -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 d46d26f2..515cc89b 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -10,8 +10,6 @@ import pytz from aiogram import F, Router, types from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder - -from bot import bot from config import ( CONNECT_ANDROID, CONNECT_IOS, @@ -26,6 +24,8 @@ from config import ( USE_COUNTRY_SELECTION, USE_NEW_PAYMENT_FLOW, ) + +from bot import bot from database import ( check_server_name_by_cluster, create_temporary_data, diff --git a/handlers/keys/subscriptions.py b/handlers/keys/subscriptions.py index 6221549f..9f4167ff 100644 --- a/handlers/keys/subscriptions.py +++ b/handlers/keys/subscriptions.py @@ -5,8 +5,8 @@ from datetime import datetime import aiohttp import asyncpg from aiohttp import web - from config import DATABASE_URL, PROJECT_NAME, SUB_MESSAGE, SUPERNODE, TRANSITION_DATE_STR, USE_COUNTRY_SELECTION + from database import get_key_details, get_servers from logger import logger diff --git a/handlers/notifications.py b/handlers/notifications.py index 6f53c5d8..821a68c3 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -9,8 +9,6 @@ from aiogram import Bot, Router, types from aiogram.exceptions import TelegramForbiddenError from aiogram.types import BufferedInputFile from aiogram.utils.keyboard import InlineKeyboardBuilder -from py3xui import AsyncApi - from config import ( ADMIN_PASSWORD, ADMIN_USERNAME, @@ -25,6 +23,8 @@ from config import ( TOTAL_GB, TRIAL_TIME, ) +from py3xui import AsyncApi + from database import ( add_notification, check_notification_time, diff --git a/handlers/pay.py b/handlers/pay.py index 44b487a9..768cddd8 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -1,7 +1,6 @@ from aiogram import F, Router from aiogram.types import CallbackQuery, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import ( CRYPTO_BOT_ENABLE, DONATIONS_ENABLE, diff --git a/handlers/profile.py b/handlers/profile.py index 87932b7d..f489a35f 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -7,8 +7,8 @@ from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import DATABASE_URL, INSTRUCTIONS_BUTTON, NEWS_MESSAGE, RENEWAL_PLANS + from database import get_balance, get_key_count, get_last_payments, get_referral_stats, get_trial from handlers.buttons.profile import ( ADD_SUB, diff --git a/handlers/start.py b/handlers/start.py index c77177c4..d2970868 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -12,8 +12,6 @@ from aiogram.types import ( Message, ) from aiogram.utils.keyboard import InlineKeyboardBuilder - -from bot import bot from config import ( CAPTCHA_ENABLE, CHANNEL_EXISTS, @@ -23,6 +21,8 @@ from config import ( DONATIONS_ENABLE, SUPPORT_CHAT_URL, ) + +from bot import bot from database import ( add_connection, add_referral, diff --git a/handlers/utils.py b/handlers/utils.py index 2d03b436..e06892a5 100644 --- a/handlers/utils.py +++ b/handlers/utils.py @@ -5,9 +5,9 @@ import string import aiohttp import asyncpg +from config import DATABASE_URL from bot import bot -from config import DATABASE_URL from database import get_all_keys, get_servers from logger import logger diff --git a/keyboards/admin/users_kb.py b/keyboards/admin/users_kb.py index ae13f2ea..0f5f7052 100644 --- a/keyboards/admin/users_kb.py +++ b/keyboards/admin/users_kb.py @@ -3,8 +3,8 @@ from datetime import datetime, timezone from aiogram.filters.callback_data import CallbackData from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder - from config import RENEWAL_PRICES + from keyboards.admin.panel_kb import build_admin_back_btn diff --git a/middlewares/admin.py b/middlewares/admin.py index bc21afe4..85819d32 100644 --- a/middlewares/admin.py +++ b/middlewares/admin.py @@ -3,7 +3,6 @@ from typing import Any from aiogram import BaseMiddleware from aiogram.types import TelegramObject - from config import ADMIN_ID diff --git a/middlewares/session.py b/middlewares/session.py index a5d34441..302523f4 100644 --- a/middlewares/session.py +++ b/middlewares/session.py @@ -4,7 +4,6 @@ from typing import Any import asyncpg from aiogram import BaseMiddleware from aiogram.types import TelegramObject - from config import DATABASE_URL diff --git a/servers.py b/servers.py index e1f35d53..bf8a4151 100644 --- a/servers.py +++ b/servers.py @@ -5,10 +5,10 @@ from datetime import datetime, timedelta import asyncpg from aiogram.types import InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder +from config import ADMIN_ID, DATABASE_URL, PING_TIME from ping3 import ping from bot import bot -from config import ADMIN_ID, DATABASE_URL, PING_TIME from database import check_unique_server_name, create_server, get_servers from logger import logger diff --git a/utils/csv_export.py b/utils/csv_export.py index 4b39c023..71ad09c7 100644 --- a/utils/csv_export.py +++ b/utils/csv_export.py @@ -5,60 +5,65 @@ from aiogram.types import BufferedInputFile async def export_users_csv(session: Any) -> BufferedInputFile: - # Получение данных о платежах пользователя + """ + Экспорт пользователей в CSV с сортировкой от самого старого к новому. + """ query = """ - SELECT - u.tg_id, - u.username, - u.first_name, - u.last_name, - u.language_code, - u.is_bot, - c.balance, - c.trial - FROM users u - LEFT JOIN connections c ON u.tg_id = c.tg_id - """ + SELECT + u.tg_id, + u.username, + u.first_name, + u.last_name, + u.language_code, + u.is_bot, + c.balance, + c.trial, + u.created_at -- Добавляем дату регистрации + FROM users u + LEFT JOIN connections c ON u.tg_id = c.tg_id + ORDER BY u.created_at ASC -- Сортировка от старых к новым + """ users = await session.fetch(query) buffer = StringIO() - buffer.write("tg_id,username,first_name,last_name,language_code,is_bot,balance,trial\n") + buffer.write("tg_id,username,first_name,last_name,language_code,is_bot,balance,trial,created_at\n") - # Запись данных for user in users: buffer.write( f"{user['tg_id']},{user['username']},{user['first_name']},{user['last_name']}," - f"{user['language_code']},{user['is_bot']},{user['balance']},{user['trial']}\n" + f"{user['language_code']},{user['is_bot']},{user['balance']},{user['trial']}," + f"{user['created_at']}\n" ) - # Перемещение указателя в начало для чтения buffer.seek(0) return BufferedInputFile(file=buffer.getvalue().encode("utf-8-sig"), filename="users_export.csv") async def export_payments_csv(session: Any) -> BufferedInputFile: - # Получение данных о всех платежах + """ + Экспорт платежей в CSV с сортировкой от самого старого к новому. + """ query = """ - SELECT - u.tg_id, - u.username, - u.first_name, - u.last_name, - p.amount, - p.payment_system, - p.status, - p.created_at - FROM users u - JOIN payments p ON u.tg_id = p.tg_id - """ + SELECT + u.tg_id, + u.username, + u.first_name, + u.last_name, + p.amount, + p.payment_system, + p.status, + p.created_at + FROM users u + JOIN payments p ON u.tg_id = p.tg_id + ORDER BY p.created_at ASC -- Сортировка по дате от старых к новым + """ payments = await session.fetch(query) return _export_payments_csv(payments, "payments_export.csv") async def export_user_payments_csv(tg_id: int, session: Any) -> BufferedInputFile: - # Получение данных о платежах пользователя query = """ SELECT u.tg_id, @@ -78,18 +83,15 @@ async def export_user_payments_csv(tg_id: int, session: Any) -> BufferedInputFil def _export_payments_csv(payments: list, filename: str) -> BufferedInputFile: - # Формирование CSV данных через StringIO buffer = StringIO() buffer.write("tg_id,username,first_name,last_name,amount,payment_system,status,created_at\n") - # Запись данных for payment in payments: buffer.write( f"{payment['tg_id']},{payment['username']},{payment['first_name']},{payment['last_name']}," f"{payment['amount']},{payment['payment_system']},{payment['status']},{payment['created_at']}\n" ) - # Перемещение указателя в начало для чтения buffer.seek(0) return BufferedInputFile(file=buffer.getvalue().encode("utf-8-sig"), filename=filename)