From 9e61a3d8a80c64af9e1f948afc0dbd80c194ba61 Mon Sep 17 00:00:00 2001
From: 3252a8 <3252a8@proton.me>
Date: Fri, 22 May 2026 13:58:34 +0300
Subject: [PATCH] feat: install instruction inside web app
---
.../bot/app/web/admin_api_impl/settings.py | 49 +-
backend/bot/app/web/admin_api_impl/tariffs.py | 6 +-
backend/bot/app/web/admin_api_impl/themes.py | 13 +-
.../app/web/admin_api_impl/webapp_runtime.py | 66 +
.../bot/app/web/admin_settings_manifest.py | 58 +-
backend/bot/app/web/subscription_webapp.py | 2 +
backend/bot/app/web/webapp/application.py | 4 +
backend/bot/app/web/webapp/cache_helpers.py | 47 +-
backend/bot/app/web/webapp/common.py | 22 +-
backend/bot/app/web/webapp/guides.py | 280 ++
backend/bot/app/web/webapp/routes.py | 7 +
backend/bot/app/web/webapp/serializers.py | 36 +-
backend/bot/services/panel_api_service.py | 48 +
.../subscription_service_impl/lifecycle.py | 4 +
.../defaults/subscription_page_multiapp.json | 4419 +++++++++++++++++
backend/config/settings.py | 20 +
backend/config/subscription_guides_config.py | 669 +++
frontend/package-lock.json | 302 +-
frontend/package.json | 3 +
frontend/src/App.svelte | 114 +-
.../src/admin/sections/SettingsSection.svelte | 49 +-
frontend/src/lib/components/ui/icons.js | 2 +
frontend/src/lib/webapp/constants.js | 1 +
frontend/src/lib/webapp/mockApi.js | 7 +
frontend/src/lib/webapp/previewMock.js | 191 +
frontend/src/lib/webapp/routes.js | 7 +
.../lib/webapp/stores/installGuidesStore.js | 91 +
frontend/src/styles/admin-controls.css | 25 +
frontend/src/styles/webapp.css | 47 +
frontend/src/webapp/WebAppShell.svelte | 2 +-
.../webapp/screens/InstallGuideScreen.svelte | 932 ++++
locales/en.json | 30 +-
locales/ru.json | 30 +-
tests/test_admin_settings_manifest_i18n.py | 25 +
tests/test_admin_webapp_runtime.py | 67 +
tests/test_panel_api_service_logging.py | 46 +
tests/test_settings.py | 17 +
tests/test_subscription_guides_config.py | 310 ++
tests/test_subscription_guides_route.py | 208 +
tests/test_webapp_redis_cache.py | 25 +-
tests/test_webapp_route_contract.py | 7 +
41 files changed, 8216 insertions(+), 72 deletions(-)
create mode 100644 backend/bot/app/web/admin_api_impl/webapp_runtime.py
create mode 100644 backend/bot/app/web/webapp/guides.py
create mode 100644 backend/config/defaults/subscription_page_multiapp.json
create mode 100644 backend/config/subscription_guides_config.py
create mode 100644 frontend/src/lib/webapp/stores/installGuidesStore.js
create mode 100644 frontend/src/webapp/screens/InstallGuideScreen.svelte
create mode 100644 tests/test_admin_webapp_runtime.py
create mode 100644 tests/test_subscription_guides_config.py
create mode 100644 tests/test_subscription_guides_route.py
diff --git a/backend/bot/app/web/admin_api_impl/settings.py b/backend/bot/app/web/admin_api_impl/settings.py
index 5f13ee4..91366b4 100644
--- a/backend/bot/app/web/admin_api_impl/settings.py
+++ b/backend/bot/app/web/admin_api_impl/settings.py
@@ -1,5 +1,11 @@
# ruff: noqa: F401,F403,F405,I001
from ._runtime import * # noqa: F403,F405
+from .webapp_runtime import refresh_webapp_runtime_after_settings_change
+
+from config.subscription_guides_config import (
+ SubscriptionGuidesConfigError,
+ subscription_guides_admin_config_json,
+)
async def admin_settings_get_route(request: web.Request) -> web.Response:
@@ -26,12 +32,25 @@ async def admin_settings_get_route(request: web.Request) -> web.Response:
override = overrides_by_key.get(key)
value = current_value(settings, key)
is_secret = bool(field.get("secret"))
+ overridden = bool(override)
+ source = None
+ read_error = None
+ if key == "SUBSCRIPTION_PAGE_CONFIG_JSON":
+ try:
+ value, source = subscription_guides_admin_config_json(settings)
+ overridden = source == "admin_json"
+ except SubscriptionGuidesConfigError as exc:
+ read_error = str(exc)
response_field = {
**field,
"value": "" if is_secret else value,
- "overridden": bool(override),
+ "overridden": overridden,
"updated_at": override.get("updated_at") if override else None,
}
+ if source:
+ response_field["source"] = source
+ if read_error:
+ response_field["read_error"] = read_error
if is_secret:
response_field["has_value"] = bool(value)
sections[section_id]["fields"].append(response_field)
@@ -51,6 +70,12 @@ async def admin_settings_patch_route(request: web.Request) -> web.Response:
return _error(400, "invalid_updates")
if not isinstance(deletes, list):
return _error(400, "invalid_deletes")
+ if "SUBSCRIPTION_PAGE_CONFIG_JSON" in updates and not str(
+ updates.get("SUBSCRIPTION_PAGE_CONFIG_JSON") or ""
+ ).strip():
+ updates = dict(updates)
+ updates.pop("SUBSCRIPTION_PAGE_CONFIG_JSON", None)
+ deletes = [*deletes, "SUBSCRIPTION_PAGE_CONFIG_JSON"]
result = await update_overrides(
settings,
@@ -65,26 +90,6 @@ async def admin_settings_patch_route(request: web.Request) -> web.Response:
status=400,
)
- # Bust the public webapp settings cache so users see new values immediately.
- cache = request.app.get("webapp_settings_cache")
- if isinstance(cache, dict):
- cache["ts"] = 0.0
- cache["data"] = {}
- if (
- "WEBAPP_LOGO_URL" in updates
- or "WEBAPP_LOGO_URL" in deletes
- or "WEBAPP_LOGO_USE_EMOJI" in updates
- or "WEBAPP_LOGO_USE_EMOJI" in deletes
- or "WEBAPP_FAVICON_URL" in updates
- or "WEBAPP_FAVICON_URL" in deletes
- or "WEBAPP_FAVICON_USE_CUSTOM" in updates
- or "WEBAPP_FAVICON_USE_CUSTOM" in deletes
- or "WEBAPP_LOGO_FAVICON_URL" in updates
- or "WEBAPP_LOGO_FAVICON_URL" in deletes
- ):
- request.app["webapp_logo_cache"] = None
- from bot.app.web.admin_api_impl.themes import prune_unused_appearance_assets
-
- prune_unused_appearance_assets(settings)
+ await refresh_webapp_runtime_after_settings_change(request, updates=updates, deletes=deletes)
return _ok({"applied": result.get("applied", 0), "reverted": result.get("reverted", 0)})
diff --git a/backend/bot/app/web/admin_api_impl/tariffs.py b/backend/bot/app/web/admin_api_impl/tariffs.py
index 16b22cf..f2b4578 100644
--- a/backend/bot/app/web/admin_api_impl/tariffs.py
+++ b/backend/bot/app/web/admin_api_impl/tariffs.py
@@ -1,5 +1,6 @@
# ruff: noqa: F401,F403,F405,I001
from ._runtime import * # noqa: F403,F405
+from .webapp_runtime import refresh_webapp_runtime_after_settings_change
async def admin_tariffs_get_route(request: web.Request) -> web.Response:
@@ -55,9 +56,6 @@ async def admin_tariffs_save_route(request: web.Request) -> web.Response:
logger.exception("Failed to write tariffs config to %s", path)
return _error(500, "write_failed", str(exc))
- cache = request.app.get("webapp_settings_cache")
- if isinstance(cache, dict):
- cache["ts"] = 0.0
- cache["data"] = {}
+ await refresh_webapp_runtime_after_settings_change(request, updates={}, deletes=[])
return _ok({"exists": True, "path": str(path), "catalog": _tariffs_config_payload(config)})
diff --git a/backend/bot/app/web/admin_api_impl/themes.py b/backend/bot/app/web/admin_api_impl/themes.py
index 8fd94c1..129cd9f 100644
--- a/backend/bot/app/web/admin_api_impl/themes.py
+++ b/backend/bot/app/web/admin_api_impl/themes.py
@@ -1,5 +1,6 @@
# ruff: noqa: F401,F403,F405,I001
from ._runtime import * # noqa: F403,F405
+from .webapp_runtime import refresh_webapp_runtime_after_settings_change
import asyncio
import hashlib
@@ -213,12 +214,7 @@ async def _persist_appearance_upload(
logger.warning("Failed to persist uploaded appearance asset settings: %s", result)
return False
- cache = request.app.get("webapp_settings_cache")
- if isinstance(cache, dict):
- cache["ts"] = 0.0
- cache["data"] = {}
- request.app["webapp_logo_cache"] = None
- prune_unused_appearance_assets(settings)
+ await refresh_webapp_runtime_after_settings_change(request, updates=updates, deletes=[])
return True
@@ -484,10 +480,7 @@ async def admin_themes_save_route(request: web.Request) -> web.Response:
logger.exception("Failed to write webapp themes to %s", settings.WEBAPP_THEMES_DIR)
return _error(500, "write_failed", str(exc))
- cache = request.app.get("webapp_settings_cache")
- if isinstance(cache, dict):
- cache["ts"] = 0.0
- cache["data"] = {}
+ await refresh_webapp_runtime_after_settings_change(request, updates={}, deletes=[])
return _ok(
{
diff --git a/backend/bot/app/web/admin_api_impl/webapp_runtime.py b/backend/bot/app/web/admin_api_impl/webapp_runtime.py
new file mode 100644
index 0000000..1219c6f
--- /dev/null
+++ b/backend/bot/app/web/admin_api_impl/webapp_runtime.py
@@ -0,0 +1,66 @@
+from __future__ import annotations
+
+from collections.abc import Mapping, Sequence
+from typing import Any
+
+from bot.app.web.webapp.cache_helpers import (
+ invalidate_all_webapp_user_payloads,
+ reset_subscription_guides_cache,
+ reset_webapp_settings_cache,
+)
+
+WEBAPP_APPEARANCE_SETTING_KEYS = frozenset(
+ {
+ "WEBAPP_LOGO_URL",
+ "WEBAPP_LOGO_USE_EMOJI",
+ "WEBAPP_LOGO_EMOJI",
+ "WEBAPP_LOGO_EMOJI_FONT",
+ "WEBAPP_FAVICON_URL",
+ "WEBAPP_FAVICON_USE_CUSTOM",
+ "WEBAPP_LOGO_FAVICON_URL",
+ }
+)
+
+WEBAPP_DEVICE_PAYLOAD_SETTING_KEYS = frozenset(
+ {
+ "MY_DEVICES_SECTION_ENABLED",
+ "USER_HWID_DEVICE_LIMIT",
+ "USER_TRAFFIC_LIMIT_GB",
+ "USER_TRAFFIC_STRATEGY",
+ }
+)
+
+
+def changed_setting_keys(
+ updates: Mapping[str, Any] | None = None,
+ deletes: Sequence[Any] | None = None,
+) -> set[str]:
+ keys = {str(key) for key in (updates or {}).keys()}
+ keys.update(str(key) for key in (deletes or []) if key is not None)
+ return keys
+
+
+async def refresh_webapp_runtime_after_settings_change(
+ request: Any,
+ *,
+ updates: Mapping[str, Any] | None = None,
+ deletes: Sequence[Any] | None = None,
+ include_user_payloads: bool = True,
+) -> None:
+ settings = request.app["settings"]
+ keys = changed_setting_keys(updates, deletes)
+
+ reset_webapp_settings_cache(request.app)
+ reset_subscription_guides_cache(request.app)
+
+ if include_user_payloads:
+ await invalidate_all_webapp_user_payloads(
+ settings,
+ include_devices=bool(keys & WEBAPP_DEVICE_PAYLOAD_SETTING_KEYS),
+ )
+
+ if keys & WEBAPP_APPEARANCE_SETTING_KEYS:
+ request.app["webapp_logo_cache"] = None
+ from bot.app.web.admin_api_impl.themes import prune_unused_appearance_assets
+
+ prune_unused_appearance_assets(settings)
diff --git a/backend/bot/app/web/admin_settings_manifest.py b/backend/bot/app/web/admin_settings_manifest.py
index 447c9fd..2bee5b2 100644
--- a/backend/bot/app/web/admin_settings_manifest.py
+++ b/backend/bot/app/web/admin_settings_manifest.py
@@ -16,7 +16,7 @@ from typing import Any, List, Optional, Tuple
@dataclass(frozen=True)
class SettingField:
key: str
- type: str # "string" | "int" | "float" | "bool" | "text" | "url" | "color" | "icon"
+ type: str # "string" | "int" | "float" | "bool" | "text" | "url" | "color" | "icon" | "json"
section: str
label: str
description: str = ""
@@ -160,6 +160,49 @@ SETTINGS_MANIFEST: List[SettingField] = [
SettingField("WEBAPP_FAVICON_URL", "url", "appearance", "URL отдельной favicon"),
SettingField("WEBAPP_LOGO_FAVICON_URL", "url", "appearance", "Favicon из логотипа"),
SettingField("WEBAPP_ENABLED", "bool", "appearance", "Web App включён"),
+ SettingField(
+ "SUBSCRIPTION_GUIDES_ENABLED",
+ "bool",
+ "subscription_guides",
+ "Embedded install guides",
+ "Open install instructions inside the Web App instead of an external connect page.",
+ ),
+ SettingField(
+ "SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED",
+ "bool",
+ "subscription_guides",
+ "Use Remnawave Panel config",
+ (
+ "Fetch Subscription Page config from Remnawave Panel by the user's "
+ "subscription short UUID."
+ ),
+ ),
+ SettingField(
+ "SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED",
+ "bool",
+ "subscription_guides",
+ "Enable admin JSON override",
+ "Use the JSON field below instead of Remnawave Panel config. Disabled by default.",
+ ),
+ SettingField(
+ "SUBSCRIPTION_PAGE_CONFIG_PATH",
+ "string",
+ "subscription_guides",
+ "Subscription Page config path",
+ "Fallback path to a Remnawave Subscription Page v1 JSON config file.",
+ placeholder="data/subpage-config/multiapp.json",
+ ),
+ SettingField(
+ "SUBSCRIPTION_PAGE_CONFIG_JSON",
+ "json",
+ "subscription_guides",
+ "Subscription Page config JSON",
+ (
+ "Optional admin JSON override. It is applied only when the JSON override "
+ "switch is enabled."
+ ),
+ placeholder="{\n \"version\": \"1\"\n}",
+ ),
# ─── Subscription periods & pricing ────────────────────────────
SettingField("MONTH_1_ENABLED", "bool", "pricing", "Тариф 1 месяц"),
SettingField("MONTH_3_ENABLED", "bool", "pricing", "Тариф 3 месяца"),
@@ -454,6 +497,18 @@ def manifest_keys() -> List[str]:
def coerce_value(field: SettingField, raw: Any) -> Any:
"""Coerce a value coming from JSON to the type declared by the field."""
+ if field.type == "json":
+ if raw is None:
+ return ""
+ text = raw if isinstance(raw, str) else str(raw)
+ text = text.strip()
+ if not text:
+ return ""
+ from config.subscription_guides_config import validate_subscription_guides_config_text
+
+ validate_subscription_guides_config_text(text)
+ return text
+
if raw is None or (isinstance(raw, str) and raw.strip() == ""):
return None
@@ -519,6 +574,7 @@ def manifest_payload() -> List[dict]:
"notifications": 7,
"support": 8,
"devices": 9,
+ "subscription_guides": 10,
}
items: List[dict] = []
for field in aggregated_manifest():
diff --git a/backend/bot/app/web/subscription_webapp.py b/backend/bot/app/web/subscription_webapp.py
index 3c2b58e..0bf1096 100644
--- a/backend/bot/app/web/subscription_webapp.py
+++ b/backend/bot/app/web/subscription_webapp.py
@@ -11,6 +11,7 @@ from bot.app.web.webapp import (
billing as _billing,
common as _common,
devices as _devices,
+ guides as _guides,
payloads as _payloads,
routes as _routes,
serializers as _serializers,
@@ -27,6 +28,7 @@ _MODULES = (
_serializers,
_billing,
_devices,
+ _guides,
_support,
_routes,
_application,
diff --git a/backend/bot/app/web/webapp/application.py b/backend/bot/app/web/webapp/application.py
index 00d7a01..ca6a38b 100644
--- a/backend/bot/app/web/webapp/application.py
+++ b/backend/bot/app/web/webapp/application.py
@@ -1,5 +1,6 @@
# ruff: noqa: F401,F403,F405,I001
from ._runtime import * # noqa: F403,F405
+from .guides import warm_subscription_guides_config
def create_subscription_webapp_application(
@@ -24,6 +25,8 @@ def create_subscription_webapp_application(
app["webapp_logo_cache"] = None
app["webapp_logo_cache_lock"] = asyncio.Lock()
app["webapp_settings_cache"] = {"ts": 0.0, "data": {}}
+ app["subscription_guides_config_cache"] = {"fingerprint": None, "status": None}
+ app["subscription_guides_config_lock"] = asyncio.Lock()
app["webapp_rate_limit_buckets"] = {}
app["webapp_rate_limit_lock"] = asyncio.Lock()
@@ -31,6 +34,7 @@ def create_subscription_webapp_application(
await _ensure_shared_http_session()
await _warm_webapp_logo_cache(app_obj)
await _warm_webapp_animated_emoji_cache(app_obj)
+ await warm_subscription_guides_config(app_obj)
async def _shutdown(app_obj: web.Application) -> None:
await _close_shared_http_session()
diff --git a/backend/bot/app/web/webapp/cache_helpers.py b/backend/bot/app/web/webapp/cache_helpers.py
index 65ef57d..97fc40e 100644
--- a/backend/bot/app/web/webapp/cache_helpers.py
+++ b/backend/bot/app/web/webapp/cache_helpers.py
@@ -2,13 +2,31 @@ from __future__ import annotations
from typing import Any, Awaitable, Callable, Optional
-from bot.infra.redis import cache_delete, redis_key
+from bot.infra.redis import cache_delete, cache_delete_pattern, redis_key
from bot.utils.ttl_cache import AsyncTTLCache
from config.settings import Settings
_WEBAPP_USER_PAYLOAD_CACHES: dict[tuple[int, str, int], AsyncTTLCache] = {}
+def reset_webapp_settings_cache(app: Any) -> None:
+ cache = app.get("webapp_settings_cache") if hasattr(app, "get") else None
+ if isinstance(cache, dict):
+ cache["ts"] = 0.0
+ cache["data"] = {}
+
+
+def reset_subscription_guides_cache(app: Any) -> None:
+ cache = app.get("subscription_guides_config_cache") if hasattr(app, "get") else None
+ if isinstance(cache, dict):
+ cache["fingerprint"] = None
+ cache["status"] = None
+
+
+def _payload_namespaces(include_devices: bool = False) -> tuple[str, ...]:
+ return ("me", "devices") if include_devices else ("me",)
+
+
def _webapp_user_payload_cache(
settings: Settings,
namespace: str,
@@ -55,6 +73,19 @@ def invalidate_local_webapp_user_payload(
cache.invalidate(key)
+def invalidate_all_local_webapp_user_payloads(
+ settings: Settings,
+ *,
+ include_devices: bool = False,
+) -> None:
+ namespaces = set(_payload_namespaces(include_devices))
+ for (settings_id, cache_namespace, _ttl), cache in tuple(
+ _WEBAPP_USER_PAYLOAD_CACHES.items()
+ ):
+ if settings_id == id(settings) and cache_namespace in namespaces:
+ cache.invalidate()
+
+
async def invalidate_webapp_user_caches(
settings: Settings,
*user_ids: Optional[int],
@@ -79,3 +110,17 @@ async def invalidate_webapp_user_caches(
invalidate_local_webapp_user_payload(settings, "devices", user_id)
if keys:
await cache_delete(settings, *keys)
+
+
+async def invalidate_all_webapp_user_payloads(
+ settings: Settings,
+ *,
+ include_devices: bool = False,
+) -> None:
+ invalidate_all_local_webapp_user_payloads(settings, include_devices=include_devices)
+ for namespace in _payload_namespaces(include_devices):
+ try:
+ pattern = redis_key(settings, "cache", "webapp", namespace, "*")
+ await cache_delete_pattern(settings, pattern)
+ except Exception:
+ continue
diff --git a/backend/bot/app/web/webapp/common.py b/backend/bot/app/web/webapp/common.py
index f55217f..53932c5 100644
--- a/backend/bot/app/web/webapp/common.py
+++ b/backend/bot/app/web/webapp/common.py
@@ -2,7 +2,7 @@
from ._runtime import * # noqa: F403,F405
from bot.app.web.webapp.cache_helpers import (
- invalidate_local_webapp_user_payload,
+ invalidate_webapp_user_caches as _invalidate_user_payload_caches,
)
@@ -26,25 +26,7 @@ async def _invalidate_webapp_user_caches(
*user_ids: Optional[int],
include_devices: bool = False,
) -> None:
- keys: List[str] = []
- seen: set[int] = set()
- for raw_user_id in user_ids:
- if raw_user_id is None:
- continue
- try:
- user_id = int(raw_user_id)
- except (TypeError, ValueError):
- continue
- if user_id in seen:
- continue
- seen.add(user_id)
- keys.append(redis_key(settings, "cache", "webapp", "me", user_id))
- invalidate_local_webapp_user_payload(settings, "me", user_id)
- if include_devices:
- keys.append(redis_key(settings, "cache", "webapp", "devices", user_id))
- invalidate_local_webapp_user_payload(settings, "devices", user_id)
- if keys:
- await cache_delete(settings, *keys)
+ await _invalidate_user_payload_caches(settings, *user_ids, include_devices=include_devices)
def _validation_error_response(exc: ValidationError) -> web.Response:
diff --git a/backend/bot/app/web/webapp/guides.py b/backend/bot/app/web/webapp/guides.py
new file mode 100644
index 0000000..bd20656
--- /dev/null
+++ b/backend/bot/app/web/webapp/guides.py
@@ -0,0 +1,280 @@
+# ruff: noqa: F401,F403,F405,I001
+from ._runtime import * # noqa: F403,F405
+
+from config.subscription_guides_config import (
+ SubscriptionGuidesConfigError,
+ subscription_guides_status,
+ validate_panel_subscription_guides_config,
+)
+
+PANEL_DEFAULT_SUBPAGE_CONFIG_UUID = "00000000-0000-0000-0000-000000000000"
+SUBSCRIPTION_GUIDES_CACHE_ERROR_TTL_SECONDS = 30
+
+
+async def warm_subscription_guides_config(app: web.Application) -> None:
+ try:
+ await _subscription_guides_status_shared(app)
+ except Exception as exc:
+ logger.warning("Failed to warm subscription guides config: %s", exc)
+
+
+async def subscription_guides_route(request: web.Request) -> web.Response:
+ _require_user_id(request)
+ status = await _subscription_guides_status_shared(request.app)
+ payload = {
+ "enabled": bool(status.get("enabled")),
+ "config": status.get("config") if status.get("enabled") else None,
+ "source": status.get("source"),
+ }
+ if status.get("error"):
+ payload["error"] = status["error"]
+ return web.json_response({"ok": True, **payload})
+
+
+async def public_subscription_guides_route(request: web.Request) -> web.Response:
+ short_uuid = _normalize_short_uuid(request.match_info.get("short_uuid"))
+ if not short_uuid:
+ return web.json_response({"ok": False, "error": "invalid_short_uuid"}, status=404)
+
+ status = await _subscription_guides_status_shared(request.app)
+ subscription = await _public_subscription_payload(request, short_uuid)
+ payload = {
+ "enabled": bool(status.get("enabled")),
+ "config": status.get("config") if status.get("enabled") else None,
+ "source": status.get("source"),
+ "subscription": subscription,
+ }
+ if status.get("error"):
+ payload["error"] = status["error"]
+ return web.json_response({"ok": True, **payload})
+
+
+async def _subscription_guides_status_shared(app: web.Application) -> Dict[str, Any]:
+ settings: Settings = app["settings"]
+ cache = app.setdefault("subscription_guides_config_cache", {})
+ lock: asyncio.Lock = app.setdefault("subscription_guides_config_lock", asyncio.Lock())
+ fingerprint = _subscription_guides_settings_fingerprint(settings)
+ now = time.monotonic()
+
+ cached = cache.get("status")
+ if cached is not None and cache.get("fingerprint") == fingerprint:
+ if cached.get("enabled") or now - float(cache.get("ts", 0.0)) < (
+ SUBSCRIPTION_GUIDES_CACHE_ERROR_TTL_SECONDS
+ ):
+ return cached
+
+ async with lock:
+ cached = cache.get("status")
+ if cached is not None and cache.get("fingerprint") == fingerprint:
+ if cached.get("enabled") or now - float(cache.get("ts", 0.0)) < (
+ SUBSCRIPTION_GUIDES_CACHE_ERROR_TTL_SECONDS
+ ):
+ return cached
+
+ status = await _load_subscription_guides_status(app, settings)
+ cache["fingerprint"] = fingerprint
+ cache["status"] = status
+ cache["ts"] = time.monotonic()
+ return status
+
+
+async def _load_subscription_guides_status(
+ app: web.Application,
+ settings: Settings,
+) -> Dict[str, Any]:
+ if not bool(getattr(settings, "SUBSCRIPTION_GUIDES_ENABLED", False)):
+ return {"enabled": False, "config": None, "source": None, "error": None}
+
+ admin_json = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON", "") or "").strip()
+ json_override_enabled = bool(
+ getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED", False)
+ )
+ if admin_json and json_override_enabled:
+ return subscription_guides_status(settings)
+
+ if bool(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED", True)):
+ panel_status = await _subscription_guides_status_from_panel_config(app, settings)
+ if panel_status.get("enabled"):
+ return panel_status
+
+ return subscription_guides_status(settings)
+
+
+async def _subscription_guides_status_from_panel_config(
+ app: web.Application,
+ settings: Settings,
+) -> Dict[str, Any]:
+ panel_service = _panel_service_from_app(app)
+ if panel_service is None:
+ return {
+ "enabled": False,
+ "config": None,
+ "source": "panel",
+ "error": "Panel service is unavailable",
+ }
+
+ try:
+ config_uuid = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_UUID", "") or "").strip()
+ if not config_uuid:
+ config_uuid = await _default_panel_subscription_page_config_uuid(panel_service)
+ config_uuid = config_uuid or PANEL_DEFAULT_SUBPAGE_CONFIG_UUID
+ detail = await panel_service.get_subscription_page_config_by_uuid(config_uuid)
+ if detail is None and config_uuid != PANEL_DEFAULT_SUBPAGE_CONFIG_UUID:
+ detail = await panel_service.get_subscription_page_config_by_uuid(
+ PANEL_DEFAULT_SUBPAGE_CONFIG_UUID
+ )
+ if detail is None:
+ raise SubscriptionGuidesConfigError(
+ f"Panel subscription page config {config_uuid} is unavailable"
+ )
+ config = validate_panel_subscription_guides_config(detail)
+ except (SubscriptionGuidesConfigError, Exception) as exc:
+ logger.warning("Failed to load subscription guides config from Remnawave Panel: %s", exc)
+ return {"enabled": False, "config": None, "source": "panel", "error": str(exc)}
+
+ return {"enabled": True, "config": config, "source": "panel", "error": None}
+
+
+async def _default_panel_subscription_page_config_uuid(panel_service: Any) -> str:
+ get_list = getattr(panel_service, "get_subscription_page_config_list", None)
+ if not callable(get_list):
+ return ""
+ payload = await get_list()
+ configs = (payload or {}).get("configs")
+ if not isinstance(configs, list):
+ return ""
+
+ candidates: list[Dict[str, Any]] = [item for item in configs if isinstance(item, dict)]
+ for item in candidates:
+ uuid = str(item.get("uuid") or "").strip()
+ if uuid == PANEL_DEFAULT_SUBPAGE_CONFIG_UUID:
+ return uuid
+
+ candidates.sort(key=lambda item: int(item.get("viewPosition") or 0))
+ for item in candidates:
+ uuid = str(item.get("uuid") or "").strip()
+ if uuid:
+ return uuid
+ return ""
+
+
+async def _public_subscription_payload(
+ request: web.Request,
+ short_uuid: str,
+) -> Dict[str, Any]:
+ settings: Settings = request.app["settings"]
+ panel_service = _panel_service_from_app(request.app)
+ raw_link = ""
+ username = ""
+ resolved_short_uuid = short_uuid
+
+ async_session_factory: sessionmaker = request.app["async_session_factory"]
+ async with async_session_factory() as session:
+ local_sub = await subscription_dal.get_subscription_by_panel_subscription_uuid(
+ session,
+ short_uuid,
+ )
+
+ if (
+ local_sub
+ and getattr(local_sub, "panel_user_uuid", None)
+ and _local_subscription_is_publicly_active(local_sub)
+ and panel_service
+ ):
+ panel_user = await panel_service.get_user_by_uuid(local_sub.panel_user_uuid)
+ if panel_user:
+ raw_link = str(panel_user.get("subscriptionUrl") or "").strip()
+ username = str(panel_user.get("username") or "").strip()
+ resolved_short_uuid = str(panel_user.get("shortUuid") or short_uuid).strip()
+
+ display_link, connect_url = await prepare_config_links(settings, raw_link)
+ return {
+ "active": bool(display_link),
+ "config_link": display_link,
+ "connect_url": connect_url or display_link,
+ "panel_short_uuid": resolved_short_uuid,
+ "username": username,
+ "share_url": _public_install_url(request, resolved_short_uuid),
+ }
+
+
+def _panel_service_from_app(app: web.Application) -> Any:
+ subscription_service: Optional[SubscriptionService] = app.get("subscription_service")
+ panel_service = (
+ getattr(subscription_service, "panel_service", None) if subscription_service else None
+ )
+ return panel_service or app.get("panel_service")
+
+
+def _subscription_guides_settings_fingerprint(settings: Settings) -> Tuple[Any, ...]:
+ admin_json = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON", "") or "")
+ return (
+ bool(getattr(settings, "SUBSCRIPTION_GUIDES_ENABLED", False)),
+ bool(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED", True)),
+ bool(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED", False)),
+ str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_PATH", "") or ""),
+ str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_UUID", "") or ""),
+ hashlib.sha256(admin_json.encode("utf-8")).hexdigest(),
+ str(getattr(settings, "PANEL_API_URL", "") or ""),
+ bool(getattr(settings, "PANEL_API_KEY", "") or ""),
+ )
+
+
+def _local_subscription_is_publicly_active(subscription: Any) -> bool:
+ end_date = getattr(subscription, "end_date", None)
+ if end_date and end_date.tzinfo is None:
+ end_date = end_date.replace(tzinfo=timezone.utc)
+ return bool(
+ getattr(subscription, "is_active", False)
+ and end_date
+ and end_date > datetime.now(timezone.utc)
+ )
+
+
+def _normalize_short_uuid(value: Any) -> str:
+ short_uuid = str(value or "").strip()
+ if not re.fullmatch(r"[A-Za-z0-9_-]{8,128}", short_uuid):
+ return ""
+ return short_uuid
+
+
+def _public_install_url(request: web.Request, short_uuid: str) -> str:
+ settings: Settings = request.app["settings"]
+ configured_base = str(getattr(settings, "SUBSCRIPTION_MINI_APP_URL", "") or "").strip()
+ if configured_base:
+ parts = urlsplit(configured_base)
+ if parts.scheme and parts.netloc:
+ base = urlunsplit((parts.scheme, parts.netloc, "", "", ""))
+ else:
+ base = configured_base.rstrip("/")
+ else:
+ host = (
+ request.headers.get("X-Forwarded-Host")
+ or request.headers.get("Host")
+ or request.host
+ )
+ proto = request.headers.get("X-Forwarded-Proto") or request.scheme or "https"
+ base = f"{proto}://{host}"
+ return f"{base.rstrip('/')}/install/share/{quote(short_uuid)}"
+
+
+def _subscription_page_request_headers(request: web.Request) -> Dict[str, str]:
+ headers = request.headers
+ host = headers.get("X-Forwarded-Host") or headers.get("Host") or request.host
+ proto = headers.get("X-Forwarded-Proto") or request.scheme or "https"
+ user_agent = headers.get(
+ "User-Agent",
+ "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome Safari",
+ )
+ return {
+ "host": host,
+ "x-forwarded-host": host,
+ "x-forwarded-proto": proto,
+ "user-agent": user_agent,
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ "accept-language": headers.get("Accept-Language", "ru,en;q=0.9"),
+ "sec-fetch-dest": "document",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-site": "none",
+ "upgrade-insecure-requests": "1",
+ }
diff --git a/backend/bot/app/web/webapp/routes.py b/backend/bot/app/web/webapp/routes.py
index 42afb2e..33001ca 100644
--- a/backend/bot/app/web/webapp/routes.py
+++ b/backend/bot/app/web/webapp/routes.py
@@ -6,6 +6,8 @@ def setup_subscription_webapp_routes(app: web.Application) -> None:
app.router.add_get("/", index_route)
app.router.add_get("/login/password", index_route)
app.router.add_get("/home", index_route)
+ app.router.add_get("/install", index_route)
+ app.router.add_get(r"/install/share/{short_uuid:[A-Za-z0-9_-]{8,128}}", index_route)
app.router.add_get("/invite", index_route)
app.router.add_get("/devices", index_route)
app.router.add_get("/settings", index_route)
@@ -60,6 +62,11 @@ def setup_subscription_webapp_routes(app: web.Application) -> None:
app.router.add_get("/api/bootstrap", bootstrap_route)
app.router.add_get("/api/i18n", i18n_route)
app.router.add_get("/api/me", me_route)
+ app.router.add_get("/api/subscription-guides", subscription_guides_route)
+ app.router.add_get(
+ r"/api/subscription-guides/public/{short_uuid:[A-Za-z0-9_-]{8,128}}",
+ public_subscription_guides_route,
+ )
app.router.add_get("/api/account/avatar", account_avatar_route)
app.router.add_post("/api/account/language", account_language_route)
app.router.add_post("/api/account/email/request", account_email_request_route)
diff --git a/backend/bot/app/web/webapp/serializers.py b/backend/bot/app/web/webapp/serializers.py
index aa5213d..1e68646 100644
--- a/backend/bot/app/web/webapp/serializers.py
+++ b/backend/bot/app/web/webapp/serializers.py
@@ -1,6 +1,7 @@
# ruff: noqa: F401,F403,F405,I001
from ._runtime import * # noqa: F403,F405
+from config.subscription_guides_config import subscription_guides_available
from config.webapp_themes_config import public_themes_catalog_payload
@@ -82,7 +83,7 @@ async def _build_user_payload(request: web.Request, user_id: int) -> Dict[str, A
"language_code": lang,
"is_admin": is_admin,
},
- "subscription": _serialize_subscription(settings, active, local_sub, lang),
+ "subscription": _serialize_subscription(request, settings, active, local_sub, lang),
"referral": {
"code": referral_code,
"bot_link": referral_link,
@@ -132,6 +133,7 @@ async def _build_user_payload(request: web.Request, user_id: int) -> Dict[str, A
"trial_traffic_limit_gb": float(settings.TRIAL_TRAFFIC_LIMIT_GB or 0),
"trial_traffic_strategy": getattr(settings, "TRIAL_TRAFFIC_STRATEGY", "NO_RESET"),
"subscription_purchase_description": settings.subscription_purchase_description(lang),
+ "subscription_guides_enabled": subscription_guides_available(settings),
"email_auth_enabled": settings.email_auth_configured,
},
}
@@ -179,6 +181,7 @@ def _build_webapp_referral_link(
def _serialize_subscription(
+ request: web.Request,
settings: Settings,
active: Optional[Dict[str, Any]],
local_sub: Optional[Any],
@@ -192,6 +195,8 @@ def _serialize_subscription(
"days_left": 0,
"config_link": None,
"connect_url": None,
+ "panel_short_uuid": None,
+ "install_share_url": None,
}
end_date = active.get("end_date")
@@ -231,6 +236,7 @@ def _serialize_subscription(
can_topup_traffic = False
can_topup_devices = False
+ panel_short_uuid = str(active.get("panel_short_uuid") or "").strip()
return {
"active": seconds_left > 0,
"status": active.get("status_from_panel") or "UNKNOWN",
@@ -240,6 +246,8 @@ def _serialize_subscription(
"remaining_text": _format_remaining(seconds_left, lang),
"config_link": active.get("config_link"),
"connect_url": active.get("connect_button_url") or active.get("config_link"),
+ "panel_short_uuid": panel_short_uuid or None,
+ "install_share_url": _build_install_share_link(request, settings, panel_short_uuid),
"traffic_limit": _format_bytes(active.get("traffic_limit_bytes"), zero_as_unlimited=True),
"traffic_used": _format_bytes(active.get("traffic_used_bytes")),
"traffic_limit_bytes": _coerce_int_or_none(active.get("traffic_limit_bytes")),
@@ -284,6 +292,32 @@ def _serialize_subscription(
}
+def _build_install_share_link(
+ request: web.Request,
+ settings: Settings,
+ short_uuid: str,
+) -> Optional[str]:
+ short_uuid = str(short_uuid or "").strip()
+ if not short_uuid:
+ return None
+ configured_base = str(getattr(settings, "SUBSCRIPTION_MINI_APP_URL", "") or "").strip()
+ if configured_base:
+ parts = urlsplit(configured_base)
+ if parts.scheme and parts.netloc:
+ base = urlunsplit((parts.scheme, parts.netloc, "", "", ""))
+ else:
+ base = configured_base.rstrip("/")
+ else:
+ host = (
+ request.headers.get("X-Forwarded-Host")
+ or request.headers.get("Host")
+ or request.host
+ )
+ proto = request.headers.get("X-Forwarded-Proto") or request.scheme or "https"
+ base = f"{proto}://{host}"
+ return f"{base.rstrip('/')}/install/share/{quote(short_uuid)}"
+
+
def _serialize_plans(
settings: Settings,
lang: str,
diff --git a/backend/bot/services/panel_api_service.py b/backend/bot/services/panel_api_service.py
index 0b1a89a..c30a060 100644
--- a/backend/bot/services/panel_api_service.py
+++ b/backend/bot/services/panel_api_service.py
@@ -597,6 +597,54 @@ class PanelApiService:
return f"{base_sub_url}/{client_type.lower()}"
return base_sub_url
+ async def get_subscription_page_config_by_short_uuid(
+ self,
+ short_uuid: str,
+ request_headers: Optional[Dict[str, str]] = None,
+ ) -> Optional[Dict[str, Any]]:
+ if not short_uuid:
+ return None
+ endpoint = f"/subscriptions/subpage-config/{short_uuid}"
+ payload = {"requestHeaders": request_headers or {}}
+ response_data = await self._request(
+ "GET",
+ endpoint,
+ json=payload,
+ log_full_response=False,
+ )
+ if response_data and not response_data.get("error"):
+ return response_data.get("response", response_data)
+ logging.error(
+ f"Failed to get subscription page config for short UUID {short_uuid}. Response: {response_data}" # noqa: E501
+ )
+ return None
+
+ async def get_subscription_page_config_list(self) -> Optional[Dict[str, Any]]:
+ endpoint = "/subscription-page-configs"
+ response_data = await self._request("GET", endpoint, log_full_response=False)
+ if response_data and not response_data.get("error"):
+ return response_data.get("response", response_data)
+ logging.error(
+ f"Failed to get subscription page config list from panel. Response: {response_data}"
+ )
+ return None
+
+ async def get_subscription_page_config_by_uuid(
+ self,
+ config_uuid: str,
+ ) -> Optional[Dict[str, Any]]:
+ config_uuid = str(config_uuid or "").strip()
+ if not config_uuid:
+ return None
+ endpoint = f"/subscription-page-configs/{config_uuid}"
+ response_data = await self._request("GET", endpoint, log_full_response=False)
+ if response_data and not response_data.get("error"):
+ return response_data.get("response", response_data)
+ logging.error(
+ f"Failed to get subscription page config {config_uuid} from panel. Response: {response_data}" # noqa: E501
+ )
+ return None
+
async def get_user_devices(self, user_uuid: str) -> Optional[List[Dict[str, Any]]]:
if self._devices_cache.ttl_seconds <= 0:
return await self._get_user_devices_uncached(user_uuid)
diff --git a/backend/bot/services/subscription_service_impl/lifecycle.py b/backend/bot/services/subscription_service_impl/lifecycle.py
index e4154ad..52425f6 100644
--- a/backend/bot/services/subscription_service_impl/lifecycle.py
+++ b/backend/bot/services/subscription_service_impl/lifecycle.py
@@ -763,6 +763,10 @@ class SubscriptionLifecycleMixin:
return {
"user_id": panel_user_data.get("uuid"),
+ "panel_subscription_uuid": panel_user_data.get("subscriptionUuid")
+ or panel_user_data.get("shortUuid")
+ or (local_active_sub.panel_subscription_uuid if local_active_sub else None),
+ "panel_short_uuid": panel_user_data.get("shortUuid"),
"end_date": panel_end_date,
"status_from_panel": panel_user_data.get("status", "UNKNOWN").upper(),
"config_link": display_link,
diff --git a/backend/config/defaults/subscription_page_multiapp.json b/backend/config/defaults/subscription_page_multiapp.json
new file mode 100644
index 0000000..24a86bf
--- /dev/null
+++ b/backend/config/defaults/subscription_page_multiapp.json
@@ -0,0 +1,4419 @@
+{
+ "locales": [
+ "en",
+ "ru",
+ "zh",
+ "fa",
+ "fr"
+ ],
+ "version": "1",
+ "uiConfig": {
+ "subscriptionInfoBlockType": "collapsed",
+ "installationGuidesBlockType": "cards"
+ },
+ "platforms": {
+ "ios": {
+ "apps": [
+ {
+ "name": "RabbitHole",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/app/rabbithole-vpn-client/id6683309629",
+ "text": {
+ "en": "App Store",
+ "fa": "اپ استور",
+ "fr": "App Store",
+ "ru": "App Store",
+ "zh": "App Store"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, click the Add VPN settings button at the bottom of the screen, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پایین صفحه روی دکمه افزودن تنظیمات VPN کلیک کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la, appuie sur le bouton Ajouter la configuration VPN en bas de l'écran ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, снизу экрана нажмите кнопку Добавить настройки VPN, в окне разрешения VPN-конфигурации нажмите Подключиться и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,点击屏幕底部的添加 VPN 设置按钮,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "violet"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "rabbithole://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the Connect button at the bottom of the screen to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه اتصال در پایین صفحه را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le bouton Connexion en bas de l'écran pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите кнопку Подключиться снизу экрана для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击屏幕底部的连接按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "RabbitHole"
+ },
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973",
+ "text": {
+ "en": "App Store (RU)",
+ "fa": "اپ استور (RU)",
+ "fr": "App Store (RU)",
+ "ru": "App Store (RU)",
+ "zh": "App Store (RU)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215",
+ "text": {
+ "en": "App Store (Global)",
+ "fa": "اپ استور (جهانی)",
+ "fr": "App Store (Global)",
+ "ru": "App Store (Global)",
+ "zh": "App Store (Global)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "violet"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "Clash Mi",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/us/app/clash-mi/id6744321968",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the App Store page and install the app.",
+ "fa": "صفحه App Store را باز کرده و برنامه را نصب کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app.",
+ "ru": "Откройте страницу в App Store и установите приложение.",
+ "zh": "打开 App Store 页面并安装应用。"
+ },
+ "svgIconColor": "blue"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "clashmi://install-config?xhwid=true&overwrite=no&name={{USERNAME}}&url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "On the main screen, tap the Disconnected button, then in the VPN configuration prompt tap Allow and enter your password to connect.",
+ "fa": "در صفحه اصلی روی دکمه Disconnected بزنید، سپس در پنجره مجوز پیکربندی VPN روی Allow ضربه بزنید و برای اتصال، رمز عبور خود را وارد کنید.",
+ "fr": "Sur l’écran principal, appuie sur « Disconnected », puis, dans la demande de configuration VPN, appuie sur « Allow » et entre ton mot de passe pour te connecter.",
+ "ru": "На главной странице нажми кнопку Disconnected, в появившемся окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль для подключения к VPN.",
+ "zh": "在主屏幕点击 Disconnected 按钮,然后在 VPN 配置提示中点击允许并输入您的密码以连接。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "ClashMi"
+ },
+ {
+ "name": "Stash",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/us/app/stash-rule-based-proxy/id1596063349",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the App Store page and install the app.",
+ "fa": "صفحه App Store را باز کرده و برنامه را نصب کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app.",
+ "ru": "Откройте страницу в App Store и установите приложение.",
+ "zh": "打开 App Store 页面并安装应用。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "stash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Tap the button below — Stash will open and the configuration will be added automatically.",
+ "fa": "روی دکمه زیر ضربه بزنید — برنامه Stash باز میشود و پیکربندی بهصورت خودکار اضافه خواهد شد.",
+ "fr": "Appuie sur le bouton ci-dessous — Stash s’ouvrira et la configuration sera ajoutée automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение Stash откроется, и конфигурация будет добавлена автоматически.",
+ "zh": "点击下方按钮,Stash 将会打开并自动添加配置。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "On the main screen, tap the Start button. When prompted, allow adding VPN configurations. After the profile is activated, open the Policy section and select the country you want to connect through.",
+ "fa": "در صفحه اصلی روی دکمه Start بزنید. در صورت نمایش درخواست، مجوز افزودن پیکربندی VPN را تأیید کنید. پس از فعال شدن پروفایل، وارد بخش Policy شوید و کشور موردنظر برای اتصال را انتخاب کنید.",
+ "fr": "Sur l’écran principal, appuie sur le bouton « Start ». Autorise ensuite l’ajout de la configuration VPN. Une fois le profil activé, ouvre la section « Policy » et choisis le pays de connexion.",
+ "ru": "На главном экране нажмите кнопку «Запуск». В появившемся окне разрешите добавление конфигураций VPN. После активации профиля перейдите в раздел «Политика» и выберите страну подключения.",
+ "zh": "在主界面点击「Start」按钮。在提示时允许添加 VPN 配置。配置启用后,进入「Policy(策略)」部分并选择要连接的国家。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Stash"
+ },
+ {
+ "name": "Shadowrocket",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/ru/app/shadowrocket/id932747118",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "shadowrocket://add/{{SUBSCRIPTION_LINK}}#{{USERNAME}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add routing rules",
+ "fa": "قوانین مسیریابی را اضافه کنید",
+ "fr": "Ajouter des règles de routage",
+ "ru": "Добавить правила маршрутизации",
+ "zh": "添加路由规则"
+ },
+ "buttons": [
+ {
+ "link": "https://cdn.sm1ky.pl/s/eB4eYAxSNGk8gja/download",
+ "text": {
+ "en": "Add rules for Russia",
+ "fa": "قوانین روسیه را اضافه کنید",
+ "fr": "Ajouter des règles pour la Russie",
+ "ru": "Добавить правила для России",
+ "zh": "添加俄罗斯规则"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "By adding routing, internal websites and applications are not passed through the VPN",
+ "fa": "با اضافه کردن مسیریابی, وب سایت ها و برنامه های داخلی از VPN عبور داده نمیشود",
+ "fr": "En ajoutant un routage, les sites et apps internes ne passent pas par le VPN.",
+ "ru": "При добавлении роутинга, сайты внутри страны будут открываться без VPN",
+ "zh": "添加路由后,国内网站和应用将不会通过 VPN 访问"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Shadowrocket"
+ },
+ {
+ "name": "Streisand",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/ru/app/streisand/id6450534064",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "streisand://import/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add routing rules",
+ "fa": "قوانین مسیریابی را اضافه کنید",
+ "fr": "Ajouter des règles de routage",
+ "ru": "Добавить правила маршрутизации",
+ "zh": "添加路由规则"
+ },
+ "buttons": [
+ {
+ "link": "streisand://aW1wb3J0L3JvdXRlOi8vWW5Cc2FYTjBNRERWQVFJREJBVUdEQlVXRjFWeWRXeGxjMTFrYjIxaGFXNU5ZWFJqYUdWeVZHNWhiV1ZlWkc5dFlXbHVVM1J5WVhSbFozbFVkWFZwWktJSEVkUUlDUW9MREEwT0VGMWtiMjFoYVc1TllYUmphR1Z5Vm1SdmJXRnBibEpwY0Z0dmRYUmliM1Z1WkZSaFoxWnNhVzVsWVhLZ29ROVlaMlZ2YVhBNmNuVldaR2x5WldOMDBoSUpFQk5iYjNWMFltOTFibVJVWVdlaEZGNXlaV2RsZUhBNkxpcGNMbkoxSkcwQVVnQlZBQzBBUkFCcEFISUFaUUJqQUhUWVBOMzMyRHpkK2xwSlVFOXVSR1Z0WVc1a1h4QWtOVU5CUmpGRU5rWXRPRVV3TWkwME5EUTFMVUkxTWpjdE5rVkVRVGN3TVRZNE1UVkRDQk1aSnl3N1FFTk1XbUZrY0hkNGVvT0tqNXVkck1mU0FBQUFBQUFBQVFFQUFBQUFBQUFBR0FBQUFBQUFBQUFBQUFBQUFBQUFBBUGs9",
+ "text": {
+ "en": "Add rules for Russia",
+ "fa": "قوانین روسیه را اضافه کنید",
+ "fr": "Ajouter des règles pour la Russie",
+ "ru": "Добавить правила для России",
+ "zh": "添加俄罗斯规则"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "streisand://aW1wb3J0L3JvdXRlOi8vWW5Cc2FYTjBNRERWQVFJREJBVUdEaU1rSlZWeWRXeGxjMTFrYjIxaGFXNU5ZWFJqYUdWeVZHNWhiV1ZlWkc5dFlXbHVVM1J5WVhSbFozbFVkWFZwWktJSEZ0VUlDUW9MREEwT0R4SVZXMjkxZEdKdmRXNWtWR0ZuWFdSdmJXRnBiazFoZEdOb1pYSldaRzl0WVdsdVVtbHdWMjVsZEhkdmNtdFdaR2x5WldOMFZtaDVZbkpwWktJUUVWOFFFMmRsYjNOcGRHVTZZMkYwWldkdmNua3RhWEpmRUE5eVpXZGxlSEE2TGlwY1hDNXBjaVNpRXhSWVoyVnZhWEE2YVhKZFoyVnZhWEE2Y0hKcGRtRjBaVjhRUkZSRFVDd2dWVVJRTENCSVZGUlFMQ0JJVkZSUVV5d2dVMU5JTENCVFRWUlFMQ0JUVGsxUUxDQk9WRkFzSUVaVVVDd2dVRTlRTXl3Z1NVMUJVQ3dnVkdWc2JtVjAxQmNZQ2d3WkRob1ZXMjkxZEdKdmRXNWtWR0ZuWFdSdmJXRnBiazFoZEdOb1pYSlZZbXh2WTJ1b0d4d2RIaDhnSVNKZkVCaG5aVzl6YVhSbE9tTmhkR1ZuYjNKNUxXRmtjeTFoYkd4ZkVCUm5aVzl6YVhSbE9tTmhkR1ZuYjNKNUxXRmtjMThRRVdkbGIzTnBkR1U2ZVdGb2IyOHRZV1J6WHhBVFoyVnZjMmwwWlRwemNHOTBhV1o1TFdGa2MxOFFFbWRsYjNOcGRHVTZaMjl2WjJ4bExXRmtjMThRRVdkbGIzTnBkR1U2WVhCd2JHVXRZV1J6WHhBU1oyVnZjMmwwWlRwaGJXRjZiMjR0WVdSelh4QVJaMlZ2YzJsMFpUcGhaRzlpWlMxaFpITnVBRWtBVWdBdEFFUUFhUUJ5QUdVQVl3QjBBQ0RZUE4zdTJEemQ5MXhKVUVsbVRtOXVUV0YwWTJoZkVDUTNNamt5T1RCRlJDMUdSVFpCTFRReE9VVXRPVE15TmkxRE1rVkJOREl3UmpWQk0wTUFDQUFUQUJrQUp3QXNBRHNBUUFCREFFNEFXZ0JvQUc4QWNnQjZBSUVBaUFDTEFLRUFzd0MyQUw4QXpRRVVBUjBCS1FFM0FUMEJSZ0ZoQVhnQmpBR2lBYmNCeXdIZ0FmUUNFUUllQUFBQUFBQUFBZ0VBQUFBQUFBQUFKZ0FBQUFBQUFBQUFBQUFBQUFBQUFrVT0=",
+ "text": {
+ "en": "Add rules for Iran",
+ "fa": "قوانین ایران را اضافه کنید",
+ "fr": "Ajouter des règles pour l’Iran",
+ "ru": "Добавить правила для Ирана",
+ "zh": "加伊朗规则"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "By adding routing, internal websites and applications are not passed through the VPN",
+ "fa": "با اضافه کردن مسیریابی, وب سایت ها و برنامه های داخلی از VPN عبور داده نمیشود",
+ "fr": "En ajoutant un routage, les sites et apps internes ne passent pas par le VPN.",
+ "ru": "При добавлении роутинга, сайты внутри страны будут открываться без VPN",
+ "zh": "添加路由后,国内网站和应用将不会通过 VPN 访问"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Streisand"
+ },
+ {
+ "name": "sing-box",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/app/sing-box-vt/id6673731168",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "gray"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "sing-box://import-remote-profile/?url={{SUBSCRIPTION_LINK}}#{{USERNAME}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "On the main Dashboard page, click the Enabled button to connect to the VPN.",
+ "fa": "در صفحه اصلی داشبورد، دکمه «فعال» را برای اتصال به VPN بزنید.",
+ "fr": "Sur la page du tableau de bord, appuie sur « Enabled » pour te connecter au VPN.",
+ "ru": "На главной странице Dashboard нажми кнопку Enabled для подключения к VPN.",
+ "zh": "在主面板页面,点击“已启用”按钮连接 VPN。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Singbox"
+ }
+ ],
+ "svgIconKey": "AppleIcon",
+ "displayName": {
+ "en": "iOS",
+ "fa": "iOS",
+ "fr": "iOS",
+ "ru": "iOS",
+ "zh": "iOS"
+ }
+ },
+ "linux": {
+ "apps": [
+ {
+ "name": "FlClashX",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-linux-amd64.deb",
+ "text": {
+ "en": "amd64 (.deb)",
+ "fa": "amd64 (.deb)",
+ "fr": "amd64 (.deb)",
+ "ru": "amd64 (.deb)",
+ "zh": "amd64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-linux-amd64.AppImage",
+ "text": {
+ "en": "amd64 (AppImage)",
+ "fa": "amd64 (AppImage)",
+ "fr": "amd64 (AppImage)",
+ "ru": "amd64 (AppImage)",
+ "zh": "amd64 (AppImage)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-linux-amd64.rpm",
+ "text": {
+ "en": "amd64 (.rpm)",
+ "fa": "amd64 (.rpm)",
+ "fr": "amd64 (.rpm)",
+ "ru": "amd64 (.rpm)",
+ "zh": "amd64 (.rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-linux-arm64.deb",
+ "text": {
+ "en": "arm64 (.deb)",
+ "fa": "arm64 (.deb)",
+ "fr": "arm64 (.deb)",
+ "ru": "arm64 (.deb)",
+ "zh": "arm64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "flclashx://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l'abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the Profiles section, click the + button, select the URL, paste your copied link and click Send",
+ "fa": "اگر بعد از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراکی را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در این صفحه در گوشه سمت راست بالا کلیک کنید، لینک را کپی کنید. در FlClashX به بخش Profiles بروید، دکمه + را کلیک کنید، URL را انتخاب کنید، پیوند کپی شده خود را جایگذاری کنید و روی ارسال کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the « Profiles » section, click the + button, select the « URL », paste your copied link and click Send.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В FlClashX перейдите в раздел Профили, нажмите кнопку +, выберите URL, вставьте вашу скопированную ссылку и нажмите Отправить",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击\"获取链接\"按钮,复制链接。在 FlClashX 的\"配置文件\"部分,点击 + 按钮,选择 URL,粘贴你复制的链接并点击发送。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added profile in the Profiles section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the TUN item. After launching, in the Proxy section, you can change the choice of the server to which you will be connected.",
+ "fa": "نمایه اضافه شده را در قسمت پروفایل ها انتخاب کنید. در داشبورد، روی دکمه فعال کردن در گوشه پایین سمت راست کلیک کنید و سپس سوئیچ کنار مورد TUN را روشن کنید. پس از راه اندازی در قسمت Proxy می توانید انتخاب سروری که به آن متصل خواهید شد را تغییر دهید.",
+ "fr": "Select the added profile in the « Profiles » section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the « TUN » item. After launching, in the « Proxy » section, you can change the choice of the server to which you will be connected.",
+ "ru": "Выберите добавленный профиль в разделе Профили. В Панели управления нажмите кнопку включить в правом нижнем углу, а затем включите переключатель у пункта TUN. После запуска в разделе Прокси вы можете изменить выбор сервера к которому вас подключит.",
+ "zh": "在\"配置文件\"部分选择已添加的配置文件。在控制面板右下角点击启用按钮,然后打开 TUN 项旁边的开关。启动后,在代理部分可以更改所连接的服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "FlClashX"
+ },
+ {
+ "name": "Koala Clash",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash_amd64.deb",
+ "text": {
+ "en": "amd64 (.deb)",
+ "fa": "amd64 (.deb)",
+ "fr": "amd64 (.deb)",
+ "ru": "amd64 (.deb)",
+ "zh": "amd64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash.x86_64.rpm",
+ "text": {
+ "en": "amd64 (.rpm)",
+ "fa": "amd64 (.rpm)",
+ "fr": "amd64 (.rpm)",
+ "ru": "amd64 (.rpm)",
+ "zh": "amd64 (.rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash_arm64.deb",
+ "text": {
+ "en": "arm64 (.deb)",
+ "fa": "arm64 (.deb)",
+ "fr": "arm64 (.deb)",
+ "ru": "arm64 (.deb)",
+ "zh": "arm64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash.aarch64.rpm",
+ "text": {
+ "en": "arm64 (.rpm)",
+ "fa": "arm64 (.rpm)",
+ "fr": "arm64 (.rpm)",
+ "ru": "arm64 (.rpm)",
+ "zh": "arm64 (.rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Warning",
+ "fa": "هشدار",
+ "fr": "Avertissement",
+ "ru": "Предупреждение",
+ "zh": "警告"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash.",
+ "fa": "اگر قبلاً از Clash Verge Rev استفاده کردهاید، باید قبل از نصب Koala Clash آن را حذف کنید.",
+ "fr": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash.",
+ "ru": "Если вы ранее использовали Clash Verge Rev, то его требуется удалить перед установкой Koala Clash.",
+ "zh": "如果您之前用过 Clash Verge Rev,请在安装 Koala Clash 前先卸载它。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "koala-clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l'abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک اضافه نشد",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the Add Profile button, paste the link into the text field, and then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در گوشه بالا سمت راست این صفحه کلیک کنید و لینک را کپی کنید. در برنامه Koala Clash به صفحه اصلی بروید، روی دکمه افزودن پروفایل کلیک کنید، لینک را در فیلد متنی قرار دهید و سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the « Add Profile » button, paste the link into the text field, and then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой странице кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите на главную страницу, нажмите кнопку Добавить профиль и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击\"获取链接\"按钮,复制链接。在 Koala Clash 主页面点击\"添加配置文件\"按钮,将链接粘贴到文本框中,然后点击\"导入\"按钮。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "fa": "میتوانید سرور را در پایین صفحه اصلی انتخاب کنید و با کلیک روی دکمه بزرگ در مرکز صفحه اصلی، VPN را فعال کنید.",
+ "fr": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "ru": "Выбрать сервер можно внизу на главной странице, включить VPN можно нажав на главной странице на большую кнопку по центру.",
+ "zh": "您可以在主页面底部选择服务器,并通过点击主页面中央的大按钮来启用 VPN。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "KoalaClash"
+ },
+ {
+ "name": "Prizrak-Box",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/linux-amd64.deb",
+ "text": {
+ "en": "amd64 (.deb)",
+ "fa": "amd64 (.deb)",
+ "fr": "amd64 (.deb)",
+ "ru": "amd64 (.deb)",
+ "zh": "amd64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/linux-amd64.rpm",
+ "text": {
+ "en": "amd64 (.rpm)",
+ "fa": "amd64 (.rpm)",
+ "fr": "amd64 (.rpm)",
+ "ru": "amd64 (.rpm)",
+ "zh": "amd64 (.rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/linux-arm64.deb",
+ "text": {
+ "en": "arm64 (.deb)",
+ "fa": "arm64 (.deb)",
+ "fr": "arm64 (.deb)",
+ "ru": "arm64 (.deb)",
+ "zh": "arm64 (.deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/linux-arm64.rpm",
+ "text": {
+ "en": "arm64 (.rpm)",
+ "fa": "arm64 (.rpm)",
+ "fr": "arm64 (.rpm)",
+ "ru": "arm64 (.rpm)",
+ "zh": "arm64 (.rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the package matching your architecture and install Prizrak-Box.",
+ "fa": "بسته مناسب معماری خود را انتخاب کرده و Prizrak-Box را نصب کنید.",
+ "fr": "Choose the package matching your architecture and install Prizrak-Box.",
+ "ru": "Выберите пакет под вашу архитектуру и установите Prizrak-Box.",
+ "zh": "选择适合您架构的安装包并安装 Prizrak-Box。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Warning",
+ "fa": "هشدار",
+ "fr": "Avertissement",
+ "ru": "Предупреждение",
+ "zh": "警告"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Run the program.",
+ "fa": "برنامه را اجرا کنید.",
+ "fr": "Run the program.",
+ "ru": "Запустите программу.",
+ "zh": "运行程序。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "prizrak-box://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add the subscription automatically.",
+ "fa": "روی دکمه زیر کلیک کنید تا اشتراک به صورت خودکار افزوده شود.",
+ "fr": "Click the button below to add the subscription automatically.",
+ "ru": "Нажмите кнопку ниже, чтобы автоматически добавить подписку.",
+ "zh": "点击下方按钮即可自动添加订阅。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. On this page, click the Get Link button in the upper right corner, copy the link. In Prizrak-Box, go to the Profiles section, click the + button, paste your copied link, and click Confirm.",
+ "fa": "اگر پس از کلیک بر روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در این صفحه، روی دکمه «دریافت پیوند» در گوشه بالا سمت راست کلیک کنید، پیوند را کپی کنید. در Prizrak-Box، به بخش «پروفایلها» بروید، روی دکمه + کلیک کنید، پیوند کپی شده خود را جایگذاری کنید و روی «تأیید» کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. On this page, click the « Get Link » button in the upper right corner, copy the link. In Prizrak-Box, go to the « Profiles » section, click the + button, paste your copied link, and click « Confirm ».",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой странице кнопку «Получить ссылку» в правом верхнем углу, скопируйте ссылку. В Prizrak-Box перейдите в раздел «Профили», нажмите кнопку «+», вставьте скопированную ссылку и нажмите «Подтвердить».",
+ "zh": "如果点击按钮后没有任何反应,请手动添加订阅。在此页面上,点击右上角的\"获取链接\"按钮,复制链接。在 Prizrak-Box 中,转到\"配置文件\"部分,点击 + 按钮,粘贴您复制的链接,然后点击\"确认\"。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added subscription in the Profiles section. You can choose the server country in the Proxy (🚀) section. Set the TUN switch to ON.",
+ "fa": "اشتراک افزودهشده را در بخش پروفایلها انتخاب کنید. میتوانید کشور سرور را در بخش Proxy (🚀) انتخاب کنید. سوئیچ TUN را روی حالت روشن قرار دهید.",
+ "fr": "Select the added subscription in the « Profiles » section. You can choose the server country in the « Proxy » (🚀) section. Set the « TUN » switch to ON.",
+ "ru": "Выберите добавленную подписку в разделе Профили. Выбрать страну сервера можно в разделе Прокси (🚀). Установите переключатель TUN в положение ВКЛ.",
+ "zh": "在\"配置文件\"部分选择已添加的订阅。可在\"代理 (🚀)\"部分选择服务器国家。将 TUN 开关切换到开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "PrizrakBox"
+ },
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/Happ-proxy/happ-desktop/releases/latest/download/Happ.linux.x64.deb",
+ "text": {
+ "en": "Linux",
+ "fa": "لینوکس",
+ "fr": "Linux",
+ "ru": "Linux",
+ "zh": "Linux"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l'app s'ouvrira et l'abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮——应用将会打开,订阅会自动添加。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N'oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "Clash Verge",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_amd64.deb",
+ "text": {
+ "en": "Linux (amd64 .deb)",
+ "fa": "لینوکس (amd64 .deb)",
+ "fr": "Linux (amd64 .deb)",
+ "ru": "Linux (amd64 .deb)",
+ "zh": "Linux(amd64 .deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge-2.4.5-1.x86_64.rpm",
+ "text": {
+ "en": "Linux (x86_64 .rpm)",
+ "fa": "لینوکس (x86_64 .rpm)",
+ "fr": "Linux (x86_64 .rpm)",
+ "ru": "Linux (x86_64 .rpm)",
+ "zh": "Linux(x86_64 .rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_arm64.deb",
+ "text": {
+ "en": "Linux (ARM64 .deb)",
+ "fa": "لینوکس (ARM64 .deb)",
+ "fr": "Linux (ARM64 .deb)",
+ "ru": "Linux (ARM64 .deb)",
+ "zh": "Linux(ARM64 .deb)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge-2.4.5-1.aarch64.rpm",
+ "text": {
+ "en": "Linux (aarch64 .rpm)",
+ "fa": "لینوکس (aarch64 .rpm)",
+ "fr": "Linux (aarch64 .rpm)",
+ "ru": "Linux (aarch64 .rpm)",
+ "zh": "Linux(aarch64 .rpm)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Change language",
+ "fa": "تغییر زبان",
+ "fr": "Change language",
+ "ru": "Смена языка",
+ "zh": "更改语言"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "fa": "پس از راهاندازی برنامه، میتوانید زبان را در تنظیمات تغییر دهید. در پنل سمت چپ، نماد چرخ دنده را پیدا کنید، سپس به Verge 设置 بروید و 语言设置 را انتخاب کنید.",
+ "fr": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "ru": "После запуска приложения вы можете сменить язык в настройках. В левой панели найдите иконку шестеренки, далее ориентируйтесь на Verge 设置 и выберите пункт 语言设置.",
+ "zh": "启动应用后,可以在设置中更改语言。在左侧面板找到齿轮图标,进入 Verge 设置,然后选择 语言设置。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l'abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Clash Verge, go to the Profiles section and paste the link in the text field, then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در گوشه بالا سمت راست این صفحه روی دکمه دریافت لینک کلیک کنید، لینک را کپی کنید. در Clash Verge به بخش پروفایلها بروید و لینک را در فیلد متنی وارد کنید، سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Clash Verge, go to the « Profiles » section and paste the link in the text field, then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Clash Verge перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在本页右上角点击获取链接按钮,复制链接。在 Clash Verge 的 Profiles 部分粘贴链接到文本框,然后点击导入按钮。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.",
+ "fa": "میتوانید در بخش پروکسی سرور را انتخاب کنید و در بخش تنظیمات VPN را فعال کنید. کلید TUN Mode را در حالت روشن قرار دهید.",
+ "fr": "You can select a server in the « Proxy » section, and enable VPN in the Settings section. Set the « TUN » Mode switch to ON.",
+ "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ.",
+ "zh": "您可以在代理部分选择服务器,在设置中启用 VPN。将 TUN 模式开关设置为开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "ClashVerge"
+ }
+ ],
+ "svgIconKey": "Ubuntu",
+ "displayName": {
+ "en": "Linux",
+ "fa": "Linux",
+ "fr": "Linux",
+ "ru": "Linux",
+ "zh": "Linux"
+ }
+ },
+ "macos": {
+ "apps": [
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973",
+ "text": {
+ "en": "App Store (RU)",
+ "fa": "اپ استور (RU)",
+ "fr": "App Store (RU)",
+ "ru": "App Store (RU)",
+ "zh": "App Store (RU)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215",
+ "text": {
+ "en": "App Store (Global)",
+ "fa": "اپ استور (جهانی)",
+ "fr": "App Store (Global)",
+ "ru": "App Store (Global)",
+ "zh": "App Store (Global)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮——应用将会打开,订阅会自动添加。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "FlClashX",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-macos-arm64.dmg",
+ "text": {
+ "en": "macOS (Apple Silicon)",
+ "fa": "مک (Apple Silicon)",
+ "fr": "macOS (Apple Silicon)",
+ "ru": "macOS (Apple Silicon)",
+ "zh": "macOS(Apple Silicon)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-macos-amd64.dmg",
+ "text": {
+ "en": "macOS (Intel)",
+ "fa": "مک (اینتل)",
+ "fr": "macOS (Intel)",
+ "ru": "macOS (Intel)",
+ "zh": "macOS(Intel)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "flclashx://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the Profiles section, click the + button, select the URL, paste your copied link and click Send",
+ "fa": "اگر بعد از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراکی را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در این صفحه در گوشه سمت راست بالا کلیک کنید، لینک را کپی کنید. در FlClashX به بخش Profiles بروید، دکمه + را کلیک کنید، URL را انتخاب کنید، پیوند کپی شده خود را جایگذاری کنید و روی ارسال کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the « Profiles » section, click the + button, select the « URL », paste your copied link and click Send.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В FlClashX перейдите в раздел Профили, нажмите кнопку +, выберите URL, вставьте вашу скопированную ссылку и нажмите Отправить",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击“获取链接”按钮,复制链接。在 FlClashX 的“配置文件”部分,点击 + 按钮,选择 URL,粘贴你复制的链接并点击发送。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added profile in the Profiles section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the TUN item. After launching, in the Proxy section, you can change the choice of the server to which you will be connected.",
+ "fa": "نمایه اضافه شده را در قسمت پروفایل ها انتخاب کنید. در داشبورد، روی دکمه فعال کردن در گوشه پایین سمت راست کلیک کنید و سپس سوئیچ کنار مورد TUN را روشن کنید. پس از راه اندازی در قسمت Proxy می توانید انتخاب سروری که به آن متصل خواهید شد را تغییر دهید.",
+ "fr": "Select the added profile in the « Profiles » section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the « TUN » item. After launching, in the « Proxy » section, you can change the choice of the server to which you will be connected.",
+ "ru": "Выберите добавленный профиль в разделе Профили. В Панели управления нажмите кнопку включить в правом нижнем углу, а затем включите переключатель у пункта TUN. После запуска в разделе Прокси вы можете изменить выбор сервера к которому вас подключит.",
+ "zh": "在“配置文件”部分选择已添加的配置文件。在控制面板右下角点击启用按钮,然后打开 TUN 项旁边的开关。启动后,在代理部分可以更改所连接的服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "FlClashX"
+ },
+ {
+ "name": "Koala Clash",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash_aarch64.dmg",
+ "text": {
+ "en": "macOS (Apple Silicon)",
+ "fa": "مک (Apple Silicon)",
+ "fr": "macOS (Apple Silicon)",
+ "ru": "macOS (Apple Silicon)",
+ "zh": "macOS(Apple Silicon)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash_x64.dmg",
+ "text": {
+ "en": "macOS (Intel)",
+ "fa": "مک (اینتل)",
+ "fr": "macOS (Intel)",
+ "ru": "macOS (Intel)",
+ "zh": "macOS(Intel)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Warning",
+ "fa": "هشدار",
+ "fr": "Avertissement",
+ "ru": "Предупреждение",
+ "zh": "警告"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash. ⚠️ Warning: If you get a notification that the application is corrupted when you run it on macOS, run this command in Terminal: sudo xattr -r -c /Applications/Koala\\ Clash.app",
+ "fa": "اگر قبلاً از Clash Verge Rev استفاده کردهاید، باید قبل از نصب Koala Clash آن را حذف کنید. ⚠️ هشدار: اگر هنگام اجرای برنامه در macOS پیامی مبنی بر خراب بودن برنامه دریافت کردید، این دستور را در ترمینال اجرا کنید: sudo xattr -r -c /Applications/Koala\\ Clash.app",
+ "fr": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash. ⚠️ Warning : If you get a notification that the application is corrupted when you run it on macOS, run this command in Terminal : sudo xattr -r -c /Applications/Koala\\ Clash.app.",
+ "ru": "Если вы ранее использовали Clash Verge Rev, то его требуется удалить перед установкой Koala Clash. ⚠️ Предупреждение: Если при запуске приложения на macOS появляется уведомление, что приложение повреждено, выполните эту команду в терминале: sudo xattr -r -c /Applications/Koala\\ Clash.app",
+ "zh": "如果您之前用过 Clash Verge Rev,请在安装 Koala Clash 前先卸载它。⚠️ 警告:如果在 macOS 上运行应用时收到应用已损坏的提示,请在终端运行以下命令:sudo xattr -r -c /Applications/Koala\\ Clash.app"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "koala-clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک اضافه نشد",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the Add Profile button, paste the link into the text field, and then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در گوشه بالا سمت راست این صفحه کلیک کنید و لینک را کپی کنید. در برنامه Koala Clash به صفحه اصلی بروید، روی دکمه افزودن پروفایل کلیک کنید، لینک را در فیلد متنی قرار دهید و سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the « Add Profile » button, paste the link into the text field, and then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой странице кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите на главную страницу, нажмите кнопку Добавить профиль и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击“获取链接”按钮,复制链接。在 Koala Clash 主页面点击“添加配置文件”按钮,将链接粘贴到文本框中,然后点击“导入”按钮。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "fa": "میتوانید سرور را در پایین صفحه اصلی انتخاب کنید و با کلیک روی دکمه بزرگ در مرکز صفحه اصلی، VPN را فعال کنید.",
+ "fr": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "ru": "Выбрать сервер можно внизу на главной странице, включить VPN можно нажав на главной странице на большую кнопку по центру.",
+ "zh": "您可以在主页面底部选择服务器,并通过点击主页面中央的大按钮来启用 VPN。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "KoalaClash"
+ },
+ {
+ "name": "Prizrak-Box",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/macos-arm64-dmg.zip",
+ "text": {
+ "en": "macOS (Apple Silicon)",
+ "fa": "macOS (Apple Silicon)",
+ "fr": "macOS (Apple Silicon)",
+ "ru": "macOS (Apple Silicon)",
+ "zh": "macOS(Apple Silicon)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/macos-amd64-dmg.zip",
+ "text": {
+ "en": "macOS (Intel)",
+ "fa": "macOS (Intel)",
+ "fr": "macOS (Intel)",
+ "ru": "macOS (Intel)",
+ "zh": "macOS(Intel)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download the archive for your chip (Apple Silicon or Intel), unzip and move Prizrak-Box.app to Applications.",
+ "fa": "فایل مناسب (Apple Silicon یا Intel) را دانلود کرده، از حالت فشرده خارج و برنامه را به Applications منتقل کنید.",
+ "fr": "Download the archive for your chip (Apple Silicon or Intel), unzip and move Prizrak-Box.app to Applications.",
+ "ru": "Скачайте архив под ваш чип (Apple Silicon или Intel), распакуйте и переместите Prizrak-Box.app в Applications.",
+ "zh": "下载适合您芯片(Apple Silicon 或 Intel)的压缩包,解压后将 Prizrak-Box.app 移入 Applications。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Read before first launch",
+ "fa": "قبل از اولین اجرا بخوانید",
+ "fr": "Read before first launch",
+ "ru": "Прочти перед первым запуском",
+ "zh": "首次启动前阅读"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/blob/v3/doc/mac/mac.md",
+ "text": {
+ "en": "Mac Guide",
+ "fa": "راهنمای مک",
+ "fr": "Mac Guide",
+ "ru": "Инструкция для Mac",
+ "zh": "Mac 使用指南"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If macOS shows security warnings, follow this guide.",
+ "fa": "اگر macOS هشدار امنیتی نشان داد، این راهنما را دنبال کنید.",
+ "fr": "If macOS shows security warnings, follow this guide.",
+ "ru": "Если macOS показывает предупреждения безопасности — следуйте инструкции.",
+ "zh": "若 macOS 显示安全警告,请按指南操作。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "prizrak-box://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add the subscription automatically.",
+ "fa": "روی دکمه زیر کلیک کنید تا اشتراک به صورت خودکار افزوده شود.",
+ "fr": "Click the button below to add the subscription automatically.",
+ "ru": "Нажмите кнопку ниже, чтобы автоматически добавить подписку.",
+ "zh": "点击下方按钮即可自动添加订阅。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. On this page, click the Get Link button in the upper right corner, copy the link. In Prizrak-Box, go to the Profiles section, click the + button, paste your copied link, and click Confirm.",
+ "fa": "اگر پس از کلیک بر روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در این صفحه، روی دکمه «دریافت پیوند» در گوشه بالا سمت راست کلیک کنید، پیوند را کپی کنید. در Prizrak-Box، به بخش «پروفایلها» بروید، روی دکمه + کلیک کنید، پیوند کپی شده خود را جایگذاری کنید و روی «تأیید» کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. On this page, click the « Get Link » button in the upper right corner, copy the link. In Prizrak-Box, go to the « Profiles » section, click the + button, paste your copied link, and click « Confirm ».",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой странице кнопку «Получить ссылку» в правом верхнем углу, скопируйте ссылку. В Prizrak-Box перейдите в раздел «Профили», нажмите кнопку «+», вставьте скопированную ссылку и нажмите «Подтвердить».",
+ "zh": "如果点击按钮后没有任何反应,请手动添加订阅。在此页面上,点击右上角的“获取链接”按钮,复制链接。在 Prizrak-Box 中,转到“配置文件”部分,点击 + 按钮,粘贴您复制的链接,然后点击“确认”。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added subscription in the Profiles section. You can choose the server country in the Proxy (🚀) section. Set the TUN switch to ON.",
+ "fa": "اشتراک افزودهشده را در بخش پروفایلها انتخاب کنید. میتوانید کشور سرور را در بخش Proxy (🚀) انتخاب کنید. سوئیچ TUN را روی حالت روشن قرار دهید.",
+ "fr": "Select the added subscription in the « Profiles » section. You can choose the server country in the « Proxy » (🚀) section. Set the « TUN » switch to ON.",
+ "ru": "Выберите добавленную подписку в разделе Профили. Выбрать страну сервера можно в разделе Прокси (🚀). Установите переключатель TUN в положение ВКЛ.",
+ "zh": "在“配置文件”部分选择已添加的订阅。可在“代理 (🚀)”部分选择服务器国家。将 TUN 开关切换到开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "PrizrakBox"
+ },
+ {
+ "name": "RabbitHole",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/app/rabbithole-vpn-client/id6683309629",
+ "text": {
+ "en": "App Store",
+ "fa": "اپ استور",
+ "fr": "App Store",
+ "ru": "App Store",
+ "zh": "App Store"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download and install the app from the App Store.",
+ "fa": "برنامه را از App Store دانلود و نصب کنید.",
+ "fr": "Télécharge et installe l'application depuis l'App Store.",
+ "ru": "Скачайте и установите приложение из App Store.",
+ "zh": "从 App Store 下载并安装应用。"
+ },
+ "svgIconColor": "violet"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "rabbithole://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription config will be imported automatically. The config will not be visible until the VPN profile is added to the system.",
+ "fa": "روی دکمه زیر کلیک کنید — برنامه باز میشود و پیکربندی اشتراک به طور خودکار وارد میشود. تا زمانی که پروفایل VPN به سیستم اضافه نشود، پیکربندی قابل مشاهده نخواهد بود.",
+ "fr": "Clique sur le bouton ci‑dessous — l'app s'ouvrira et la configuration sera importée automatiquement. La configuration ne sera pas visible tant que le profil VPN n'aura pas été ajouté au système.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и конфигурация подписки импортируется автоматически. Конфиг не будет виден до тех пор, пока профиль VPN не добавится в систему.",
+ "zh": "点击下方按钮,应用将打开并自动导入订阅配置。在 VPN 配置文件添加到系统之前,配置将不可见。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add VPN Configuration",
+ "fa": "افزودن پیکربندی VPN",
+ "fr": "Ajouter la configuration VPN",
+ "ru": "Добавление конфигурации VPN",
+ "zh": "添加 VPN 配置"
+ },
+ "buttons": [],
+ "svgIconKey": "ShieldPlus",
+ "description": {
+ "en": "In the app, click the Add VPN Configuration button. In the permission window that appears, click Allow to let the app add a VPN profile to your system.",
+ "fa": "در برنامه، روی دکمه افزودن پیکربندی VPN کلیک کنید. در پنجره مجوزی که ظاهر میشود، روی Allow کلیک کنید تا برنامه بتواند پروفایل VPN را به سیستم شما اضافه کند.",
+ "fr": "Dans l'app, clique sur le bouton Ajouter la configuration VPN. Dans la fenêtre d'autorisation qui apparaît, clique sur « Autoriser » pour permettre à l'app d'ajouter un profil VPN à ton système.",
+ "ru": "В приложении нажмите кнопку Добавить конфигурацию VPN. В появившемся окне разрешения нажмите Разрешить, чтобы приложение могло добавить VPN-профиль в систему.",
+ "zh": "在应用中,点击\"添加 VPN 配置\"按钮。在弹出的权限窗口中,点击\"允许\"以让应用将 VPN 配置文件添加到您的系统。"
+ },
+ "svgIconColor": "orange"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Click the Connect button in the app to connect to VPN. If needed, select a different server from the server list.",
+ "fa": "برای اتصال به VPN روی دکمه اتصال در برنامه کلیک کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Clique sur le bouton Connexion dans l'app pour te connecter au VPN. Si besoin, choisis un autre serveur dans la liste.",
+ "ru": "Нажмите кнопку Подключиться в приложении для подключения к VPN. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "点击应用中的连接按钮以连接 VPN。如有需要,可从服务器列表中选择其他服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "RabbitHole"
+ },
+ {
+ "name": "Clash Verge",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_x64.dmg",
+ "text": {
+ "en": "macOS (Intel)",
+ "fa": "مک (اینتل)",
+ "fr": "macOS (Intel)",
+ "ru": "macOS (Intel)",
+ "zh": "macOS(Intel)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_aarch64.dmg",
+ "text": {
+ "en": "macOS (Apple Silicon)",
+ "fa": "مک (Apple Silicon)",
+ "fr": "macOS (Apple Silicon)",
+ "ru": "macOS (Apple Silicon)",
+ "zh": "macOS(Apple Silicon)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Change language",
+ "fa": "تغییر زبان",
+ "fr": "Change language",
+ "ru": "Смена языка",
+ "zh": "更改语言"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "fa": "پس از راهاندازی برنامه، میتوانید زبان را در تنظیمات تغییر دهید. در پنل سمت چپ، نماد چرخ دنده را پیدا کنید، سپس به Verge 设置 بروید و 语言设置 را انتخاب کنید.",
+ "fr": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "ru": "После запуска приложения вы можете сменить язык в настройках. В левой панели найдите иконку шестеренки, далее ориентируйтесь на Verge 设置 и выберите пункт 语言设置.",
+ "zh": "启动应用后,可以在设置中更改语言。在左侧面板找到齿轮图标,进入 Verge 设置,然后选择 语言设置。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l'abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Clash Verge, go to the Profiles section and paste the link in the text field, then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در گوشه بالا سمت راست این صفحه روی دکمه دریافت لینک کلیک کنید، لینک را کپی کنید. در Clash Verge به بخش پروفایلها بروید و لینک را در فیلد متنی وارد کنید، سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Clash Verge, go to the « Profiles » section and paste the link in the text field, then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Clash Verge перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在本页右上角点击获取链接按钮,复制链接。在 Clash Verge 的 Profiles 部分粘贴链接到文本框,然后点击导入按钮。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.",
+ "fa": "میتوانید در بخش پروکسی سرور را انتخاب کنید و در بخش تنظیمات VPN را فعال کنید. کلید TUN Mode را در حالت روشن قرار دهید.",
+ "fr": "You can select a server in the « Proxy » section, and enable VPN in the Settings section. Set the « TUN » Mode switch to ON.",
+ "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ.",
+ "zh": "您可以在代理部分选择服务器,在设置中启用 VPN。将 TUN 模式开关设置为开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "ClashVerge"
+ }
+ ],
+ "svgIconKey": "macOS",
+ "displayName": {
+ "en": "macOS",
+ "fa": "macOS",
+ "fr": "macOS",
+ "ru": "macOS",
+ "zh": "macOS"
+ }
+ },
+ "android": {
+ "apps": [
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://play.google.com/store/apps/details?id=com.happproxy",
+ "text": {
+ "en": "Open in Google Play",
+ "fa": "باز کردن در Google Play",
+ "fr": "Ouvre dans Google Play",
+ "ru": "Открыть в Google Play",
+ "zh": "在 Google Play 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.",
+ "fa": "صفحه را در Google Play باز کنید و برنامه را نصب کنید. یا برنامه را مستقیماً از فایل APK نصب کنید، اگر Google Play کار نمی کند.",
+ "fr": "Ouvre la page dans Google Play et installe l’app. Si Google Play ne fonctionne pas, installe‑la directement via l’APK.",
+ "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает.",
+ "zh": "在 Google Play 打开页面并安装应用。如果 Google Play 无法使用,也可以直接通过 APK 文件安装此应用。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Open the app and connect to the server",
+ "fa": "برنامه را باز کنید و به سرور متصل شوید",
+ "fr": "Ouvre l’app et connecte‑toi au serveur.",
+ "ru": "Откройте приложение и подключитесь к серверу",
+ "zh": "打开应用并连接到服务器"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "FlClashX",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-android-arm64-v8a.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download and install FlClashX APK",
+ "fa": "دانلود و نصب FlClashX APK",
+ "fr": "Télécharge et installe l’APK FlClashX.",
+ "ru": "Скачайте и установите FlClashX APK",
+ "zh": "下载并安装 FlClashX APK"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "flclashx://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the Profiles section, click the + button, select the URL, paste your copied link and click Send",
+ "fa": "اگر بعد از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراکی را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در این صفحه در گوشه سمت راست بالا کلیک کنید، لینک را کپی کنید. در FlClashX به بخش Profiles بروید، دکمه + را کلیک کنید، URL را انتخاب کنید، پیوند کپی شده خود را جایگذاری کنید و روی ارسال کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the « Profiles » section, click the + button, select the « URL », paste your copied link and click Send.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В FlClashX перейдите в раздел Профили, нажмите кнопку +, выберите URL, вставьте вашу скопированную ссылку и нажмите Отправить",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在本页右上角点击获取链接按钮,复制链接。在 FlClashX 的 Profiles 部分点击 + 按钮,选择 URL,粘贴你复制的链接并点击发送。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added profile in the Profiles section. In the Control Panel, click the Enable button in the bottom right corner. Once it's running, you can change the server you're connected to in the Proxy section.",
+ "fa": "پروفایل افزودهشده را در بخش پروفایلها انتخاب کنید. در پنل کنترل، روی دکمه فعالسازی در گوشه پایین سمت راست کلیک کنید. پس از اجرا، میتوانید در بخش پروکسی، سروری را که به آن متصل میشوید تغییر دهید.",
+ "fr": "Select the added profile in the « Profiles » section. In the Control Panel, click the « Enable » button in the bottom right corner. Once it's running, you can change the server you're connected to in the « Proxy » section.",
+ "ru": "Выберите добавленный профиль в разделе Профили. В Панели управления нажмите кнопку включить в правом нижнем углу. После запуска в разделе Прокси вы можете изменить выбор сервера к которому вас подключит.",
+ "zh": "在 Profiles 部分选择已添加的配置文件。在控制面板右下角点击启用按钮。启动后,你可以在 Proxy 部分更换连接的服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "FlClashX"
+ },
+ {
+ "name": "Clash Meta",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/MetaCubeX/ClashMetaForAndroid/releases/download/v2.11.23/cmfa-2.11.23-meta-universal-release.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://f-droid.org/packages/com.github.metacubex.clash.meta/",
+ "text": {
+ "en": "Open in F-Droid",
+ "fa": "در F-Droid باز کنید",
+ "fr": "Ouvre dans F‑Droid",
+ "ru": "Открыть в F-Droid",
+ "zh": "在 F-Droid 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download and install Clash Meta APK",
+ "fa": "دانلود و نصب Clash Meta APK",
+ "fr": "Télécharge et installe l’APK Clash Meta.",
+ "ru": "Скачайте и установите Clash Meta APK",
+ "zh": "下载并安装 Clash Meta APK"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "clashmeta://install-config?name={{USERNAME}}&url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to open the profile creation window. You will need to specify the auto-update period, for example, 720 minutes. Click the Save button in the top right corner.",
+ "fa": "دکمه زیر را بزنید تا پنجره ایجاد پروفایل باز شود. شما باید دوره بهروزرسانی خودکار را مشخص کنید، مثلاً ۷۲۰ دقیقه. دکمه ذخیره را در بالا سمت راست بزنید.",
+ "fr": "Clique sur le bouton ci‑dessous pour ouvrir la fenêtre de création de profil. Indique la période d’actualisation automatique, par exemple 720 minutes. Appuie sur « « Save » » en haut à droite.",
+ "ru": "Нажми кнопку ниже — откроется окно создания профиля. Тебе потребуется указать период автообновления, например, 720 минут. Справа вверху нажми на кнопку Сохранить.",
+ "zh": "点击下方按钮打开配置文件创建窗口。你需要指定自动更新周期,例如 720 分钟。点击右上角的保存按钮。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Go to the Profiles section and select the created profile, then return to the main page. Now you can connect by clicking the Stopped button.",
+ "fa": "به بخش پروفایلها بروید و پروفایل ایجاد شده را انتخاب کنید، سپس به صفحه اصلی بازگردید. اکنون میتوانید با زدن دکمه «متوقف شده» متصل شوید.",
+ "fr": "Va dans « Profiles » et sélectionne le profil créé, puis reviens à la page principale. Tu peux maintenant te connecter en appuyant sur « Stopped ».",
+ "ru": "Перейди в пункт Профили и выбери созданный профиль, затем вернись на главную страницу. Теперь ты можешь подключиться, нажав на кнопку Остановлен",
+ "zh": "进入“配置文件”部分并选择已创建的配置文件,然后返回主页面。现在你可以点击“已停止”按钮连接。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "ClashMeta"
+ },
+ {
+ "name": "v2rayNG",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/2dust/v2rayNG/releases/download/2.0.9/v2rayNG_2.0.9_universal.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download and install v2rayNG APK",
+ "fa": "دانلود و نصب v2rayNG APK",
+ "fr": "Télécharge et installe l’APK v2rayNG.",
+ "ru": "Скачайте и установите v2rayNG APK",
+ "zh": "下载并安装 v2rayNG APK"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "v2rayng://install-config?name={{USERNAME}}&url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Update subscriptions",
+ "fa": "بهروزرسانی اشتراکها",
+ "fr": "Mettre à jour les abonnements",
+ "ru": "Обновление подписки",
+ "zh": "更新订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Tap the three dots in the top-right corner and select Update subscription. After that, the available servers will appear in the list.",
+ "fa": "روی سه نقطه در گوشه بالا سمت راست کلیک کنید و گزینه بهروزرسانی اشتراک را انتخاب کنید. سپس سرورهای موجود در لیست ظاهر میشوند.",
+ "fr": "Appuie sur les trois points en haut à droite et sélectionne « Update subscription ». Les serveurs disponibles apparaîtront alors dans la liste.",
+ "ru": "Нажмите на три точечки справа сверху и выберите Обновить подписку. После этого в списке появятся доступные серверы",
+ "zh": "点击右上角的三个点,选择“更新订阅”。之后,列表中会显示可用服务器。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the required server and click the Enable button in the bottom right corner.",
+ "fa": "سرور موردنظر را انتخاب کنید و روی دکمه فعالسازی در گوشه پایین سمت راست کلیک کنید.",
+ "fr": "Sélectionne le serveur souhaité puis appuie sur « Enable » en bas à droite.",
+ "ru": "Выберите требуемый сервер и нажмите кнопку Включить в правом нижнем углу",
+ "zh": "选择所需服务器并点击右下角的启用按钮。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "VRayNG"
+ }
+ ],
+ "svgIconKey": "Android",
+ "displayName": {
+ "en": "Android",
+ "fa": "Android",
+ "fr": "Android",
+ "ru": "Android",
+ "zh": "Android"
+ }
+ },
+ "appleTV": {
+ "apps": [
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274",
+ "text": {
+ "en": "App Store",
+ "fa": "App Store",
+ "fr": "App Store",
+ "ru": "App Store",
+ "zh": "App Store"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in the App Store on your Apple TV and install the app. Launch it, allow VPN configuration if prompted, and enter your passcode if required.",
+ "fa": "صفحه را در App Store بر روی Apple TV باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در صورت درخواست مجوز پیکربندی VPN را بدهید و در صورت نیاز رمز عبور خود را وارد کنید.",
+ "fr": "Open the page in the App Store on your Apple TV and install the app. Launch it, allow VPN configuration if prompted, and enter your passcode if required.",
+ "ru": "Откройте страницу в App Store на Apple TV и установите приложение. Запустите его, предоставьте разрешение на VPN-конфигурацию, если потребуется, и введите свой пароль.",
+ "zh": "在 Apple TV 的 App Store 打开页面并安装应用。启动后,如有提示请允许 VPN 配置,并在需要时输入您的密码。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Installation instructions",
+ "fa": "دستورالعمل نصب",
+ "fr": "Installation instructions",
+ "ru": "Инструкции по установке",
+ "zh": "安装说明"
+ },
+ "buttons": [
+ {
+ "link": "https://www.happ.su/main/ru/faq/apple-tv-tvos",
+ "text": {
+ "en": "In Russian",
+ "fa": "به زبان روسی",
+ "fr": "In Russian",
+ "ru": "На русском",
+ "zh": "俄语"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://www.happ.su/main/faq/apple-tv-tvos",
+ "text": {
+ "en": "In English",
+ "fa": "به زبان انگلیسی",
+ "fr": "In English",
+ "ru": "На английском",
+ "zh": "英语"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Detailed instructions to help you set up Happ on your device.",
+ "fa": "راهنمای دقیق برای کمک به تنظیم Happ روی دستگاه شما.",
+ "fr": "Detailed instructions to help you set up Happ on your device.",
+ "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве.",
+ "zh": "详细说明,帮助您在设备上设置 Happ。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید، اگر صفحه اشتراک را روی تلویزیون باز کردهاید",
+ "fr": "Click the button below to add subscription, if you opened the subscription page on your TV.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре",
+ "zh": "如果你已在电视上打开订阅页面,点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Open the app and connect to the server",
+ "fa": "برنامه را باز کنید و به سرور متصل شوید",
+ "fr": "Ouvre l’app et connecte‑toi au serveur.",
+ "ru": "Откройте приложение и подключитесь к серверу",
+ "zh": "打开应用并连接到服务器"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "Stash",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/us/app/stash-rule-based-proxy/id1596063349",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the App Store page and install the app.",
+ "fa": "صفحه App Store را باز کرده و برنامه را نصب کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app.",
+ "ru": "Откройте страницу в App Store и установите приложение.",
+ "zh": "打开 App Store 页面并安装应用。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "stash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Tap the button below — Stash will open and the configuration will be added automatically.",
+ "fa": "روی دکمه زیر ضربه بزنید — برنامه Stash باز میشود و پیکربندی بهصورت خودکار اضافه خواهد شد.",
+ "fr": "Appuie sur le bouton ci-dessous — Stash s’ouvrira et la configuration sera ajoutée automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение Stash откроется, и конфигурация будет добавлена автоматически.",
+ "zh": "点击下方按钮,Stash 将会打开并自动添加配置。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "On the main screen, tap the Start button. When prompted, allow adding VPN configurations. After the profile is activated, open the Policy section and select the country you want to connect through.",
+ "fa": "در صفحه اصلی روی دکمه Start بزنید. در صورت نمایش درخواست، مجوز افزودن پیکربندی VPN را تأیید کنید. پس از فعال شدن پروفایل، وارد بخش Policy شوید و کشور موردنظر برای اتصال را انتخاب کنید.",
+ "fr": "Sur l’écran principal, appuie sur le bouton « Start ». Autorise ensuite l’ajout de la configuration VPN. Une fois le profil activé, ouvre la section « Policy » et choisis le pays de connexion.",
+ "ru": "На главном экране нажмите кнопку «Запуск». В появившемся окне разрешите добавление конфигураций VPN. После активации профиля перейдите в раздел «Политика» и выберите страну подключения.",
+ "zh": "在主界面点击「Start」按钮。在提示时允许添加 VPN 配置。配置启用后,进入「Policy(策略)」部分并选择要连接的国家。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Stash"
+ },
+ {
+ "name": "Shadowrocket",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/ru/app/shadowrocket/id932747118",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "shadowrocket://add/{{SUBSCRIPTION_LINK}}#{{USERNAME}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Shadowrocket"
+ },
+ {
+ "name": "sing-box",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://apps.apple.com/app/sing-box-vt/id6673731168",
+ "text": {
+ "en": "Open in App Store",
+ "fa": "باز کردن در App Store",
+ "fr": "Ouvre dans l’App Store",
+ "ru": "Открыть в App Store",
+ "zh": "在 App Store 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.",
+ "fa": "صفحه را در App Store باز کنید و برنامه را نصب کنید. آن را اجرا کنید، در پنجره مجوز پیکربندی VPN روی Allow کلیک کنید و رمز عبور خود را وارد کنید.",
+ "fr": "Ouvre la page de l’App Store et installe l’app. Lance-la ; dans la fenêtre d’autorisation de configuration VPN, appuie sur « Allow » puis entre ton code.",
+ "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль.",
+ "zh": "在 App Store 打开页面并安装应用。启动应用后,在 VPN 配置权限窗口点击“允许”,并输入您的密码。"
+ },
+ "svgIconColor": "gray"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "sing-box://import-remote-profile/?url={{SUBSCRIPTION_LINK}}#{{USERNAME}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮,应用将会打开,并自动添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "On the main Dashboard page, click the Enabled button to connect to the VPN.",
+ "fa": "در صفحه اصلی داشبورد، دکمه «فعال» را برای اتصال به VPN بزنید.",
+ "fr": "Sur la page du tableau de bord, appuie sur « Enabled » pour te connecter au VPN.",
+ "ru": "На главной странице Dashboard нажми кнопку Enabled для подключения к VPN.",
+ "zh": "在主面板页面,点击“已启用”按钮连接 VPN。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Singbox"
+ }
+ ],
+ "svgIconKey": "TV",
+ "displayName": {
+ "en": "Apple TV",
+ "fa": "Apple TV",
+ "fr": "Apple TV",
+ "ru": "Apple TV",
+ "zh": "Apple TV"
+ }
+ },
+ "windows": {
+ "apps": [
+ {
+ "name": "FlClashX",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-windows-amd64-setup.exe",
+ "text": {
+ "en": "Windows (Setup)",
+ "fa": "ویندوز (نصب)",
+ "fr": "Windows (Setup)",
+ "ru": "Windows (Установщик)",
+ "zh": "Windows(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-windows-arm64-setup.exe",
+ "text": {
+ "en": "Windows on ARM (Setup)",
+ "fa": "ویندوز ARM (نصب)",
+ "fr": "Windows on ARM (Setup)",
+ "ru": "Windows на ARM (Установщик)",
+ "zh": "Windows on ARM(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "flclashx://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the Profiles section, click the + button, select the URL, paste your copied link and click Send",
+ "fa": "اگر بعد از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراکی را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در این صفحه در گوشه سمت راست بالا کلیک کنید، لینک را کپی کنید. در FlClashX به بخش Profiles بروید، دکمه + را کلیک کنید، URL را انتخاب کنید، پیوند کپی شده خود را جایگذاری کنید و روی ارسال کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClashX, go to the « Profiles » section, click the + button, select the « URL », paste your copied link and click Send.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В FlClashX перейдите в раздел Профили, нажмите кнопку +, выберите URL, вставьте вашу скопированную ссылку и нажмите Отправить",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击“获取链接”按钮,复制链接。在 FlClashX 的“配置文件”部分,点击 + 按钮,选择 URL,粘贴你复制的链接并点击发送。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added profile in the Profiles section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the TUN item. After launching, in the Proxy section, you can change the choice of the server to which you will be connected.",
+ "fa": "نمایه اضافه شده را در قسمت پروفایل ها انتخاب کنید. در داشبورد، روی دکمه فعال کردن در گوشه پایین سمت راست کلیک کنید و سپس سوئیچ کنار مورد TUN را روشن کنید. پس از راه اندازی در قسمت Proxy می توانید انتخاب سروری که به آن متصل خواهید شد را تغییر دهید.",
+ "fr": "Select the added profile in the « Profiles » section. In the Dashboard, click the enable button in the lower right corner, and then turn on the switch next to the « TUN » item. After launching, in the « Proxy » section, you can change the choice of the server to which you will be connected.",
+ "ru": "Выберите добавленный профиль в разделе Профили. В Панели управления нажмите кнопку включить в правом нижнем углу, а затем включите переключатель у пункта TUN. После запуска в разделе Прокси вы можете изменить выбор сервера к которому вас подключит.",
+ "zh": "在“配置文件”部分选择已添加的配置文件。在控制面板右下角点击启用按钮,然后打开 TUN 项旁边的开关。启动后,在代理部分可以更改所连接的服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "FlClashX"
+ },
+ {
+ "name": "Koala Clash",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/coolcoala/clash-verge-rev-lite/releases/latest/download/Koala.Clash_x64-setup.exe",
+ "text": {
+ "en": "Windows (Setup)",
+ "fa": "ویندوز (نصب)",
+ "fr": "Windows (Setup)",
+ "ru": "Windows (Установщик)",
+ "zh": "Windows(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Warning",
+ "fa": "هشدار",
+ "fr": "Avertissement",
+ "ru": "Предупреждение",
+ "zh": "警告"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash.",
+ "fa": "اگر قبلاً از Clash Verge Rev استفاده کردهاید، باید قبل از نصب Koala Clash آن را حذف کنید.",
+ "fr": "If you have previously used Clash Verge Rev, you need to uninstall it before installing Koala Clash.",
+ "ru": "Если вы ранее использовали Clash Verge Rev, то его требуется удалить перед установкой Koala Clash.",
+ "zh": "如果您之前用过 Clash Verge Rev,请在安装 Koala Clash 前先卸载它。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "koala-clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l’abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک اضافه نشد",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the Add Profile button, paste the link into the text field, and then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در گوشه بالا سمت راست این صفحه کلیک کنید و لینک را کپی کنید. در برنامه Koala Clash به صفحه اصلی بروید، روی دکمه افزودن پروفایل کلیک کنید، لینک را در فیلد متنی قرار دهید و سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Koala Clash, go to the main page, click the « Add Profile » button, paste the link into the text field, and then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой странице кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите на главную страницу, нажмите кнопку Добавить профиль и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击“获取链接”按钮,复制链接。在 Koala Clash 主页面点击“添加配置文件”按钮,将链接粘贴到文本框中,然后点击“导入”按钮。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "fa": "میتوانید سرور را در پایین صفحه اصلی انتخاب کنید و با کلیک روی دکمه بزرگ در مرکز صفحه اصلی، VPN را فعال کنید.",
+ "fr": "You can select a server at the bottom of the main page, and enable VPN by clicking on the large button in the center of the main page.",
+ "ru": "Выбрать сервер можно внизу на главной странице, включить VPN можно нажав на главной странице на большую кнопку по центру.",
+ "zh": "您可以在主页面底部选择服务器,并通过点击主页面中央的大按钮来启用 VPN。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "KoalaClash"
+ },
+ {
+ "name": "Prizrak-Box",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/windows-amd64.msi",
+ "text": {
+ "en": "Windows (Setup)",
+ "fa": "ویندوز (نصب)",
+ "fr": "Windows (Setup)",
+ "ru": "Windows (Установщик)",
+ "zh": "Windows(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/legiz-ru/Prizrak-Box/releases/latest/download/windows-arm64.msi",
+ "text": {
+ "en": "Windows on ARM (Setup)",
+ "fa": "ویندوز ARM (نصب)",
+ "fr": "Windows on ARM (Setup)",
+ "ru": "Windows на ARM (Установщик)",
+ "zh": "Windows on ARM(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose your architecture (installer preferred for automatic integration) and install or unzip Prizrak-Box.",
+ "fa": "معماری مناسب را انتخاب کنید (نصبکننده ترجیح دارد) و Prizrak-Box را نصب یا از حالت فشرده خارج کنید.",
+ "fr": "Choose your architecture (installer preferred for automatic integration) and install or unzip Prizrak-Box.",
+ "ru": "Выберите архитектуру (предпочтительно установщик) и установите или распакуйте Prizrak-Box.",
+ "zh": "选择适合的架构(建议使用安装程序)并安装或解压 Prizrak-Box。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Warning",
+ "fa": "هشدار",
+ "fr": "Avertissement",
+ "ru": "Предупреждение",
+ "zh": "警告"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Run the program as an administrator.",
+ "fa": "برنامه را به عنوان مدیر اجرا کنید.",
+ "fr": "Run the program as an administrator.",
+ "ru": "Запустите программу от имени администратора.",
+ "zh": "以管理员身份运行程序。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "prizrak-box://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add the subscription automatically.",
+ "fa": "روی دکمه زیر کلیک کنید تا اشتراک به صورت خودکار افزوده شود.",
+ "fr": "Click the button below to add the subscription automatically.",
+ "ru": "Нажмите кнопку ниже, чтобы автоматически добавить подписку.",
+ "zh": "点击下方按钮即可自动添加订阅。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l’abonnement ne s’ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. On this page, click the Get Link button in the upper right corner, copy the link. In Prizrak-Box, go to the Profiles section, click the + button, paste your copied link, and click Confirm.",
+ "fa": "اگر پس از کلیک بر روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در این صفحه، روی دکمه «دریافت پیوند» در گوشه بالا سمت راست کلیک کنید، پیوند را کپی کنید. در Prizrak-Box، به بخش «پروفایلها» بروید، روی دکمه + کلیک کنید، پیوند کپی شده خود را جایگذاری کنید و روی «تأیید» کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. On this page, click the « Get Link » button in the upper right corner, copy the link. In Prizrak-Box, go to the « Profiles » section, click the + button, paste your copied link, and click « Confirm ».",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Prizrak-Box перейдите в раздел Профили, нажмите кнопку +, вставьте вашу скопированную ссылку и нажмите Потдвердить.",
+ "zh": "如果点击按钮后没有任何反应,请手动添加订阅。在此页面上,点击右上角的“获取链接”按钮,复制链接。在 Prizrak-Box 中,转到“配置文件”部分,点击 + 按钮,粘贴您复制的链接,然后点击“确认”。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added subscription in the Profiles section. You can choose the server country in the Proxy (🚀) section. Set the TUN switch to ON.",
+ "fa": "اشتراک افزودهشده را در بخش پروفایلها انتخاب کنید. میتوانید کشور سرور را در بخش Proxy (🚀) انتخاب کنید. سوئیچ TUN را روی حالت روشن قرار دهید.",
+ "fr": "Select the added subscription in the « Profiles » section. You can choose the server country in the « Proxy » (🚀) section. Set the « TUN » switch to ON.",
+ "ru": "Выберите добавленную подписку в разделе Профили. Выбрать страну сервера можно в разделе Прокси (🚀). Установите переключатель TUN в положение ВКЛ.",
+ "zh": "在“配置文件”部分选择已添加的订阅。可在“代理 (🚀)”部分选择服务器国家。将 TUN 开关切换到开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "PrizrakBox"
+ },
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/Happ-proxy/happ-desktop/releases/latest/download/setup-Happ.x64.exe",
+ "text": {
+ "en": "Windows",
+ "fa": "ویندوز",
+ "fr": "Windows",
+ "ru": "Windows",
+ "zh": "Windows"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l’app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below — the app will open and the subscription will be added automatically",
+ "fa": "برای افزودن خودکار اشتراک روی دکمه زیر کلیک کنید - برنامه باز خواهد شد",
+ "fr": "Clique sur le bouton ci‑dessous — l’app s’ouvrira et l’abonnement sera ajouté automatiquement.",
+ "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически.",
+ "zh": "点击下方按钮——应用将会打开,订阅会自动添加。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.",
+ "fa": "در بخش اصلی، دکمه بزرگ روشن/خاموش در مرکز را برای اتصال به VPN کلیک کنید. فراموش نکنید که یک سرور را از لیست سرورها انتخاب کنید. در صورت نیاز، سرور دیگری را از لیست سرورها انتخاب کنید.",
+ "fr": "Dans la section principale, appuie sur le grand bouton central pour te connecter au VPN. N’oublie pas de choisir un serveur dans la liste ; si besoin, choisis‑en un autre.",
+ "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов.",
+ "zh": "在主界面,点击中央的大电源按钮以连接 VPN。不要忘记从服务器列表中选择服务器。如有需要,可选择其它服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "Clash Verge",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_x64-setup.exe",
+ "text": {
+ "en": "Windows (Setup)",
+ "fa": "ویندوز (نصب)",
+ "fr": "Windows (Setup)",
+ "ru": "Windows (Установщик)",
+ "zh": "Windows(安装程序)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.4.5/Clash.Verge_2.4.5_arm64-setup.exe",
+ "text": {
+ "en": "Windows on ARM (Setup)",
+ "fa": "ویندوز ARM (نصب)",
+ "fr": "Windows on ARM (Setup)",
+ "ru": "Windows на ARM (Установщик)",
+ "zh": "Windows on ARM(安装包)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Choose the version for your device, click the button below and install the app.",
+ "fa": "نسخه مناسب برای دستگاه خود را انتخاب کنید، دکمه زیر را فشار دهید و برنامه را نصب کنید",
+ "fr": "Choisis la version pour ton appareil, clique sur le bouton ci‑dessous et installe l'app.",
+ "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение.",
+ "zh": "选择适合您设备的版本,点击下方按钮并安装应用程序。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Change language",
+ "fa": "تغییر زبان",
+ "fr": "Change language",
+ "ru": "Смена языка",
+ "zh": "更改语言"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "fa": "پس از راهاندازی برنامه، میتوانید زبان را در تنظیمات تغییر دهید. در پنل سمت چپ، نماد چرخ دنده را پیدا کنید، سپس به Verge 设置 بروید و 语言设置 را انتخاب کنید.",
+ "fr": "After launching the app, you can change the language in settings. In the left panel, find the gear icon, then navigate to Verge 设置 and select 语言设置.",
+ "ru": "После запуска приложения вы можете сменить язык в настройках. В левой панели найдите иконку шестеренки, далее ориентируйтесь на Verge 设置 и выберите пункт 语言设置.",
+ "zh": "启动应用后,可以在设置中更改语言。在左侧面板找到齿轮图标,进入 Verge 设置,然后选择 语言设置。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "clash://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید",
+ "fr": "Clique sur le bouton ci‑dessous pour ajouter l'abonnement.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку",
+ "zh": "点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Clash Verge, go to the Profiles section and paste the link in the text field, then click the Import button.",
+ "fa": "اگر پس از کلیک روی دکمه اتفاقی نیفتاد، اشتراک را به صورت دستی اضافه کنید. در گوشه بالا سمت راست این صفحه روی دکمه دریافت لینک کلیک کنید، لینک را کپی کنید. در Clash Verge به بخش پروفایلها بروید و لینک را در فیلد متنی وارد کنید، سپس روی دکمه وارد کردن کلیک کنید.",
+ "fr": "If nothing happens after clicking the button, add the subscription manually. Click the « Get Link » button in the top right corner of this page, copy the link. In Clash Verge, go to the « Profiles » section and paste the link in the text field, then click the « Import » button.",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Clash Verge перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт.",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在本页右上角点击获取链接按钮,复制链接。在 Clash Verge 的 Profiles 部分粘贴链接到文本框,然后点击导入按钮。"
+ },
+ "svgIconColor": "red"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.",
+ "fa": "میتوانید در بخش پروکسی سرور را انتخاب کنید و در بخش تنظیمات VPN را فعال کنید. کلید TUN Mode را در حالت روشن قرار دهید.",
+ "fr": "You can select a server in the « Proxy » section, and enable VPN in the Settings section. Set the « TUN » Mode switch to ON.",
+ "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ.",
+ "zh": "您可以在代理部分选择服务器,在设置中启用 VPN。将 TUN 模式开关设置为开启。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "ClashVerge"
+ }
+ ],
+ "svgIconKey": "Windows",
+ "displayName": {
+ "en": "Windows",
+ "fa": "Windows",
+ "fr": "Windows",
+ "ru": "Windows",
+ "zh": "Windows"
+ }
+ },
+ "androidTV": {
+ "apps": [
+ {
+ "name": "Happ",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://play.google.com/store/apps/details?id=com.happproxy",
+ "text": {
+ "en": "Open in Google Play",
+ "fa": "باز کردن در Google Play",
+ "fr": "Ouvre dans Google Play",
+ "ru": "Открыть в Google Play",
+ "zh": "在 Google Play 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.",
+ "fa": "صفحه را در Google Play باز کنید و برنامه را نصب کنید. یا برنامه را مستقیماً از فایل APK نصب کنید، اگر Google Play کار نمی کند.",
+ "fr": "Ouvre la page dans Google Play et installe l’app. Si Google Play ne fonctionne pas, installe‑la directement via l’APK.",
+ "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает.",
+ "zh": "在 Google Play 打开页面并安装应用。如果 Google Play 无法使用,可直接通过 APK 文件安装应用。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Installation instructions",
+ "fa": "دستورالعمل نصب",
+ "fr": "Installation instructions",
+ "ru": "Инструкции по установке",
+ "zh": "安装说明"
+ },
+ "buttons": [
+ {
+ "link": "https://www.happ.su/main/ru/faq/android-tv",
+ "text": {
+ "en": "In Russian",
+ "fa": "به زبان روسی",
+ "fr": "In Russian",
+ "ru": "На русском",
+ "zh": "俄语"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://www.happ.su/main/faq/android-tv",
+ "text": {
+ "en": "In English",
+ "fa": "به زبان انگلیسی",
+ "fr": "In English",
+ "ru": "На английском",
+ "zh": "英语"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Detailed instructions to help you set up Happ on your device.",
+ "fa": "راهنمای دقیق برای کمک به تنظیم Happ روی دستگاه شما.",
+ "fr": "Detailed instructions to help you set up Happ on your device.",
+ "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве.",
+ "zh": "详细说明,帮助您在设备上设置 Happ。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "happ://add/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید، اگر صفحه اشتراک را روی تلویزیون باز کردهاید",
+ "fr": "Click the button below to add subscription, if you opened the subscription page on your TV.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре",
+ "zh": "如果你已在电视上打开订阅页面,点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Open the app and connect to the server",
+ "fa": "برنامه را باز کنید و به سرور متصل شوید",
+ "fr": "Ouvre l’app et connecte‑toi au serveur.",
+ "ru": "Откройте приложение и подключитесь к серверу",
+ "zh": "打开应用并连接到服务器"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "Happ"
+ },
+ {
+ "name": "FlClashX",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-android-arm64-v8a.apk",
+ "text": {
+ "en": "Download APK (ARMv8)",
+ "fa": "دانلود APK (ARMv8)",
+ "fr": "Download APK (ARMv8)",
+ "ru": "Скачать APK (ARMv8)",
+ "zh": "下载 APK (ARMv8)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-android-armeabi-v7a.apk",
+ "text": {
+ "en": "Download APK (ARMv7)",
+ "fa": "دانلود APK (ARMv7)",
+ "fr": "Download APK (ARMv7)",
+ "ru": "Скачать APK (ARMv7)",
+ "zh": "下载 APK (ARMv7)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://github.com/pluralplay/FlClashX/releases/latest/download/FlClashX-android-x86_64.apk",
+ "text": {
+ "en": "Download APK (x86_64)",
+ "fa": "دانلود APK (x86_64)",
+ "fr": "Download APK (x86_64)",
+ "ru": "Скачать APK (x86_64)",
+ "zh": "下载 APK (x86_64)"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Download and install FlClash APK on your TV. Most modern TVs use ARMv8 (64-bit). If installation fails, try ARMv7 (32-bit). x86_64 is for TVs or boxes with Intel or AMD processors (rare).",
+ "fa": "دانلود و نصب FlClash APK روی تلویزیون شما. اکثر تلویزیونهای جدید از ARMv8 (64 بیتی) استفاده میکنند. اگر نصب انجام نشد، ARMv7 (32 بیتی) را امتحان کنید. نسخه x86_64 مخصوص تلویزیونها یا باکسهایی با پردازندههای اینتل یا AMD است (نادر).",
+ "fr": "Download and install FlClash APK on your TV. Most modern TVs use ARMv8 (64-bit). If installation fails, try ARMv7 (32-bit). x86_64 is for TVs or boxes with Intel or AMD processors (rare).",
+ "ru": "Скачайте и установите FlClash APK на ваш телевизор. Большинство современных телевизоров используют ARMv8 (64-бит). Если установка не удалась, попробуйте ARMv7 (32-бит). x86_64 предназначен для ТВ или приставок с процессорами Intel или AMD (редко).",
+ "zh": "在电视上下载并安装 FlClash APK。大多数现代电视使用 ARMv8(64 位)。如果安装失败,请尝试 ARMv7(32 位)。x86_64 适用于带有 Intel 或 AMD 处理器的电视或盒子(较少见)。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "How to install APK on TV",
+ "fa": "نحوه نصب APK روی تلویزیون",
+ "fr": "Comment installer l'APK sur la TV",
+ "ru": "Как установить APK на телевизор",
+ "zh": "如何在电视上安装 APK"
+ },
+ "buttons": [
+ {
+ "link": "https://club.dns-shop.ru/blog/t-132-televizoryi/43999-failyi-apk-dlya-umnyih-televizorov-na-android/?utm_referrer=https%3A%2F%2Fwww.google.com%2F",
+ "text": {
+ "en": "Installation Guide",
+ "fa": "راهنمای نصب",
+ "fr": "Guide d'installation",
+ "ru": "Инструкция по установке",
+ "zh": "安装指南"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Learn how to install APK files on your smart TV",
+ "fa": "نحوه نصب فایلهای APK روی تلویزیون هوشمند خود را یاد بگیرید",
+ "fr": "Apprenez à installer des fichiers APK sur votre smart TV",
+ "ru": "Узнайте, как установить APK-файлы на вашем умном телевизоре",
+ "zh": "了解如何在智能电视上安装 APK 文件"
+ },
+ "svgIconColor": "blue"
+ },
+ {
+ "title": {
+ "en": "How to add a subscription on TV",
+ "fa": "نحوه افزودن اشتراک در تلویزیون",
+ "fr": "Comment ajouter un abonnement sur TV",
+ "ru": "Как добавить подписку на телевизоре",
+ "zh": "如何在电视上添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "In the TV app, click the Add Profile button in the Profiles section, select Add from phone. On your phone, in the Profiles section, tap the three-dot menu and choose Send to TV.",
+ "fa": "در برنامه تلویزیون، روی دکمه افزودن پروفایل در بخش پروفایلها کلیک کنید، گزینه افزودن از تلفن را انتخاب کنید. در تلفن، در بخش پروفایلها روی منوی سه نقطه بزنید و گزینه ارسال به تلویزیون را انتخاب کنید.",
+ "fr": "Dans l'application TV, cliquez sur le bouton « Ajouter un profil » dans la section « Profils », sélectionnez « Ajouter depuis le téléphone ». Sur votre téléphone, dans la section « Profils », appuyez sur le menu à trois points et choisissez « Envoyer vers la TV ».",
+ "ru": "В приложении на телевизоре нажмите кнопку Добавить профиль в разделе Профили, выберите пункт Добавить с телефона. На телефоне в разделе Профили нажмите кнопку с тремя точками и выберите пункт Отправить на ТВ.",
+ "zh": "在电视应用中,在\"配置文件\"部分点击\"添加配置文件\"按钮,选择\"从手机添加\"。在手机的\"配置文件\"部分,点击三点菜单并选择\"发送到电视\"。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "flclashx://install-config?url={{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید، اگر صفحه اشتراک را روی تلویزیون باز کردهاید",
+ "fr": "Cliquez sur le bouton ci-dessous pour ajouter l'abonnement, si vous avez ouvert la page d'abonnement sur votre TV",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре",
+ "zh": "如果你已在电视上打开订阅页面,点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "If the subscription is not added",
+ "fa": "اگر اشتراک در برنامه نصب نشده است",
+ "fr": "Si l'abonnement ne s'ajoute pas",
+ "ru": "Если подписка не добавилась",
+ "zh": "如果未添加订阅"
+ },
+ "buttons": [],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "If nothing happens after clicking the button, add a subscription manually. Click the Get link button on this page in the upper right corner, copy the link. In FlClash, go to the Profiles section, click the + button, select the URL, paste your copied link and click Send",
+ "fa": "اگر بعد از کلیک روی دکمه هیچ اتفاقی نیفتاد، اشتراکی را به صورت دستی اضافه کنید. روی دکمه دریافت لینک در این صفحه در گوشه سمت راست بالا کلیک کنید، لینک را کپی کنید. در FlClash به بخش Profiles بروید، دکمه + را کلیک کنید، URL را انتخاب کنید، پیوند کپی شده خود را جایگذاری کنید و روی ارسال کلیک کنید.",
+ "fr": "Si rien ne se passe après avoir cliqué sur le bouton, ajoutez l'abonnement manuellement. Cliquez sur le bouton « Obtenir le lien » en haut à droite de cette page, copiez le lien. Dans FlClash, allez dans la section « Profils », cliquez sur le bouton +, sélectionnez « URL », collez votre lien copié et cliquez sur Envoyer",
+ "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В FlClash перейдите в раздел Профили, нажмите кнопку +, выберите URL, вставьте вашу скопированную ссылку и нажмите Отправить",
+ "zh": "如果点击按钮后没有反应,请手动添加订阅。在此页面右上角点击\"获取链接\"按钮,复制链接。在 FlClash 的\"配置文件\"部分,点击 + 按钮,选择 URL,粘贴你复制的链接并点击发送。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Select the added profile in the Profiles section. In the Control Panel, click the Enable button in the bottom right corner. Once it's running, you can change the server you're connected to in the Proxy section.",
+ "fa": "پروفایل افزودهشده را در بخش پروفایلها انتخاب کنید. در پنل کنترل، روی دکمه فعالسازی در گوشه پایین سمت راست کلیک کنید. پس از اجرا، میتوانید در بخش پروکسی، سروری را که به آن متصل میشوید تغییر دهید.",
+ "fr": "Sélectionnez le profil ajouté dans la section « Profils ». Dans le panneau de contrôle, cliquez sur le bouton Activer dans le coin inférieur droit. Une fois en cours d'exécution, vous pouvez changer le serveur auquel vous êtes connecté dans la section « Proxy ».",
+ "ru": "Выберите добавленный профиль в разделе Профили. В Панели управления нажмите кнопку включить в правом нижнем углу. После запуска в разделе Прокси вы можете изменить выбор сервера к которому вас подключит.",
+ "zh": "在\"配置文件\"部分选择已添加的配置文件。在控制面板右下角点击启用按钮。运行后,你可以在\"代理\"部分更改所连接的服务器。"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": true,
+ "svgIconKey": "FlClashX"
+ },
+ {
+ "name": "vpn4tv",
+ "blocks": [
+ {
+ "title": {
+ "en": "App Installation",
+ "fa": "نصب برنامه",
+ "fr": "Installation de l'application",
+ "ru": "Установка приложения",
+ "zh": "应用安装"
+ },
+ "buttons": [
+ {
+ "link": "https://play.google.com/store/apps/details?id=com.vpn4tv.hiddify",
+ "text": {
+ "en": "Open in Google Play",
+ "fa": "باز کردن در Google Play",
+ "fr": "Ouvre dans Google Play",
+ "ru": "Открыть в Google Play",
+ "zh": "在 Google Play 打开"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://vpn4tv.com/download/vpn4tv.apk",
+ "text": {
+ "en": "Download APK",
+ "fa": "دانلود APK",
+ "fr": "Télécharge l’APK",
+ "ru": "Скачать APK",
+ "zh": "下载 APK"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "DownloadIcon",
+ "description": {
+ "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.",
+ "fa": "صفحه را در Google Play باز کنید و برنامه را نصب کنید. یا برنامه را مستقیماً از فایل APK نصب کنید، اگر Google Play کار نمی کند.",
+ "fr": "Ouvre la page dans Google Play et installe l’app. Si Google Play ne fonctionne pas, installe‑la directement via l’APK.",
+ "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает.",
+ "zh": "在 Google Play 打开页面并安装应用。如果 Google Play 无法使用,也可以直接通过 APK 文件安装此应用。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Installation instructions",
+ "fa": "دستورالعمل نصب",
+ "fr": "Installation instructions",
+ "ru": "Инструкции по установке",
+ "zh": "安装说明"
+ },
+ "buttons": [
+ {
+ "link": "https://vpn4tv.com/quick-guide.html",
+ "text": {
+ "en": "Quick Guide",
+ "fa": "راهنمای سریع",
+ "fr": "Quick Guide",
+ "ru": "Краткое руководство",
+ "zh": "快速指南"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ },
+ {
+ "link": "https://vpn4tv.com/sber.html",
+ "text": {
+ "en": "Sber Box Guide",
+ "fa": "راهنمای Sber Box",
+ "fr": "Sber Box Guide",
+ "ru": "Инструкция для Sber Box",
+ "zh": "Sber Box 指南"
+ },
+ "type": "external",
+ "svgIconKey": "ExternalLink"
+ }
+ ],
+ "svgIconKey": "Gear",
+ "description": {
+ "en": "Detailed instructions to help you set up VPN4TV on your device.",
+ "fa": "راهنمای دقیق برای کمک به تنظیم VPN4TV روی دستگاه شما.",
+ "fr": "Detailed instructions to help you set up VPN4TV on your device.",
+ "ru": "Подробные инструкции, чтобы помочь вам настроить VPN4TV на вашем устройстве.",
+ "zh": "详细说明,帮助您在设备上设置 VPN4TV。"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавление подписки",
+ "zh": "添加订阅"
+ },
+ "buttons": [
+ {
+ "link": "hiddify://import/{{SUBSCRIPTION_LINK}}",
+ "text": {
+ "en": "Add Subscription",
+ "fa": "اضافه کردن اشتراک",
+ "fr": "Ajouter une souscription",
+ "ru": "Добавить подписку",
+ "zh": "添加订阅"
+ },
+ "type": "subscriptionLink",
+ "svgIconKey": "Plus"
+ }
+ ],
+ "svgIconKey": "CloudDownload",
+ "description": {
+ "en": "Click the button below to add subscription, if you opened the subscription page on your TV",
+ "fa": "برای افزودن اشتراک روی دکمه زیر کلیک کنید، اگر صفحه اشتراک را روی تلویزیون باز کردهاید",
+ "fr": "Click the button below to add subscription, if you opened the subscription page on your TV.",
+ "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре",
+ "zh": "如果你已在电视上打开订阅页面,点击下方按钮以添加订阅"
+ },
+ "svgIconColor": "cyan"
+ },
+ {
+ "title": {
+ "en": "Connect and use",
+ "fa": "متصل شوید و استفاده کنید",
+ "fr": "Se connecter et utiliser",
+ "ru": "Подключение и использование",
+ "zh": "连接并使用"
+ },
+ "buttons": [],
+ "svgIconKey": "Check",
+ "description": {
+ "en": "Open the app and connect to the server",
+ "fa": "برنامه را باز کنید و به سرور متصل شوید",
+ "fr": "Ouvre l’app et connecte‑toi au serveur.",
+ "ru": "Откройте приложение и подключитесь к серверу",
+ "zh": "打开应用并连接到服务器"
+ },
+ "svgIconColor": "teal"
+ }
+ ],
+ "featured": false,
+ "svgIconKey": "VpnForTV"
+ }
+ ],
+ "svgIconKey": "TV",
+ "displayName": {
+ "en": "Android TV",
+ "fa": "Android TV",
+ "fr": "Android TV",
+ "ru": "Android TV",
+ "zh": "Android TV"
+ }
+ }
+ },
+ "svgLibrary": {
+ "TV": "",
+ "Gear": "",
+ "Happ": "\n",
+ "Husi": "\n",
+ "Loon": "\n",
+ "Plus": "",
+ "Star": "",
+ "Xray": "\n",
+ "Check": "",
+ "Stash": "\n",
+ "macOS": "",
+ "Karing": "\n",
+ "Throne": "\n",
+ "Ubuntu": "",
+ "VRayNG": "\n",
+ "Android": "",
+ "ClashMi": "\n",
+ "Exclave": "\n",
+ "FlClash": "\n",
+ "Hiddify": "\n",
+ "OneXray": "\n",
+ "Singbox": "\n",
+ "VTwoBox": "\n",
+ "Windows": "",
+ "vrayTUN": "\n",
+ "FlClashX": "\n",
+ "VpnForTV": "\n",
+ "AppleIcon": "",
+ "ClashMeta": "\n",
+ "Streisand": "\n",
+ "ClashVerge": "\n",
+ "KoalaClash": "",
+ "PandoraBox": "\n",
+ "PrizrakBox": "\n",
+ "RabbitHole": "",
+ "DownloadIcon": "",
+ "ExternalLink": "",
+ "Shadowrocket": "\n",
+ "CloudDownload": "",
+ "ShieldPlus": ""
+ },
+ "baseSettings": {
+ "metaTitle": "Orion Subscription Page",
+ "metaDescription": "Orion Subscription Page",
+ "hideGetLinkButton": false,
+ "showConnectionKeys": true
+ },
+ "baseTranslations": {
+ "name": {
+ "en": "Username",
+ "fa": "نام کاربری",
+ "fr": "Nom d'utilisateur",
+ "ru": "Имя пользователя",
+ "zh": "用户名"
+ },
+ "active": {
+ "en": "Active",
+ "fa": "فعال",
+ "fr": "Actif",
+ "ru": "Активна",
+ "zh": "活跃"
+ },
+ "status": {
+ "en": "Status",
+ "fa": "وضعیت",
+ "fr": "Statut",
+ "ru": "Статус",
+ "zh": "状态"
+ },
+ "expired": {
+ "en": "Expired",
+ "fa": "منقضی شده در",
+ "fr": "Expiré",
+ "ru": "Истекла",
+ "zh": "已于"
+ },
+ "expires": {
+ "en": "Expires",
+ "fa": "منقضی میشود",
+ "fr": "Expire",
+ "ru": "Истекает",
+ "zh": "到期时间"
+ },
+ "getLink": {
+ "en": "Get Link",
+ "fa": "دریافت لینک",
+ "fr": "Obtenir le lien",
+ "ru": "Получение ссылки",
+ "zh": "获取链接"
+ },
+ "unknown": {
+ "en": "Unknown",
+ "fa": "نامعلوم",
+ "fr": "Inconnu",
+ "ru": "Неизвестно",
+ "zh": "未知"
+ },
+ "copyLink": {
+ "en": "Copy link",
+ "fa": "کپی لینک",
+ "fr": "Copier le lien",
+ "ru": "Скопировать ссылку",
+ "zh": "复制链接"
+ },
+ "inactive": {
+ "en": "Inactive",
+ "fa": "غیرفعال",
+ "fr": "Inactif",
+ "ru": "Неактивна",
+ "zh": "未激活"
+ },
+ "bandwidth": {
+ "en": "Bandwidth",
+ "fa": "پهنای باند",
+ "fr": "Bande passante",
+ "ru": "Трафик",
+ "zh": "流量"
+ },
+ "expiresIn": {
+ "en": "Expires",
+ "fa": "منقضی میشود در",
+ "fr": "Expire",
+ "ru": "Истекает",
+ "zh": "后到期"
+ },
+ "linkCopied": {
+ "en": "Likn copied",
+ "fa": "لینک کپی شد",
+ "fr": "Lien copié",
+ "ru": "Ссылка скопирована",
+ "zh": "链接已复制"
+ },
+ "scanQrCode": {
+ "en": "Scan the QR code above in the client",
+ "fa": "کد QR بالا را در کلاینت اسکن کنید",
+ "fr": "Scannez le code QR ci-dessus dans le client",
+ "ru": "Отсканируйте QR-код в приложении",
+ "zh": "在客户端中扫描上方二维码"
+ },
+ "indefinitely": {
+ "en": "Indefinitely",
+ "fa": "هیچوقت",
+ "fr": "Indéfiniment",
+ "ru": "Бессрочно",
+ "zh": "永久"
+ },
+ "scanToImport": {
+ "en": "Scan to import this key",
+ "fa": "برای وارد کردن این کلید اسکن کنید",
+ "fr": "Scannez pour importer cette clé",
+ "ru": "Отсканируйте QR-код для импорта ключа",
+ "zh": "扫描以导入此密钥"
+ },
+ "connectionKeysHeader": {
+ "en": "Connection Keys",
+ "fa": "کلیدهای اتصال",
+ "fr": "Clés de connexion",
+ "ru": "Ключи подключения",
+ "zh": "连接密钥"
+ },
+ "linkCopiedToClipboard": {
+ "en": "Link copied to clipboard",
+ "fa": "لینک به کلیپبورد کپی شد",
+ "fr": "Lien copié dans le presse-papiers",
+ "ru": "Ссылка скопирована в буфер обмена",
+ "zh": "链接已复制到剪贴板"
+ },
+ "scanQrCodeDescription": {
+ "en": "Easily add the subscription to any client. There's another option: copy the link below and paste it into the client",
+ "fa": "افزودن آسان اشتراک به هر کلاینت. گزینه دیگری هم وجود دارد: لینک زیر را کپی کرده و در کلاینت جایگذاری کنید",
+ "fr": "Ajoutez facilement l'abonnement à n'importe quel client. Il y a une autre option : copiez le lien ci-dessous et collez-le dans le client",
+ "ru": "Простое добавление подписки в любой клиент. Есть и другой вариант: скопируйте ссылку ниже и вставьте в клиент.",
+ "zh": "轻松将订阅添加到任何客户端。还有另一种选择:复制下面的链接并粘贴到客户端中"
+ },
+ "installationGuideHeader": {
+ "en": "Installation",
+ "fa": "نصب",
+ "fr": "Installation",
+ "ru": "Установка",
+ "zh": "安装"
+ }
+ },
+ "brandingSettings": {
+ "title": "Orion multiapp",
+ "logoUrl": "https://raw.githubusercontent.com/arpicme/Proxy-App-Icon-set/refs/heads/main/white_background/Prizrak-box.svg",
+ "supportUrl": "https://t.me/legiz_trashbag"
+ }
+}
\ No newline at end of file
diff --git a/backend/config/settings.py b/backend/config/settings.py
index 70589e1..d003055 100644
--- a/backend/config/settings.py
+++ b/backend/config/settings.py
@@ -323,6 +323,26 @@ class Settings(BaseSettings):
WEBAPP_FAVICON_USE_CUSTOM: bool = Field(default=False)
WEBAPP_FAVICON_URL: Optional[str] = Field(default=None)
WEBAPP_LOGO_FAVICON_URL: Optional[str] = Field(default=None)
+ SUBSCRIPTION_GUIDES_ENABLED: bool = Field(
+ default=True,
+ description="Show embedded install instructions inside the subscription Mini App.",
+ )
+ SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED: bool = Field(
+ default=True,
+ description="Use Remnawave Panel Subscription Page config for embedded guides when available.",
+ )
+ SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED: bool = Field(
+ default=False,
+ description="Enable admin JSON override for embedded guides config.",
+ )
+ SUBSCRIPTION_PAGE_CONFIG_PATH: str = Field(
+ default="data/subpage-config/multiapp.json",
+ description="Path to Remnawave Subscription Page v1 JSON config for embedded guides.",
+ )
+ SUBSCRIPTION_PAGE_CONFIG_JSON: str = Field(
+ default="",
+ description="Admin-provided Remnawave Subscription Page v1 JSON config override.",
+ )
WEBAPP_SESSION_SECRET: str = Field(default_factory=lambda: secrets.token_urlsafe(32))
WEBHOOK_SECRET_TOKEN: str = Field(default_factory=lambda: secrets.token_urlsafe(32))
WEBAPP_SESSION_TTL_SECONDS: int = Field(default=24 * 60 * 60)
diff --git a/backend/config/subscription_guides_config.py b/backend/config/subscription_guides_config.py
new file mode 100644
index 0000000..e1a5c7e
--- /dev/null
+++ b/backend/config/subscription_guides_config.py
@@ -0,0 +1,669 @@
+"""Loader and validator for Remnawave Subscription Page v1 configs."""
+
+from __future__ import annotations
+
+import copy
+import hashlib
+import json
+import re
+from pathlib import Path
+from typing import Any, Dict, Iterable, Mapping, Optional, Tuple
+from urllib.parse import urlsplit
+
+
+class SubscriptionGuidesConfigError(ValueError):
+ """Raised when the embedded subscription guides config is invalid."""
+
+
+APP_ROOT = Path(__file__).resolve().parents[2]
+DEFAULT_CONFIG_PATH = "data/subpage-config/multiapp.json"
+DEFAULT_CONFIG_BUNDLED_PATH = (
+ Path(__file__).resolve().parent / "defaults" / "subscription_page_multiapp.json"
+)
+ALLOWED_LOCALES = {
+ "az",
+ "be",
+ "de",
+ "en",
+ "es",
+ "fa",
+ "fr",
+ "hi",
+ "id",
+ "ja",
+ "pl",
+ "pt",
+ "ru",
+ "th",
+ "tk",
+ "tr",
+ "uk",
+ "uz",
+ "vi",
+ "zh",
+}
+ALLOWED_PLATFORMS = {
+ "android",
+ "androidTV",
+ "appleTV",
+ "ios",
+ "linux",
+ "macos",
+ "windows",
+}
+ALLOWED_BUTTON_TYPES = {"copyButton", "external", "subscriptionLink"}
+BASE_TRANSLATION_KEYS = (
+ "active",
+ "bandwidth",
+ "connectionKeysHeader",
+ "copyLink",
+ "expired",
+ "expires",
+ "expiresIn",
+ "getLink",
+ "inactive",
+ "indefinitely",
+ "installationGuideHeader",
+ "linkCopied",
+ "linkCopiedToClipboard",
+ "name",
+ "scanQrCode",
+ "scanQrCodeDescription",
+ "scanToImport",
+ "status",
+ "unknown",
+)
+ALLOWED_SVG_COLORS = {
+ "red",
+ "orange",
+ "amber",
+ "yellow",
+ "lime",
+ "green",
+ "emerald",
+ "teal",
+ "cyan",
+ "sky",
+ "blue",
+ "indigo",
+ "violet",
+ "purple",
+ "fuchsia",
+ "pink",
+ "rose",
+ "slate",
+ "gray",
+ "zinc",
+ "neutral",
+ "stone",
+}
+UI_SUBSCRIPTION_INFO_TYPES = {"cards", "collapsed", "expanded", "hidden"}
+UI_INSTALLATION_GUIDE_TYPES = {"accordion", "cards", "minimal", "timeline"}
+SVG_KEY_RE = re.compile(r"^[A-Za-z]+$")
+HEX_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{3,8}$")
+CONTROL_CHARS_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
+UNSAFE_SVG_RE = re.compile(
+ r"(<\s*/?\s*(?:script|foreignObject|iframe|object|embed|image|use|style|a)\b)"
+ r"|(\son[a-z]+\s*=)"
+ r"|(javascript\s*:)"
+ r"|(data\s*:)",
+ re.IGNORECASE,
+)
+_CONFIG_CACHE: Dict[Tuple[str, str], Dict[str, Any]] = {}
+PANEL_CONFIG_KEYS = (
+ "config",
+ "subscriptionPageConfig",
+ "subpageConfig",
+ "subPageConfig",
+ "pageConfig",
+)
+PANEL_WRAPPER_KEYS = ("response", "data", "result", *PANEL_CONFIG_KEYS)
+
+
+def validate_subscription_guides_config_text(raw: str) -> Dict[str, Any]:
+ """Parse and validate a v1 subscription guides config JSON string."""
+
+ try:
+ payload = json.loads(raw)
+ except json.JSONDecodeError as exc:
+ raise SubscriptionGuidesConfigError(f"Invalid JSON: {exc.msg}") from exc
+ return validate_subscription_guides_config(payload)
+
+
+def default_subscription_guides_config_text() -> str:
+ try:
+ return DEFAULT_CONFIG_BUNDLED_PATH.read_text(encoding="utf-8")
+ except OSError as exc:
+ raise SubscriptionGuidesConfigError(
+ f"Bundled default config is unavailable: {exc}"
+ ) from exc
+
+
+def resolve_subscription_guides_config_path(settings: Any) -> Path:
+ configured_path = str(
+ getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_PATH", DEFAULT_CONFIG_PATH)
+ or DEFAULT_CONFIG_PATH
+ ).strip()
+ if not configured_path:
+ raise SubscriptionGuidesConfigError("SUBSCRIPTION_PAGE_CONFIG_PATH is empty")
+ path = Path(configured_path)
+ if not path.is_absolute():
+ path = APP_ROOT / path
+ return path
+
+
+def ensure_subscription_guides_config_file(settings: Any) -> Path:
+ path = resolve_subscription_guides_config_path(settings)
+ if not path.exists():
+ raise SubscriptionGuidesConfigError(f"Config file does not exist: {path}")
+ return path
+
+
+def subscription_guides_admin_config_json(settings: Any) -> Tuple[str, str]:
+ admin_json = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON", "") or "").strip()
+ if admin_json:
+ return admin_json, "admin_json"
+ return "", "empty"
+
+
+def load_subscription_guides_config(settings: Any) -> Tuple[Dict[str, Any], str]:
+ """Load the enabled guides config from admin JSON or a configured file path."""
+
+ source, raw = _read_config_source(settings)
+ digest = hashlib.sha256(raw.encode("utf-8")).hexdigest()
+ cache_key = (source, digest)
+ cached = _CONFIG_CACHE.get(cache_key)
+ if cached is not None:
+ return copy.deepcopy(cached), source
+ config = validate_subscription_guides_config_text(raw)
+ _CONFIG_CACHE[cache_key] = copy.deepcopy(config)
+ return config, source
+
+
+def subscription_guides_status(settings: Any) -> Dict[str, Any]:
+ """Return a safe status payload for user-facing guide availability checks."""
+
+ if not bool(getattr(settings, "SUBSCRIPTION_GUIDES_ENABLED", False)):
+ return {"enabled": False, "config": None, "source": None, "error": None}
+ try:
+ config, source = load_subscription_guides_config(settings)
+ except SubscriptionGuidesConfigError as exc:
+ return {"enabled": False, "config": None, "source": None, "error": str(exc)}
+ return {"enabled": True, "config": config, "source": source, "error": None}
+
+
+def subscription_guides_available(settings: Any) -> bool:
+ if not bool(getattr(settings, "SUBSCRIPTION_GUIDES_ENABLED", False)):
+ return False
+ admin_json = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON", "") or "").strip()
+ if (
+ not (
+ admin_json
+ and bool(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED", False))
+ )
+ and bool(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED", True))
+ and getattr(settings, "PANEL_API_URL", None)
+ and getattr(settings, "PANEL_API_KEY", None)
+ ):
+ return True
+ status = subscription_guides_status(settings)
+ return bool(status.get("enabled") and status.get("config"))
+
+
+def extract_subscription_guides_config_from_panel(payload: Any) -> Any:
+ """Extract Subscription Page v1 config from flexible Panel API response shapes."""
+
+ return _extract_config_candidate(payload, set())
+
+
+def validate_panel_subscription_guides_config(
+ payload: Any,
+ *,
+ allow_default_when_missing: bool = False,
+) -> Dict[str, Any]:
+ config = extract_subscription_guides_config_from_panel(payload)
+ if config is None:
+ if allow_default_when_missing and panel_subscription_page_allowed(payload):
+ default_text = default_subscription_guides_config_text()
+ return validate_subscription_guides_config_text(default_text)
+ raise SubscriptionGuidesConfigError("Panel response does not contain a v1 config")
+ return validate_subscription_guides_config(config)
+
+
+def panel_subscription_page_allowed(payload: Any) -> bool:
+ candidate = _find_panel_response_object(payload, set())
+ return bool(candidate and candidate.get("webpageAllowed") is True)
+
+
+def validate_subscription_guides_config(payload: Any) -> Dict[str, Any]:
+ if not isinstance(payload, Mapping):
+ raise SubscriptionGuidesConfigError("Config root must be an object")
+ if payload.get("version") != "1":
+ raise SubscriptionGuidesConfigError(
+ "Only Subscription Page config version '1' is supported"
+ )
+
+ locales = _validate_locales(payload.get("locales"))
+ svg_library = _validate_svg_library(payload.get("svgLibrary"))
+ branding = _validate_branding(payload.get("brandingSettings"))
+ ui_config = _validate_ui_config(payload.get("uiConfig"))
+ base_settings = _validate_base_settings(payload.get("baseSettings"))
+ base_translations = _validate_base_translations(payload.get("baseTranslations"), locales)
+ platforms = _validate_platforms(payload.get("platforms"), locales, svg_library)
+
+ return {
+ "version": "1",
+ "locales": locales,
+ "brandingSettings": branding,
+ "uiConfig": ui_config,
+ "baseSettings": base_settings,
+ "baseTranslations": base_translations,
+ "svgLibrary": svg_library,
+ "platforms": platforms,
+ }
+
+
+def _read_config_source(settings: Any) -> Tuple[str, str]:
+ admin_json = str(getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON", "") or "").strip()
+ json_override_enabled = bool(
+ getattr(settings, "SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED", False)
+ )
+ if admin_json and json_override_enabled:
+ return "admin_json", admin_json
+
+ path = ensure_subscription_guides_config_file(settings)
+ try:
+ return "file", path.read_text(encoding="utf-8")
+ except OSError as exc:
+ raise SubscriptionGuidesConfigError(f"Failed to read config file: {exc}") from exc
+
+
+def _extract_config_candidate(value: Any, seen: set[int]) -> Any:
+ if isinstance(value, str):
+ text = value.strip()
+ if not text.startswith(("{", "[")):
+ return None
+ try:
+ return _extract_config_candidate(json.loads(text), seen)
+ except json.JSONDecodeError as exc:
+ raise SubscriptionGuidesConfigError(f"Invalid JSON in Panel config: {exc.msg}") from exc
+
+ if not isinstance(value, Mapping):
+ return None
+
+ value_id = id(value)
+ if value_id in seen:
+ return None
+ seen.add(value_id)
+
+ if _looks_like_v1_config(value):
+ return value
+
+ for key in PANEL_WRAPPER_KEYS:
+ if key not in value:
+ continue
+ candidate = _extract_config_candidate(value.get(key), seen)
+ if candidate is not None:
+ return candidate
+ return None
+
+
+def _looks_like_v1_config(value: Mapping[str, Any]) -> bool:
+ return (
+ value.get("version") == "1"
+ and "locales" in value
+ and "svgLibrary" in value
+ and "platforms" in value
+ )
+
+
+def _find_panel_response_object(value: Any, seen: set[int]) -> Optional[Mapping[str, Any]]:
+ if not isinstance(value, Mapping):
+ return None
+
+ value_id = id(value)
+ if value_id in seen:
+ return None
+ seen.add(value_id)
+
+ if "webpageAllowed" in value:
+ return value
+
+ for key in ("response", "data", "result"):
+ candidate = _find_panel_response_object(value.get(key), seen)
+ if candidate is not None:
+ return candidate
+ return None
+
+
+def _validate_locales(value: Any) -> list[str]:
+ if not isinstance(value, list) or not value:
+ raise SubscriptionGuidesConfigError("locales must be a non-empty array")
+ locales: list[str] = []
+ for index, item in enumerate(value):
+ locale = str(item or "").strip()
+ if locale not in ALLOWED_LOCALES:
+ raise SubscriptionGuidesConfigError(f"Unsupported locale at locales[{index}]: {locale}")
+ if locale not in locales:
+ locales.append(locale)
+ return locales
+
+
+def _validate_branding(value: Any) -> Dict[str, str]:
+ data = _require_object(value, "brandingSettings")
+ result = {
+ "title": _require_text(data, "title", "brandingSettings.title"),
+ "logoUrl": _require_text(data, "logoUrl", "brandingSettings.logoUrl"),
+ "supportUrl": _require_text(data, "supportUrl", "brandingSettings.supportUrl"),
+ }
+ _assert_http_url(result["logoUrl"], "brandingSettings.logoUrl")
+ _assert_http_url(result["supportUrl"], "brandingSettings.supportUrl")
+ return result
+
+
+def _validate_ui_config(value: Any) -> Dict[str, str]:
+ data = _require_object(value, "uiConfig")
+ subscription_info = _require_text(
+ data,
+ "subscriptionInfoBlockType",
+ "uiConfig.subscriptionInfoBlockType",
+ )
+ installation_guides = _require_text(
+ data,
+ "installationGuidesBlockType",
+ "uiConfig.installationGuidesBlockType",
+ )
+ if subscription_info not in UI_SUBSCRIPTION_INFO_TYPES:
+ raise SubscriptionGuidesConfigError(
+ f"Unsupported uiConfig.subscriptionInfoBlockType: {subscription_info}"
+ )
+ if installation_guides not in UI_INSTALLATION_GUIDE_TYPES:
+ raise SubscriptionGuidesConfigError(
+ f"Unsupported uiConfig.installationGuidesBlockType: {installation_guides}"
+ )
+ return {
+ "subscriptionInfoBlockType": subscription_info,
+ "installationGuidesBlockType": installation_guides,
+ }
+
+
+def _validate_base_settings(value: Any) -> Dict[str, Any]:
+ data = value if isinstance(value, Mapping) else {}
+ return {
+ "metaTitle": _optional_text(data, "metaTitle") or "Subscription",
+ "metaDescription": _optional_text(data, "metaDescription") or "Subscription",
+ "showConnectionKeys": bool(data.get("showConnectionKeys", False)),
+ "hideGetLinkButton": bool(data.get("hideGetLinkButton", False)),
+ }
+
+
+def _validate_base_translations(value: Any, locales: Iterable[str]) -> Dict[str, Dict[str, str]]:
+ data = _require_object(value, "baseTranslations")
+ result: Dict[str, Dict[str, str]] = {}
+ for key in BASE_TRANSLATION_KEYS:
+ result[key] = _validate_locale_strings(
+ data.get(key),
+ locales,
+ f"baseTranslations.{key}",
+ )
+ return result
+
+
+def _validate_svg_library(value: Any) -> Dict[str, str]:
+ data = _require_object(value, "svgLibrary")
+ if not data:
+ raise SubscriptionGuidesConfigError("svgLibrary must not be empty")
+ result: Dict[str, str] = {}
+ for key, raw_svg in data.items():
+ svg_key = str(key or "").strip()
+ if not SVG_KEY_RE.fullmatch(svg_key):
+ raise SubscriptionGuidesConfigError(f"Invalid svgLibrary key: {svg_key}")
+ result[svg_key] = _sanitize_svg(raw_svg, f"svgLibrary.{svg_key}")
+ return result
+
+
+def _validate_platforms(
+ value: Any,
+ locales: Iterable[str],
+ svg_library: Mapping[str, str],
+) -> Dict[str, Dict[str, Any]]:
+ data = _require_object(value, "platforms")
+ if not data:
+ raise SubscriptionGuidesConfigError("platforms must not be empty")
+ result: Dict[str, Dict[str, Any]] = {}
+ for platform_key, raw_platform in data.items():
+ key = str(platform_key or "").strip()
+ if key not in ALLOWED_PLATFORMS:
+ raise SubscriptionGuidesConfigError(f"Unsupported platform: {key}")
+ platform = _require_object(raw_platform, f"platforms.{key}")
+ icon_key = _validate_svg_icon_key(
+ platform.get("svgIconKey"),
+ svg_library,
+ f"platforms.{key}.svgIconKey",
+ )
+ apps = _validate_apps(platform.get("apps"), locales, svg_library, f"platforms.{key}.apps")
+ result[key] = {
+ "displayName": _validate_localized_or_text(
+ platform.get("displayName"),
+ locales,
+ f"platforms.{key}.displayName",
+ ),
+ "svgIconKey": icon_key,
+ "apps": apps,
+ }
+ return result
+
+
+def _validate_apps(
+ value: Any,
+ locales: Iterable[str],
+ svg_library: Mapping[str, str],
+ path: str,
+) -> list[Dict[str, Any]]:
+ if not isinstance(value, list) or not value:
+ raise SubscriptionGuidesConfigError(f"{path} must be a non-empty array")
+ apps: list[Dict[str, Any]] = []
+ for index, raw_app in enumerate(value):
+ app_path = f"{path}[{index}]"
+ app = _require_object(raw_app, app_path)
+ name = _require_text(app, "name", f"{app_path}.name")
+ if len(name) < 2:
+ raise SubscriptionGuidesConfigError(f"{app_path}.name must contain at least 2 chars")
+ icon_key = _optional_svg_icon_key(
+ app.get("svgIconKey"),
+ svg_library,
+ f"{app_path}.svgIconKey",
+ )
+ apps.append(
+ {
+ "name": name,
+ "svgIconKey": icon_key,
+ "featured": bool(app.get("featured", False)),
+ "blocks": _validate_blocks(
+ app.get("blocks"),
+ locales,
+ svg_library,
+ f"{app_path}.blocks",
+ ),
+ }
+ )
+ return apps
+
+
+def _validate_blocks(
+ value: Any,
+ locales: Iterable[str],
+ svg_library: Mapping[str, str],
+ path: str,
+) -> list[Dict[str, Any]]:
+ if not isinstance(value, list) or not value:
+ raise SubscriptionGuidesConfigError(f"{path} must be a non-empty array")
+ blocks: list[Dict[str, Any]] = []
+ for index, raw_block in enumerate(value):
+ block_path = f"{path}[{index}]"
+ block = _require_object(raw_block, block_path)
+ color = _optional_text(block, "svgIconColor")
+ if color and color not in ALLOWED_SVG_COLORS and not HEX_COLOR_RE.fullmatch(color):
+ raise SubscriptionGuidesConfigError(f"{block_path}.svgIconColor is invalid")
+ blocks.append(
+ {
+ "svgIconKey": _validate_svg_icon_key(
+ block.get("svgIconKey"),
+ svg_library,
+ f"{block_path}.svgIconKey",
+ ),
+ "svgIconColor": color or "",
+ "title": _validate_locale_strings(
+ block.get("title"),
+ locales,
+ f"{block_path}.title",
+ ),
+ "description": _validate_locale_strings(
+ block.get("description"),
+ locales,
+ f"{block_path}.description",
+ ),
+ "buttons": _validate_buttons(
+ block.get("buttons"),
+ locales,
+ svg_library,
+ f"{block_path}.buttons",
+ ),
+ }
+ )
+ return blocks
+
+
+def _validate_buttons(
+ value: Any,
+ locales: Iterable[str],
+ svg_library: Mapping[str, str],
+ path: str,
+) -> list[Dict[str, Any]]:
+ if value is None:
+ return []
+ if not isinstance(value, list):
+ raise SubscriptionGuidesConfigError(f"{path} must be an array")
+ buttons: list[Dict[str, Any]] = []
+ for index, raw_button in enumerate(value):
+ button_path = f"{path}[{index}]"
+ button = _require_object(raw_button, button_path)
+ button_type = _require_text(button, "type", f"{button_path}.type")
+ if button_type not in ALLOWED_BUTTON_TYPES:
+ raise SubscriptionGuidesConfigError(
+ f"Unsupported button type at {button_path}: {button_type}"
+ )
+ link = _require_text(button, "link", f"{button_path}.link")
+ _validate_button_link(link, button_type, f"{button_path}.link")
+ buttons.append(
+ {
+ "type": button_type,
+ "link": link,
+ "text": _validate_locale_strings(
+ button.get("text"),
+ locales,
+ f"{button_path}.text",
+ ),
+ "svgIconKey": _validate_svg_icon_key(
+ button.get("svgIconKey"),
+ svg_library,
+ f"{button_path}.svgIconKey",
+ ),
+ }
+ )
+ return buttons
+
+
+def _validate_locale_strings(value: Any, locales: Iterable[str], path: str) -> Dict[str, str]:
+ data = _require_object(value, path)
+ result: Dict[str, str] = {}
+ for locale in locales:
+ text = data.get(locale)
+ if not isinstance(text, str) or not text.strip():
+ raise SubscriptionGuidesConfigError(f"{path}.{locale} is required")
+ result[locale] = text.strip()
+ return result
+
+
+def _validate_localized_or_text(
+ value: Any,
+ locales: Iterable[str],
+ path: str,
+) -> str | Dict[str, str]:
+ if isinstance(value, str):
+ text = value.strip()
+ if text:
+ return text
+ return _validate_locale_strings(value, locales, path)
+
+
+def _validate_svg_icon_key(value: Any, svg_library: Mapping[str, str], path: str) -> str:
+ key = _string_value(value)
+ if not key:
+ raise SubscriptionGuidesConfigError(f"{path} is required")
+ if key not in svg_library:
+ raise SubscriptionGuidesConfigError(f"{path} references missing svgLibrary key: {key}")
+ return key
+
+
+def _optional_svg_icon_key(value: Any, svg_library: Mapping[str, str], path: str) -> Optional[str]:
+ key = _string_value(value)
+ if not key:
+ return None
+ if key not in svg_library:
+ raise SubscriptionGuidesConfigError(f"{path} references missing svgLibrary key: {key}")
+ return key
+
+
+def _validate_button_link(link: str, _button_type: str, path: str) -> None:
+ _assert_safe_link(link, path)
+
+
+def _assert_safe_link(value: str, path: str) -> None:
+ if CONTROL_CHARS_RE.search(value):
+ raise SubscriptionGuidesConfigError(f"{path} contains control characters")
+ lower = value.strip().lower()
+ if lower.startswith(("javascript:", "data:", "vbscript:")):
+ raise SubscriptionGuidesConfigError(f"{path} uses an unsafe URL scheme")
+
+
+def _assert_http_url(value: str, path: str) -> None:
+ _assert_safe_link(value, path)
+ parts = urlsplit(value)
+ if parts.scheme not in {"http", "https"} or not parts.netloc:
+ raise SubscriptionGuidesConfigError(f"{path} must be an http(s) URL")
+
+
+def _sanitize_svg(value: Any, path: str) -> str:
+ svg = _string_value(value)
+ if not svg:
+ raise SubscriptionGuidesConfigError(f"{path} is required")
+ trimmed = svg.strip()
+ if not trimmed.lower().startswith("