mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
140 lines
4.3 KiB
Python
140 lines
4.3 KiB
Python
from __future__ import annotations
|
||
|
||
import asyncio
|
||
from dataclasses import dataclass
|
||
from typing import List
|
||
|
||
from aiogram import types
|
||
from aiogram.types import InlineKeyboardButton
|
||
from sqlalchemy import select
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.database.models import (
|
||
MainMenuButton,
|
||
MainMenuButtonActionType,
|
||
MainMenuButtonVisibility,
|
||
)
|
||
from app.config import settings
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class _MainMenuButtonData:
|
||
text: str
|
||
action_type: MainMenuButtonActionType
|
||
action_value: str
|
||
visibility: MainMenuButtonVisibility
|
||
is_active: bool
|
||
display_order: int
|
||
|
||
|
||
class MainMenuButtonService:
|
||
_cache: List[_MainMenuButtonData] | None = None
|
||
_lock: asyncio.Lock = asyncio.Lock()
|
||
|
||
@classmethod
|
||
def invalidate_cache(cls) -> None:
|
||
cls._cache = None
|
||
|
||
@classmethod
|
||
async def _load_cache(cls, db: AsyncSession) -> List[_MainMenuButtonData]:
|
||
if cls._cache is not None:
|
||
return cls._cache
|
||
|
||
async with cls._lock:
|
||
if cls._cache is not None:
|
||
return cls._cache
|
||
|
||
result = await db.execute(
|
||
select(MainMenuButton).order_by(
|
||
MainMenuButton.display_order.asc(),
|
||
MainMenuButton.id.asc(),
|
||
)
|
||
)
|
||
|
||
items: List[_MainMenuButtonData] = []
|
||
for record in result.scalars().all():
|
||
text = (record.text or "").strip()
|
||
action_value = (record.action_value or "").strip()
|
||
|
||
if not text or not action_value:
|
||
continue
|
||
|
||
try:
|
||
action_type = MainMenuButtonActionType(record.action_type)
|
||
except ValueError:
|
||
continue
|
||
|
||
try:
|
||
visibility = MainMenuButtonVisibility(record.visibility)
|
||
except ValueError:
|
||
visibility = MainMenuButtonVisibility.ALL
|
||
|
||
items.append(
|
||
_MainMenuButtonData(
|
||
text=text,
|
||
action_type=action_type,
|
||
action_value=action_value,
|
||
visibility=visibility,
|
||
is_active=bool(record.is_active),
|
||
display_order=int(record.display_order or 0),
|
||
)
|
||
)
|
||
|
||
cls._cache = items
|
||
return items
|
||
|
||
@classmethod
|
||
async def get_buttons_for_user(
|
||
cls,
|
||
db: AsyncSession,
|
||
*,
|
||
is_admin: bool,
|
||
has_active_subscription: bool,
|
||
subscription_is_active: bool,
|
||
) -> list[InlineKeyboardButton]:
|
||
data = await cls._load_cache(db)
|
||
has_subscription = bool(has_active_subscription and subscription_is_active)
|
||
|
||
buttons: list[InlineKeyboardButton] = []
|
||
for item in data:
|
||
if not item.is_active:
|
||
continue
|
||
|
||
if item.visibility == MainMenuButtonVisibility.ADMINS and not is_admin:
|
||
continue
|
||
|
||
if item.visibility == MainMenuButtonVisibility.SUBSCRIBERS and not has_subscription:
|
||
continue
|
||
|
||
# Проверка реферальной программы: скрыть кнопки, связанные с рефералами, если программа отключена
|
||
if (
|
||
not settings.is_referral_program_enabled()
|
||
and (
|
||
"partner" in item.text.lower()
|
||
or "referr" in item.text.lower()
|
||
or "партнер" in item.text.lower()
|
||
or "реферал" in item.text.lower()
|
||
or "referral" in item.action_value.lower()
|
||
)
|
||
):
|
||
continue
|
||
|
||
button = cls._build_button(item)
|
||
if button:
|
||
buttons.append(button)
|
||
|
||
return buttons
|
||
|
||
@staticmethod
|
||
def _build_button(item: _MainMenuButtonData) -> InlineKeyboardButton | None:
|
||
if item.action_type == MainMenuButtonActionType.URL:
|
||
return InlineKeyboardButton(text=item.text, url=item.action_value)
|
||
|
||
if item.action_type == MainMenuButtonActionType.MINI_APP:
|
||
return InlineKeyboardButton(
|
||
text=item.text,
|
||
web_app=types.WebAppInfo(url=item.action_value),
|
||
)
|
||
|
||
return None
|