RemnaWave-bot Update
This commit is contained in:
130
app/config.py
130
app/config.py
@@ -13,6 +13,28 @@ class PaymentPlanSettings:
|
||||
code: str
|
||||
days: int
|
||||
amount_rub: int
|
||||
title: str = ""
|
||||
description: str = ""
|
||||
squad_groups: tuple[str, ...] = ()
|
||||
|
||||
|
||||
PAYMENT_PRODUCT_PLAN_SPECS: dict[str, tuple[str, str, tuple[str, ...]]] = {
|
||||
"vpn_white": (
|
||||
"VPN + Белые списки",
|
||||
"VPN и белые списки в одном тарифе",
|
||||
("vpn", "white"),
|
||||
),
|
||||
"white": (
|
||||
"Белые списки",
|
||||
"Доступ только к белым спискам",
|
||||
("white",),
|
||||
),
|
||||
"vpn": (
|
||||
"VPN",
|
||||
"Доступ только к VPN без белых списков",
|
||||
("vpn",),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
@@ -78,6 +100,10 @@ class Settings(BaseSettings):
|
||||
default="30:250,180:600,365:1000",
|
||||
validation_alias="PAYMENT_PLANS",
|
||||
)
|
||||
payment_product_plans_raw: str = Field(
|
||||
default="",
|
||||
validation_alias="PAYMENT_PRODUCT_PLANS",
|
||||
)
|
||||
payment_transfer_text: str = Field(
|
||||
default="Переведите оплату по указанным реквизитам и отправьте чек в бот.",
|
||||
validation_alias="PAYMENT_TRANSFER_TEXT",
|
||||
@@ -125,6 +151,14 @@ class Settings(BaseSettings):
|
||||
default="",
|
||||
validation_alias="PAYMENT_INTERNAL_SQUAD_UUIDS",
|
||||
)
|
||||
payment_vpn_squad_uuids_raw: str = Field(
|
||||
default="",
|
||||
validation_alias="PAYMENT_VPN_SQUAD_UUIDS",
|
||||
)
|
||||
payment_white_squad_uuids_raw: str = Field(
|
||||
default="",
|
||||
validation_alias="PAYMENT_WHITE_SQUAD_UUIDS",
|
||||
)
|
||||
payment_external_squad_uuid: str = Field(
|
||||
default="",
|
||||
validation_alias="PAYMENT_EXTERNAL_SQUAD_UUID",
|
||||
@@ -237,12 +271,15 @@ class Settings(BaseSettings):
|
||||
|
||||
@cached_property
|
||||
def payment_internal_squad_uuids(self) -> list[str]:
|
||||
values: list[str] = []
|
||||
for raw_part in self.payment_internal_squad_uuids_raw.split(","):
|
||||
part = raw_part.strip()
|
||||
if part:
|
||||
values.append(part)
|
||||
return values
|
||||
return self._parse_csv_list(self.payment_internal_squad_uuids_raw)
|
||||
|
||||
@cached_property
|
||||
def payment_vpn_squad_uuids(self) -> list[str]:
|
||||
return self._parse_csv_list(self.payment_vpn_squad_uuids_raw)
|
||||
|
||||
@cached_property
|
||||
def payment_white_squad_uuids(self) -> list[str]:
|
||||
return self._parse_csv_list(self.payment_white_squad_uuids_raw)
|
||||
|
||||
@cached_property
|
||||
def payment_external_squad_uuid_normalized(self) -> str | None:
|
||||
@@ -265,6 +302,13 @@ class Settings(BaseSettings):
|
||||
|
||||
@cached_property
|
||||
def payment_plans(self) -> list[PaymentPlanSettings]:
|
||||
product_plans = self._parse_product_plans(
|
||||
self.payment_product_plans_raw,
|
||||
default_days=max(self.payment_plan_duration_days, 1),
|
||||
)
|
||||
if product_plans:
|
||||
return product_plans
|
||||
|
||||
values: list[PaymentPlanSettings] = []
|
||||
for raw_part in self.payment_plans_raw.split(","):
|
||||
part = raw_part.strip()
|
||||
@@ -303,3 +347,77 @@ class Settings(BaseSettings):
|
||||
]
|
||||
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _parse_csv_list(raw_value: str) -> list[str]:
|
||||
values: list[str] = []
|
||||
for raw_part in raw_value.split(","):
|
||||
part = raw_part.strip()
|
||||
if part:
|
||||
values.append(part)
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def _parse_product_plans(
|
||||
raw_value: str,
|
||||
*,
|
||||
default_days: int,
|
||||
) -> list[PaymentPlanSettings]:
|
||||
values: list[PaymentPlanSettings] = []
|
||||
seen_codes: set[str] = set()
|
||||
|
||||
for raw_part in raw_value.split(","):
|
||||
part = raw_part.strip()
|
||||
if not part:
|
||||
continue
|
||||
|
||||
fragments = [fragment.strip() for fragment in part.split(":")]
|
||||
|
||||
# Legacy format: code:amount (2 fields) → single duration using default_days
|
||||
if len(fragments) == 2:
|
||||
code, raw_amount = fragments
|
||||
pairs = [(str(default_days), raw_amount)]
|
||||
# Standard format: code:days:amount (3 fields) → single duration
|
||||
elif len(fragments) == 3:
|
||||
code = fragments[0]
|
||||
pairs = [(fragments[1], fragments[2])]
|
||||
# Extended format: code:days1:amount1:days2:amount2:... (odd number ≥ 5)
|
||||
elif len(fragments) >= 5 and len(fragments) % 2 == 1:
|
||||
code = fragments[0]
|
||||
pairs = [
|
||||
(fragments[i], fragments[i + 1])
|
||||
for i in range(1, len(fragments), 2)
|
||||
]
|
||||
else:
|
||||
continue
|
||||
|
||||
normalized_code = code.lower()
|
||||
spec = PAYMENT_PRODUCT_PLAN_SPECS.get(normalized_code)
|
||||
if spec is None or normalized_code in seen_codes:
|
||||
continue
|
||||
|
||||
title, description, squad_groups = spec
|
||||
for raw_days, raw_amount in pairs:
|
||||
if not raw_days.isdigit() or not raw_amount.isdigit():
|
||||
continue
|
||||
|
||||
days = int(raw_days)
|
||||
amount_rub = int(raw_amount)
|
||||
if days <= 0 or amount_rub <= 0:
|
||||
continue
|
||||
|
||||
plan_code = f"{normalized_code}_{days}"
|
||||
values.append(
|
||||
PaymentPlanSettings(
|
||||
code=plan_code,
|
||||
days=days,
|
||||
amount_rub=amount_rub,
|
||||
title=title,
|
||||
description=description,
|
||||
squad_groups=squad_groups,
|
||||
)
|
||||
)
|
||||
|
||||
seen_codes.add(normalized_code)
|
||||
|
||||
return values
|
||||
|
||||
Reference in New Issue
Block a user