Files

135 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Лимиты и пакеты трафика."""
from __future__ import annotations
from dataclasses import dataclass
# База на подписку + докупка
BASE_TRAFFIC_GB = 1000.0
PACK_GB = 500.0
PACK_PRICE_RUB = 50.0
GB = 1024 * 1024 * 1024
def gb_to_bytes(gb: float) -> float:
return float(gb) * GB
def bytes_to_gb(value: float | int | None) -> float:
if not value:
return 0.0
return float(value) / GB
def format_gb(gb: float, *, digits: int = 1) -> str:
if gb >= 100 and abs(gb - round(gb)) < 0.05:
return f"{int(round(gb))} ГБ"
if digits <= 0:
return f"{int(round(gb))} ГБ"
text = f"{gb:.{digits}f}".rstrip("0").rstrip(".")
return f"{text} ГБ"
def effective_limit_bytes(limit_bytes: float | int | None) -> float:
"""0 / None в панели = безлимит; для нас база = 1000 ГБ."""
raw = float(limit_bytes or 0)
if raw <= 0:
return gb_to_bytes(BASE_TRAFFIC_GB)
return raw
def ensure_base_limit_bytes(current_limit_bytes: float | int | None) -> float:
"""Не меньше базовых 1000 ГБ; безлимит (0) → база."""
raw = float(current_limit_bytes or 0)
base = gb_to_bytes(BASE_TRAFFIC_GB)
if raw <= 0:
return base
return max(raw, base)
@dataclass(frozen=True, slots=True)
class TrafficPack:
packs: int
gb: float
price: float
@property
def id(self) -> str:
return f"pack_{self.packs}"
@property
def title(self) -> str:
return f"+{format_gb(self.gb, digits=0)}"
def format_price(self) -> str:
if self.price == int(self.price):
return f"{int(self.price)} ₽/мес"
return f"{self.price:.2f} ₽/мес"
# Готовые пакеты: 1 / 2 / 4 / 10 × 500 ГБ
TRAFFIC_PACKS: tuple[TrafficPack, ...] = tuple(
TrafficPack(
packs=n,
gb=PACK_GB * n,
price=PACK_PRICE_RUB * n,
)
for n in (1, 2, 4, 10)
)
def get_traffic_pack(pack_id: str) -> TrafficPack | None:
for p in TRAFFIC_PACKS:
if p.id == pack_id or str(p.packs) == pack_id:
return p
return None
@dataclass(frozen=True, slots=True)
class TrafficUsage:
used_bytes: float
limit_bytes: float
@property
def used_gb(self) -> float:
return bytes_to_gb(self.used_bytes)
@property
def limit_gb(self) -> float:
return bytes_to_gb(self.limit_bytes)
@property
def left_gb(self) -> float:
return max(0.0, self.limit_gb - self.used_gb)
@property
def percent(self) -> float:
if self.limit_bytes <= 0:
return 0.0
return min(100.0, max(0.0, 100.0 * self.used_bytes / self.limit_bytes))
def format_line(self) -> str:
return (
f"📦 Трафик: <b>{format_gb(self.used_gb)} / {format_gb(self.limit_gb)}</b> "
f"({self.percent:.0f}%)\n"
f"Осталось: <b>{format_gb(self.left_gb)}</b>"
)
def as_dict(self) -> dict:
return {
"used_gb": round(self.used_gb, 2),
"limit_gb": round(self.limit_gb, 2),
"left_gb": round(self.left_gb, 2),
"percent": round(self.percent, 1),
"used_label": format_gb(self.used_gb),
"limit_label": format_gb(self.limit_gb),
"left_label": format_gb(self.left_gb),
}
def traffic_from_panel_user(user) -> TrafficUsage:
used = float(getattr(user, "used_traffic_bytes", 0) or 0)
limit = effective_limit_bytes(getattr(user, "traffic_limit_bytes", 0))
return TrafficUsage(used_bytes=used, limit_bytes=limit)