diff --git a/client.py b/client.py
index 96c105f5..c7609ec8 100644
--- a/client.py
+++ b/client.py
@@ -72,7 +72,14 @@ async def add_client(xui: py3xui.AsyncApi, config: ClientConfig) -> dict[str, An
async def extend_client_key(
- xui: py3xui.AsyncApi, inbound_id: int, email: str, new_expiry_time: int, client_id: str, total_gb: int, sub_id: str, tg_id: int
+ xui: py3xui.AsyncApi,
+ inbound_id: int,
+ email: str,
+ new_expiry_time: int,
+ client_id: str,
+ total_gb: int,
+ sub_id: str,
+ tg_id: int,
) -> bool | None:
"""
Обновляет срок действия ключа клиента.
diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py
index 9e658fb9..99a4ec98 100644
--- a/handlers/admin/admin_users.py
+++ b/handlers/admin/admin_users.py
@@ -12,7 +12,6 @@ from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import TOTAL_GB
-from utils.csv_export import export_referrals_csv
from database import (
delete_key,
@@ -27,7 +26,7 @@ from database import (
from filters.admin import IsAdminFilter
from handlers.keys.key_utils import delete_key_from_cluster, get_user_traffic, renew_key_in_cluster, update_subscription
from handlers.utils import sanitize_key_name
-from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb, build_admin_back_btn
+from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_btn, build_admin_back_kb
from keyboards.admin.users_kb import (
AdminUserEditorCallback,
AdminUserKeyEditorCallback,
@@ -43,6 +42,7 @@ from keyboards.admin.users_kb import (
build_users_key_show_kb,
)
from logger import logger
+from utils.csv_export import export_referrals_csv
MOSCOW_TZ = pytz.timezone("Europe/Moscow")
@@ -733,10 +733,11 @@ async def confirm_restore_trials(callback_query: types.CallbackQuery):
await callback_query.message.edit_text(
text="⚠ Вы уверены, что хотите восстановить пробники для пользователей? \n\n"
- "Только для тех, у кого нет активной подписки!",
- reply_markup=builder.as_markup()
+ "Только для тех, у кого нет активной подписки!",
+ reply_markup=builder.as_markup(),
)
+
@router.callback_query(AdminPanelCallback.filter(F.action == "confirm_restore_trials"), IsAdminFilter())
async def restore_trials(callback_query: types.CallbackQuery, session: Any):
"""
@@ -759,15 +760,13 @@ async def restore_trials(callback_query: types.CallbackQuery, session: Any):
await callback_query.message.edit_text(
text="✅ Пробники успешно восстановлены для пользователей, у которых нет активных подписок.",
- reply_markup=builder.as_markup()
+ reply_markup=builder.as_markup(),
)
@router.callback_query(AdminUserEditorCallback.filter(F.action == "users_export_referrals"), IsAdminFilter())
async def handle_users_export_referrals(
- callback_query: types.CallbackQuery,
- callback_data: AdminUserEditorCallback,
- session: Any
+ callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback, session: Any
):
"""
Обработчик: получает tg_id реферера из callback_data,
@@ -783,6 +782,5 @@ async def handle_users_export_referrals(
return
await callback_query.message.answer_document(
- document=csv_file,
- caption=f"Список рефералов для пользователя {referrer_tg_id}."
- )
\ No newline at end of file
+ document=csv_file, caption=f"Список рефералов для пользователя {referrer_tg_id}."
+ )
diff --git a/handlers/buttons/yookassa.py b/handlers/buttons/yookassa.py
index 387fc745..5aaded81 100644
--- a/handlers/buttons/yookassa.py
+++ b/handlers/buttons/yookassa.py
@@ -4,4 +4,4 @@ BACK = "⬅️ Назад"
CUSTOM_SUM = "💰 Ввести свою сумму"
CUSTOM_SUM_ANSWER = "Пожалуйста, введите сумму пополнения."
PROFILE = "👤 Личный кабинет"
-DEFAULT_PAYMENT_MESSAGE = "Вы выбрали пополнение на {amount} рублей. Перейдите по ссылке для оплаты:"
\ No newline at end of file
+DEFAULT_PAYMENT_MESSAGE = "Вы выбрали пополнение на {amount} рублей. Перейдите по ссылке для оплаты:"
diff --git a/handlers/captcha.py b/handlers/captcha.py
index 8f294895..8738522b 100644
--- a/handlers/captcha.py
+++ b/handlers/captcha.py
@@ -8,6 +8,7 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from config import CAPTCHA_EMOJIS
+
from handlers.texts import CAPTCHA_PROMPT_MSG
from logger import logger
diff --git a/handlers/coupons.py b/handlers/coupons.py
index 807aa2f2..42d2b5f6 100644
--- a/handlers/coupons.py
+++ b/handlers/coupons.py
@@ -14,11 +14,12 @@ from database import (
update_coupon_usage_count,
)
from handlers.texts import (
+ COUPON_ACTIVATED_SUCCESS_MSG,
+ COUPON_ALREADY_USED_MSG,
COUPON_INPUT_PROMPT,
COUPON_NOT_FOUND_MSG,
- COUPON_ALREADY_USED_MSG,
- COUPON_ACTIVATED_SUCCESS_MSG,
)
+
from .utils import edit_or_send_message
diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py
index 9debef3e..42f074b7 100644
--- a/handlers/keys/key_management.py
+++ b/handlers/keys/key_management.py
@@ -54,12 +54,12 @@ from handlers.keys.key_utils import create_client_on_server, create_key_on_clust
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_message_success,
CREATING_CONNECTION_MSG,
- SELECT_TARIFF_PLAN_MSG,
+ DISCOUNTS,
INSUFFICIENT_FUNDS_MSG,
SELECT_COUNTRY_MSG,
+ SELECT_TARIFF_PLAN_MSG,
+ key_message_success,
)
from handlers.utils import edit_or_send_message, generate_random_email, get_least_loaded_cluster
from logger import logger
diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py
index d9540275..ada5d1bd 100644
--- a/handlers/keys/key_utils.py
+++ b/handlers/keys/key_utils.py
@@ -2,10 +2,11 @@ import asyncio
from typing import Any
-from config import ADMIN_PASSWORD, ADMIN_USERNAME, LIMIT_IP, PUBLIC_LINK, SUPERNODE, TOTAL_GB, DATABASE_URL
-from py3xui import AsyncApi
import asyncpg
+from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, LIMIT_IP, PUBLIC_LINK, SUPERNODE, TOTAL_GB
+from py3xui import AsyncApi
+
from client import ClientConfig, add_client, delete_client, extend_client_key, get_client_traffic, toggle_client
from database import get_servers, store_key
from handlers.utils import get_least_loaded_cluster
diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py
index 2f6d2b15..9c3d0517 100644
--- a/handlers/keys/keys.py
+++ b/handlers/keys/keys.py
@@ -23,10 +23,10 @@ from config import (
ENABLE_UPDATE_SUBSCRIPTION_BUTTON,
PUBLIC_LINK,
RENEWAL_PLANS,
+ TOGGLE_CLIENT,
TOTAL_GB,
USE_COUNTRY_SELECTION,
USE_NEW_PAYMENT_FLOW,
- TOGGLE_CLIENT
)
from bot import bot
@@ -53,27 +53,27 @@ from handlers.buttons.add_subscribe import (
from handlers.keys.key_utils import (
delete_key_from_cluster,
renew_key_in_cluster,
- update_subscription,
toggle_client_on_cluster,
+ 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 (
+ DELETE_KEY_CONFIRM_MSG,
DISCOUNTS,
+ FREEZE_SUBSCRIPTION_CONFIRM_MSG,
+ FROZEN_SUBSCRIPTION_MSG,
+ INSUFFICIENT_FUNDS_RENEWAL_MSG,
+ KEY_DELETED_MSG_SIMPLE,
KEY_NOT_FOUND_MSG,
+ NO_SUBSCRIPTIONS_MSG,
PLAN_SELECTION_MSG,
SUBSCRIPTION_DESCRIPTION,
- SUCCESS_RENEWAL_MSG,
- key_message,
- NO_SUBSCRIPTIONS_MSG,
- FROZEN_SUBSCRIPTION_MSG,
- UNFREEZE_SUBSCRIPTION_CONFIRM_MSG,
- SUBSCRIPTION_UNFROZEN_MSG,
- FREEZE_SUBSCRIPTION_CONFIRM_MSG,
SUBSCRIPTION_FROZEN_MSG,
- DELETE_KEY_CONFIRM_MSG,
- KEY_DELETED_MSG_SIMPLE,
- INSUFFICIENT_FUNDS_RENEWAL_MSG,
+ SUBSCRIPTION_UNFROZEN_MSG,
+ SUCCESS_RENEWAL_MSG,
+ UNFREEZE_SUBSCRIPTION_CONFIRM_MSG,
+ key_message,
)
from handlers.utils import edit_or_send_message, handle_error
from logger import logger
@@ -177,7 +177,7 @@ async def process_callback_view_key(callback_query: CallbackQuery, session: Any)
reply_markup=keyboard,
media_path=image_path,
)
-
+
else:
key = record["key"]
expiry_time = record["expiry_time"]
@@ -188,17 +188,13 @@ async def process_callback_view_key(callback_query: CallbackQuery, session: Any)
time_left = expiry_date - current_date
if time_left.total_seconds() <= 0:
- days_left_message = (
- "🕒 Статус подписки:\n🔴 Истекла\nОсталось часов: 0\nОсталось минут: 0"
- )
+ days_left_message = "🕒 Статус подписки:\n🔴 Истекла\nОсталось часов: 0\nОсталось минут: 0"
else:
total_seconds = int(time_left.total_seconds())
days = total_seconds // 86400
hours = (total_seconds % 86400) // 3600
minutes = (total_seconds % 3600) // 60
- days_left_message = (
- f"Осталось: {days} дней, {hours} часов, {minutes} минут"
- )
+ days_left_message = f"Осталось: {days} дней, {hours} часов, {minutes} минут"
formatted_expiry_date = expiry_date.strftime("%d %B %Y года")
response_message = key_message(
@@ -248,18 +244,12 @@ async def process_callback_view_key(callback_query: CallbackQuery, session: Any)
)
else:
builder.row(
- InlineKeyboardButton(
- text="⏳ Продлить подписку",
- callback_data=f"renew_key|{key_name}"
- )
+ InlineKeyboardButton(text="⏳ Продлить подписку", callback_data=f"renew_key|{key_name}")
)
if USE_COUNTRY_SELECTION:
builder.row(
- InlineKeyboardButton(
- text="🌍 Сменить локацию",
- callback_data=f"change_location|{key_name}"
- )
+ InlineKeyboardButton(text="🌍 Сменить локацию", callback_data=f"change_location|{key_name}")
)
if TOGGLE_CLIENT:
@@ -355,7 +345,7 @@ async def process_callback_unfreeze_subscription_confirm(callback_query: Callbac
""",
new_expiry_time,
record["tg_id"],
- client_id
+ client_id,
)
await renew_key_in_cluster(
@@ -363,13 +353,11 @@ async def process_callback_unfreeze_subscription_confirm(callback_query: Callbac
email=email,
client_id=client_id,
new_expiry_time=new_expiry_time,
- total_gb=TOTAL_GB
+ total_gb=TOTAL_GB,
)
text_ok = SUBSCRIPTION_UNFROZEN_MSG
builder = InlineKeyboardBuilder()
- builder.row(
- InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")
- )
+ builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}"))
await edit_or_send_message(
target_message=callback_query.message,
text=text_ok,
@@ -377,13 +365,10 @@ async def process_callback_unfreeze_subscription_confirm(callback_query: Callbac
)
else:
text_error = (
- "Произошла ошибка при включении подписки.\n"
- f"Детали: {result.get('error') or result.get('results')}"
+ f"Произошла ошибка при включении подписки.\nДетали: {result.get('error') or result.get('results')}"
)
builder = InlineKeyboardBuilder()
- builder.row(
- InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")
- )
+ builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}"))
await edit_or_send_message(
target_message=callback_query.message,
text=text_error,
@@ -399,7 +384,6 @@ async def process_callback_freeze_subscription(callback_query: CallbackQuery, se
"""
Показывает пользователю диалог подтверждения заморозки (отключения) подписки.
"""
- tg_id = callback_query.message.chat.id
key_name = callback_query.data.split("|")[1]
confirm_text = FREEZE_SUBSCRIPTION_CONFIRM_MSG
@@ -447,9 +431,9 @@ async def process_callback_freeze_subscription_confirm(callback_query: CallbackQ
now_ms = int(time.time() * 1000)
time_left = record["expiry_time"] - now_ms
if time_left < 0:
- time_left = 0
+ time_left = 0
- update_result = await session.execute(
+ await session.execute(
"""
UPDATE keys
SET expiry_time = $1,
@@ -459,14 +443,12 @@ async def process_callback_freeze_subscription_confirm(callback_query: CallbackQ
""",
time_left,
record["tg_id"],
- client_id
+ client_id,
)
text_ok = SUBSCRIPTION_FROZEN_MSG
builder = InlineKeyboardBuilder()
- builder.row(
- InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")
- )
+ builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}"))
await edit_or_send_message(
target_message=callback_query.message,
text=text_ok,
@@ -474,13 +456,10 @@ async def process_callback_freeze_subscription_confirm(callback_query: CallbackQ
)
else:
text_error = (
- "Произошла ошибка при заморозке подписки.\n"
- f"Детали: {result.get('error') or result.get('results')}"
+ f"Произошла ошибка при заморозке подписки.\nДетали: {result.get('error') or result.get('results')}"
)
builder = InlineKeyboardBuilder()
- builder.row(
- InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")
- )
+ builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}"))
await edit_or_send_message(
target_message=callback_query.message,
text=text_error,
@@ -568,14 +547,10 @@ async def process_callback_delete_key(callback_query: CallbackQuery):
if callback_query.message.caption:
await callback_query.message.edit_caption(
- caption=DELETE_KEY_CONFIRM_MSG,
- reply_markup=confirmation_keyboard
+ caption=DELETE_KEY_CONFIRM_MSG, reply_markup=confirmation_keyboard
)
else:
- await callback_query.message.edit_text(
- text=DELETE_KEY_CONFIRM_MSG,
- reply_markup=confirmation_keyboard
- )
+ await callback_query.message.edit_text(text=DELETE_KEY_CONFIRM_MSG, reply_markup=confirmation_keyboard)
except Exception as e:
logger.error(f"Ошибка при обработке запроса на удаление ключа {client_id}: {e}")
diff --git a/handlers/keys/subscriptions.py b/handlers/keys/subscriptions.py
index d789359a..412fd6c3 100644
--- a/handlers/keys/subscriptions.py
+++ b/handlers/keys/subscriptions.py
@@ -328,7 +328,7 @@ async def handle_subscription(request: web.Request, old_subscription: bool = Fal
stored_tg_id = client_data.get("tg_id")
server_id = client_data["server_id"]
- if not old_subscription and str(tg_id) != str(stored_tg_id):
+ if not old_subscription and int(tg_id) != int(stored_tg_id):
logger.warning(f"Неверный tg_id для клиента с email {email}.")
return web.Response(text="❌ Неверные данные. Получите свой ключ в боте.", status=403)
diff --git a/handlers/notifications/general_notifications.py b/handlers/notifications/general_notifications.py
index 2cfaa6f3..7d330c7c 100644
--- a/handlers/notifications/general_notifications.py
+++ b/handlers/notifications/general_notifications.py
@@ -1,5 +1,7 @@
import asyncio
+
from datetime import datetime, timedelta
+
import asyncpg
import pytz
@@ -9,14 +11,15 @@ from config import (
NOTIFICATION_TIME,
NOTIFY_DELETE_DELAY,
NOTIFY_DELETE_KEY,
+ NOTIFY_INACTIVE_TRAFFIC,
NOTIFY_MAXPRICE,
NOTIFY_RENEW,
NOTIFY_RENEW_EXPIRED,
RENEWAL_PRICES,
TOTAL_GB,
TRIAL_TIME_DISABLE,
- NOTIFY_INACTIVE_TRAFFIC
)
+
from database import (
add_notification,
check_notification_time,
@@ -29,25 +32,28 @@ from database import (
)
from handlers.keys.key_utils import delete_key_from_cluster, renew_key_in_cluster
from handlers.texts import (
- KEY_EXPIRY_10H,
- KEY_EXPIRY_24H,
- KEY_RENEWED,
- KEY_RENEWED_TEMP_MSG,
KEY_DELETED_MSG,
KEY_EXPIRED_DELAY_HOURS_MINUTES_MSG,
KEY_EXPIRED_DELAY_HOURS_MSG,
KEY_EXPIRED_DELAY_MINUTES_MSG,
KEY_EXPIRED_NO_DELAY_MSG,
+ KEY_EXPIRY_10H,
+ KEY_EXPIRY_24H,
+ KEY_RENEWED,
+ KEY_RENEWED_TEMP_MSG,
)
from keyboards.notifications.notify_kb import build_notification_expired_kb, build_notification_kb
from logger import logger
+
from .notify_utils import send_notification
from .special_notifications import notify_inactive_trial_users, notify_users_no_traffic
+
router = Router()
moscow_tz = pytz.timezone("Europe/Moscow")
+
async def periodic_notifications(bot: Bot):
"""
Обработчик, который:
@@ -98,6 +104,7 @@ async def periodic_notifications(bot: Bot):
await asyncio.sleep(NOTIFICATION_TIME)
+
async def notify_24h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, threshold_time_24h: int, keys: list):
logger.info("Начало проверки подписок, истекающих через 24 часа.")
@@ -149,6 +156,7 @@ async def notify_24h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int,
logger.info("✅ Обработка всех уведомлений за 24 часа завершена.")
await asyncio.sleep(1)
+
async def notify_10h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, threshold_time_10h: int, keys: list):
"""
Отправляет уведомления пользователям о том, что их подписка истекает через 10 часов.
@@ -208,6 +216,7 @@ async def notify_10h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int,
logger.info("✅ Обработка всех уведомлений за 10 часов завершена.")
await asyncio.sleep(1)
+
async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, keys: list):
"""
Обрабатывает истекшие ключи, проверяя продление или удаление.
@@ -319,6 +328,7 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time:
logger.info("✅ Обработка истекших ключей завершена.")
await asyncio.sleep(1)
+
async def process_auto_renew_or_notify(
bot, conn, key: dict, notification_id: str, renewal_period_months: int, standard_photo: str, standard_caption: str
):
diff --git a/handlers/notifications/special_notifications.py b/handlers/notifications/special_notifications.py
index 9ca81461..a63c1ea8 100644
--- a/handlers/notifications/special_notifications.py
+++ b/handlers/notifications/special_notifications.py
@@ -15,8 +15,8 @@ from database import (
check_notification_time,
create_blocked_user,
)
-from handlers.texts import TRIAL_INACTIVE_FIRST_MSG, TRIAL_INACTIVE_BONUS_MSG, ZERO_TRAFFIC_MSG
from handlers.keys.key_utils import get_user_traffic
+from handlers.texts import TRIAL_INACTIVE_BONUS_MSG, TRIAL_INACTIVE_FIRST_MSG, ZERO_TRAFFIC_MSG
from logger import logger
@@ -84,16 +84,11 @@ async def notify_inactive_trial_users(bot: Bot, conn: asyncpg.Connection):
if trial_extended:
total_days = NOTIFY_EXTRA_DAYS + TRIAL_TIME
message = TRIAL_INACTIVE_BONUS_MSG.format(
- display_name=display_name,
- NOTIFY_EXTRA_DAYS=NOTIFY_EXTRA_DAYS,
- total_days=total_days
+ display_name=display_name, NOTIFY_EXTRA_DAYS=NOTIFY_EXTRA_DAYS, total_days=total_days
)
await conn.execute("UPDATE connections SET trial = -1 WHERE tg_id = $1", tg_id)
else:
- message = TRIAL_INACTIVE_FIRST_MSG.format(
- display_name=display_name,
- TRIAL_TIME=TRIAL_TIME
- )
+ message = TRIAL_INACTIVE_FIRST_MSG.format(display_name=display_name, TRIAL_TIME=TRIAL_TIME)
try:
await bot.send_message(tg_id, message, reply_markup=keyboard)
diff --git a/handlers/pay.py b/handlers/pay.py
index 036df442..d922ae21 100644
--- a/handlers/pay.py
+++ b/handlers/pay.py
@@ -9,7 +9,9 @@ from config import (
YOOKASSA_ENABLE,
YOOMONEY_ENABLE,
)
+
from handlers.texts import PAYMENT_METHODS_MSG
+
from .utils import edit_or_send_message
diff --git a/handlers/profile.py b/handlers/profile.py
index f11c410e..e2083c6c 100644
--- a/handlers/profile.py
+++ b/handlers/profile.py
@@ -41,12 +41,12 @@ from handlers.buttons.profile import (
PAYMENT,
)
from handlers.texts import (
+ BALANCE_HISTORY_HEADER,
+ BALANCE_MANAGEMENT_TEXT,
+ INVITE_TEXT_NON_INLINE,
get_referral_link,
invite_message_send,
profile_message_send,
- BALANCE_MANAGEMENT_TEXT,
- BALANCE_HISTORY_HEADER,
- INVITE_TEXT_NON_INLINE
)
from keyboards.admin.panel_kb import AdminPanelCallback
from logger import logger
diff --git a/handlers/start.py b/handlers/start.py
index 10a8667e..3b38f344 100644
--- a/handlers/start.py
+++ b/handlers/start.py
@@ -1,4 +1,5 @@
import os
+
from typing import Any
from aiogram import F, Router
@@ -32,16 +33,16 @@ from database import (
from handlers.captcha import generate_captcha
from handlers.keys.key_management import create_key
from handlers.texts import (
+ COUPON_SUCCESS_MSG,
+ GIFT_ALREADY_USED_OR_NOT_EXISTS_MSG,
+ NEW_REFERRAL_NOTIFICATION,
+ NOT_SUBSCRIBED_YET_MSG,
+ REFERRAL_SUCCESS_MSG,
+ SUBSCRIPTION_CHECK_ERROR_MSG,
+ SUBSCRIPTION_CONFIRMED_MSG,
+ SUBSCRIPTION_REQUIRED_MSG,
WELCOME_TEXT,
get_about_vpn,
- SUBSCRIPTION_REQUIRED_MSG,
- NOT_SUBSCRIBED_YET_MSG,
- SUBSCRIPTION_CONFIRMED_MSG,
- SUBSCRIPTION_CHECK_ERROR_MSG,
- GIFT_ALREADY_USED_OR_NOT_EXISTS_MSG,
- REFERRAL_SUCCESS_MSG,
- NEW_REFERRAL_NOTIFICATION,
- COUPON_SUCCESS_MSG,
)
from keyboards.admin.panel_kb import AdminPanelCallback
from logger import logger
@@ -274,8 +275,7 @@ async def process_start_logic(
await message.answer(REFERRAL_SUCCESS_MSG.format(referrer_tg_id=referrer_tg_id))
try:
await bot.send_message(
- referrer_tg_id,
- NEW_REFERRAL_NOTIFICATION.format(referred_id=message.chat.id)
+ referrer_tg_id, NEW_REFERRAL_NOTIFICATION.format(referred_id=message.chat.id)
)
logger.info(
f"Уведомление отправлено пользователю {referrer_tg_id} о новом реферале {message.chat.id}"
diff --git a/handlers/utils.py b/handlers/utils.py
index b08c2475..4020c904 100644
--- a/handlers/utils.py
+++ b/handlers/utils.py
@@ -66,7 +66,7 @@ async def get_least_loaded_cluster() -> str:
"""
servers = await get_servers()
server_to_cluster = {}
- cluster_loads = {cluster: 0 for cluster in servers.keys()}
+ cluster_loads = dict.fromkeys(servers.keys(), 0)
for cluster_name, cluster_servers in servers.items():
for server in cluster_servers:
server_to_cluster[server["server_name"]] = cluster_name
diff --git a/middlewares/loggings.py b/middlewares/loggings.py
index db4a2ebf..f329759f 100644
--- a/middlewares/loggings.py
+++ b/middlewares/loggings.py
@@ -1,5 +1,5 @@
from collections.abc import Awaitable, Callable
-from typing import Any,TypedDict
+from typing import Any, TypedDict
from aiogram import BaseMiddleware
from aiogram.types import CallbackQuery, InlineQuery, Message, TelegramObject, User
diff --git a/middlewares/throttling.py b/middlewares/throttling.py
index 50e20bf4..710744c5 100644
--- a/middlewares/throttling.py
+++ b/middlewares/throttling.py
@@ -28,4 +28,4 @@ class ThrottlingMiddleware(BaseMiddleware):
else:
self.cache[user_id] = current_count + 1
- return await handler(event, data)
\ No newline at end of file
+ return await handler(event, data)
diff --git a/servers.py b/servers.py
index d23bda94..b403e24f 100644
--- a/servers.py
+++ b/servers.py
@@ -60,10 +60,7 @@ async def notify_admin(server_name: str, status: str, down_duration: timedelta =
)
else:
downtime = str(down_duration).split(".")[0]
- message = (
- f"✅ Сервер '{server_name}' снова в сети!\n\n"
- f"⏳ Время простоя: {downtime}."
- )
+ message = f"✅ Сервер '{server_name}' снова в сети!\n\n⏳ Время простоя: {downtime}."
for admin_id in ADMIN_ID:
logger.info(f"📨 Отправляем уведомление '{status}' администратору {admin_id} о сервере {server_name}")
@@ -119,11 +116,13 @@ async def check_servers():
if last_ping_time is None:
last_ping_times[server_name] = current_time
- last_down_times[server_name] = current_time
+ last_down_times[server_name] = current_time
if last_ping_time and (current_time - last_ping_time > timedelta(seconds=PING_TIME * 3)):
if server_name not in notified_servers:
- logger.warning(f"🚨 Уведомление: сервер {server_name} не отвечает более {PING_TIME * 3} секунд!")
+ logger.warning(
+ f"🚨 Уведомление: сервер {server_name} не отвечает более {PING_TIME * 3} секунд!"
+ )
await notify_admin(server_name, "down")
notified_servers.add(server_name)
last_down_times[server_name] = current_time
@@ -145,4 +144,4 @@ async def check_servers():
def extract_host(api_url: str) -> str:
"""Извлекает хост из `api_url`."""
match = re.match(r"(https?://)?([^:/]+)", api_url)
- return match.group(2) if match else api_url
\ No newline at end of file
+ return match.group(2) if match else api_url
diff --git a/utils/csv_export.py b/utils/csv_export.py
index 276c79ea..d4f2ed30 100644
--- a/utils/csv_export.py
+++ b/utils/csv_export.py
@@ -1,6 +1,7 @@
+import csv
+
from io import StringIO
from typing import Any
-import csv
from aiogram.types import BufferedInputFile
@@ -115,14 +116,14 @@ async def export_referrals_csv(referrer_tg_id: int, session: Any) -> BufferedInp
WHERE r.referrer_tg_id = $1
ORDER BY r.referred_tg_id
""",
- referrer_tg_id
+ referrer_tg_id,
)
if not rows:
return None
output = StringIO()
- writer = csv.writer(output, delimiter=';')
+ writer = csv.writer(output, delimiter=";")
writer.writerow(["Приглашённый (tg_id)", "Имя"])
for row in rows:
@@ -136,4 +137,4 @@ async def export_referrals_csv(referrer_tg_id: int, session: Any) -> BufferedInp
csv_data = output.getvalue().encode("utf-8")
filename = f"referrals_{referrer_tg_id}.csv"
- return BufferedInputFile(file=csv_data, filename=filename)
\ No newline at end of file
+ return BufferedInputFile(file=csv_data, filename=filename)