mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-05-04 20:15:56 +00:00
Add button open mode and webapp url
This commit is contained in:
@@ -135,6 +135,8 @@ DEFAULT_MENU_CONFIG: Dict[str, Any] = {
|
||||
"visibility": "subscribers",
|
||||
"conditions": None,
|
||||
"dynamic_text": False,
|
||||
"open_mode": "callback", # "callback" или "direct"
|
||||
"webapp_url": None, # URL для Mini App при open_mode="direct"
|
||||
},
|
||||
"happ_download": {
|
||||
"type": "builtin",
|
||||
@@ -308,6 +310,7 @@ BUILTIN_BUTTONS_INFO = [
|
||||
"callback_data": "subscription_connect",
|
||||
"default_conditions": {"has_active_subscription": True, "subscription_is_active": True},
|
||||
"supports_dynamic_text": False,
|
||||
"supports_direct_open": True, # Может открывать Mini App напрямую
|
||||
},
|
||||
{
|
||||
"id": "happ_download",
|
||||
@@ -1021,6 +1024,8 @@ class MenuLayoutService:
|
||||
button_type = button_config.get("type", "builtin")
|
||||
text_config = button_config.get("text", {})
|
||||
action = button_config.get("action", "")
|
||||
open_mode = button_config.get("open_mode", "callback")
|
||||
webapp_url = button_config.get("webapp_url")
|
||||
|
||||
# Получаем текст
|
||||
text = cls._get_localized_text(text_config, context.language)
|
||||
@@ -1039,8 +1044,15 @@ class MenuLayoutService:
|
||||
text=text, web_app=types.WebAppInfo(url=action)
|
||||
)
|
||||
else:
|
||||
# builtin - callback_data
|
||||
return InlineKeyboardButton(text=text, callback_data=action)
|
||||
# builtin - проверяем open_mode
|
||||
if open_mode == "direct" and webapp_url:
|
||||
# Прямое открытие Mini App через WebAppInfo
|
||||
return InlineKeyboardButton(
|
||||
text=text, web_app=types.WebAppInfo(url=webapp_url)
|
||||
)
|
||||
else:
|
||||
# Стандартный callback_data
|
||||
return InlineKeyboardButton(text=text, callback_data=action)
|
||||
|
||||
@classmethod
|
||||
async def build_keyboard(
|
||||
|
||||
@@ -71,6 +71,8 @@ def _serialize_config(config: dict, is_enabled: bool, updated_at) -> MenuLayoutR
|
||||
if btn_data.get("conditions")
|
||||
else None,
|
||||
dynamic_text=btn_data.get("dynamic_text", False),
|
||||
open_mode=btn_data.get("open_mode", "callback"),
|
||||
webapp_url=btn_data.get("webapp_url"),
|
||||
)
|
||||
|
||||
return MenuLayoutResponse(
|
||||
@@ -143,6 +145,7 @@ async def list_builtin_buttons(
|
||||
if btn_info.get("default_conditions")
|
||||
else None,
|
||||
supports_dynamic_text=btn_info.get("supports_dynamic_text", False),
|
||||
supports_direct_open=btn_info.get("supports_direct_open", False),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -163,6 +166,10 @@ async def update_button(
|
||||
if "visibility" in updates and updates["visibility"] is not None:
|
||||
if hasattr(updates["visibility"], "value"):
|
||||
updates["visibility"] = updates["visibility"].value
|
||||
# Конвертируем open_mode в строку если есть
|
||||
if "open_mode" in updates and updates["open_mode"] is not None:
|
||||
if hasattr(updates["open_mode"], "value"):
|
||||
updates["open_mode"] = updates["open_mode"].value
|
||||
# Конвертируем conditions - убираем None значения если это dict
|
||||
if "conditions" in updates and updates["conditions"] is not None:
|
||||
if isinstance(updates["conditions"], dict):
|
||||
@@ -183,6 +190,8 @@ async def update_button(
|
||||
if button.get("conditions")
|
||||
else None,
|
||||
dynamic_text=button.get("dynamic_text", False),
|
||||
open_mode=button.get("open_mode", "callback"),
|
||||
webapp_url=button.get("webapp_url"),
|
||||
)
|
||||
except KeyError as e:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, str(e)) from e
|
||||
@@ -290,6 +299,8 @@ async def add_custom_button(
|
||||
if button.get("conditions")
|
||||
else None,
|
||||
dynamic_text=button.get("dynamic_text", False),
|
||||
open_mode=button.get("open_mode", "callback"),
|
||||
webapp_url=button.get("webapp_url"),
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, str(e)) from e
|
||||
|
||||
@@ -26,6 +26,13 @@ class ButtonVisibility(str, Enum):
|
||||
SUBSCRIBERS = "subscribers" # Только подписчикам
|
||||
|
||||
|
||||
class ButtonOpenMode(str, Enum):
|
||||
"""Режим открытия кнопки."""
|
||||
|
||||
CALLBACK = "callback" # Отправляет callback_data боту (по умолчанию)
|
||||
DIRECT = "direct" # Сразу открывает Mini App через WebAppInfo
|
||||
|
||||
|
||||
class ButtonConditions(BaseModel):
|
||||
"""Условия показа кнопки."""
|
||||
|
||||
@@ -96,6 +103,14 @@ class MenuButtonConfig(BaseModel):
|
||||
dynamic_text: bool = Field(
|
||||
default=False, description="Текст содержит плейсхолдеры ({balance} и т.д.)"
|
||||
)
|
||||
open_mode: ButtonOpenMode = Field(
|
||||
default=ButtonOpenMode.CALLBACK,
|
||||
description="Режим открытия: callback (через бота) или direct (сразу Mini App)",
|
||||
)
|
||||
webapp_url: Optional[str] = Field(
|
||||
default=None,
|
||||
description="URL для Mini App при open_mode=direct",
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
@@ -156,6 +171,9 @@ class BuiltinButtonInfo(BaseModel):
|
||||
supports_dynamic_text: bool = Field(
|
||||
default=False, description="Поддерживает ли динамический текст"
|
||||
)
|
||||
supports_direct_open: bool = Field(
|
||||
default=False, description="Поддерживает ли прямое открытие Mini App"
|
||||
)
|
||||
|
||||
|
||||
class BuiltinButtonsListResponse(BaseModel):
|
||||
@@ -197,6 +215,12 @@ class ButtonUpdateRequest(BaseModel):
|
||||
action: Optional[str] = Field(
|
||||
default=None, description="Новый action (для URL/MiniApp кнопок)"
|
||||
)
|
||||
open_mode: Optional[ButtonOpenMode] = Field(
|
||||
default=None, description="Режим открытия: callback или direct"
|
||||
)
|
||||
webapp_url: Optional[str] = Field(
|
||||
default=None, description="URL для Mini App при open_mode=direct"
|
||||
)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user