Merge pull request #169 from izzzzzi/main
Исправление сравнения tg_id в обработчике подписок.
This commit is contained in:
@@ -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:
|
||||
"""
|
||||
Обновляет срок действия ключа клиента.
|
||||
|
||||
@@ -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}."
|
||||
)
|
||||
document=csv_file, caption=f"Список рефералов для пользователя {referrer_tg_id}."
|
||||
)
|
||||
|
||||
@@ -4,4 +4,4 @@ BACK = "⬅️ Назад"
|
||||
CUSTOM_SUM = "💰 Ввести свою сумму"
|
||||
CUSTOM_SUM_ANSWER = "Пожалуйста, введите сумму пополнения."
|
||||
PROFILE = "👤 Личный кабинет"
|
||||
DEFAULT_PAYMENT_MESSAGE = "Вы выбрали пополнение на {amount} рублей. Перейдите по ссылке для оплаты:"
|
||||
DEFAULT_PAYMENT_MESSAGE = "Вы выбрали пополнение на {amount} рублей. Перейдите по ссылке для оплаты:"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-2
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+30
-55
@@ -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 = (
|
||||
"<b>🕒 Статус подписки:</b>\n🔴 Истекла\nОсталось часов: 0\nОсталось минут: 0"
|
||||
)
|
||||
days_left_message = "<b>🕒 Статус подписки:</b>\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"Осталось: <b>{days}</b> дней, <b>{hours}</b> часов, <b>{minutes}</b> минут"
|
||||
)
|
||||
days_left_message = f"Осталось: <b>{days}</b> дней, <b>{hours}</b> часов, <b>{minutes}</b> минут"
|
||||
|
||||
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}")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+10
-10
@@ -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}"
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,4 +28,4 @@ class ThrottlingMiddleware(BaseMiddleware):
|
||||
else:
|
||||
self.cache[user_id] = current_count + 1
|
||||
|
||||
return await handler(event, data)
|
||||
return await handler(event, data)
|
||||
|
||||
+6
-7
@@ -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"✅ <b>Сервер '{server_name}' снова в сети!</b>\n\n"
|
||||
f"⏳ Время простоя: {downtime}."
|
||||
)
|
||||
message = f"✅ <b>Сервер '{server_name}' снова в сети!</b>\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
|
||||
return match.group(2) if match else api_url
|
||||
|
||||
+5
-4
@@ -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)
|
||||
return BufferedInputFile(file=csv_data, filename=filename)
|
||||
|
||||
Reference in New Issue
Block a user