fix TelegramBadRequest in traffic pricing and null in configurator. Extended description of billing in the settings
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -537,7 +537,9 @@ async def handle_sync_cluster(
|
||||
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}")
|
||||
@@ -725,7 +734,6 @@ async def handle_sync_cluster(
|
||||
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")
|
||||
@@ -750,7 +758,7 @@ async def handle_sync_cluster(
|
||||
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']} — "
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
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())
|
||||
|
||||
@@ -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="📦 Тариф",
|
||||
|
||||
@@ -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(
|
||||
@@ -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>"
|
||||
|
||||
|
||||
@@ -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
|
||||
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")
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,9 +419,7 @@ 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
|
||||
|
||||
await state.update_data(
|
||||
|
||||
@@ -43,6 +43,7 @@ from .utils import (
|
||||
format_traffic_label,
|
||||
)
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,9 +570,7 @@ 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
|
||||
|
||||
data = await state.get_data()
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
import time
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any
|
||||
|
||||
|
||||
+2
-4
@@ -61,13 +61,11 @@ class UserMiddleware(BaseMiddleware):
|
||||
return db_user
|
||||
|
||||
def _fingerprint(self, user: User) -> str:
|
||||
return "|".join(
|
||||
[
|
||||
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",
|
||||
]
|
||||
)
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user