mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-21 20:01:47 +00:00
Merge pull request #631 from Fr1ngg/bedolaga/add-service-name-configuration-option-d3ekaj
Add configurable miniapp branding
This commit is contained in:
@@ -290,6 +290,10 @@ CONNECT_BUTTON_MODE=guide
|
||||
|
||||
# URL для режима miniapp_custom (обязателен при CONNECT_BUTTON_MODE=miniapp_custom)
|
||||
MINIAPP_CUSTOM_URL=
|
||||
MINIAPP_SERVICE_NAME_EN=Bedolaga VPN
|
||||
MINIAPP_SERVICE_NAME_RU=Bedolaga VPN
|
||||
MINIAPP_SERVICE_DESCRIPTION_EN=Secure & Fast Connection
|
||||
MINIAPP_SERVICE_DESCRIPTION_RU=Безопасное и быстрое подключение
|
||||
|
||||
# Параметры режима happ_cryptolink
|
||||
CONNECT_BUTTON_HAPP_DOWNLOAD_ENABLED=false
|
||||
|
||||
@@ -535,6 +535,10 @@ CONNECT_BUTTON_MODE=guide
|
||||
|
||||
# URL для режима miniapp_custom (обязателен при CONNECT_BUTTON_MODE=miniapp_custom)
|
||||
MINIAPP_CUSTOM_URL=
|
||||
MINIAPP_SERVICE_NAME_EN=Bedolaga VPN
|
||||
MINIAPP_SERVICE_NAME_RU=Bedolaga VPN
|
||||
MINIAPP_SERVICE_DESCRIPTION_EN=Secure & Fast Connection
|
||||
MINIAPP_SERVICE_DESCRIPTION_RU=Безопасное и быстрое подключение
|
||||
|
||||
# Параметры режима happ_cryptolink
|
||||
CONNECT_BUTTON_HAPP_DOWNLOAD_ENABLED=false
|
||||
|
||||
@@ -213,6 +213,10 @@ class Settings(BaseSettings):
|
||||
|
||||
CONNECT_BUTTON_MODE: str = "guide"
|
||||
MINIAPP_CUSTOM_URL: str = ""
|
||||
MINIAPP_SERVICE_NAME_EN: str = "Bedolaga VPN"
|
||||
MINIAPP_SERVICE_NAME_RU: str = "Bedolaga VPN"
|
||||
MINIAPP_SERVICE_DESCRIPTION_EN: str = "Secure & Fast Connection"
|
||||
MINIAPP_SERVICE_DESCRIPTION_RU: str = "Безопасное и быстрое подключение"
|
||||
CONNECT_BUTTON_HAPP_DOWNLOAD_ENABLED: bool = False
|
||||
HAPP_CRYPTOLINK_REDIRECT_TEMPLATE: Optional[str] = None
|
||||
HAPP_DOWNLOAD_LINK_IOS: Optional[str] = None
|
||||
@@ -518,6 +522,34 @@ class Settings(BaseSettings):
|
||||
|
||||
def is_deep_links_enabled(self) -> bool:
|
||||
return self.ENABLE_DEEP_LINKS
|
||||
|
||||
def get_miniapp_branding(self) -> Dict[str, Dict[str, Optional[str]]]:
|
||||
def _clean(value: Optional[str]) -> Optional[str]:
|
||||
if value is None:
|
||||
return None
|
||||
value_str = str(value).strip()
|
||||
return value_str or None
|
||||
|
||||
name_en = _clean(self.MINIAPP_SERVICE_NAME_EN)
|
||||
name_ru = _clean(self.MINIAPP_SERVICE_NAME_RU)
|
||||
desc_en = _clean(self.MINIAPP_SERVICE_DESCRIPTION_EN)
|
||||
desc_ru = _clean(self.MINIAPP_SERVICE_DESCRIPTION_RU)
|
||||
|
||||
default_name = name_en or name_ru or "RemnaWave VPN"
|
||||
default_description = desc_en or desc_ru or "Secure & Fast Connection"
|
||||
|
||||
return {
|
||||
"service_name": {
|
||||
"default": default_name,
|
||||
"en": name_en,
|
||||
"ru": name_ru,
|
||||
},
|
||||
"service_description": {
|
||||
"default": default_description,
|
||||
"en": desc_en,
|
||||
"ru": desc_ru,
|
||||
},
|
||||
}
|
||||
|
||||
def get_app_config_cache_ttl(self) -> int:
|
||||
return self.APP_CONFIG_CACHE_TTL
|
||||
|
||||
@@ -89,6 +89,7 @@ class BotConfigurationService:
|
||||
"HAPP": "🅷 Happ настройки",
|
||||
"SKIP": "⚡ Быстрый старт",
|
||||
"ADDITIONAL": "📱 Приложения и DeepLinks",
|
||||
"MINIAPP": "📱 Mini App",
|
||||
"DATABASE": "🗄️ Режим БД",
|
||||
"POSTGRES": "🐘 PostgreSQL",
|
||||
"SQLITE": "💾 SQLite",
|
||||
@@ -189,6 +190,7 @@ class BotConfigurationService:
|
||||
"CONNECT_BUTTON_HAPP": "HAPP",
|
||||
"HAPP_": "HAPP",
|
||||
"SKIP_": "SKIP",
|
||||
"MINIAPP_": "MINIAPP",
|
||||
"MONITORING_": "MONITORING",
|
||||
"NOTIFICATION_": "NOTIFICATIONS",
|
||||
"SERVER_STATUS": "SERVER",
|
||||
|
||||
@@ -383,5 +383,6 @@ async def get_subscription_details(
|
||||
else None,
|
||||
subscription_type="trial" if subscription.is_trial else "paid",
|
||||
autopay_enabled=bool(subscription.autopay_enabled),
|
||||
branding=settings.get_miniapp_branding(),
|
||||
)
|
||||
|
||||
|
||||
@@ -6,6 +6,11 @@ from typing import Any, Dict, List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class MiniAppBranding(BaseModel):
|
||||
service_name: Dict[str, Optional[str]] = Field(default_factory=dict)
|
||||
service_description: Dict[str, Optional[str]] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class MiniAppSubscriptionRequest(BaseModel):
|
||||
init_data: str = Field(..., alias="initData")
|
||||
|
||||
@@ -86,4 +91,5 @@ class MiniAppSubscriptionResponse(BaseModel):
|
||||
promo_group: Optional[MiniAppPromoGroup] = None
|
||||
subscription_type: str
|
||||
autopay_enabled: bool = False
|
||||
branding: Optional[MiniAppBranding] = None
|
||||
|
||||
|
||||
@@ -892,6 +892,72 @@
|
||||
}
|
||||
};
|
||||
|
||||
function applyBrandingOverrides(branding) {
|
||||
if (!branding || typeof branding !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
service_name: rawServiceName = {},
|
||||
service_description: rawServiceDescription = {}
|
||||
} = branding;
|
||||
|
||||
function normalizeMap(map) {
|
||||
const normalized = {};
|
||||
Object.entries(map || {}).forEach(([lang, value]) => {
|
||||
if (typeof value !== 'string') {
|
||||
return;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
normalized[lang.toLowerCase()] = trimmed;
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function applyKey(key, map) {
|
||||
const normalized = normalizeMap(map);
|
||||
if (!Object.keys(normalized).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultValue = normalized.default
|
||||
|| normalized.en
|
||||
|| normalized.ru
|
||||
|| null;
|
||||
|
||||
const languages = new Set(
|
||||
Object.keys(translations).map(lang => lang.toLowerCase())
|
||||
);
|
||||
|
||||
Object.keys(normalized).forEach(lang => {
|
||||
if (lang !== 'default') {
|
||||
languages.add(lang);
|
||||
}
|
||||
});
|
||||
|
||||
languages.forEach(lang => {
|
||||
const value = Object.prototype.hasOwnProperty.call(normalized, lang)
|
||||
? normalized[lang]
|
||||
: defaultValue;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const targetLang = lang.toLowerCase();
|
||||
if (!translations[targetLang]) {
|
||||
translations[targetLang] = {};
|
||||
}
|
||||
translations[targetLang][key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
applyKey('app.name', rawServiceName);
|
||||
applyKey('app.title', rawServiceName);
|
||||
applyKey('app.subtitle', rawServiceDescription);
|
||||
}
|
||||
|
||||
let userData = null;
|
||||
let appsConfig = {};
|
||||
let currentPlatform = 'android';
|
||||
@@ -1100,6 +1166,9 @@
|
||||
userData = await response.json();
|
||||
userData.subscriptionUrl = userData.subscription_url || null;
|
||||
userData.subscriptionCryptoLink = userData.subscription_crypto_link || null;
|
||||
if (userData.branding) {
|
||||
applyBrandingOverrides(userData.branding);
|
||||
}
|
||||
|
||||
const responseLanguage = resolveLanguage(userData?.user?.language);
|
||||
if (responseLanguage && !languageLockedByUser) {
|
||||
|
||||
Reference in New Issue
Block a user