diff --git a/handlers/admin/management/management_handler.py b/handlers/admin/management/management_handler.py index b7aa138b..3cb12065 100644 --- a/handlers/admin/management/management_handler.py +++ b/handlers/admin/management/management_handler.py @@ -5,6 +5,7 @@ import subprocess import sys import time import traceback +import re from asyncio import sleep from datetime import datetime @@ -97,7 +98,7 @@ async def process_new_domain(message: Message, state: FSMContext, session: Async """Обновляет домен в таблице keys.""" new_domain = message.text.strip() - if not new_domain or " " in new_domain or not new_domain.replace(".", "").isalnum(): + if not re.fullmatch(r"[a-zA-Z0-9.-]+", new_domain) or " " in new_domain: logger.warning("[DomainChange] Некорректный домен") await message.answer( "🚫 Некорректный домен! Введите домен без http:// и без пробелов.", diff --git a/handlers/admin/users/users_handler.py b/handlers/admin/users/users_handler.py index 622b14af..82f4100a 100644 --- a/handlers/admin/users/users_handler.py +++ b/handlers/admin/users/users_handler.py @@ -559,15 +559,22 @@ async def handle_key_edit( key_value = key_details.get("key") or key_details.get("remnawave_link") or "—" alias = key_details.get("alias") - utc_tz = pytz.utc + created_at_raw = key_details.get("created_at") if created_at_raw: - created_at_dt = datetime.fromtimestamp(int(created_at_raw) / 1000, tz=utc_tz).astimezone(MOSCOW_TZ) + created_at_dt = datetime.fromtimestamp(int(created_at_raw) / 1000) + timedelta(hours=3) created_at = created_at_dt.strftime("%d %B %Y года %H:%M") else: created_at = "—" - expiry_date = key_details.get("expiry_date") or "—" + + expiry_time_raw = key_details.get("expiry_time") + if expiry_time_raw: + expiry_dt = datetime.fromtimestamp(int(expiry_time_raw) / 1000) + expiry_date = expiry_dt.strftime("%d %B %Y года %H:%M") + else: + expiry_date = "—" + tariff_name = "—" subgroup_title = "—" if key_details.get("tariff_id"): diff --git a/handlers/payments/gift.cpython-312-x86_64-linux-gnu.so b/handlers/payments/gift.cpython-312-x86_64-linux-gnu.so index 16ae2bdf..0af4ea25 100644 Binary files a/handlers/payments/gift.cpython-312-x86_64-linux-gnu.so and b/handlers/payments/gift.cpython-312-x86_64-linux-gnu.so differ diff --git a/handlers/payments/wata.py b/handlers/payments/wata.py index 47c527f0..2a16ff5b 100644 --- a/handlers/payments/wata.py +++ b/handlers/payments/wata.py @@ -141,6 +141,22 @@ async def process_cassa_selection(callback_query: types.CallbackQuery, state: FS await state.set_state(ReplenishBalanceWataState.choosing_amount) +@router.message(ReplenishBalanceWataState.entering_custom_amount) +async def handle_custom_amount_text_input(message: types.Message, state: FSMContext): + text = message.text.strip() + + try: + amount = int(text) + if amount <= 0: + raise ValueError + + await state.update_data(required_amount=amount) + await handle_custom_amount_input(message, state) + + except ValueError: + await message.answer("❌ Введите корректную сумму: только цифры, без символов.") + + @router.callback_query(F.data.startswith("wata_custom_amount|")) async def process_custom_amount_button(callback_query: types.CallbackQuery, state: FSMContext): cassa_name = callback_query.data.split("|")[1] @@ -156,12 +172,9 @@ async def process_custom_amount_button(callback_query: types.CallbackQuery, stat await state.set_state(ReplenishBalanceWataState.entering_custom_amount) -async def handle_custom_amount_input( - callback_query: types.CallbackQuery, - state: FSMContext, -): - tg_id = callback_query.from_user.id - target_message = callback_query.message +async def handle_custom_amount_input(event: types.Message | types.CallbackQuery, state: FSMContext): + tg_id = event.from_user.id + target_message = getattr(event, "message", None) or event try: data = await state.get_data()