fix TelegramBadRequest in traffic pricing and null in configurator. Extended description of billing in the settings

This commit is contained in:
Vladless
2025-12-23 14:41:05 +03:00
parent 03763a79f6
commit 38a8fe251a
19 changed files with 143 additions and 222 deletions
+1 -1
View File
@@ -1,7 +1,6 @@
from .coupons import CouponBase, CouponResponse, CouponUpdate, CouponUsageResponse
from .gifts import GiftBase, GiftResponse, GiftUpdate, GiftUsageResponse
from .keys import KeyDetailsResponse, KeyResponse
from .settings import SettingResponse, SettingUpsert
from .misc import (
BlockedUserResponse,
ManualBanResponse,
@@ -12,5 +11,6 @@ from .misc import (
)
from .referrals import ReferralResponse
from .servers import ServerBase, ServerResponse, ServerUpdate
from .settings import SettingResponse, SettingUpsert
from .tariffs import TariffBase, TariffResponse, TariffUpdate
from .users import UserBase, UserResponse, UserUpdate
+31 -23
View File
@@ -286,7 +286,7 @@ async def handle_sync_server(
try:
if key["panel_type"] == "remnawave":
tariff = tariffs_cache.get(key["tariff_id"]) if key["tariff_id"] else None
if tariff and server_info:
if tariff.get("subgroup_title") and tariff.get("subgroup_title") not in server_info.get(
"tariff_subgroups", []
@@ -313,7 +313,7 @@ async def handle_sync_server(
current_traffic_limit_gb_from_key = key.get("current_traffic_limit")
selected_device_limit_from_key = key.get("selected_device_limit")
selected_traffic_limit_gb_from_key = key.get("selected_traffic_limit")
if tariff:
if current_traffic_limit_gb_from_key is not None:
traffic_limit_bytes = int(current_traffic_limit_gb_from_key * 1024**3)
@@ -499,9 +499,9 @@ async def handle_sync_cluster(
batch_size = 50
total_keys = len(keys_to_sync)
processed_count = 0
for batch_start in range(0, total_keys, batch_size):
batch = keys_to_sync[batch_start:batch_start + batch_size]
batch = keys_to_sync[batch_start : batch_start + batch_size]
batch_end = batch_start + len(batch)
logger.info(f"[Sync] Обработка батча {batch_start}-{batch_end} из {total_keys}")
@@ -516,7 +516,7 @@ async def handle_sync_cluster(
current_traffic_limit_gb_from_key = key.get("current_traffic_limit")
selected_device_limit_from_key = key.get("selected_device_limit")
selected_traffic_limit_gb_from_key = key.get("selected_traffic_limit")
if tariff:
if current_traffic_limit_gb_from_key is not None:
traffic_limit_bytes = int(current_traffic_limit_gb_from_key * 1024**3)
@@ -533,11 +533,13 @@ async def handle_sync_cluster(
hwid_limit = int(selected_device_limit_from_key)
else:
hwid_limit = tariff.get("device_limit")
subgroup_title = tariff.get("subgroup_title")
expire_iso = (
datetime.utcfromtimestamp(key["expiry_time"] / 1000).replace(tzinfo=timezone.utc).isoformat()
datetime.utcfromtimestamp(key["expiry_time"] / 1000)
.replace(tzinfo=timezone.utc)
.isoformat()
)
if use_country_selection:
@@ -599,7 +601,14 @@ async def handle_sync_cluster(
"hwid_limit": hwid_limit,
}
else:
return {"key": key, "success": False, "needs_recreate": True, "tariff": tariff, "traffic_limit_bytes": traffic_limit_bytes, "hwid_limit": hwid_limit}
return {
"key": key,
"success": False,
"needs_recreate": True,
"tariff": tariff,
"traffic_limit_bytes": traffic_limit_bytes,
"hwid_limit": hwid_limit,
}
except Exception as e:
logger.error(f"[Sync] Ошибка API для {key.get('email')}: {e}")
@@ -610,24 +619,24 @@ async def handle_sync_cluster(
bulk_updates = []
recreate_tasks = []
for result in results:
if isinstance(result, Exception):
logger.error(f"[Sync] Exception в батче: {result}")
continue
if not isinstance(result, dict):
continue
key = result.get("key")
if not key:
continue
try:
if result.get("success") and result.get("new_link"):
new_link = result["new_link"]
tariff = result.get("tariff")
key_value = await make_aggregated_link(
session=session,
cluster_all=cluster_servers,
@@ -644,10 +653,10 @@ async def handle_sync_cluster(
"remnawave_link": new_link,
"key": key_value,
})
elif result.get("needs_recreate"):
recreate_tasks.append((key, result))
except Exception as e:
logger.error(f"[Sync] Ошибка подготовки для {key.get('email')}: {e}")
@@ -682,7 +691,7 @@ async def handle_sync_cluster(
delete(Key).where(Key.tg_id == key["tg_id"], Key.client_id == key["client_id"])
)
await session.commit()
cluster_id_for_recreate = key["server_id"] if use_country_selection else cluster_name
await create_key_on_cluster(
cluster_id_for_recreate,
@@ -707,7 +716,7 @@ async def handle_sync_cluster(
processed_count = batch_end
progress_percent = int((processed_count / total_keys) * 100)
progress_bar = "" * (progress_percent // 5) + "" * (20 - progress_percent // 5)
try:
await callback_query.message.edit_text(
text=(
@@ -719,20 +728,19 @@ async def handle_sync_cluster(
)
except Exception:
pass
else:
for key in keys_to_sync:
try:
traffic_limit_bytes = 0
hwid_limit = 0
subgroup_title = None
tariff = tariffs_cache.get(key["tariff_id"]) if key["tariff_id"] else None
current_device_limit_from_key = key.get("current_device_limit")
current_traffic_limit_gb_from_key = key.get("current_traffic_limit")
selected_device_limit_from_key = key.get("selected_device_limit")
selected_traffic_limit_gb_from_key = key.get("selected_traffic_limit")
if tariff:
if current_traffic_limit_gb_from_key is not None:
traffic_limit_bytes = int(current_traffic_limit_gb_from_key * 1024**3)
@@ -742,15 +750,15 @@ async def handle_sync_cluster(
traffic_limit_bytes = int(tariff.get("traffic_limit") * 1024**3)
else:
traffic_limit_bytes = 0
if current_device_limit_from_key is not None:
hwid_limit = int(current_device_limit_from_key)
elif selected_device_limit_from_key is not None:
hwid_limit = int(selected_device_limit_from_key)
else:
hwid_limit = tariff.get("device_limit")
subgroup_title = tariff.get("subgroup_title")
tariff.get("subgroup_title")
elif key["tariff_id"]:
logger.warning(
f"[Sync] Ключ {key['client_id']} с несуществующим тарифом ID={key['tariff_id']}"
+1 -1
View File
@@ -9,6 +9,6 @@ from . import (
domain, # noqa: F401
file_upload, # noqa: F401
import_3xui, # noqa: F401
import_remnawave, # noqa: F401
maintenance, # noqa: F401
import_remnawave # noqa: F401
)
+2 -2
View File
@@ -1,8 +1,9 @@
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn
from core.settings.money_config import get_currency_mode
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn
from .settings_config import (
BUTTON_TITLES,
MODES_TITLES,
@@ -217,4 +218,3 @@ def build_settings_money_kb(money_state: dict[str, object]) -> InlineKeyboardMar
)
return builder.as_markup()
+13 -5
View File
@@ -77,10 +77,18 @@ def build_tariffs_settings_text() -> str:
"⚙️ Настройки тарификации",
"",
f"• Понижение условий при изменении: {'включено' if allow_downgrade else 'выключено'}",
f"• Режим доплат пакетами: {format_pack_mode_label(pack_mode)}",
f"• Режим доплат: {format_pack_mode_label(pack_mode)}",
f"• Перерасчёт при докупке: {'да' if recalc_enabled else 'нет'}",
"",
"Нажмите «Режим пакетов», чтобы подробнее настроить доплаты к активной подписке.",
"ℹ️ Как работают режимы доплат:",
"",
"🔹 <b>Пакеты</b>",
"<blockquote>Позволяет докупать трафик и устройства к активной подписке неограниченное количество раз.",
"При продлении подписки все параметры возвращаются к исходно сконфигурированному тарифу.",
"</blockquote>\n\n🔹 <b>Базовый конфигуратор (выкл. режим пакетов)</b>",
"<blockquote>Позволяет выбрать параметры тарифа из доступных опций.",
"Выбранные условия сохраняются и будут использоваться при последующих продлениях.",
"При включённом понижении клиент сможет понижать условия тарифа.</blockquote>",
]
return "\n".join(lines)
@@ -145,14 +153,14 @@ def build_tariffs_packs_text() -> str:
"При активной подписке можно продавать не новый тариф, а доплаты к текущим лимитам.",
"",
"Режимы:",
"<blockquote>",
"• Выкл — доплаты пакетами отключены, работает обычный конфигуратор лимитов (клиент меняет конфигурацию тарифа для продления).",
"• Только трафик — пользователь докупает ГБ до продления, количество устройств не меняется.",
"• Только устройства — пользователь докупает устройства до продления, лимит трафика не меняется.",
"• Трафик и устройства — можно одновременно докупать и трафик, и устройства до продления как единый пакет.",
f"</blockquote>\nТекущий режим: {format_pack_mode_label(current)}",
"",
f"Текущий режим: {format_pack_mode_label(current)}",
"",
"Выберите режим, который будет использоваться при доплате за расширение уже существующей подписки.",
"Выберите режим, который покажет варианты для доплаты.\n ❗Доплаты не переносятся при продлении.",
]
return "\n".join(lines)
@@ -1,6 +1,7 @@
from datetime import datetime
from aiogram import F
from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message
from sqlalchemy import select
@@ -146,7 +147,12 @@ async def clear_traffic_overrides(callback: CallbackQuery, state: FSMContext, se
await session.commit()
text, markup = build_traffic_overrides_screen(tariff)
await callback.message.edit_text(text=text, reply_markup=markup)
try:
await callback.message.edit_text(text=text, reply_markup=markup)
except TelegramBadRequest as e:
if "message is not modified" not in str(e):
raise
@router.message(TariffConfigState.entering_traffic_overrides, IsAdminFilter())
+3 -1
View File
@@ -262,7 +262,9 @@ def build_key_edit_kb(key_details: dict, email: str, is_configurable: bool = Fal
)
builder.button(
text="🔁 Пересоздать",
callback_data=AdminUserEditorCallback(action="users_recreate_key", data=email, tg_id=key_details["tg_id"]).pack(),
callback_data=AdminUserEditorCallback(
action="users_recreate_key", data=email, tg_id=key_details["tg_id"]
).pack(),
)
builder.button(
text="📦 Тариф",
+8 -10
View File
@@ -14,8 +14,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
from config import USE_COUNTRY_SELECTION, REMNAWAVE_TOKEN_LOGIN_ENABLED, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD
from panels.remnawave import RemnawaveAPI
from config import REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, REMNAWAVE_TOKEN_LOGIN_ENABLED, USE_COUNTRY_SELECTION
from core.bootstrap import MODES_CONFIG
from database import (
delete_key,
@@ -43,6 +42,7 @@ from handlers.utils import generate_random_email, handle_error
from hooks.hook_buttons import insert_hook_buttons
from hooks.processors import process_admin_key_edit_menu
from logger import logger
from panels.remnawave import RemnawaveAPI
from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn, build_admin_back_kb
from .keyboard import (
@@ -738,9 +738,7 @@ async def handle_recreate_key_confirm(
)
return
remnawave_servers = [
s for s in cluster if s.get("panel_type", "3x-ui").lower() == "remnawave"
]
remnawave_servers = [s for s in cluster if s.get("panel_type", "3x-ui").lower() == "remnawave"]
if not remnawave_servers:
await callback_query.message.edit_text(
@@ -1396,8 +1394,8 @@ async def handle_admin_unfreeze_subscription(
async def change_expiry_time(expiry_time: int, email: str, session: AsyncSession) -> Exception | None:
result = await session.execute(
select(
Key.client_id,
Key.tariff_id,
Key.client_id,
Key.tariff_id,
Key.server_id,
Key.current_device_limit,
Key.current_traffic_limit,
@@ -1551,7 +1549,7 @@ async def render_config_menu(callback_query: CallbackQuery, state: FSMContext, s
extra_traf_str = f" + {extra_traffic} ГБ (докуплено)" if extra_traffic > 0 else ""
text += f"📊 <b>Трафик:</b> {base_traffic} ГБ{extra_traf_str}\n"
else:
text += f"📊 <b>Трафик:</b> безлимит\n"
text += "📊 <b>Трафик:</b> безлимит\n"
text += "\n<i>Выберите что редактировать:</i>"
@@ -1771,7 +1769,7 @@ async def handle_cfg_input_addon(message: Message, state: FSMContext, session: A
extra_traf_str = f" + {extra_traffic} ГБ (докуплено)" if extra_traffic > 0 else ""
text += f"📊 <b>Трафик:</b> {base_traffic} ГБ{extra_traf_str}\n"
else:
text += f"📊 <b>Трафик:</b> безлимит\n"
text += "📊 <b>Трафик:</b> безлимит\n"
text += "\n<i>Выберите что редактировать:</i>"
@@ -1820,7 +1818,7 @@ async def handle_cfg_save(callback_query: CallbackQuery, state: FSMContext, sess
tariff_base_traffic = tariff.get("traffic_limit") or 0
extra_base_traffic = max(0, (base_traffic or 0) - tariff_base_traffic) if base_traffic else 0
traffic_extra_price = extra_base_traffic * traffic_step
selected_price = base_price + devices_extra_price + traffic_extra_price
result = await session.execute(select(Key).where(Key.email == email))
+2 -2
View File
@@ -74,7 +74,7 @@ async def handle_discount_entry(callback: CallbackQuery, session: AsyncSession):
tariffs = await get_tariffs(session=session, group_code=group_code)
except Exception as e:
logger.error(f"[DISCOUNT] Не удалось получить обычные тарифы: {e}")
if not tariffs:
await callback.message.edit_text("❌ Тарифы временно недоступны.")
return
@@ -152,7 +152,7 @@ async def handle_ultra_discount(callback: CallbackQuery, session: AsyncSession):
tariffs = await get_tariffs(session=session, group_code=group_code)
except Exception as e:
logger.error(f"[DISCOUNT_MAX] Не удалось получить обычные тарифы: {e}")
if not tariffs:
await callback.message.edit_text("❌ Тарифы временно недоступны.")
return
+13 -9
View File
@@ -339,7 +339,9 @@ async def show_tariffs_in_renew_subgroup(callback: CallbackQuery, state: FSMCont
filtered = [t for t in tariffs if t["subgroup_title"] == subgroup and t["is_active"]]
if not filtered and discount_info.get("available"):
logger.warning(f"[RENEW_SUBGROUP] Нет тарифов со скидкой {group_code} в подгруппе '{subgroup}', fallback на {original_group_code}")
logger.warning(
f"[RENEW_SUBGROUP] Нет тарифов со скидкой {group_code} в подгруппе '{subgroup}', fallback на {original_group_code}"
)
group_code = original_group_code
subgroup = await find_subgroup_by_hash(session, subgroup_hash, group_code)
if subgroup:
@@ -700,11 +702,13 @@ async def complete_key_renewal(
new_tariff_device_limit = tariff.get("device_limit")
new_tariff_traffic_limit_bytes = tariff.get("traffic_limit")
new_tariff_traffic_limit_gb = int(new_tariff_traffic_limit_bytes / GB) if new_tariff_traffic_limit_bytes else None
selected_device_limit_db = key_info.get("selected_device_limit")
selected_traffic_limit_db = key_info.get("selected_traffic_limit")
current_traffic_limit_db = key_info.get("current_traffic_limit")
new_tariff_traffic_limit_gb = (
int(new_tariff_traffic_limit_bytes / GB) if new_tariff_traffic_limit_bytes else None
)
key_info.get("selected_device_limit")
key_info.get("selected_traffic_limit")
key_info.get("current_traffic_limit")
if new_tariff_device_limit is None:
final_device_limit = None
@@ -813,9 +817,9 @@ async def complete_key_renewal(
effective_client_id = key_row["client_id"] if key_row else client_id
await update_key_expiry(session, effective_client_id, new_expiry_time)
update_values = {"tariff_id": tariff_id}
if not tariff.get("configurable"):
if new_tariff_device_limit is None:
update_values["selected_device_limit"] = None
@@ -830,7 +834,7 @@ async def complete_key_renewal(
else:
update_values["selected_traffic_limit"] = new_tariff_traffic_limit_gb
update_values["current_traffic_limit"] = final_traffic_limit
await session.execute(update(Key).where(Key.email == email).values(**update_values))
await update_balance(session, tg_id, -cost)
@@ -1,7 +1,9 @@
import asyncio
from datetime import datetime, timedelta
import pytz
from aiogram import Bot, Router
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
@@ -55,7 +55,9 @@ async def notify_hot_leads(bot: Bot, session: AsyncSession):
discount_tariffs = await get_tariffs(session, group_code="discounts")
active_discount_tariffs = [t for t in discount_tariffs if t.get("is_active")]
if not active_discount_tariffs:
logger.warning(f"[HOT LEAD] Пропуск шага 2 для {tg_id}: нет активных тарифов со скидкой (discounts)")
logger.warning(
f"[HOT LEAD] Пропуск шага 2 для {tg_id}: нет активных тарифов со скидкой (discounts)"
)
continue
keyboard = build_hot_lead_kb()
@@ -110,7 +112,9 @@ async def notify_hot_leads(bot: Bot, session: AsyncSession):
discount_max_tariffs = await get_tariffs(session, group_code="discounts_max")
active_discount_max_tariffs = [t for t in discount_max_tariffs if t.get("is_active")]
if not active_discount_max_tariffs:
logger.warning(f"[HOT LEAD] Пропуск шага 3 для {tg_id}: нет активных тарифов с максимальной скидкой (discounts_max)")
logger.warning(
f"[HOT LEAD] Пропуск шага 3 для {tg_id}: нет активных тарифов с максимальной скидкой (discounts_max)"
)
continue
keyboard = build_hot_lead_kb(final=True)
+13 -64
View File
@@ -81,8 +81,8 @@ async def render_addons_screen(callback: CallbackQuery, state: FSMContext, sessi
tariff_name = tariff.get("name") or "подписка"
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
raw_device_options = cfg.get("device_options") or []
raw_traffic_options = cfg.get("traffic_options_gb") or []
try:
device_options = sorted(
@@ -115,9 +115,9 @@ async def render_addons_screen(callback: CallbackQuery, state: FSMContext, sessi
continue
has_device_option = bool(device_int_options)
has_device_choice = len(device_int_options) > 1
has_traffic_option = bool(traffic_int_options)
has_device_choice = len(device_int_options) > 1
has_traffic_choice = len(traffic_int_options) > 1
if selected_devices is None and has_device_option:
@@ -340,60 +340,6 @@ async def start_key_addons(callback: CallbackQuery, state: FSMContext, session:
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
device_int_options: list[int] = []
for value in raw_device_options:
try:
device_int_options.append(int(value))
except (TypeError, ValueError):
continue
traffic_int_options: list[int] = []
for value in raw_traffic_options:
try:
traffic_int_options.append(int(value))
except (TypeError, ValueError):
continue
base_device_limit = cfg.get("base_device_limit")
if base_device_limit is None:
base_device_limit = tariff.get("device_limit")
if base_device_limit is not None:
try:
base_device_int = int(base_device_limit)
if base_device_int not in device_int_options:
raw_device_options.append(base_device_int)
device_int_options.append(base_device_int)
except (TypeError, ValueError):
pass
device_overrides_cfg = cfg.get("device_price_overrides") or tariff.get("device_overrides") or {}
if "0" in device_overrides_cfg and 0 not in device_int_options:
raw_device_options.append(0)
device_int_options.append(0)
base_traffic_gb = cfg.get("base_traffic_gb")
if base_traffic_gb is None:
raw_limit = tariff.get("traffic_limit")
if raw_limit:
raw_limit = int(raw_limit)
if raw_limit >= GB:
base_traffic_gb = int(raw_limit / GB)
else:
base_traffic_gb = raw_limit
if base_traffic_gb is not None:
try:
base_traffic_int = int(base_traffic_gb)
if base_traffic_int not in traffic_int_options:
raw_traffic_options.append(base_traffic_int)
traffic_int_options.append(base_traffic_int)
except (TypeError, ValueError):
pass
traffic_overrides_cfg = cfg.get("traffic_price_overrides") or tariff.get("traffic_overrides") or {}
if "0" in traffic_overrides_cfg and 0 not in traffic_int_options:
raw_traffic_options.append(0)
traffic_int_options.append(0)
try:
device_options = sorted(
raw_device_options,
@@ -444,8 +390,13 @@ async def start_key_addons(callback: CallbackQuery, state: FSMContext, session:
else (int(selected_traffic_limit_db) if selected_traffic_limit_db is not None else base_traffic_gb_from_tariff)
)
current_devices_for_price = int(current_devices) if current_devices is not None else None
current_traffic_gb_for_price = int(current_traffic_gb) if current_traffic_gb is not None else None
has_device_option = bool(device_options)
has_traffic_option = bool(traffic_options)
current_devices_for_price = int(current_devices) if current_devices is not None and has_device_option else None
current_traffic_gb_for_price = (
int(current_traffic_gb) if current_traffic_gb is not None and has_traffic_option else None
)
config_price_for_current = calculate_config_price(
tariff=tariff,
@@ -468,10 +419,8 @@ async def start_key_addons(callback: CallbackQuery, state: FSMContext, session:
)
cfg_for_state = dict(cfg)
if device_options:
cfg_for_state["device_options"] = device_options
if traffic_options:
cfg_for_state["traffic_options_gb"] = traffic_options
cfg_for_state["device_options"] = device_options
cfg_for_state["traffic_options_gb"] = traffic_options
await state.update_data(
addon_key_email=email,
@@ -43,6 +43,7 @@ from .utils import (
format_traffic_label,
)
router = Router()
+1 -1
View File
@@ -73,7 +73,7 @@ def calc_remaining_ratio_seconds(expiry_time: Any, tariff: dict) -> tuple[int, i
if isinstance(expiry_time, datetime):
expiry_dt = expiry_time
elif isinstance(expiry_time, (int, float)):
elif isinstance(expiry_time, int | float):
ts = float(expiry_time)
if ts > 10_000_000_000:
ts = ts / 1000.0
+29 -89
View File
@@ -51,8 +51,11 @@ def calculate_config_price(
base_price = int(tariff.get("price_rub") or 0)
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
raw_device_options = tariff.get("device_options")
raw_traffic_options = tariff.get("traffic_options_gb")
raw_device_options = raw_device_options if isinstance(raw_device_options, list) else []
raw_traffic_options = raw_traffic_options if isinstance(raw_traffic_options, list) else []
device_values: list[int] = []
for value in raw_device_options:
@@ -298,21 +301,19 @@ async def render_user_config_screen(
logger.warning(f"[TARIFF_CFG] render_user_config_screen tariff_not_found: tariff_id={tariff_id}")
return
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_device_options = tariff.get("device_options")
raw_traffic_options = tariff.get("traffic_options_gb")
raw_device_options = raw_device_options if isinstance(raw_device_options, list) else []
raw_traffic_options = raw_traffic_options if isinstance(raw_traffic_options, list) else []
try:
device_options = sorted(
raw_device_options,
key=lambda v: (int(v) == 0, int(v)),
)
device_options = sorted(raw_device_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
device_options = raw_device_options
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
try:
traffic_options = sorted(
raw_traffic_options,
key=lambda v: (int(v) == 0, int(v)),
)
traffic_options = sorted(raw_traffic_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
traffic_options = raw_traffic_options
@@ -536,76 +537,19 @@ async def start_user_tariff_configurator(
"""Запускает конфигуратор тарифа для пользователя."""
cfg = normalize_tariff_config(tariff)
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
raw_device_options = tariff.get("device_options")
raw_traffic_options = tariff.get("traffic_options_gb")
device_int_options: list[int] = []
for value in raw_device_options:
try:
device_int_options.append(int(value))
except (TypeError, ValueError):
continue
traffic_int_options: list[int] = []
for value in raw_traffic_options:
try:
traffic_int_options.append(int(value))
except (TypeError, ValueError):
continue
base_device_limit = cfg.get("base_device_limit")
if base_device_limit is None:
base_device_limit = tariff.get("device_limit")
if base_device_limit is not None:
try:
base_device_int = int(base_device_limit)
if base_device_int not in device_int_options:
raw_device_options.append(base_device_int)
device_int_options.append(base_device_int)
except (TypeError, ValueError):
pass
device_overrides_cfg = cfg.get("device_price_overrides") or tariff.get("device_overrides") or {}
if "0" in device_overrides_cfg and 0 not in device_int_options:
raw_device_options.append(0)
device_int_options.append(0)
base_traffic_gb = cfg.get("base_traffic_gb")
if base_traffic_gb is None:
raw_limit = tariff.get("traffic_limit")
if raw_limit:
raw_limit = int(raw_limit)
if raw_limit >= GB:
base_traffic_gb = int(raw_limit / GB)
else:
base_traffic_gb = raw_limit
if base_traffic_gb is not None:
try:
base_traffic_int = int(base_traffic_gb)
if base_traffic_int not in traffic_int_options:
raw_traffic_options.append(base_traffic_int)
traffic_int_options.append(base_traffic_int)
except (TypeError, ValueError):
pass
traffic_overrides_cfg = cfg.get("traffic_price_overrides") or tariff.get("traffic_overrides") or {}
if "0" in traffic_overrides_cfg and 0 not in traffic_int_options:
raw_traffic_options.append(0)
traffic_int_options.append(0)
raw_device_options = raw_device_options if isinstance(raw_device_options, list) else []
raw_traffic_options = raw_traffic_options if isinstance(raw_traffic_options, list) else []
try:
device_options = sorted(
raw_device_options,
key=lambda v: (int(v) == 0, int(v)),
)
device_options = sorted(raw_device_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
device_options = raw_device_options
try:
traffic_options = sorted(
raw_traffic_options,
key=lambda v: (int(v) == 0, int(v)),
)
traffic_options = sorted(raw_traffic_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
traffic_options = raw_traffic_options
@@ -626,10 +570,8 @@ async def start_user_tariff_configurator(
return
cfg_for_state = dict(cfg)
if device_options:
cfg_for_state["device_options"] = device_options
if traffic_options:
cfg_for_state["traffic_options_gb"] = traffic_options
cfg_for_state["device_options"] = device_options
cfg_for_state["traffic_options_gb"] = traffic_options
data = await state.get_data()
renew_mode = data.get("renew_mode")
@@ -686,21 +628,19 @@ async def finalize_config_and_purchase(callback_query: CallbackQuery, state: FSM
duration_days = int(tariff.get("duration_days") or 30)
raw_device_options = cfg.get("device_options") or tariff.get("device_options") or []
raw_device_options = tariff.get("device_options")
raw_traffic_options = tariff.get("traffic_options_gb")
raw_device_options = raw_device_options if isinstance(raw_device_options, list) else []
raw_traffic_options = raw_traffic_options if isinstance(raw_traffic_options, list) else []
try:
device_options = sorted(
raw_device_options,
key=lambda v: (int(v) == 0, int(v)),
)
device_options = sorted(raw_device_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
device_options = raw_device_options
raw_traffic_options = cfg.get("traffic_options_gb") or tariff.get("traffic_options_gb") or []
try:
traffic_options = sorted(
raw_traffic_options,
key=lambda v: (int(v) == 0, int(v)),
)
traffic_options = sorted(raw_traffic_options, key=lambda v: (int(v) == 0, int(v)))
except (TypeError, ValueError):
traffic_options = raw_traffic_options
+1 -1
View File
@@ -13,7 +13,7 @@ from database.models import Admin
class AdminMiddleware(BaseMiddleware):
"""Проверяет, является ли пользователь администратором."""
_admin_ids: set[int] = set(ADMIN_ID) if isinstance(ADMIN_ID, (list, tuple)) else {ADMIN_ID}
_admin_ids: set[int] = set(ADMIN_ID) if isinstance(ADMIN_ID, list | tuple) else {ADMIN_ID}
async def __call__(
self,
+1
View File
@@ -1,4 +1,5 @@
import time
from collections.abc import Awaitable, Callable
from typing import Any
+8 -10
View File
@@ -61,13 +61,11 @@ class UserMiddleware(BaseMiddleware):
return db_user
def _fingerprint(self, user: User) -> str:
return "|".join(
[
str(user.id),
user.username or "",
user.first_name or "",
user.last_name or "",
user.language_code or "",
"1" if user.is_bot else "0",
]
)
return "|".join([
str(user.id),
user.username or "",
user.first_name or "",
user.last_name or "",
user.language_code or "",
"1" if user.is_bot else "0",
])