refactor: rename project to PCAtelegram_web

This commit is contained in:
Андрей Бобырев
2026-06-06 12:40:51 +03:00
parent a01e0ddfba
commit 687d1dbadd
24 changed files with 489 additions and 489 deletions

View File

@@ -1,45 +1,45 @@
# GoTelegram Pro
# PCAtelegram_web
Отдельный проект GoTelegram Pro: MTProxy на базе `telemt`, Telegram-бот, локальная web-admin панель, статистика, backup/restore, шаблоны сайта.
Отдельный проект PCAtelegram_web: MTProxy на базе `telemt`, Telegram-бот, локальная web-admin панель, статистика, backup/restore, шаблоны сайта.
## Установка
На сервере под `root`:
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/gotelegram/main/bootstrap.sh | bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/PCAtelegram_web/main/bootstrap.sh | bash
```
Если репозиторий private, первый `curl` тоже должен получить GitHub token:
```bash
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
https://raw.githubusercontent.com/andrey271192/gotelegram/main/bootstrap.sh | \
https://raw.githubusercontent.com/andrey271192/PCAtelegram_web/main/bootstrap.sh | \
GITHUB_TOKEN="$GITHUB_TOKEN" bash
```
После установки команда доступна как:
```bash
gotelegram
pcatelegram_web
```
## Локальная проверка без GitHub
```bash
rsync -a ./ root@SERVER:/opt/gotelegram/
ssh root@SERVER 'chmod +x /opt/gotelegram/install.sh /opt/gotelegram/install_gotelegram_bot.sh /opt/gotelegram/lib/*.sh && ln -sf /opt/gotelegram/install.sh /usr/local/bin/gotelegram && gotelegram'
rsync -a ./ root@SERVER:/opt/pcatelegram_web/
ssh root@SERVER 'chmod +x /opt/pcatelegram_web/install.sh /opt/pcatelegram_web/install_pcatelegram_web_bot.sh /opt/pcatelegram_web/lib/*.sh && ln -sf /opt/pcatelegram_web/install.sh /usr/local/bin/pcatelegram_web && pcatelegram_web'
```
## Состав
| Путь | Назначение |
| --- | --- |
| `bootstrap.sh` | загружает файлы проекта в `/opt/gotelegram` и запускает меню |
| `install.sh` | основное CLI-меню GoTelegram |
| `install_gotelegram_bot.sh` | отдельная установка Telegram-бота |
| `bootstrap.sh` | загружает файлы проекта в `/opt/pcatelegram_web` и запускает меню |
| `install.sh` | основное CLI-меню PCAtelegram_web |
| `install_pcatelegram_web_bot.sh` | отдельная установка Telegram-бота |
| `lib/` | общие функции, telemt, nginx/site, stats, backup, i18n |
| `gotelegram-bot/` | Python Telegram bot |
| `pcatelegram_web-bot/` | Python Telegram bot |
| `admin-web/` | локальная web-admin панель |
| `templates_catalog.json` | каталог HTML-шаблонов |
@@ -47,17 +47,17 @@ ssh root@SERVER 'chmod +x /opt/gotelegram/install.sh /opt/gotelegram/install_got
| Переменная | По умолчанию | Описание |
| --- | --- | --- |
| `GOTELEGRAM_BASE` | `https://raw.githubusercontent.com/andrey271192/gotelegram/main` | база загрузки файлов |
| `GOTELEGRAM_INSTALL_DIR` | `/opt/gotelegram` | путь установки |
| `PCATELEGRAM_WEB_BASE` | `https://raw.githubusercontent.com/andrey271192/PCAtelegram_web/main` | база загрузки файлов |
| `PCATELEGRAM_WEB_INSTALL_DIR` | `/opt/pcatelegram_web` | путь установки |
| `GITHUB_TOKEN` / `GH_TOKEN` | пусто | token для private GitHub raw downloads |
| `GOTELEGRAM_ADMIN_HOST` | `127.0.0.1` | bind web-admin |
| `GOTELEGRAM_ADMIN_PORT` | `1984` | port web-admin |
| `GOTELEGRAM_ADMIN_USER` | `admin` | Basic Auth login |
| `GOTELEGRAM_ADMIN_PASSWORD` | пусто | Basic Auth password; обязателен при public bind |
| `PCATELEGRAM_WEB_ADMIN_HOST` | `127.0.0.1` | bind web-admin |
| `PCATELEGRAM_WEB_ADMIN_PORT` | `1984` | port web-admin |
| `PCATELEGRAM_WEB_ADMIN_USER` | `admin` | Basic Auth login |
| `PCATELEGRAM_WEB_ADMIN_PASSWORD` | пусто | Basic Auth password; обязателен при public bind |
## Web-admin
После первого запуска `gotelegram` локальная web-admin панель ставится как `gotelegram-admin.service`.
После первого запуска `pcatelegram_web` локальная web-admin панель ставится как `pcatelegram_web-admin.service`.
Безопасный локальный доступ через SSH tunnel:
@@ -70,10 +70,10 @@ open http://127.0.0.1:1984/
```ini
[Service]
Environment=GOTELEGRAM_ADMIN_HOST=0.0.0.0
Environment=GOTELEGRAM_ADMIN_PORT=1984
Environment=GOTELEGRAM_ADMIN_USER=admin
Environment=GOTELEGRAM_ADMIN_PASSWORD=change-me
Environment=PCATELEGRAM_WEB_ADMIN_HOST=0.0.0.0
Environment=PCATELEGRAM_WEB_ADMIN_PORT=1984
Environment=PCATELEGRAM_WEB_ADMIN_USER=admin
Environment=PCATELEGRAM_WEB_ADMIN_PASSWORD=change-me
```
Без HTTPS public Basic Auth гонит пароль открытым текстом. Для постоянного доступа лучше reverse proxy с TLS.
@@ -81,6 +81,6 @@ Environment=GOTELEGRAM_ADMIN_PASSWORD=change-me
## Проверки
```bash
bash -n bootstrap.sh install.sh install_gotelegram_bot.sh lib/*.sh lib/lang/*.sh
bash -n bootstrap.sh install.sh install_pcatelegram_web_bot.sh lib/*.sh lib/lang/*.sh
rg -i "old-brand-name" .
```

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python3
"""
goTelegram Pro local web admin.
PCAtelegram_web local web admin.
The service is intentionally bound to 127.0.0.1:1984. Operators reach it
through an SSH tunnel by default. If exposed through a reverse proxy or public
bind, enable GOTELEGRAM_ADMIN_PASSWORD.
bind, enable PCATELEGRAM_WEB_ADMIN_PASSWORD.
"""
from __future__ import annotations
@@ -32,35 +32,35 @@ from pathlib import Path
from typing import Any
ADMIN_DIR = Path(os.getenv("GOTELEGRAM_ADMIN_DIR", "/opt/gotelegram-admin"))
STATIC_DIR = Path(os.getenv("GOTELEGRAM_ADMIN_STATIC", str(ADMIN_DIR / "static")))
ADMIN_DIR = Path(os.getenv("PCATELEGRAM_WEB_ADMIN_DIR", "/opt/pcatelegram_web-admin"))
STATIC_DIR = Path(os.getenv("PCATELEGRAM_WEB_ADMIN_STATIC", str(ADMIN_DIR / "static")))
GOTELEGRAM_CONFIG = Path(os.getenv("GOTELEGRAM_CONFIG", "/opt/gotelegram/config.json"))
PCATELEGRAM_WEB_CONFIG = Path(os.getenv("PCATELEGRAM_WEB_CONFIG", "/opt/pcatelegram_web/config.json"))
TELEMT_CONFIG = Path(os.getenv("TELEMT_CONFIG", "/etc/telemt/config.toml"))
HISTORY_FILE = Path(os.getenv("GOTELEGRAM_STATS_HISTORY", "/opt/gotelegram/stats_history.csv"))
USER_HISTORY_FILE = Path(os.getenv("GOTELEGRAM_USER_STATS_HISTORY", "/opt/gotelegram/user_stats_history.csv"))
CURRENT_STATS = Path(os.getenv("GOTELEGRAM_STATS_CURRENT", "/run/gotelegram/stats_current.json"))
BACKUP_DIR = Path(os.getenv("GOTELEGRAM_BACKUP_DIR", "/opt/gotelegram/backups"))
INSTALL_DIR = Path(os.getenv("GOTELEGRAM_DIR", "/opt/gotelegram"))
BOT_DIR = Path(os.getenv("GOTELEGRAM_BOT_DIR", "/opt/gotelegram-bot"))
DISABLED_USERS_FILE = Path(os.getenv("GOTELEGRAM_DISABLED_USERS", "/opt/gotelegram/disabled_users.json"))
USER_LOCK_FILE = Path(os.getenv("GOTELEGRAM_USER_LOCK", "/run/gotelegram/admin-users.lock"))
SHARED_443_CONFIG = Path(os.getenv("GOTELEGRAM_SHARED_443", "/opt/gotelegram/shared-443.json"))
BACKUP_SCHEDULE_FILE = Path(os.getenv("GOTELEGRAM_BACKUP_SCHEDULE", "/opt/gotelegram/backup_schedule.json"))
BACKUP_RESTORE_LOG = Path(os.getenv("GOTELEGRAM_BACKUP_RESTORE_LOG", "/var/log/gotelegram-restore.log"))
HISTORY_FILE = Path(os.getenv("PCATELEGRAM_WEB_STATS_HISTORY", "/opt/pcatelegram_web/stats_history.csv"))
USER_HISTORY_FILE = Path(os.getenv("PCATELEGRAM_WEB_USER_STATS_HISTORY", "/opt/pcatelegram_web/user_stats_history.csv"))
CURRENT_STATS = Path(os.getenv("PCATELEGRAM_WEB_STATS_CURRENT", "/run/pcatelegram_web/stats_current.json"))
BACKUP_DIR = Path(os.getenv("PCATELEGRAM_WEB_BACKUP_DIR", "/opt/pcatelegram_web/backups"))
INSTALL_DIR = Path(os.getenv("PCATELEGRAM_WEB_DIR", "/opt/pcatelegram_web"))
BOT_DIR = Path(os.getenv("PCATELEGRAM_WEB_BOT_DIR", "/opt/pcatelegram_web-bot"))
DISABLED_USERS_FILE = Path(os.getenv("PCATELEGRAM_WEB_DISABLED_USERS", "/opt/pcatelegram_web/disabled_users.json"))
USER_LOCK_FILE = Path(os.getenv("PCATELEGRAM_WEB_USER_LOCK", "/run/pcatelegram_web/admin-users.lock"))
SHARED_443_CONFIG = Path(os.getenv("PCATELEGRAM_WEB_SHARED_443", "/opt/pcatelegram_web/shared-443.json"))
BACKUP_SCHEDULE_FILE = Path(os.getenv("PCATELEGRAM_WEB_BACKUP_SCHEDULE", "/opt/pcatelegram_web/backup_schedule.json"))
BACKUP_RESTORE_LOG = Path(os.getenv("PCATELEGRAM_WEB_BACKUP_RESTORE_LOG", "/var/log/pcatelegram_web-restore.log"))
HOST = os.getenv("GOTELEGRAM_ADMIN_HOST", "127.0.0.1")
PORT = int(os.getenv("GOTELEGRAM_ADMIN_PORT", "1984"))
ADMIN_USER = os.getenv("GOTELEGRAM_ADMIN_USER", "admin")
ADMIN_PASSWORD = os.getenv("GOTELEGRAM_ADMIN_PASSWORD", "")
ADMIN_REALM = os.getenv("GOTELEGRAM_ADMIN_REALM", "GoTelegram")
HOST = os.getenv("PCATELEGRAM_WEB_ADMIN_HOST", "127.0.0.1")
PORT = int(os.getenv("PCATELEGRAM_WEB_ADMIN_PORT", "1984"))
ADMIN_USER = os.getenv("PCATELEGRAM_WEB_ADMIN_USER", "admin")
ADMIN_PASSWORD = os.getenv("PCATELEGRAM_WEB_ADMIN_PASSWORD", "")
ADMIN_REALM = os.getenv("PCATELEGRAM_WEB_ADMIN_REALM", "PCAtelegram_web")
VERSION = "2.5.0"
USER_RE = re.compile(r"^[A-Za-z0-9_.-]{1,48}$")
LANG_RE = re.compile(r"^(en|ru)$")
SENSITIVE_CONFIG_KEYS = {"secret"}
BACKUP_NAME_RE = re.compile(r"^[A-Za-z0-9_.-]+\.tar\.gz(\.enc)?$")
MAX_UNIQUE_IP_LIMIT = 1000000
TELEMT_RESTART_DEBOUNCE_SECONDS = float(os.getenv("GOTELEGRAM_TELEMT_RESTART_DEBOUNCE", "8"))
TELEMT_RESTART_DEBOUNCE_SECONDS = float(os.getenv("PCATELEGRAM_WEB_TELEMT_RESTART_DEBOUNCE", "8"))
_LAST_TELEMT_RESTART = 0.0
TRAFFIC_WINDOWS = {
"15m": 15 * 60,
@@ -135,7 +135,7 @@ def save_json(path: Path, data: Any, mode: int = 0o600) -> None:
def read_language(config: dict[str, Any] | None = None) -> str:
config = config or load_json(GOTELEGRAM_CONFIG, {}) or {}
config = config or load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
lang = str(config.get("language") or config.get("lang") or "").strip().lower()
marker = INSTALL_DIR / ".language"
if lang not in {"en", "ru"} and marker.exists():
@@ -154,12 +154,12 @@ def write_language(lang: str) -> dict[str, Any]:
lang = str(lang or "").strip().lower()
if not LANG_RE.match(lang):
raise ValueError("unsupported language")
config = load_json(GOTELEGRAM_CONFIG, {}) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
if not isinstance(config, dict):
config = {}
config["language"] = lang
config["updated_at"] = utc_now()
save_json(GOTELEGRAM_CONFIG, config)
save_json(PCATELEGRAM_WEB_CONFIG, config)
INSTALL_DIR.mkdir(parents=True, exist_ok=True)
(INSTALL_DIR / ".language").write_text(lang + "\n", encoding="utf-8")
bot_env = BOT_DIR / ".env"
@@ -593,7 +593,7 @@ def listener_for_target(target: str) -> dict[str, Any] | None:
def routed_behind_443() -> list[dict[str, Any]]:
config = load_json(GOTELEGRAM_CONFIG, {}) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
mode = str(config.get("mode") or "")
domain = str(config.get("domain") or "")
settings = read_telemt_edge_settings()
@@ -694,7 +694,7 @@ def public_ip() -> str:
def proxy_link(secret: str) -> str:
config = load_json(GOTELEGRAM_CONFIG, {}) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
mode = str(config.get("mode", "lite"))
port = int(config.get("port", 443) or 443)
domain = str(config.get("domain", "") or "")
@@ -721,7 +721,7 @@ def telemt_api(path: str) -> Any:
def site_status(config: dict[str, Any] | None = None) -> dict[str, Any]:
config = config or load_json(GOTELEGRAM_CONFIG, {}) or {}
config = config or load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
host = str(config.get("domain") or "").strip()
if not host:
return {"host": "", "url": "", "http_code": 0, "ok": False, "checked": False, "error": "domain_missing"}
@@ -1002,7 +1002,7 @@ def count_user_history_rows(name: str | None = None) -> int:
def stats_status(current: dict[str, Any] | None = None, history: list[dict[str, int]] | None = None) -> dict[str, Any]:
current = current if current is not None else (load_json(CURRENT_STATS, {}) or {})
history = history if history is not None else load_stats_history(limit=2)
service = service_status("gotelegram-stats")
service = service_status("pcatelegram_web-stats")
now = int(time.time())
ts = int(current.get("ts") or 0) if isinstance(current, dict) else 0
age = max(0, now - ts) if ts else None
@@ -1034,9 +1034,9 @@ def stats_status(current: dict[str, Any] | None = None, history: list[dict[str,
def run_stats_action(action: str) -> tuple[bool, str, dict[str, Any]]:
if action == "repair":
body = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/stats.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/stats.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
"install_stats_collector; "
"stats_collect"
@@ -1044,8 +1044,8 @@ def run_stats_action(action: str) -> tuple[bool, str, dict[str, Any]]:
timeout = 180
else:
body = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/stats.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/stats.sh; "
"stats_init >/dev/null 2>&1 || true; "
"stats_collect"
)
@@ -1100,9 +1100,9 @@ def backup_schedule_status() -> dict[str, Any]:
except ValueError:
frequency = "off"
calendar = None
active_code, active, _ = run(["systemctl", "is-active", "gotelegram-backup.timer"], timeout=5)
enabled_code, enabled, _ = run(["systemctl", "is-enabled", "gotelegram-backup.timer"], timeout=5)
_, next_run, _ = run(["systemctl", "show", "gotelegram-backup.timer", "--property=NextElapseUSecRealtime", "--value"], timeout=5)
active_code, active, _ = run(["systemctl", "is-active", "pcatelegram_web-backup.timer"], timeout=5)
enabled_code, enabled, _ = run(["systemctl", "is-enabled", "pcatelegram_web-backup.timer"], timeout=5)
_, next_run, _ = run(["systemctl", "show", "pcatelegram_web-backup.timer", "--property=NextElapseUSecRealtime", "--value"], timeout=5)
return {
"frequency": frequency,
"calendar": calendar,
@@ -1116,9 +1116,9 @@ def backup_schedule_status() -> dict[str, Any]:
def set_backup_schedule(frequency: str) -> tuple[bool, str, dict[str, Any]]:
backup_schedule_calendar(frequency)
script = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
f"set_backup_schedule {shlex.quote(frequency)}"
)
@@ -1129,11 +1129,11 @@ def set_backup_schedule(frequency: str) -> tuple[bool, str, dict[str, Any]]:
def create_backup() -> tuple[bool, str]:
script = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/telemt.sh; "
"source /opt/gotelegram/lib/website.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/telemt.sh; "
"source /opt/pcatelegram_web/lib/website.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
"create_backup \"\"; "
"cleanup_old_backups 30"
@@ -1166,11 +1166,11 @@ def launch_restore_backup(name: str, password: str = "") -> dict[str, Any]:
quoted_log = shlex.quote(str(BACKUP_RESTORE_LOG))
script = (
"sleep 1; "
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/telemt.sh; "
"source /opt/gotelegram/lib/website.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/telemt.sh; "
"source /opt/pcatelegram_web/lib/website.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
"create_backup \"\" >/dev/null 2>&1 || true; "
f"restore_backup {quoted_path} {quoted_password} yes; "
@@ -1201,7 +1201,7 @@ def user_qr_png(name: str) -> tuple[bytes, str]:
def read_log_payload(service: str) -> dict[str, Any]:
allowed = {"telemt", "nginx", "gotelegram-bot", "gotelegram-stats", "gotelegram-admin"}
allowed = {"telemt", "nginx", "pcatelegram_web-bot", "pcatelegram_web-stats", "pcatelegram_web-admin"}
if service not in allowed:
raise ValueError("unsupported service")
code, stdout, stderr = run(["journalctl", "-u", service, "-n", "180", "--no-pager", "-o", "short-iso"], timeout=10)
@@ -1249,7 +1249,7 @@ def user_payload(
def overview_payload() -> dict[str, Any]:
config = load_json(GOTELEGRAM_CONFIG, {}) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG, {}) or {}
language = read_language(config)
users = read_user_records()
current = load_json(CURRENT_STATS, {}) or {}
@@ -1258,9 +1258,9 @@ def overview_payload() -> dict[str, Any]:
services = {
"telemt": service_status("telemt"),
"nginx": service_status("nginx"),
"bot": service_status("gotelegram-bot"),
"stats": service_status("gotelegram-stats"),
"admin": service_status("gotelegram-admin"),
"bot": service_status("pcatelegram_web-bot"),
"stats": service_status("pcatelegram_web-stats"),
"admin": service_status("pcatelegram_web-admin"),
}
return {
"version": VERSION,
@@ -1282,7 +1282,7 @@ def overview_payload() -> dict[str, Any]:
class AdminHandler(BaseHTTPRequestHandler):
server_version = "goTelegramProAdmin/2.5.0"
server_version = "PCAtelegram_webProAdmin/2.5.0"
def log_message(self, fmt: str, *args: Any) -> None:
print("%s - %s" % (self.address_string(), fmt % args))
@@ -1345,7 +1345,7 @@ class AdminHandler(BaseHTTPRequestHandler):
return json.loads(self.rfile.read(length).decode("utf-8"))
def require_write_guard(self) -> bool:
if self.command in {"POST", "PUT", "PATCH", "DELETE"} and self.headers.get("X-GoTelegram-Admin") != "1":
if self.command in {"POST", "PUT", "PATCH", "DELETE"} and self.headers.get("X-PCAtelegram-Web-Admin") != "1":
self.send_error_json(403, "missing write guard")
return False
return True
@@ -1602,7 +1602,7 @@ class AdminHandler(BaseHTTPRequestHandler):
self.send_json({"ok": True, "data": lang_payload})
elif path.startswith("/api/services/") and path.endswith("/restart"):
service = path[len("/api/services/"):-len("/restart")]
allowed = {"telemt", "nginx", "gotelegram-bot", "gotelegram-stats"}
allowed = {"telemt", "nginx", "pcatelegram_web-bot", "pcatelegram_web-stats"}
if service not in allowed:
self.send_error_json(400, "unsupported service")
return
@@ -1698,7 +1698,7 @@ def main() -> None:
if not STATIC_DIR.exists():
raise SystemExit(f"static dir not found: {STATIC_DIR}")
httpd = ThreadingHTTPServer((HOST, PORT), AdminHandler)
print(f"goTelegram Pro admin listening on http://{HOST}:{PORT}")
print(f"PCAtelegram_web admin listening on http://{HOST}:{PORT}")
httpd.serve_forever()

View File

@@ -86,7 +86,7 @@ const i18n = {
backupScheduleTitle: "Automatic backups",
backupScheduleLoading: "Loading schedule...",
backupIncludesTitle: "Backup contents",
backupIncludesText: "telemt config, goTelegram settings, keys, disabled keys, site, templates, SSL certificates, bot, admin panel and traffic history.",
backupIncludesText: "telemt config, PCAtelegram_web settings, keys, disabled keys, site, templates, SSL certificates, bot, admin panel and traffic history.",
scheduleOff: "Off",
scheduleDaily: "Daily",
scheduleWeekly: "Weekly",
@@ -210,7 +210,7 @@ const i18n = {
ariaTrafficRange: "Traffic range",
ariaTrafficView: "Traffic view",
promoEyebrow: "Promo",
promoTitle: "Support goTelegram Pro",
promoTitle: "Support PCAtelegram_web",
promoHosting1: "Hosting #1",
promoHosting2: "Hosting #2",
promoTips: "Tips",
@@ -313,7 +313,7 @@ const i18n = {
backupScheduleTitle: "Автобекапы",
backupScheduleLoading: "Загрузка расписания...",
backupIncludesTitle: "Что входит в бекап",
backupIncludesText: "конфиг telemt, настройки goTelegram, ключи, отключённые ключи, сайт, шаблоны, SSL-сертификаты, бот, админка и история трафика.",
backupIncludesText: "конфиг telemt, настройки PCAtelegram_web, ключи, отключённые ключи, сайт, шаблоны, SSL-сертификаты, бот, админка и история трафика.",
scheduleOff: "Выкл",
scheduleDaily: "Каждый день",
scheduleWeekly: "Каждую неделю",
@@ -437,7 +437,7 @@ const i18n = {
ariaTrafficRange: "Период трафика",
ariaTrafficView: "Вид трафика",
promoEyebrow: "Промо",
promoTitle: "Поддержать goTelegram Pro",
promoTitle: "Поддержать PCAtelegram_web",
promoHosting1: "Хостинг #1",
promoHosting2: "Хостинг #2",
promoTips: "Чаевые",
@@ -478,7 +478,7 @@ const state = {
qrLink: "",
pendingUsers: new Set(),
refreshingAll: false,
autoRefreshEnabled: localStorage.getItem("gotelegram-auto-refresh") !== "0",
autoRefreshEnabled: localStorage.getItem("pcatelegram_web-auto-refresh") !== "0",
};
const t = (key) => (i18n[state.lang] && i18n[state.lang][key]) || i18n.en[key] || key;
@@ -562,7 +562,7 @@ function syncAutoRefreshTimer() {
function setAutoRefresh(enabled) {
state.autoRefreshEnabled = Boolean(enabled);
localStorage.setItem("gotelegram-auto-refresh", state.autoRefreshEnabled ? "1" : "0");
localStorage.setItem("pcatelegram_web-auto-refresh", state.autoRefreshEnabled ? "1" : "0");
updateAutoRefreshToggle();
syncAutoRefreshTimer();
}
@@ -570,7 +570,7 @@ function setAutoRefresh(enabled) {
async function api(path, options = {}) {
const headers = {
"Accept": "application/json",
"X-GoTelegram-Admin": "1",
"X-PCAtelegram-Web-Admin": "1",
...(options.headers || {}),
};
if (options.body && !headers["Content-Type"]) headers["Content-Type"] = "application/json";
@@ -610,7 +610,7 @@ function applyI18n() {
function setTheme(theme) {
state.theme = theme === "dark" ? "dark" : "light";
document.documentElement.dataset.theme = state.theme;
localStorage.setItem("gotelegram-theme", state.theme);
localStorage.setItem("pcatelegram_web-theme", state.theme);
applyI18n();
if (state.overview) renderStats();
if (state.userTraffic) renderUserTraffic();
@@ -691,9 +691,9 @@ function renderServices(services = {}) {
const items = [
{ key: "telemt", label: "telemt", api: "telemt" },
{ key: "nginx", label: "nginx", api: "nginx" },
{ key: "bot", label: "bot", api: "gotelegram-bot" },
{ key: "stats", label: "stats", api: "gotelegram-stats" },
{ key: "admin", label: "admin", api: "gotelegram-admin" },
{ key: "bot", label: "bot", api: "pcatelegram_web-bot" },
{ key: "stats", label: "stats", api: "pcatelegram_web-stats" },
{ key: "admin", label: "admin", api: "pcatelegram_web-admin" },
];
$("#services").innerHTML = items.map((item) => {
const status = services[item.key] || "unknown";
@@ -1639,7 +1639,7 @@ async function copyText(value) {
}
function maybeShowPromo() {
const key = "gotelegram-promo-last";
const key = "pcatelegram_web-promo-last";
const now = Math.floor(Date.now() / 1000);
const last = Number(localStorage.getItem(key) || 0);
if (now - last < 86400) return;

View File

@@ -3,10 +3,10 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>goTelegram Pro Admin</title>
<title>PCAtelegram_web Admin</title>
<script>
(function () {
var stored = localStorage.getItem("gotelegram-theme");
var stored = localStorage.getItem("pcatelegram_web-theme");
var theme = stored || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
document.documentElement.dataset.theme = theme;
}());
@@ -19,7 +19,7 @@
<div class="brand">
<div class="brand-mark">GT</div>
<div>
<strong>goTelegram Pro</strong>
<strong>PCAtelegram_web</strong>
<span data-i18n="brandSubtitle">Local Admin</span>
</div>
</div>
@@ -66,7 +66,7 @@
<section class="page-panel active" data-page="dashboard">
<section class="visual-overview">
<div>
<p class="eyebrow">goTelegram Pro</p>
<p class="eyebrow">PCAtelegram_web</p>
<h2 id="visualTitle">Port 443</h2>
<p id="visualText">Website, MTProxy and local admin status in one operational view.</p>
</div>
@@ -279,7 +279,7 @@
</div>
<div class="backup-includes">
<strong data-i18n="backupIncludesTitle">Backup contents</strong>
<span data-i18n="backupIncludesText">telemt config, goTelegram settings, keys, disabled keys, site, templates, SSL certificates, bot, admin panel and traffic history.</span>
<span data-i18n="backupIncludesText">telemt config, PCAtelegram_web settings, keys, disabled keys, site, templates, SSL certificates, bot, admin panel and traffic history.</span>
</div>
<div id="backupsList" class="backup-list"></div>
</section>
@@ -307,9 +307,9 @@
<select id="logService">
<option value="telemt">telemt</option>
<option value="nginx">nginx</option>
<option value="gotelegram-bot">bot</option>
<option value="gotelegram-stats">stats</option>
<option value="gotelegram-admin">admin</option>
<option value="pcatelegram_web-bot">bot</option>
<option value="pcatelegram_web-stats">stats</option>
<option value="pcatelegram_web-admin">admin</option>
</select>
<button id="loadLogsBtn" type="button" data-i18n="loadLogs">Load</button>
</div>
@@ -376,7 +376,7 @@
<div class="promo-card" role="dialog" aria-modal="true" aria-labelledby="promoTitle">
<button id="promoClose" class="icon-btn ghost" type="button" aria-label="Close" data-i18n-aria-label="ariaClose">×</button>
<p class="eyebrow" data-i18n="promoEyebrow">Promo</p>
<h2 id="promoTitle" data-i18n="promoTitle">Support goTelegram Pro</h2>
<h2 id="promoTitle" data-i18n="promoTitle">Support PCAtelegram_web</h2>
<div class="promo-grid">
<a href="https://vk.cc/ct29NQ" target="_blank" rel="noreferrer">
<strong data-i18n="promoHosting1">Hosting #1</strong>

26
bootstrap.sh Normal file → Executable file
View File

@@ -1,9 +1,9 @@
#!/usr/bin/env bash
# GoTelegram Pro — bootstrap installer from this repository.
# PCAtelegram_web — bootstrap installer from this repository.
set -euo pipefail
GOTELEGRAM_BASE="${GOTELEGRAM_BASE:-https://raw.githubusercontent.com/andrey271192/gotelegram/main}"
INSTALL_DIR="${GOTELEGRAM_INSTALL_DIR:-/opt/gotelegram}"
PCATELEGRAM_WEB_BASE="${PCATELEGRAM_WEB_BASE:-https://raw.githubusercontent.com/andrey271192/PCAtelegram_web/main}"
INSTALL_DIR="${PCATELEGRAM_WEB_INSTALL_DIR:-/opt/pcatelegram_web}"
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -23,13 +23,13 @@ for cmd in curl jq; do
done
FILES=(
"install.sh" "install_gotelegram_bot.sh" "templates_catalog.json"
"install.sh" "install_pcatelegram_web_bot.sh" "templates_catalog.json"
"lib/common.sh" "lib/telemt.sh" "lib/telemt_config.sh" "lib/backup.sh"
"lib/website.sh" "lib/templates_catalog.sh" "lib/stats.sh" "lib/i18n.sh"
"lib/lang/en.sh" "lib/lang/ru.sh"
"gotelegram-bot/bot.py" "gotelegram-bot/i18n.py"
"gotelegram-bot/lang/en.json" "gotelegram-bot/lang/ru.json"
"gotelegram-bot/config.example.env" "gotelegram-bot/requirements.txt" "gotelegram-bot/README.md"
"pcatelegram_web-bot/bot.py" "pcatelegram_web-bot/i18n.py"
"pcatelegram_web-bot/lang/en.json" "pcatelegram_web-bot/lang/ru.json"
"pcatelegram_web-bot/config.example.env" "pcatelegram_web-bot/requirements.txt" "pcatelegram_web-bot/README.md"
"admin-web/server.py" "admin-web/static/index.html" "admin-web/static/styles.css" "admin-web/static/app.js"
)
@@ -45,7 +45,7 @@ download_file() {
mkdir -p "$(dirname "$local_path")"
for attempt in 1 2 3; do
http_code=$(curl -sL "${curl_headers[@]}" -w "%{http_code}" -o "$local_path" "${GOTELEGRAM_BASE%/}/${remote_path}" 2>/dev/null || echo "000")
http_code=$(curl -sL "${curl_headers[@]}" -w "%{http_code}" -o "$local_path" "${PCATELEGRAM_WEB_BASE%/}/${remote_path}" 2>/dev/null || echo "000")
if [ "$http_code" = "200" ]; then
return 0
fi
@@ -56,8 +56,8 @@ download_file() {
return 1
}
echo -e " ${CYAN}${NC} Загрузка GoTelegram из ${GOTELEGRAM_BASE%/}..."
mkdir -p "${INSTALL_DIR}/lib/lang" "${INSTALL_DIR}/gotelegram-bot/lang" "${INSTALL_DIR}/admin-web/static"
echo -e " ${CYAN}${NC} Загрузка PCAtelegram_web из ${PCATELEGRAM_WEB_BASE%/}..."
mkdir -p "${INSTALL_DIR}/lib/lang" "${INSTALL_DIR}/pcatelegram_web-bot/lang" "${INSTALL_DIR}/admin-web/static"
failed=0
for f in "${FILES[@]}"; do
@@ -69,10 +69,10 @@ for f in "${FILES[@]}"; do
done
[ "$failed" -eq 0 ] || exit 1
chmod +x "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_gotelegram_bot.sh"
chmod +x "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_pcatelegram_web_bot.sh"
chmod +x "${INSTALL_DIR}"/lib/*.sh
chmod +x "${INSTALL_DIR}/admin-web/server.py" 2>/dev/null || true
sed -i 's/\r$//' "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_gotelegram_bot.sh" "${INSTALL_DIR}"/lib/*.sh "${INSTALL_DIR}"/lib/lang/*.sh 2>/dev/null || true
ln -sf "${INSTALL_DIR}/install.sh" /usr/local/bin/gotelegram
sed -i 's/\r$//' "${INSTALL_DIR}/install.sh" "${INSTALL_DIR}/install_pcatelegram_web_bot.sh" "${INSTALL_DIR}"/lib/*.sh "${INSTALL_DIR}"/lib/lang/*.sh 2>/dev/null || true
ln -sf "${INSTALL_DIR}/install.sh" /usr/local/bin/pcatelegram_web
exec bash "${INSTALL_DIR}/install.sh" "$@"

150
install.sh Normal file → Executable file
View File

@@ -1,6 +1,6 @@
#!/bin/bash
# ══════════════════════════════════════════════════════════════════════════════
# goTelegram Pro v2.5.0 — MTProxy powered by telemt (Rust + Tokio)
# PCAtelegram_web v2.5.0 — MTProxy powered by telemt (Rust + Tokio)
# Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance • i18n (EN/RU)
#
# Install:
@@ -46,7 +46,7 @@ show_main_menu() {
# ── Header (no right border — ANSI breaks alignment) ──
echo ""
echo -e " ${BOLD}${CYAN}${line}${NC}"
echo -e " ${BOLD}${WHITE} goTelegram Pro v${GOTELEGRAM_VERSION}${NC} ${DIM}$(t dashboard_title)${NC}"
echo -e " ${BOLD}${WHITE} PCAtelegram_web v${PCATELEGRAM_WEB_VERSION}${NC} ${DIM}$(t dashboard_title)${NC}"
echo -e " ${BOLD}${CYAN}${line}${NC}"
# ── Service health ──
@@ -239,7 +239,7 @@ menu_version() {
echo ""
echo -e " ${BOLD}${WHITE}$(t version_title)${NC}"
echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}"
echo -e " ${WHITE}$(t version_label)${NC} v${GOTELEGRAM_VERSION}"
echo -e " ${WHITE}$(t version_label)${NC} v${PCATELEGRAM_WEB_VERSION}"
echo -e " ${WHITE}$(t version_engine)${NC} telemt (Rust + Tokio)"
echo -e " ${WHITE}$(t version_tech)${NC} Anti-DPI, Fake TLS, TCP Splice"
echo -e " ${WHITE}$(t version_license)${NC} MIT"
@@ -248,21 +248,21 @@ menu_version() {
# ── Upgrade migration ────────────────────────────────────────────────────────
snapshot_preupgrade_state() {
local marker="$GOTELEGRAM_DIR/.preupgrade_${GOTELEGRAM_VERSION}_done"
local marker="$PCATELEGRAM_WEB_DIR/.preupgrade_${PCATELEGRAM_WEB_VERSION}_done"
[ -f "$marker" ] && return 0
mkdir -p "$BACKUP_DIR"
local ts tmp archive
ts=$(date +%Y%m%d_%H%M%S)
tmp="/tmp/gotelegram_preupgrade_${ts}"
archive="$BACKUP_DIR/preupgrade_${GOTELEGRAM_VERSION}_${ts}.tar.gz"
tmp="/tmp/pcatelegram_web_preupgrade_${ts}"
archive="$BACKUP_DIR/preupgrade_${PCATELEGRAM_WEB_VERSION}_${ts}.tar.gz"
mkdir -p "$tmp"
[ -f "$GOTELEGRAM_CONFIG" ] && mkdir -p "$tmp/opt/gotelegram" && cp "$GOTELEGRAM_CONFIG" "$tmp/opt/gotelegram/config.json" 2>/dev/null
[ -f "$PCATELEGRAM_WEB_CONFIG" ] && mkdir -p "$tmp/opt/pcatelegram_web" && cp "$PCATELEGRAM_WEB_CONFIG" "$tmp/opt/pcatelegram_web/config.json" 2>/dev/null
[ -f "$TELEMT_CONFIG" ] && mkdir -p "$tmp/etc/telemt" && cp "$TELEMT_CONFIG" "$tmp/etc/telemt/config.toml" 2>/dev/null
[ -f "$NGINX_SITE_CONF" ] && mkdir -p "$tmp/etc/nginx/sites-available" && cp "$NGINX_SITE_CONF" "$tmp/etc/nginx/sites-available/gotelegram" 2>/dev/null
[ -d "$WEBSITE_ROOT" ] && mkdir -p "$tmp/var/www/gotelegram-site" && cp -a "$WEBSITE_ROOT/." "$tmp/var/www/gotelegram-site/" 2>/dev/null
[ -f "$BOT_DIR/.env" ] && mkdir -p "$tmp/opt/gotelegram-bot" && cp "$BOT_DIR/.env" "$tmp/opt/gotelegram-bot/.env" 2>/dev/null
[ -f "$NGINX_SITE_CONF" ] && mkdir -p "$tmp/etc/nginx/sites-available" && cp "$NGINX_SITE_CONF" "$tmp/etc/nginx/sites-available/pcatelegram_web" 2>/dev/null
[ -d "$WEBSITE_ROOT" ] && mkdir -p "$tmp/var/www/pcatelegram_web-site" && cp -a "$WEBSITE_ROOT/." "$tmp/var/www/pcatelegram_web-site/" 2>/dev/null
[ -f "$BOT_DIR/.env" ] && mkdir -p "$tmp/opt/pcatelegram_web-bot" && cp "$BOT_DIR/.env" "$tmp/opt/pcatelegram_web-bot/.env" 2>/dev/null
if tar czf "$archive" -C "$tmp" . 2>/dev/null; then
log_dim "Pre-upgrade snapshot: $archive"
@@ -278,8 +278,8 @@ read_config_or_default() {
detect_deployed_template_id() {
local tpl=""
if [ -f "$WEBSITE_ROOT/.gotelegram_template_id" ]; then
tpl=$(head -1 "$WEBSITE_ROOT/.gotelegram_template_id" 2>/dev/null || echo "")
if [ -f "$WEBSITE_ROOT/.pcatelegram_web_template_id" ]; then
tpl=$(head -1 "$WEBSITE_ROOT/.pcatelegram_web_template_id" 2>/dev/null || echo "")
[ -n "$tpl" ] && { echo "$tpl"; return 0; }
fi
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then
@@ -293,15 +293,15 @@ detect_deployed_template_id() {
detect_template_source() {
local src
if [ -f "$WEBSITE_ROOT/.gotelegram_template_source" ]; then
src=$(head -1 "$WEBSITE_ROOT/.gotelegram_template_source" 2>/dev/null || echo "")
if [ -f "$WEBSITE_ROOT/.pcatelegram_web_template_source" ]; then
src=$(head -1 "$WEBSITE_ROOT/.pcatelegram_web_template_source" 2>/dev/null || echo "")
[ -n "$src" ] && { echo "$src"; return 0; }
fi
[ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ] && return 0
read_config_or_default template_source ""
}
write_normalized_gotelegram_config() {
write_normalized_pcatelegram_web_config() {
local mode="$1" port="$2" secret="$3" mask_host="$4" domain="$5" tpl_id="$6" tpl_source="$7"
local lang installed_at stats_enabled tmp
lang=$(read_config_or_default language "$(get_language 2>/dev/null || echo en)")
@@ -310,7 +310,7 @@ write_normalized_gotelegram_config() {
tmp=$(mktemp) || return 1
jq -n \
--arg version "$GOTELEGRAM_VERSION" \
--arg version "$PCATELEGRAM_WEB_VERSION" \
--arg engine "telemt" \
--arg mode "$mode" \
--argjson port "$port" \
@@ -340,22 +340,22 @@ write_normalized_gotelegram_config() {
+ (if $stats_enabled == "true" then {stats_enabled: true} elif $stats_enabled == "false" then {stats_enabled: false} else {} end)' \
> "$tmp" || { rm -f "$tmp"; return 1; }
mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")"
mv "$tmp" "$GOTELEGRAM_CONFIG"
chmod 600 "$GOTELEGRAM_CONFIG"
mkdir -p "$(dirname "$PCATELEGRAM_WEB_CONFIG")"
mv "$tmp" "$PCATELEGRAM_WEB_CONFIG"
chmod 600 "$PCATELEGRAM_WEB_CONFIG"
}
auto_migrate_legacy_state() {
local marker="$GOTELEGRAM_DIR/.migrated_${GOTELEGRAM_VERSION}"
local marker="$PCATELEGRAM_WEB_DIR/.migrated_${PCATELEGRAM_WEB_VERSION}"
local current_version
current_version=$(read_config_or_default version "")
if [ -f "$marker" ] && [ "$current_version" = "$GOTELEGRAM_VERSION" ]; then
if [ -f "$marker" ] && [ "$current_version" = "$PCATELEGRAM_WEB_VERSION" ]; then
return 0
fi
[ -f "$TELEMT_CONFIG" ] || [ -f "$GOTELEGRAM_CONFIG" ] || [ -d "$WEBSITE_ROOT" ] || return 0
[ -f "$TELEMT_CONFIG" ] || [ -f "$PCATELEGRAM_WEB_CONFIG" ] || [ -d "$WEBSITE_ROOT" ] || return 0
log_step "Миграция состояния goTelegram Pro"
log_step "Миграция состояния PCAtelegram_web"
snapshot_preupgrade_state
local mode port secret mask_host domain mask_port tpl_id tpl_source users_block tls_emulation changed=0 users_block_needs_write=0
@@ -405,14 +405,14 @@ auto_migrate_legacy_state() {
tpl_id=$(detect_deployed_template_id)
tpl_source=$(detect_template_source || echo "")
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ] && [ -n "$tpl_id" ]; then
echo "$tpl_id" > "$WEBSITE_ROOT/.gotelegram_template_id" 2>/dev/null || true
[ -n "$tpl_source" ] && echo "$tpl_source" > "$WEBSITE_ROOT/.gotelegram_template_source" 2>/dev/null || true
echo "$tpl_id" > "$WEBSITE_ROOT/.pcatelegram_web_template_id" 2>/dev/null || true
[ -n "$tpl_source" ] && echo "$tpl_source" > "$WEBSITE_ROOT/.pcatelegram_web_template_source" 2>/dev/null || true
fi
if [ -f "$TELEMT_CONFIG" ]; then
if ! grep -q '\[server.api\]' "$TELEMT_CONFIG" 2>/dev/null || \
! grep -q 'metrics_listen' "$TELEMT_CONFIG" 2>/dev/null || \
! grep -q "goTelegram Pro v${GOTELEGRAM_VERSION}" "$TELEMT_CONFIG" 2>/dev/null; then
! grep -q "PCAtelegram_web v${PCATELEGRAM_WEB_VERSION}" "$TELEMT_CONFIG" 2>/dev/null; then
generate_telemt_toml "$secret" "$port" "$mode" "$mask_host" "$mask_port" "$TELEMT_CONFIG" >&2
replace_telemt_users_block "$users_block" "$TELEMT_CONFIG"
changed=1
@@ -423,7 +423,7 @@ auto_migrate_legacy_state() {
fi
fi
write_normalized_gotelegram_config "$mode" "$port" "$secret" "$mask_host" "$domain" "$tpl_id" "$tpl_source" || \
write_normalized_pcatelegram_web_config "$mode" "$port" "$secret" "$mask_host" "$domain" "$tpl_id" "$tpl_source" || \
log_warning "Не удалось нормализовать config.json"
if [ "$changed" = "1" ] && systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
@@ -520,15 +520,15 @@ install_lite_mode() {
# Start
start_telemt || return
# Save goTelegram Pro config
save_gotelegram_config "telemt" "lite" "$port" "$secret" "$domain" "" ""
# Save PCAtelegram_web config
save_pcatelegram_web_config "telemt" "lite" "$port" "$secret" "$domain" "" ""
# Credits
show_credits
# Result
show_proxy_info
log_success "$(tf install_done "$GOTELEGRAM_VERSION" "Lite")"
log_success "$(tf install_done "$PCATELEGRAM_WEB_VERSION" "Lite")"
}
# ── Pro mode ────────────────────────────────────────────────────────────────
@@ -620,12 +620,12 @@ install_pro_mode() {
# Save config
local tpl_id
tpl_id=$(basename "$template_dir")
save_gotelegram_config "telemt" "pro" "443" "$raw_secret" "$user_domain" "$user_domain" "$tpl_id"
save_pcatelegram_web_config "telemt" "pro" "443" "$raw_secret" "$user_domain" "$user_domain" "$tpl_id"
# Result — use domain and fake-TLS link
show_proxy_info_pro "$user_domain" "$faketls_secret"
echo -e " ${WHITE}$(t svc_site):${NC} ${GREEN}https://${user_domain}${NC}"
log_success "$(tf install_done "$GOTELEGRAM_VERSION" "Pro")"
log_success "$(tf install_done "$PCATELEGRAM_WEB_VERSION" "Pro")"
}
# ── Статус ───────────────────────────────────────────────────────────────────
@@ -699,7 +699,7 @@ menu_share() {
echo ""
echo -e " ${BOLD}$(t share_title)${NC}"
echo ""
printf "$(t share_line1)\n" "$GOTELEGRAM_VERSION"
printf "$(t share_line1)\n" "$PCATELEGRAM_WEB_VERSION"
echo ""
printf "$(t share_server)\n" "$server_display"
printf "$(t share_port)\n" "$port"
@@ -842,7 +842,7 @@ menu_remove() {
if [ "$mode" = "pro" ]; then
remove_pro_mode
fi
rm -f "$GOTELEGRAM_CONFIG"
rm -f "$PCATELEGRAM_WEB_CONFIG"
log_success "$(t remove_proxy_done)"
;;
2)
@@ -861,7 +861,7 @@ menu_remove() {
if [ "$mode" = "pro" ]; then
remove_pro_mode
fi
rm -f "$GOTELEGRAM_CONFIG"
rm -f "$PCATELEGRAM_WEB_CONFIG"
# Bot
if [ "$(bot_service_status)" != "not_installed" ]; then
systemctl stop "$BOT_SERVICE" 2>/dev/null
@@ -877,8 +877,8 @@ menu_remove() {
}
# ── Telegram-бот ────────────────────────────────────────────────────────────
BOT_DIR="/opt/gotelegram-bot"
BOT_SERVICE="gotelegram-bot"
BOT_DIR="/opt/pcatelegram_web-bot"
BOT_SERVICE="pcatelegram_web-bot"
admin_web_service_status() {
if ! systemctl list-unit-files "$ADMIN_WEB_SERVICE.service" &>/dev/null 2>&1; then
@@ -906,7 +906,7 @@ install_admin_web() {
python_bin=$(command -v python3)
cat > "/etc/systemd/system/${ADMIN_WEB_SERVICE}.service" << SVCEOF
[Unit]
Description=goTelegram Pro v${GOTELEGRAM_VERSION} Local Web Admin
Description=PCAtelegram_web v${PCATELEGRAM_WEB_VERSION} Local Web Admin
After=network.target
[Service]
@@ -915,8 +915,8 @@ WorkingDirectory=$ADMIN_WEB_DIR
ExecStart=$python_bin $ADMIN_WEB_DIR/server.py
Restart=always
RestartSec=5
Environment=GOTELEGRAM_ADMIN_HOST=$ADMIN_WEB_HOST
Environment=GOTELEGRAM_ADMIN_PORT=$ADMIN_WEB_PORT
Environment=PCATELEGRAM_WEB_ADMIN_HOST=$ADMIN_WEB_HOST
Environment=PCATELEGRAM_WEB_ADMIN_PORT=$ADMIN_WEB_PORT
[Install]
WantedBy=multi-user.target
@@ -964,20 +964,20 @@ bot_service_status() {
}
auto_update_bot_if_possible() {
[ -d "$SCRIPT_DIR/gotelegram-bot" ] || return 0
[ -d "$SCRIPT_DIR/pcatelegram_web-bot" ] || return 0
[ "$(bot_service_status)" = "not_installed" ] && return 0
[ -f "$BOT_DIR/.env" ] || return 0
local needs_update=0
[ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ] && \
! cmp -s "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/bot.py" && needs_update=1
[ -f "$SCRIPT_DIR/gotelegram-bot/i18n.py" ] && \
! cmp -s "$SCRIPT_DIR/gotelegram-bot/i18n.py" "$BOT_DIR/i18n.py" && needs_update=1
[ -f "$SCRIPT_DIR/gotelegram-bot/requirements.txt" ] && \
! cmp -s "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/requirements.txt" && needs_update=1
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" ] && \
! cmp -s "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" "$BOT_DIR/bot.py" && needs_update=1
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/i18n.py" ] && \
! cmp -s "$SCRIPT_DIR/pcatelegram_web-bot/i18n.py" "$BOT_DIR/i18n.py" && needs_update=1
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/requirements.txt" ] && \
! cmp -s "$SCRIPT_DIR/pcatelegram_web-bot/requirements.txt" "$BOT_DIR/requirements.txt" && needs_update=1
local lang_file lang_name
for lang_file in "$SCRIPT_DIR"/gotelegram-bot/lang/*.json; do
for lang_file in "$SCRIPT_DIR"/pcatelegram_web-bot/lang/*.json; do
[ -e "$lang_file" ] || continue
lang_name=$(basename "$lang_file")
! cmp -s "$lang_file" "$BOT_DIR/lang/$lang_name" && needs_update=1
@@ -1101,27 +1101,27 @@ bot_install() {
# Copy bot files
mkdir -p "$BOT_DIR"
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/"
cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && \
cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/"
if [ -f "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" ]; then
cp "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" "$BOT_DIR/"
cp "$SCRIPT_DIR/pcatelegram_web-bot/requirements.txt" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/config.example.env" ] && \
cp "$SCRIPT_DIR/pcatelegram_web-bot/config.example.env" "$BOT_DIR/"
# Copy i18n language files for bot
if [ -d "$SCRIPT_DIR/gotelegram-bot/lang" ]; then
if [ -d "$SCRIPT_DIR/pcatelegram_web-bot/lang" ]; then
mkdir -p "$BOT_DIR/lang"
cp -f "$SCRIPT_DIR/gotelegram-bot/lang/"*.json "$BOT_DIR/lang/" 2>/dev/null
cp -f "$SCRIPT_DIR/pcatelegram_web-bot/lang/"*.json "$BOT_DIR/lang/" 2>/dev/null
fi
[ -f "$SCRIPT_DIR/gotelegram-bot/i18n.py" ] && \
cp "$SCRIPT_DIR/gotelegram-bot/i18n.py" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/i18n.py" ] && \
cp "$SCRIPT_DIR/pcatelegram_web-bot/i18n.py" "$BOT_DIR/"
else
log_error "$(tf bot_files_not_found "$SCRIPT_DIR/gotelegram-bot/")"
log_error "$(tf bot_files_not_found "$SCRIPT_DIR/pcatelegram_web-bot/")"
return 1
fi
# Templates catalog — skip if source and dest are the same file (symlink install case)
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
local src_tc="$SCRIPT_DIR/templates_catalog.json"
local dst_tc="$GOTELEGRAM_DIR/templates_catalog.json"
local dst_tc="$PCATELEGRAM_WEB_DIR/templates_catalog.json"
if [ "$(readlink -f "$src_tc" 2>/dev/null)" != "$(readlink -f "$dst_tc" 2>/dev/null)" ]; then
cp "$src_tc" "$dst_tc"
fi
@@ -1217,7 +1217,7 @@ bot_install() {
# Systemd
cat > "/etc/systemd/system/${BOT_SERVICE}.service" << SVCEOF
[Unit]
Description=goTelegram Pro v${GOTELEGRAM_VERSION} Telegram Bot
Description=PCAtelegram_web v${PCATELEGRAM_WEB_VERSION} Telegram Bot
After=network.target
[Service]
@@ -1407,7 +1407,7 @@ bot_remove() {
_promo_block() {
# Print a promo section without width-fragile box borders (i18n safe)
local line2; line2=$(printf '─%.0s' {1..54})
local youtube_link="${GOTELEGRAM_YOUTUBE_LINK:-}"
local youtube_link="${PCATELEGRAM_WEB_YOUTUBE_LINK:-}"
echo ""
echo -e " ${DIM}${line2}${NC}"
echo -e " ${BOLD}${YELLOW}$(t promo_host1_title)${NC}"
@@ -1437,7 +1437,7 @@ menu_promo() {
# ── Проверка: показывать ли промо (раз в сутки) ────────────────────────────
should_show_promo() {
local stamp_file="$GOTELEGRAM_DIR/.promo_last_shown"
local stamp_file="$PCATELEGRAM_WEB_DIR/.promo_last_shown"
if [ ! -f "$stamp_file" ]; then
return 0 # никогда не показывали
fi
@@ -1452,8 +1452,8 @@ should_show_promo() {
}
mark_promo_shown() {
mkdir -p "$GOTELEGRAM_DIR"
date +%s > "$GOTELEGRAM_DIR/.promo_last_shown"
mkdir -p "$PCATELEGRAM_WEB_DIR"
date +%s > "$PCATELEGRAM_WEB_DIR/.promo_last_shown"
}
_promo_qr() {
@@ -1473,7 +1473,7 @@ show_promo_with_qr() {
_promo_qr "$(t promo_qr_host1)" "https://vk.cc/ct29NQ"
_promo_qr "$(t promo_qr_host2)" "https://vk.cc/cUxAhj"
_promo_qr "$(t promo_qr_tips)" "https://pay.cloudtips.ru/p/7410814f"
_promo_qr "$(t promo_qr_youtube)" "${GOTELEGRAM_YOUTUBE_LINK:-}"
_promo_qr "$(t promo_qr_youtube)" "${PCATELEGRAM_WEB_YOUTUBE_LINK:-}"
fi
mark_promo_shown
@@ -1489,10 +1489,10 @@ show_promo_with_qr() {
# ── First-run: pick language ─────────────────────────────────────────────────
first_run_language_picker() {
# Show picker only if language not yet saved
local marker="${GOTELEGRAM_DIR:-/opt/gotelegram}/.language"
local marker="${PCATELEGRAM_WEB_DIR:-/opt/pcatelegram_web}/.language"
local cfg_lang=""
if [ -f "$GOTELEGRAM_CONFIG" ] && command -v jq >/dev/null 2>&1; then
cfg_lang=$(jq -r '.language // empty' "$GOTELEGRAM_CONFIG" 2>/dev/null)
if [ -f "$PCATELEGRAM_WEB_CONFIG" ] && command -v jq >/dev/null 2>&1; then
cfg_lang=$(jq -r '.language // empty' "$PCATELEGRAM_WEB_CONFIG" 2>/dev/null)
fi
if [ -f "$marker" ] || [ -n "$cfg_lang" ]; then
return 0
@@ -1524,8 +1524,8 @@ menu_language() {
# Non-interactive action dispatcher (bot / CI / scripting interface)
# ══════════════════════════════════════════════════════════════════════════════
# Usage examples:
# gotelegram --action=change-template --template=th_ariclaw --json
# gotelegram --action=change-lite-domain --domain=google.com --json
# pcatelegram_web --action=change-template --template=th_ariclaw --json
# pcatelegram_web --action=change-lite-domain --domain=google.com --json
#
# Rules for action handlers:
# - Only JSON may be written to stdout (the caller parses it).
@@ -1558,7 +1558,7 @@ bot_emit_json() {
bot_update_config_field() {
local key="$1"
local value="$2"
if [ ! -f "$GOTELEGRAM_CONFIG" ]; then
if [ ! -f "$PCATELEGRAM_WEB_CONFIG" ]; then
return 1
fi
local tmp now
@@ -1566,9 +1566,9 @@ bot_update_config_field() {
now=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S%z)
if jq --arg k "$key" --arg v "$value" --arg t "$now" \
'.[$k] = $v | .updated_at = $t' \
"$GOTELEGRAM_CONFIG" > "$tmp" 2>/dev/null; then
mv "$tmp" "$GOTELEGRAM_CONFIG"
chmod 600 "$GOTELEGRAM_CONFIG"
"$PCATELEGRAM_WEB_CONFIG" > "$tmp" 2>/dev/null; then
mv "$tmp" "$PCATELEGRAM_WEB_CONFIG"
chmod 600 "$PCATELEGRAM_WEB_CONFIG"
return 0
fi
rm -f "$tmp"
@@ -1713,7 +1713,7 @@ bot_action_change_lite_domain() {
# parallel `change-lite-domain` calls raced on the jq-rewrite of config.json
# and one process would see a truncated file ("no secret in config").
bot_action_dispatch() {
local lock_file="/var/lock/gotelegram-bot-action.lock"
local lock_file="/var/lock/pcatelegram_web-bot-action.lock"
# Make sure /var/lock exists (it does on Debian/Ubuntu; be defensive for minimal images)
[ -d /var/lock ] || mkdir -p /var/lock 2>/dev/null || true

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.5.0 — Установка Telegram-бота
# PCAtelegram_web v2.5.0 — Установка Telegram-бота
# Создаёт venv, ставит зависимости, настраивает systemd
set -e
@@ -9,11 +9,11 @@ YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
BOT_DIR="/opt/gotelegram-bot"
SERVICE_NAME="gotelegram-bot"
GOTELEGRAM_DIR="/opt/gotelegram"
ADMIN_WEB_DIR="/opt/gotelegram-admin"
ADMIN_WEB_SERVICE="gotelegram-admin"
BOT_DIR="/opt/pcatelegram_web-bot"
SERVICE_NAME="pcatelegram_web-bot"
PCATELEGRAM_WEB_DIR="/opt/pcatelegram_web"
ADMIN_WEB_DIR="/opt/pcatelegram_web-admin"
ADMIN_WEB_SERVICE="pcatelegram_web-admin"
ADMIN_WEB_PORT="1984"
if [ "$EUID" -ne 0 ]; then
@@ -22,7 +22,7 @@ if [ "$EUID" -ne 0 ]; then
fi
echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ${GREEN}GoTelegram v2.5.0 — Установка бота${NC} ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${GREEN}PCAtelegram_web v2.5.0 — Установка бота${NC} ${CYAN}${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}"
echo ""
@@ -44,20 +44,20 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -f "$SCRIPT_DIR/lib/common.sh" ] && source "$SCRIPT_DIR/lib/common.sh" || true
[ -f "$SCRIPT_DIR/lib/stats.sh" ] && source "$SCRIPT_DIR/lib/stats.sh" || true
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
if [ -f "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" ]; then
echo -e "${GREEN}[*] Копирование файлов бота...${NC}"
cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/"
cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/"
cp "$SCRIPT_DIR/pcatelegram_web-bot/bot.py" "$BOT_DIR/"
cp "$SCRIPT_DIR/pcatelegram_web-bot/requirements.txt" "$BOT_DIR/"
[ -f "$SCRIPT_DIR/pcatelegram_web-bot/config.example.env" ] && cp "$SCRIPT_DIR/pcatelegram_web-bot/config.example.env" "$BOT_DIR/"
else
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}"
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/pcatelegram_web-bot/${NC}"
exit 1
fi
# Копируем каталог шаблонов
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/"
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$SCRIPT_DIR/templates_catalog.json" "$PCATELEGRAM_WEB_DIR/"
echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}"
fi
@@ -98,7 +98,7 @@ fi
# ── Systemd ──────────────────────────────────────────────────────────────────
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
[Unit]
Description=GoTelegram v2.5.0 Telegram Bot
Description=PCAtelegram_web v2.5.0 Telegram Bot
After=network.target
[Service]
@@ -130,7 +130,7 @@ if [ -f "$SCRIPT_DIR/admin-web/server.py" ]; then
PYTHON_BIN=$(command -v python3)
cat > "/etc/systemd/system/${ADMIN_WEB_SERVICE}.service" << EOF
[Unit]
Description=GoTelegram v2.5.0 Local Web Admin
Description=PCAtelegram_web v2.5.0 Local Web Admin
After=network.target
[Service]
@@ -139,8 +139,8 @@ WorkingDirectory=$ADMIN_WEB_DIR
ExecStart=$PYTHON_BIN $ADMIN_WEB_DIR/server.py
Restart=always
RestartSec=5
Environment=GOTELEGRAM_ADMIN_HOST=127.0.0.1
Environment=GOTELEGRAM_ADMIN_PORT=$ADMIN_WEB_PORT
Environment=PCATELEGRAM_WEB_ADMIN_HOST=127.0.0.1
Environment=PCATELEGRAM_WEB_ADMIN_PORT=$ADMIN_WEB_PORT
[Install]
WantedBy=multi-user.target

142
lib/backup.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# goTelegram Pro v2.5.0 — backup and restore (i18n-aware)
# PCAtelegram_web v2.5.0 — backup and restore (i18n-aware)
# ── Создание бекапа ──────────────────────────────────────────────────────────
create_backup() {
@@ -12,9 +12,9 @@ create_backup() {
mkdir -p "$output_dir"
while true; do
if [ "$suffix" -eq 0 ]; then
backup_name="gotelegram_backup_${timestamp}"
backup_name="pcatelegram_web_backup_${timestamp}"
else
backup_name="gotelegram_backup_${timestamp}_${suffix}"
backup_name="pcatelegram_web_backup_${timestamp}_${suffix}"
fi
tmp_dir="/tmp/${backup_name}"
if [ ! -e "$tmp_dir" ] && \
@@ -34,20 +34,20 @@ create_backup() {
cp "$TELEMT_CONFIG" "$tmp_dir/config.toml"
fi
# goTelegram Pro конфиг
if [ -f "$GOTELEGRAM_CONFIG" ]; then
cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json"
# PCAtelegram_web конфиг
if [ -f "$PCATELEGRAM_WEB_CONFIG" ]; then
cp "$PCATELEGRAM_WEB_CONFIG" "$tmp_dir/pcatelegram_web.json"
fi
if [ -f "$GOTELEGRAM_DIR/disabled_users.json" ]; then
cp "$GOTELEGRAM_DIR/disabled_users.json" "$tmp_dir/disabled_users.json" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/disabled_users.json" ]; then
cp "$PCATELEGRAM_WEB_DIR/disabled_users.json" "$tmp_dir/disabled_users.json" 2>/dev/null
fi
if [ -f "$GOTELEGRAM_DIR/backup_schedule.json" ]; then
cp "$GOTELEGRAM_DIR/backup_schedule.json" "$tmp_dir/backup_schedule.json" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/backup_schedule.json" ]; then
cp "$PCATELEGRAM_WEB_DIR/backup_schedule.json" "$tmp_dir/backup_schedule.json" 2>/dev/null
fi
# Language marker (i18n)
if [ -f "$GOTELEGRAM_DIR/.language" ]; then
cp "$GOTELEGRAM_DIR/.language" "$tmp_dir/.language"
if [ -f "$PCATELEGRAM_WEB_DIR/.language" ]; then
cp "$PCATELEGRAM_WEB_DIR/.language" "$tmp_dir/.language"
fi
# nginx конфиг (stealth mode)
@@ -76,12 +76,12 @@ create_backup() {
fi
# Custom templates and catalog
if [ -d "$GOTELEGRAM_DIR/custom_templates" ]; then
if [ -d "$PCATELEGRAM_WEB_DIR/custom_templates" ]; then
mkdir -p "$tmp_dir/custom_templates"
cp -a "$GOTELEGRAM_DIR/custom_templates/." "$tmp_dir/custom_templates/" 2>/dev/null
cp -a "$PCATELEGRAM_WEB_DIR/custom_templates/." "$tmp_dir/custom_templates/" 2>/dev/null
fi
if [ -f "$GOTELEGRAM_DIR/templates_catalog.json" ]; then
cp "$GOTELEGRAM_DIR/templates_catalog.json" "$tmp_dir/templates_catalog.json" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/templates_catalog.json" ]; then
cp "$PCATELEGRAM_WEB_DIR/templates_catalog.json" "$tmp_dir/templates_catalog.json" 2>/dev/null
fi
# Bot state (.env has BotFather token, so encrypted backups are strongly recommended)
@@ -100,14 +100,14 @@ create_backup() {
fi
# Traffic history
if [ -f "$GOTELEGRAM_DIR/stats_history.csv" ]; then
cp "$GOTELEGRAM_DIR/stats_history.csv" "$tmp_dir/stats_history.csv" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/stats_history.csv" ]; then
cp "$PCATELEGRAM_WEB_DIR/stats_history.csv" "$tmp_dir/stats_history.csv" 2>/dev/null
fi
if [ -f "$GOTELEGRAM_DIR/user_stats_history.csv" ]; then
cp "$GOTELEGRAM_DIR/user_stats_history.csv" "$tmp_dir/user_stats_history.csv" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/user_stats_history.csv" ]; then
cp "$PCATELEGRAM_WEB_DIR/user_stats_history.csv" "$tmp_dir/user_stats_history.csv" 2>/dev/null
fi
if [ -f "$GOTELEGRAM_DIR/shared-443.json" ]; then
cp "$GOTELEGRAM_DIR/shared-443.json" "$tmp_dir/shared-443.json" 2>/dev/null
if [ -f "$PCATELEGRAM_WEB_DIR/shared-443.json" ]; then
cp "$PCATELEGRAM_WEB_DIR/shared-443.json" "$tmp_dir/shared-443.json" 2>/dev/null
fi
# Метаданные
@@ -124,7 +124,7 @@ create_backup() {
cat > "$tmp_dir/metadata.json" << EOMETA
{
"backup_version": "1.6",
"gotelegram_version": "$GOTELEGRAM_VERSION",
"pcatelegram_web_version": "$PCATELEGRAM_WEB_VERSION",
"created_at": "$(date -Iseconds)",
"hostname": "$(hostname)",
"ip": "$ip",
@@ -201,7 +201,7 @@ restore_backup() {
return 1
fi
local tmp_dir="/tmp/gotelegram_restore_$$"
local tmp_dir="/tmp/pcatelegram_web_restore_$$"
mkdir -p "$tmp_dir"
# Расшифровываем если нужно
@@ -212,7 +212,7 @@ restore_backup() {
read -rs password
echo ""
fi
tar_file="/tmp/gotelegram_restore_$$.tar.gz"
tar_file="/tmp/pcatelegram_web_restore_$$.tar.gz"
openssl enc -aes-256-cbc -d -pbkdf2 -in "$backup_file" -out "$tar_file" -pass "pass:${password}" 2>/dev/null
if [ $? -ne 0 ]; then
log_error "$(_t_or backup_bad_pass 'Неверный пароль или повреждённый файл')"
@@ -233,25 +233,25 @@ restore_backup() {
# Находим папку бекапа
local backup_dir
backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1)
backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "pcatelegram_web_backup_*" | head -1)
[ -z "$backup_dir" ] && backup_dir="$tmp_dir"
# Legacy bot backups before v2.5.0 stored absolute paths directly in tar:
# opt/gotelegram/config.json and etc/telemt/config.toml.
# opt/pcatelegram_web/config.json and etc/telemt/config.toml.
if [ ! -f "$backup_dir/config.toml" ] && [ -f "$tmp_dir/etc/telemt/config.toml" ]; then
cp "$tmp_dir/etc/telemt/config.toml" "$backup_dir/config.toml" 2>/dev/null || true
fi
if [ ! -f "$backup_dir/gotelegram.json" ] && [ -f "$tmp_dir/opt/gotelegram/config.json" ]; then
cp "$tmp_dir/opt/gotelegram/config.json" "$backup_dir/gotelegram.json" 2>/dev/null || true
if [ ! -f "$backup_dir/pcatelegram_web.json" ] && [ -f "$tmp_dir/opt/pcatelegram_web/config.json" ]; then
cp "$tmp_dir/opt/pcatelegram_web/config.json" "$backup_dir/pcatelegram_web.json" 2>/dev/null || true
fi
if [ ! -f "$backup_dir/disabled_users.json" ] && [ -f "$tmp_dir/opt/gotelegram/disabled_users.json" ]; then
cp "$tmp_dir/opt/gotelegram/disabled_users.json" "$backup_dir/disabled_users.json" 2>/dev/null || true
if [ ! -f "$backup_dir/disabled_users.json" ] && [ -f "$tmp_dir/opt/pcatelegram_web/disabled_users.json" ]; then
cp "$tmp_dir/opt/pcatelegram_web/disabled_users.json" "$backup_dir/disabled_users.json" 2>/dev/null || true
fi
# Проверяем метаданные
if [ -f "$backup_dir/metadata.json" ]; then
local bk_version bk_mode bk_ip bk_lang bk_date
bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json")
bk_version=$(jq -r '.pcatelegram_web_version // "unknown"' "$backup_dir/metadata.json")
bk_mode=$(jq -r '.mode // "unknown"' "$backup_dir/metadata.json")
bk_ip=$(jq -r '.ip // "unknown"' "$backup_dir/metadata.json")
bk_lang=$(jq -r '.language // "-"' "$backup_dir/metadata.json")
@@ -280,24 +280,24 @@ restore_backup() {
log_success "$(_t_or backup_restored_telemt 'telemt конфиг восстановлен')"
fi
# Восстанавливаем goTelegram Pro конфиг
if [ -f "$backup_dir/gotelegram.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG"
log_success "$(_t_or backup_restored_gotelegram 'GoTelegram конфиг восстановлен')"
# Восстанавливаем PCAtelegram_web конфиг
if [ -f "$backup_dir/pcatelegram_web.json" ]; then
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$backup_dir/pcatelegram_web.json" "$PCATELEGRAM_WEB_CONFIG"
log_success "$(_t_or backup_restored_pcatelegram_web 'PCAtelegram_web конфиг восстановлен')"
fi
if [ -f "$backup_dir/disabled_users.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$backup_dir/disabled_users.json" "$GOTELEGRAM_DIR/disabled_users.json"
chmod 600 "$GOTELEGRAM_DIR/disabled_users.json" 2>/dev/null || true
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$backup_dir/disabled_users.json" "$PCATELEGRAM_WEB_DIR/disabled_users.json"
chmod 600 "$PCATELEGRAM_WEB_DIR/disabled_users.json" 2>/dev/null || true
fi
if [ -f "$backup_dir/backup_schedule.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$backup_dir/backup_schedule.json" "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null
chmod 600 "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || true
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$backup_dir/backup_schedule.json" "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null
chmod 600 "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null || true
if command -v jq >/dev/null 2>&1; then
local restored_schedule
restored_schedule=$(jq -r '.frequency // "off"' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "off")
restored_schedule=$(jq -r '.frequency // "off"' "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null || echo "off")
case "$restored_schedule" in
off|daily|weekly|monthly) set_backup_schedule "$restored_schedule" >/dev/null 2>&1 || true ;;
esac
@@ -306,8 +306,8 @@ restore_backup() {
# Восстанавливаем language marker (i18n)
if [ -f "$backup_dir/.language" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$backup_dir/.language" "$GOTELEGRAM_DIR/.language"
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$backup_dir/.language" "$PCATELEGRAM_WEB_DIR/.language"
log_success "$(_t_or backup_restored_lang 'Язык интерфейса восстановлен')"
fi
@@ -347,23 +347,23 @@ restore_backup() {
# Восстанавливаем custom templates/catalog/statistics
if [ -d "$backup_dir/custom_templates" ]; then
mkdir -p "$GOTELEGRAM_DIR/custom_templates"
cp -a "$backup_dir/custom_templates/." "$GOTELEGRAM_DIR/custom_templates/" 2>/dev/null
mkdir -p "$PCATELEGRAM_WEB_DIR/custom_templates"
cp -a "$backup_dir/custom_templates/." "$PCATELEGRAM_WEB_DIR/custom_templates/" 2>/dev/null
log_success "Пользовательские шаблоны восстановлены"
fi
if [ -f "$backup_dir/templates_catalog.json" ]; then
cp "$backup_dir/templates_catalog.json" "$GOTELEGRAM_DIR/templates_catalog.json" 2>/dev/null
cp "$backup_dir/templates_catalog.json" "$PCATELEGRAM_WEB_DIR/templates_catalog.json" 2>/dev/null
fi
if [ -f "$backup_dir/stats_history.csv" ]; then
cp "$backup_dir/stats_history.csv" "$GOTELEGRAM_DIR/stats_history.csv" 2>/dev/null
cp "$backup_dir/stats_history.csv" "$PCATELEGRAM_WEB_DIR/stats_history.csv" 2>/dev/null
log_success "История статистики восстановлена"
fi
if [ -f "$backup_dir/user_stats_history.csv" ]; then
cp "$backup_dir/user_stats_history.csv" "$GOTELEGRAM_DIR/user_stats_history.csv" 2>/dev/null
cp "$backup_dir/user_stats_history.csv" "$PCATELEGRAM_WEB_DIR/user_stats_history.csv" 2>/dev/null
log_success "История статистики пользователей восстановлена"
fi
if [ -f "$backup_dir/shared-443.json" ]; then
cp "$backup_dir/shared-443.json" "$GOTELEGRAM_DIR/shared-443.json" 2>/dev/null
cp "$backup_dir/shared-443.json" "$PCATELEGRAM_WEB_DIR/shared-443.json" 2>/dev/null
fi
# Восстанавливаем состояние бота
@@ -392,8 +392,8 @@ restore_backup() {
start_telemt
fi
command -v nginx &>/dev/null && systemctl start nginx 2>/dev/null
systemctl restart gotelegram-bot 2>/dev/null || true
systemctl restart gotelegram-admin 2>/dev/null || true
systemctl restart pcatelegram_web-bot 2>/dev/null || true
systemctl restart pcatelegram_web-admin 2>/dev/null || true
# Очистка
rm -rf "$tmp_dir"
@@ -469,43 +469,43 @@ set_backup_schedule() {
return 1
fi
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR"
mkdir -p "$PCATELEGRAM_WEB_DIR" "$BACKUP_DIR"
if [ "$frequency" = "off" ]; then
systemctl disable --now gotelegram-backup.timer >/dev/null 2>&1 || true
rm -f /etc/systemd/system/gotelegram-backup.timer /etc/systemd/system/gotelegram-backup.service
systemctl disable --now pcatelegram_web-backup.timer >/dev/null 2>&1 || true
rm -f /etc/systemd/system/pcatelegram_web-backup.timer /etc/systemd/system/pcatelegram_web-backup.service
systemctl daemon-reload >/dev/null 2>&1 || true
else
cat > /etc/systemd/system/gotelegram-backup.service << 'EOSVC'
cat > /etc/systemd/system/pcatelegram_web-backup.service << 'EOSVC'
[Unit]
Description=goTelegram Pro backup
Description=PCAtelegram_web backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
Environment=GOTELEGRAM_BACKUP_KEEP=30
ExecStart=/bin/bash -lc 'source /opt/gotelegram/lib/common.sh; source /opt/gotelegram/lib/i18n.sh; source /opt/gotelegram/lib/telemt.sh; source /opt/gotelegram/lib/website.sh; source /opt/gotelegram/lib/backup.sh; load_language "$(detect_language 2>/dev/null || echo en)"; create_backup ""; cleanup_old_backups "${GOTELEGRAM_BACKUP_KEEP:-30}"'
Environment=PCATELEGRAM_WEB_BACKUP_KEEP=30
ExecStart=/bin/bash -lc 'source /opt/pcatelegram_web/lib/common.sh; source /opt/pcatelegram_web/lib/i18n.sh; source /opt/pcatelegram_web/lib/telemt.sh; source /opt/pcatelegram_web/lib/website.sh; source /opt/pcatelegram_web/lib/backup.sh; load_language "$(detect_language 2>/dev/null || echo en)"; create_backup ""; cleanup_old_backups "${PCATELEGRAM_WEB_BACKUP_KEEP:-30}"'
EOSVC
cat > /etc/systemd/system/gotelegram-backup.timer << EOTIMER
cat > /etc/systemd/system/pcatelegram_web-backup.timer << EOTIMER
[Unit]
Description=goTelegram Pro scheduled backup
Description=PCAtelegram_web scheduled backup
[Timer]
OnCalendar=$calendar
Persistent=true
RandomizedDelaySec=15m
Unit=gotelegram-backup.service
Unit=pcatelegram_web-backup.service
[Install]
WantedBy=timers.target
EOTIMER
systemctl daemon-reload >/dev/null 2>&1 || return 1
systemctl enable --now gotelegram-backup.timer >/dev/null 2>&1 || return 1
systemctl enable --now pcatelegram_web-backup.timer >/dev/null 2>&1 || return 1
fi
cat > "$GOTELEGRAM_DIR/backup_schedule.json" << EOSCHEDULE
cat > "$PCATELEGRAM_WEB_DIR/backup_schedule.json" << EOSCHEDULE
{
"frequency": "$frequency",
"calendar": "$calendar",
@@ -513,19 +513,19 @@ EOTIMER
"updated_at": "$(date -Iseconds)"
}
EOSCHEDULE
chmod 600 "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || true
chmod 600 "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null || true
log_success "Backup schedule: $frequency"
echo "$frequency"
}
backup_schedule_status() {
local frequency="off" calendar=""
if [ -f "$GOTELEGRAM_DIR/backup_schedule.json" ] && command -v jq >/dev/null 2>&1; then
frequency=$(jq -r '.frequency // "off"' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "off")
calendar=$(jq -r '.calendar // ""' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "")
if [ -f "$PCATELEGRAM_WEB_DIR/backup_schedule.json" ] && command -v jq >/dev/null 2>&1; then
frequency=$(jq -r '.frequency // "off"' "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null || echo "off")
calendar=$(jq -r '.calendar // ""' "$PCATELEGRAM_WEB_DIR/backup_schedule.json" 2>/dev/null || echo "")
fi
echo "frequency=$frequency calendar=$calendar"
systemctl list-timers gotelegram-backup.timer --no-pager 2>/dev/null || true
systemctl list-timers pcatelegram_web-backup.timer --no-pager 2>/dev/null || true
}
# ── Интерактивный бекап ──────────────────────────────────────────────────────

96
lib/common.sh Normal file → Executable file
View File

@@ -1,32 +1,32 @@
#!/bin/bash
# goTelegram Pro v2.5.0 — common utilities
# PCAtelegram_web v2.5.0 — common utilities
# Colors, logging, spinner, system helpers, v1 compat, i18n-aware
# ── Version ───────────────────────────────────────────────────────────────────
GOTELEGRAM_VERSION="2.5.0"
GOTELEGRAM_NAME="goTelegram Pro"
PCATELEGRAM_WEB_VERSION="2.5.0"
PCATELEGRAM_WEB_NAME="PCAtelegram_web"
# ── Пути ──────────────────────────────────────────────────────────────────────
GOTELEGRAM_DIR="/opt/gotelegram"
GOTELEGRAM_CONFIG="$GOTELEGRAM_DIR/config.json"
PCATELEGRAM_WEB_DIR="/opt/pcatelegram_web"
PCATELEGRAM_WEB_CONFIG="$PCATELEGRAM_WEB_DIR/config.json"
TELEMT_CONFIG="/etc/telemt/config.toml"
TELEMT_BIN="/usr/local/bin/telemt"
TELEMT_SERVICE="telemt"
NGINX_SITE_CONF="/etc/nginx/sites-available/gotelegram"
NGINX_SITE_LINK="/etc/nginx/sites-enabled/gotelegram"
WEBSITE_ROOT="/var/www/gotelegram-site"
BACKUP_DIR="$GOTELEGRAM_DIR/backups"
LOG_FILE="/var/log/gotelegram.log"
BOT_DIR="/opt/gotelegram-bot"
ADMIN_WEB_DIR="/opt/gotelegram-admin"
ADMIN_WEB_SERVICE="gotelegram-admin"
NGINX_SITE_CONF="/etc/nginx/sites-available/pcatelegram_web"
NGINX_SITE_LINK="/etc/nginx/sites-enabled/pcatelegram_web"
WEBSITE_ROOT="/var/www/pcatelegram_web-site"
BACKUP_DIR="$PCATELEGRAM_WEB_DIR/backups"
LOG_FILE="/var/log/pcatelegram_web.log"
BOT_DIR="/opt/pcatelegram_web-bot"
ADMIN_WEB_DIR="/opt/pcatelegram_web-admin"
ADMIN_WEB_SERVICE="pcatelegram_web-admin"
ADMIN_WEB_HOST="127.0.0.1"
ADMIN_WEB_PORT="1984"
# ── V1 совместимость ─────────────────────────────────────────────────────────
V1_CONTAINER_NAME="mtproto-proxy"
V1_CONFIG_FILE="/opt/gotelegram-bot/proxy.json"
V1_SERVICE_NAME="gotelegram-bot"
V1_CONFIG_FILE="/opt/pcatelegram_web-bot/proxy.json"
V1_SERVICE_NAME="pcatelegram_web-bot"
# ── Цвета ────────────────────────────────────────────────────────────────────
RED='\033[0;31m'
@@ -93,7 +93,7 @@ progress_bar() {
# ── Выполнение с индикатором ─────────────────────────────────────────────────
run_with_spinner() {
local label="$1"; shift
local err_file="/tmp/.gotelegram_spinner_err_$$"
local err_file="/tmp/.pcatelegram_web_spinner_err_$$"
spinner_start "$label"
"$@" >/dev/null 2>"$err_file"
local rc=$?
@@ -119,11 +119,11 @@ show_banner() {
echo ""
echo -e "${CYAN}${line}${NC}"
if type tf &>/dev/null; then
echo -e " ${BOLD}${WHITE}🚀 $(tf banner_title "$GOTELEGRAM_VERSION")${NC}"
echo -e " ${BOLD}${WHITE}🚀 $(tf banner_title "$PCATELEGRAM_WEB_VERSION")${NC}"
echo -e " ${DIM}$(t banner_subtitle)${NC}"
echo -e " ${DIM}$(t banner_features)${NC}"
else
echo -e " ${BOLD}${WHITE}🚀 goTelegram Pro v${GOTELEGRAM_VERSION}${NC}"
echo -e " ${BOLD}${WHITE}🚀 PCAtelegram_web v${PCATELEGRAM_WEB_VERSION}${NC}"
echo -e " ${DIM}MTProxy powered by telemt (Rust + Tokio)${NC}"
echo -e " ${DIM}Anti-DPI • Fake TLS • TCP Splice • JA3/JA4${NC}"
fi
@@ -306,7 +306,7 @@ apt_update() {
return 0
}
# ── Зависимости GoTelegram ──────────────────────────────────────────────────
# ── Зависимости PCAtelegram_web ──────────────────────────────────────────────────
# Полный список внешних команд, которые скрипт использует. Для каждой команды
# указан пакет на apt и dnf/yum (имена различаются: например dig = dnsutils на
# Debian, bind-utils на RHEL).
@@ -429,7 +429,7 @@ ensure_deps() {
if [ ${#still_missing[@]} -gt 0 ]; then
log_error "Critical dependencies still missing: ${still_missing[*]}"
log_error "Install manually and re-run gotelegram"
log_error "Install manually and re-run pcatelegram_web"
return 1
fi
@@ -483,32 +483,32 @@ detect_3xui_443_listener() {
warn_3xui_443_conflict() {
detect_3xui_443_listener || return 1
log_warning "Обнаружен 3x-ui/Xray, который уже слушает TCP/443."
log_warning "goTelegram Pro не будет молча останавливать или переписывать 3x-ui."
log_dim "Для настоящего shared-443 нужен один фронтовой TLS/SNI-диспетчер и разные SNI-домены для Xray и goTelegram Pro."
mkdir -p "$GOTELEGRAM_DIR" 2>/dev/null
cat > "$GOTELEGRAM_DIR/shared-443-3xui.md" <<'EOF' 2>/dev/null || true
# goTelegram Pro + 3x-ui on one TCP/443
log_warning "PCAtelegram_web не будет молча останавливать или переписывать 3x-ui."
log_dim "Для настоящего shared-443 нужен один фронтовой TLS/SNI-диспетчер и разные SNI-домены для Xray и PCAtelegram_web."
mkdir -p "$PCATELEGRAM_WEB_DIR" 2>/dev/null
cat > "$PCATELEGRAM_WEB_DIR/shared-443-3xui.md" <<'EOF' 2>/dev/null || true
# PCAtelegram_web + 3x-ui on one TCP/443
goTelegram Pro detected that 3x-ui/Xray already owns TCP/443. Two independent
PCAtelegram_web detected that 3x-ui/Xray already owns TCP/443. Two independent
processes cannot bind the same IP:port at the same time. A safe shared setup
needs one front TLS/SNI dispatcher on 443 and internal backends, for example:
- dispatcher: 0.0.0.0:443 (nginx stream ssl_preread)
- goTelegram Pro telemt: 127.0.0.1:7443
- PCAtelegram_web telemt: 127.0.0.1:7443
- 3x-ui/Xray inbound: 127.0.0.1:9443
- goTelegram Pro nginx mask site: 127.0.0.1:8443
- PCAtelegram_web nginx mask site: 127.0.0.1:8443
The dispatcher routes Xray SNI domains to Xray. Everything else goes to telemt;
telemt then decides whether the session is MTProxy or regular HTTPS and forwards
the website to nginx through dns_overrides.
goTelegram Pro can generate the dispatcher with:
PCAtelegram_web can generate the dispatcher with:
source /opt/gotelegram/lib/shared443.sh
shared443_enable <gotelegram-domain> <xray-sni-domain> 127.0.0.1:9443
source /opt/pcatelegram_web/lib/shared443.sh
shared443_enable <pcatelegram_web-domain> <xray-sni-domain> 127.0.0.1:9443
Move the 3x-ui/Xray inbound from 0.0.0.0:443 to 127.0.0.1:9443 in the panel first,
or nginx will not be able to own the public 443 socket. goTelegram Pro intentionally
or nginx will not be able to own the public 443 socket. PCAtelegram_web intentionally
does not rewrite the 3x-ui SQLite database or generated Xray config without explicit
operator confirmation, because 3x-ui can overwrite manual JSON edits on the next
panel change.
@@ -531,14 +531,14 @@ check_disk_space() {
return 0
}
# ── Конфигурация GoTelegram (JSON) ──────────────────────────────────────────
save_gotelegram_config() {
mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")"
# ── Конфигурация PCAtelegram_web (JSON) ──────────────────────────────────────────
save_pcatelegram_web_config() {
mkdir -p "$(dirname "$PCATELEGRAM_WEB_CONFIG")"
local cur_lang
cur_lang=$(type get_language &>/dev/null && get_language || echo en)
cat > "$GOTELEGRAM_CONFIG" << EOJSON
cat > "$PCATELEGRAM_WEB_CONFIG" << EOJSON
{
"version": "$GOTELEGRAM_VERSION",
"version": "$PCATELEGRAM_WEB_VERSION",
"engine": "${1:-telemt}",
"mode": "${2:-lite}",
"port": ${3:-443},
@@ -551,12 +551,12 @@ save_gotelegram_config() {
"updated_at": "$(date -Iseconds)"
}
EOJSON
chmod 600 "$GOTELEGRAM_CONFIG"
chmod 600 "$PCATELEGRAM_WEB_CONFIG"
}
load_gotelegram_config() {
if [ -f "$GOTELEGRAM_CONFIG" ]; then
cat "$GOTELEGRAM_CONFIG"
load_pcatelegram_web_config() {
if [ -f "$PCATELEGRAM_WEB_CONFIG" ]; then
cat "$PCATELEGRAM_WEB_CONFIG"
return 0
fi
echo "{}"
@@ -565,11 +565,11 @@ load_gotelegram_config() {
config_get() {
local key="$1"
if [ ! -f "$GOTELEGRAM_CONFIG" ]; then
if [ ! -f "$PCATELEGRAM_WEB_CONFIG" ]; then
return 2 # file missing
fi
local val
val=$(jq -r ".$key // empty" "$GOTELEGRAM_CONFIG" 2>/dev/null)
val=$(jq -r ".$key // empty" "$PCATELEGRAM_WEB_CONFIG" 2>/dev/null)
if [ $? -ne 0 ]; then
return 3 # invalid JSON
fi
@@ -671,12 +671,12 @@ migrate_v1_to_v2() {
# Backup v1 config
if [ -f "$V1_CONFIG_FILE" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$V1_CONFIG_FILE" "$GOTELEGRAM_DIR/v1_backup_proxy.json" 2>/dev/null
mkdir -p "$PCATELEGRAM_WEB_DIR"
cp "$V1_CONFIG_FILE" "$PCATELEGRAM_WEB_DIR/v1_backup_proxy.json" 2>/dev/null
if type tf &>/dev/null; then
log_success "$(tf v1_config_saved "$GOTELEGRAM_DIR/v1_backup_proxy.json")"
log_success "$(tf v1_config_saved "$PCATELEGRAM_WEB_DIR/v1_backup_proxy.json")"
else
log_success "v1 config saved to $GOTELEGRAM_DIR/v1_backup_proxy.json"
log_success "v1 config saved to $PCATELEGRAM_WEB_DIR/v1_backup_proxy.json"
fi
fi
@@ -735,6 +735,6 @@ validate_domain() {
# ── Init: создание директорий ────────────────────────────────────────────────
init_dirs() {
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null
mkdir -p "$PCATELEGRAM_WEB_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null
touch "$LOG_FILE" 2>/dev/null
}

12
lib/i18n.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.5.0 — i18n engine
# PCAtelegram_web v2.5.0 — i18n engine
# Internationalization support: EN (English) / RU (Русский)
#
# Usage:
@@ -74,14 +74,14 @@ get_language() {
# ── Detect saved language from config.json, default en ──
detect_language() {
local cfg="${GOTELEGRAM_CONFIG:-/opt/gotelegram/config.json}"
local cfg="${PCATELEGRAM_WEB_CONFIG:-/opt/pcatelegram_web/config.json}"
local lang=""
if [ -f "$cfg" ] && command -v jq >/dev/null 2>&1; then
lang=$(jq -r '.language // empty' "$cfg" 2>/dev/null)
fi
# Also check marker file (language set before config.json exists)
if [ -z "$lang" ]; then
local marker="${GOTELEGRAM_DIR:-/opt/gotelegram}/.language"
local marker="${PCATELEGRAM_WEB_DIR:-/opt/pcatelegram_web}/.language"
if [ -f "$marker" ]; then
lang=$(head -c 2 "$marker" 2>/dev/null | tr -d '[:space:]')
fi
@@ -100,11 +100,11 @@ save_language() {
if ! [[ "$lang" =~ ^(en|ru)$ ]]; then
return 1
fi
mkdir -p "${GOTELEGRAM_DIR:-/opt/gotelegram}" 2>/dev/null
mkdir -p "${PCATELEGRAM_WEB_DIR:-/opt/pcatelegram_web}" 2>/dev/null
# Always write marker for early-access (before config.json exists)
echo "$lang" > "${GOTELEGRAM_DIR:-/opt/gotelegram}/.language" 2>/dev/null
echo "$lang" > "${PCATELEGRAM_WEB_DIR:-/opt/pcatelegram_web}/.language" 2>/dev/null
local cfg="${GOTELEGRAM_CONFIG:-/opt/gotelegram/config.json}"
local cfg="${PCATELEGRAM_WEB_CONFIG:-/opt/pcatelegram_web/config.json}"
if [ -f "$cfg" ] && command -v jq >/dev/null 2>&1; then
local tmp
tmp=$(mktemp) || return 1

18
lib/lang/en.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# goTelegram Pro v2.5.0 — English translations
# PCAtelegram_web v2.5.0 — English translations
# shellcheck disable=SC2034,SC2148
# ── Common words ────────────────────────────────────────────────────────
@@ -25,7 +25,7 @@ I18N[success]="Done"
I18N[wait]="Please wait..."
# ── Banner ──────────────────────────────────────────────────────────────
I18N[banner_title]="goTelegram Pro v%s"
I18N[banner_title]="PCAtelegram_web v%s"
I18N[banner_subtitle]="MTProxy powered by telemt (Rust + Tokio)"
I18N[banner_features]="Anti-DPI • Fake TLS • TCP Splice • JA3/JA4"
I18N[credits_title]="Credits / Thanks"
@@ -75,7 +75,7 @@ I18N[submenu_about_title]=" ABOUT"
I18N[about_version_info]="Version info"
I18N[about_promo]="Promo / Donate"
I18N[version_title]="🔍 Information"
I18N[version_label]="goTelegram Pro:"
I18N[version_label]="PCAtelegram_web:"
I18N[version_engine]="Engine:"
I18N[version_tech]="Technology:"
I18N[version_license]="License:"
@@ -106,7 +106,7 @@ I18N[install_cfg_mode]="Mode:"
I18N[install_cfg_domain]="Domain:"
I18N[install_confirm_proxy]="Install proxy?"
I18N[install_confirm_proxy_site]="Install proxy + website?"
I18N[install_done]="goTelegram Pro v%s installed! (%s mode)"
I18N[install_done]="PCAtelegram_web v%s installed! (%s mode)"
I18N[install_arch_desc1]="telemt accepts all traffic on 443 (HTTPS masquerade)"
I18N[install_arch_desc2]="nginx serves the site on internal port %s"
I18N[install_arch_desc3]="ISP only sees HTTPS traffic to %s:443"
@@ -125,7 +125,7 @@ I18N[logs_telemt_title]="📋 telemt logs (last %s lines):"
# ── Link / Share ────────────────────────────────────────────────────────
I18N[link_title]="🔗 Connection link:"
I18N[share_title]="📤 Forward this message:"
I18N[share_line1]="🔐 MTProxy for Telegram (goTelegram Pro v%s)"
I18N[share_line1]="🔐 MTProxy for Telegram (PCAtelegram_web v%s)"
I18N[share_server]="🌍 Server: %s"
I18N[share_port]="🔌 Port: %s"
I18N[share_connect_cta]="👉 Connect with one tap:"
@@ -141,7 +141,7 @@ I18N[website_restart_nginx]="Restart nginx"
I18N[website_change_template]="Change template"
# ── Remove ──────────────────────────────────────────────────────────────
I18N[remove_title]="🗑 Remove goTelegram Pro"
I18N[remove_title]="🗑 Remove PCAtelegram_web"
I18N[remove_proxy_only]="Remove proxy only (telemt)"
I18N[remove_bot_only]="Remove Telegram bot only"
I18N[remove_all]="Remove everything (proxy + bot + settings)"
@@ -151,7 +151,7 @@ I18N[remove_backup_before]="Create a backup before removal?"
I18N[remove_warn_all]="This will remove EVERYTHING: proxy, bot, site, settings."
I18N[remove_confirm_all]="Are you absolutely sure?"
I18N[remove_proxy_done]="Proxy removed"
I18N[remove_all_done]="goTelegram Pro fully removed (proxy + bot)"
I18N[remove_all_done]="PCAtelegram_web fully removed (proxy + bot)"
# ── Telegram bot submenu ────────────────────────────────────────────────
I18N[bot_title]="🤖 Telegram bot"
@@ -332,7 +332,7 @@ I18N[backup_lang_label]="Language"
I18N[backup_date_label]="Date"
I18N[backup_confirm_restore]="Restore configuration? Current settings will be overwritten."
I18N[backup_restored_telemt]="telemt config restored"
I18N[backup_restored_gotelegram]="goTelegram Pro config restored"
I18N[backup_restored_pcatelegram_web]="PCAtelegram_web config restored"
I18N[backup_restored_lang]="Interface language restored"
I18N[backup_restored_nginx]="nginx config restored"
I18N[backup_restored_ssl]="SSL certificates restored"
@@ -362,7 +362,7 @@ I18N[auto_refresh]="Refresh in 30 sec"
I18N[deps_installing]="Installing dependencies: %s"
# ── Migration ───────────────────────────────────────────────────────────
I18N[v1_detected]="⚠️ goTelegram Pro v1 (mtg) installation detected"
I18N[v1_detected]="⚠️ PCAtelegram_web v1 (mtg) installation detected"
I18N[v1_container]="Container: %s"
I18N[v1_migration_step]="Migrating from v1 (mtg) to v2 (telemt)"
I18N[v1_found_title]="Found v1 (mtg) installation:"

18
lib/lang/ru.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# goTelegram Pro v2.5.0 — Russian translations
# PCAtelegram_web v2.5.0 — Russian translations
# shellcheck disable=SC2034,SC2148
# ── Common words ────────────────────────────────────────────────────────
@@ -25,7 +25,7 @@ I18N[success]="Готово"
I18N[wait]="Подождите..."
# ── Banner ──────────────────────────────────────────────────────────────
I18N[banner_title]="goTelegram Pro v%s"
I18N[banner_title]="PCAtelegram_web v%s"
I18N[banner_subtitle]="MTProxy на ядре telemt (Rust + Tokio)"
I18N[banner_features]="Anti-DPI • Fake TLS • TCP Splice • JA3/JA4"
I18N[credits_title]="Благодарности / Credits"
@@ -75,7 +75,7 @@ I18N[submenu_about_title]=" О ПРОГРАММЕ"
I18N[about_version_info]="Информация о версии"
I18N[about_promo]="Промо / Донат"
I18N[version_title]="🔍 Информация"
I18N[version_label]="goTelegram Pro:"
I18N[version_label]="PCAtelegram_web:"
I18N[version_engine]="Ядро:"
I18N[version_tech]="Технология:"
I18N[version_license]="Лицензия:"
@@ -106,7 +106,7 @@ I18N[install_cfg_mode]="Режим:"
I18N[install_cfg_domain]="Домен:"
I18N[install_confirm_proxy]="Установить прокси?"
I18N[install_confirm_proxy_site]="Установить прокси + сайт?"
I18N[install_done]="goTelegram Pro v%s установлен! (%s-режим)"
I18N[install_done]="PCAtelegram_web v%s установлен! (%s-режим)"
I18N[install_arch_desc1]="telemt принимает весь трафик на 443 (маскировка под HTTPS)"
I18N[install_arch_desc2]="nginx обслуживает сайт на внутреннем порту %s"
I18N[install_arch_desc3]="Провайдер видит только HTTPS-трафик к %s:443"
@@ -125,7 +125,7 @@ I18N[logs_telemt_title]="📋 Логи telemt (последние %s строк)
# ── Link / Share ────────────────────────────────────────────────────────
I18N[link_title]="🔗 Ссылка для подключения:"
I18N[share_title]="📤 Перешлите это сообщение:"
I18N[share_line1]="🔐 MTProxy для Telegram (goTelegram Pro v%s)"
I18N[share_line1]="🔐 MTProxy для Telegram (PCAtelegram_web v%s)"
I18N[share_server]="🌍 Сервер: %s"
I18N[share_port]="🔌 Порт: %s"
I18N[share_connect_cta]="👉 Подключиться одним нажатием:"
@@ -141,7 +141,7 @@ I18N[website_restart_nginx]="Перезапустить nginx"
I18N[website_change_template]="Сменить шаблон"
# ── Remove ──────────────────────────────────────────────────────────────
I18N[remove_title]="🗑 Удаление goTelegram Pro"
I18N[remove_title]="🗑 Удаление PCAtelegram_web"
I18N[remove_proxy_only]="Удалить только прокси (telemt)"
I18N[remove_bot_only]="Удалить только Telegram-бота"
I18N[remove_all]="Удалить всё (прокси + бот + настройки)"
@@ -151,7 +151,7 @@ I18N[remove_backup_before]="Сделать бекап перед удалени
I18N[remove_warn_all]="Это удалит ВСЁ: прокси, бот, сайт, настройки."
I18N[remove_confirm_all]="Вы точно уверены?"
I18N[remove_proxy_done]="Прокси удалён"
I18N[remove_all_done]="goTelegram Pro полностью удалён (прокси + бот)"
I18N[remove_all_done]="PCAtelegram_web полностью удалён (прокси + бот)"
# ── Telegram bot submenu ────────────────────────────────────────────────
I18N[bot_title]="🤖 Telegram-бот"
@@ -332,7 +332,7 @@ I18N[backup_lang_label]="Язык"
I18N[backup_date_label]="Дата"
I18N[backup_confirm_restore]="Восстановить конфигурацию? Текущие настройки будут перезаписаны."
I18N[backup_restored_telemt]="telemt конфиг восстановлен"
I18N[backup_restored_gotelegram]="goTelegram Pro конфиг восстановлен"
I18N[backup_restored_pcatelegram_web]="PCAtelegram_web конфиг восстановлен"
I18N[backup_restored_lang]="Язык интерфейса восстановлен"
I18N[backup_restored_nginx]="nginx конфиг восстановлен"
I18N[backup_restored_ssl]="SSL сертификаты восстановлены"
@@ -362,7 +362,7 @@ I18N[auto_refresh]="Обновление через 30 сек"
I18N[deps_installing]="Установка зависимостей: %s"
# ── Migration ───────────────────────────────────────────────────────────
I18N[v1_detected]="⚠️ Обнаружена установка goTelegram Pro v1 (mtg)"
I18N[v1_detected]="⚠️ Обнаружена установка PCAtelegram_web v1 (mtg)"
I18N[v1_container]="Контейнер: %s"
I18N[v1_migration_step]="Миграция с v1 (mtg) на v2 (telemt)"
I18N[v1_found_title]="Найдена установка v1 (mtg):"

60
lib/stats.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# stats.sh — Traffic statistics module for GoTelegram v2.5.0
# stats.sh — Traffic statistics module for PCAtelegram_web v2.5.0
# Tracks proxy (telemt port 443) and site (nginx port 8443) traffic
# Uses iptables counters + real-time snapshots + historical CSV
@@ -10,12 +10,12 @@ YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
STATS_DIR="/run/gotelegram"
HISTORY_FILE="/opt/gotelegram/stats_history.csv"
USER_HISTORY_FILE="/opt/gotelegram/user_stats_history.csv"
STATS_DIR="/run/pcatelegram_web"
HISTORY_FILE="/opt/pcatelegram_web/stats_history.csv"
USER_HISTORY_FILE="/opt/pcatelegram_web/user_stats_history.csv"
SNAPSHOTS_DIR="$STATS_DIR/snapshots"
CURRENT_SNAPSHOT="$STATS_DIR/stats_current.json"
CONFIG_FILE="/opt/gotelegram/config.json"
CONFIG_FILE="/opt/pcatelegram_web/config.json"
TELEMT_CONFIG_FILE="/etc/telemt/config.toml"
STATS_RETENTION_DAYS="${STATS_RETENTION_DAYS:-365}"
STATS_MINUTE_RETENTION_DAYS="${STATS_MINUTE_RETENTION_DAYS:-31}"
@@ -35,23 +35,23 @@ stats_init() {
chmod 755 "$STATS_DIR" "$SNAPSHOTS_DIR" 2>/dev/null
# Create iptables chain if not exists
if ! iptables -L GOTELEGRAM_STATS -n >/dev/null 2>&1; then
iptables -N GOTELEGRAM_STATS 2>/dev/null
if ! iptables -L PCATELEGRAM_WEB_STATS -n >/dev/null 2>&1; then
iptables -N PCATELEGRAM_WEB_STATS 2>/dev/null
fi
# Add chain to INPUT if not already present
if ! iptables -C INPUT -j GOTELEGRAM_STATS 2>/dev/null; then
iptables -I INPUT -j GOTELEGRAM_STATS 2>/dev/null
if ! iptables -C INPUT -j PCATELEGRAM_WEB_STATS 2>/dev/null; then
iptables -I INPUT -j PCATELEGRAM_WEB_STATS 2>/dev/null
fi
# Add rule for proxy traffic (port 443, TCP)
if ! iptables -C GOTELEGRAM_STATS -p tcp --dport 443 2>/dev/null; then
iptables -A GOTELEGRAM_STATS -p tcp --dport 443 2>/dev/null
if ! iptables -C PCATELEGRAM_WEB_STATS -p tcp --dport 443 2>/dev/null; then
iptables -A PCATELEGRAM_WEB_STATS -p tcp --dport 443 2>/dev/null
fi
# Add rule for site traffic (loopback, port 8443, TCP)
if ! iptables -C GOTELEGRAM_STATS -i lo -p tcp --dport 8443 2>/dev/null; then
iptables -A GOTELEGRAM_STATS -i lo -p tcp --dport 8443 2>/dev/null
if ! iptables -C PCATELEGRAM_WEB_STATS -i lo -p tcp --dport 8443 2>/dev/null; then
iptables -A PCATELEGRAM_WEB_STATS -i lo -p tcp --dport 8443 2>/dev/null
fi
# Initialize CSV header if file doesn't exist
@@ -81,7 +81,7 @@ stats_collect() {
# Parse iptables output: format is "pkts bytes target"
# We need to extract bytes (2nd column) for each rule
local iptables_output=$(iptables -L GOTELEGRAM_STATS -v -n -x 2>/dev/null)
local iptables_output=$(iptables -L PCATELEGRAM_WEB_STATS -v -n -x 2>/dev/null)
# Extract counters for port 443 (proxy)
proxy_bytes=$(echo "$iptables_output" | grep "dpt:443" | grep -v "lo" | awk '{print $2}')
@@ -140,7 +140,7 @@ EOF
}
# Print active telemt usernames from [access.users]. Usernames are restricted by
# goTelegram to A-Z/a-z/0-9/_.- so they are safe in URLs and CSV fields.
# PCAtelegram_web to A-Z/a-z/0-9/_.- so they are safe in URLs and CSV fields.
stats_active_users() {
[[ -f "$TELEMT_CONFIG_FILE" ]] || return 0
awk '
@@ -478,9 +478,9 @@ toggle_stats() {
fi
# Remove iptables rules
iptables -D INPUT -j GOTELEGRAM_STATS 2>/dev/null
iptables -F GOTELEGRAM_STATS 2>/dev/null
iptables -X GOTELEGRAM_STATS 2>/dev/null
iptables -D INPUT -j PCATELEGRAM_WEB_STATS 2>/dev/null
iptables -F PCATELEGRAM_WEB_STATS 2>/dev/null
iptables -X PCATELEGRAM_WEB_STATS 2>/dev/null
# Clean up directories
rm -rf "$STATS_DIR" 2>/dev/null
@@ -504,7 +504,7 @@ toggle_stats() {
# Install systemd service for stats collection
install_stats_collector() {
local service_file="/etc/systemd/system/gotelegram-stats.service"
local service_file="/etc/systemd/system/pcatelegram_web-stats.service"
# Check if running as root
if [[ $EUID -ne 0 ]]; then
@@ -527,14 +527,14 @@ install_stats_collector() {
# Create systemd service file
cat > "$service_file" <<'EOF'
[Unit]
Description=goTelegram Pro Traffic Stats Collector
Description=PCAtelegram_web Traffic Stats Collector
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/bin/bash -c 'source /opt/gotelegram/lib/common.sh; source /opt/gotelegram/lib/stats.sh; stats_init; while true; do stats_collect; sleep 1; done'
ExecStart=/bin/bash -c 'source /opt/pcatelegram_web/lib/common.sh; source /opt/pcatelegram_web/lib/stats.sh; stats_init; while true; do stats_collect; sleep 1; done'
Restart=always
RestartSec=5
StandardOutput=journal
@@ -546,8 +546,8 @@ EOF
chmod 644 "$service_file"
systemctl daemon-reload
systemctl enable gotelegram-stats.service
systemctl restart gotelegram-stats.service
systemctl enable pcatelegram_web-stats.service
systemctl restart pcatelegram_web-stats.service
if [[ -f "$CONFIG_FILE" ]] && command -v jq &>/dev/null; then
local tmp
@@ -560,7 +560,7 @@ EOF
fi
fi
echo "Сервис gotelegram-stats установлен и запущен" >&2
echo "Сервис pcatelegram_web-stats установлен и запущен" >&2
}
# Remove stats collector service
@@ -570,15 +570,15 @@ remove_stats_collector() {
return 1
fi
systemctl stop gotelegram-stats.service 2>/dev/null
systemctl disable gotelegram-stats.service 2>/dev/null
rm -f /etc/systemd/system/gotelegram-stats.service
systemctl stop pcatelegram_web-stats.service 2>/dev/null
systemctl disable pcatelegram_web-stats.service 2>/dev/null
rm -f /etc/systemd/system/pcatelegram_web-stats.service
systemctl daemon-reload
# Remove iptables rules
iptables -D INPUT -j GOTELEGRAM_STATS 2>/dev/null
iptables -F GOTELEGRAM_STATS 2>/dev/null
iptables -X GOTELEGRAM_STATS 2>/dev/null
iptables -D INPUT -j PCATELEGRAM_WEB_STATS 2>/dev/null
iptables -F PCATELEGRAM_WEB_STATS 2>/dev/null
iptables -X PCATELEGRAM_WEB_STATS 2>/dev/null
# Clean up directories and files
rm -rf "$STATS_DIR" 2>/dev/null

4
lib/telemt.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.5.0 — Управление telemt binary
# PCAtelegram_web v2.5.0 — Управление telemt binary
# Скачивание, обновление, запуск, остановка через systemd
TELEMT_GITHUB="telemt/telemt"
@@ -159,7 +159,7 @@ install_telemt_service() {
cat > "/etc/systemd/system/${TELEMT_SERVICE}.service" << EOF
[Unit]
Description=goTelegram Pro MTProxy (telemt engine)
Description=PCAtelegram_web MTProxy (telemt engine)
Documentation=https://github.com/telemt/telemt
After=network-online.target
Wants=network-online.target

4
lib/telemt_config.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.5.0 — Генерация TOML конфигурации для telemt
# PCAtelegram_web v2.5.0 — Генерация TOML конфигурации для telemt
# ── Популярные домены (не заблокированные в РФ) ──────────────────────────────
QUICK_DOMAINS=(
@@ -44,7 +44,7 @@ generate_telemt_toml() {
fi
cat > "$output" << EOTOML
# GoTelegram v${GOTELEGRAM_VERSION} — telemt v3 configuration
# PCAtelegram_web v${PCATELEGRAM_WEB_VERSION} — telemt v3 configuration
# Сгенерировано: $(date -Iseconds)
# Режим: ${mask_mode}

4
lib/templates_catalog.sh Normal file → Executable file
View File

@@ -1,10 +1,10 @@
#!/bin/bash
# GoTelegram v2.5.0 — website templates catalog
# PCAtelegram_web v2.5.0 — website templates catalog
# Pick from ~1800 templates, preview links, git sparse-checkout downloads,
# + custom git URL templates (user-supplied public repos)
CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json"
TEMPLATES_CACHE="/tmp/gotelegram_templates"
TEMPLATES_CACHE="/tmp/pcatelegram_web_templates"
# Custom git template limits
CUSTOM_GIT_MAX_SIZE_MB=100

14
lib/website.sh Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.5.0 — Управление сайтом (nginx + certbot + шаблоны)
# PCAtelegram_web v2.5.0 — Управление сайтом (nginx + certbot + шаблоны)
# ── Установка nginx ──────────────────────────────────────────────────────────
install_nginx() {
@@ -39,7 +39,7 @@ generate_nginx_config() {
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
cat > "$NGINX_SITE_CONF" << 'EONGINX'
# GoTelegram v2.5.0 — nginx config
# PCAtelegram_web v2.5.0 — nginx config
# Pro: nginx на 127.0.0.1:8443 (внутренний), telemt на 0.0.0.0:443 (внешний)
# Обычный браузер → :443 → telemt → 127.0.0.1:8443 → nginx (сайт)
@@ -88,7 +88,7 @@ server {
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Корень сайта
root /var/www/gotelegram-site;
root /var/www/pcatelegram_web-site;
index index.html;
location / {
@@ -127,7 +127,7 @@ generate_nginx_temp_config() {
local domain="$1"
cat > "$NGINX_SITE_CONF" << EONGINX_TEMP
# GoTelegram — временный конфиг (до получения SSL)
# PCAtelegram_web — временный конфиг (до получения SSL)
server {
listen 80;
listen [::]:80;
@@ -138,7 +138,7 @@ server {
allow all;
}
root /var/www/gotelegram-site;
root /var/www/pcatelegram_web-site;
index index.html;
location / {
@@ -257,8 +257,8 @@ deploy_template_to_nginx() {
mkdir -p "$WEBSITE_ROOT"
cp -a "$template_dir/." "$WEBSITE_ROOT/"
rm -f "$WEBSITE_ROOT/.custom_git_source" 2>/dev/null || true
echo "$template_id" > "$WEBSITE_ROOT/.gotelegram_template_id" 2>/dev/null || true
[ -n "$source_url" ] && echo "$source_url" > "$WEBSITE_ROOT/.gotelegram_template_source" 2>/dev/null || true
echo "$template_id" > "$WEBSITE_ROOT/.pcatelegram_web_template_id" 2>/dev/null || true
[ -n "$source_url" ] && echo "$source_url" > "$WEBSITE_ROOT/.pcatelegram_web_template_source" 2>/dev/null || true
chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null || chown -R nginx:nginx "$WEBSITE_ROOT" 2>/dev/null
chmod -R 755 "$WEBSITE_ROOT"

View File

@@ -1,4 +1,4 @@
# goTelegram Pro v2.5.0 Bot
# PCAtelegram_web v2.5.0 Bot
Production-quality Telegram bot for managing MTProxy (telemt engine) on Linux servers.
@@ -34,10 +34,10 @@ Production-quality Telegram bot for managing MTProxy (telemt engine) on Linux se
Recommended installation path is the main CLI menu:
```bash
gotelegram
pcatelegram_web
```
Then choose `12) Telegram-bot` → install/update. On repeat goTelegram Pro bootstrap/update, an already installed bot is refreshed automatically: code, i18n files and requirements are copied to `/opt/gotelegram-bot`, `.env` is preserved, dependencies are checked and `gotelegram-bot` is restarted.
Then choose `12) Telegram-bot` → install/update. On repeat PCAtelegram_web bootstrap/update, an already installed bot is refreshed automatically: code, i18n files and requirements are copied to `/opt/pcatelegram_web-bot`, `.env` is preserved, dependencies are checked and `pcatelegram_web-bot` is restarted.
### Prerequisites
@@ -76,16 +76,16 @@ For systemd service:
```bash
[Unit]
Description=goTelegram Pro Bot
Description=PCAtelegram_web Bot
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/gotelegram-bot
ExecStart=/opt/gotelegram-bot/venv/bin/python /opt/gotelegram-bot/bot.py
WorkingDirectory=/opt/pcatelegram_web-bot
ExecStart=/opt/pcatelegram_web-bot/venv/bin/python /opt/pcatelegram_web-bot/bot.py
Restart=always
RestartSec=5
Environment=PATH=/opt/gotelegram-bot/venv/bin:/usr/bin
Environment=PATH=/opt/pcatelegram_web-bot/venv/bin:/usr/bin
[Install]
WantedBy=multi-user.target
@@ -97,17 +97,17 @@ WantedBy=multi-user.target
- `BOT_TOKEN` - Telegram bot token (required)
- `ALLOWED_IDS` - Comma-separated user IDs (optional, all users allowed if empty)
- `BOT_LANG` - Default language inherited from goTelegram Pro install language
- `BOT_LANG` - Default language inherited from PCAtelegram_web install language
### System Paths
- `GOTELEGRAM_CONFIG` - `/opt/gotelegram/config.json`
- `PCATELEGRAM_WEB_CONFIG` - `/opt/pcatelegram_web/config.json`
- `TELEMT_CONFIG` - `/etc/telemt/config.toml`
- `TELEMT_SERVICE` - `telemt` (systemd service name)
- `WEBSITE_ROOT` - `/var/www/gotelegram-site`
- `BACKUP_DIR` - `/opt/gotelegram/backups`
- `BACKUP_SCHEDULE_FILE` - `/opt/gotelegram/backup_schedule.json`
- `TEMPLATES_CATALOG` - `/opt/gotelegram/templates_catalog.json`
- `WEBSITE_ROOT` - `/var/www/pcatelegram_web-site`
- `BACKUP_DIR` - `/opt/pcatelegram_web/backups`
- `BACKUP_SCHEDULE_FILE` - `/opt/pcatelegram_web/backup_schedule.json`
- `TEMPLATES_CATALOG` - `/opt/pcatelegram_web/templates_catalog.json`
## Architecture
@@ -158,4 +158,4 @@ code, stdout, stderr = await sh("command", "arg1", "arg2")
## License
goTelegram Pro v2.5.0 - Open source community project
PCAtelegram_web v2.5.0 - Open source community project

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
goTelegram Pro v2.5.0 Bot - MTProxy Management for Linux
PCAtelegram_web v2.5.0 Bot - MTProxy Management for Linux
Manages telemt engine via Telegram interface with full CLI feature parity
Uses python-telegram-bot v21+
Supports EN/RU UI with per-user language preferences.
@@ -103,28 +103,28 @@ logger = logging.getLogger(__name__)
# CONFIGURATION
# ============================================================================
GOTELEGRAM_VERSION = "2.5.0"
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
DISABLED_USERS_FILE = "/opt/gotelegram/disabled_users.json"
USER_STATS_HISTORY = "/opt/gotelegram/user_stats_history.csv"
USER_LOCK_FILE = "/run/gotelegram/admin-users.lock"
PCATELEGRAM_WEB_VERSION = "2.5.0"
PCATELEGRAM_WEB_CONFIG = "/opt/pcatelegram_web/config.json"
DISABLED_USERS_FILE = "/opt/pcatelegram_web/disabled_users.json"
USER_STATS_HISTORY = "/opt/pcatelegram_web/user_stats_history.csv"
USER_LOCK_FILE = "/run/pcatelegram_web/admin-users.lock"
TELEMT_CONFIG = "/etc/telemt/config.toml"
TELEMT_SERVICE = "telemt"
WEBSITE_ROOT = "/var/www/gotelegram-site"
BACKUP_DIR = "/opt/gotelegram/backups"
BACKUP_SCHEDULE_FILE = "/opt/gotelegram/backup_schedule.json"
TEMPLATES_CATALOG = "/opt/gotelegram/templates_catalog.json"
INSTALL_SH = "/opt/gotelegram/install.sh"
WEBSITE_ROOT = "/var/www/pcatelegram_web-site"
BACKUP_DIR = "/opt/pcatelegram_web/backups"
BACKUP_SCHEDULE_FILE = "/opt/pcatelegram_web/backup_schedule.json"
TEMPLATES_CATALOG = "/opt/pcatelegram_web/templates_catalog.json"
INSTALL_SH = "/opt/pcatelegram_web/install.sh"
PROMO_LINK_1 = "https://vk.cc/ct29NQ"
PROMO_LINK_2 = "https://vk.cc/cUxAhj"
TIP_LINK = "https://pay.cloudtips.ru/p/7410814f"
YOUTUBE_LINK = os.getenv("GOTELEGRAM_YOUTUBE_LINK", "").strip()
PROMO_STAMP_FILE = "/opt/gotelegram/.promo_bot_last_shown"
YOUTUBE_LINK = os.getenv("PCATELEGRAM_WEB_YOUTUBE_LINK", "").strip()
PROMO_STAMP_FILE = "/opt/pcatelegram_web/.promo_bot_last_shown"
BOT_TOKEN = os.getenv("BOT_TOKEN")
ENV_FILE = "/opt/gotelegram-bot/.env"
ADMIN_WEB_SERVICE = "gotelegram-admin"
ENV_FILE = "/opt/pcatelegram_web-bot/.env"
ADMIN_WEB_SERVICE = "pcatelegram_web-admin"
ADMIN_WEB_PORT = 1984
@@ -263,7 +263,7 @@ async def sh(*args, timeout: int = 60) -> Tuple[int, str, str]:
# Per-host mutex preventing concurrent install.sh --action invocations. Two
# admins hitting "change template" at the same second could race each other
# and corrupt /var/www/gotelegram-site. One global lock is fine — these are
# and corrupt /var/www/pcatelegram_web-site. One global lock is fine — these are
# rare operations and should serialize cleanly.
_BOT_ACTION_LOCK = asyncio.Lock()
@@ -279,7 +279,7 @@ _DOMAIN_RE = re.compile(
)
_USER_NAME_RE = re.compile(r"^[A-Za-z0-9_.-]{1,48}$")
MAX_UNIQUE_IP_LIMIT = 1000000
TELEMT_RESTART_DEBOUNCE_SECONDS = float(os.getenv("GOTELEGRAM_TELEMT_RESTART_DEBOUNCE", "8"))
TELEMT_RESTART_DEBOUNCE_SECONDS = float(os.getenv("PCATELEGRAM_WEB_TELEMT_RESTART_DEBOUNCE", "8"))
_LAST_TELEMT_RESTART = 0.0
@@ -391,7 +391,7 @@ def template_display_name(template_id: str) -> str:
if template_id in ("deployed_site", "existing_site"):
return "Existing deployed site"
if template_id.startswith("custom_"):
config = load_json(GOTELEGRAM_CONFIG) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
source = config.get("template_source", "")
return f"{template_id} ({source})" if source else template_id
catalog = load_json(TEMPLATES_CATALOG) or {}
@@ -652,7 +652,7 @@ async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
return
welcome = (
f"<b>{_tf(user_id, 'welcome_title', GOTELEGRAM_VERSION)}</b>\n\n"
f"<b>{_tf(user_id, 'welcome_title', PCATELEGRAM_WEB_VERSION)}</b>\n\n"
f"{_t(user_id, 'welcome_subtitle')}\n"
f"{_t(user_id, 'welcome_powered')}\n\n"
f"{_t(user_id, 'welcome_prompt')}"
@@ -750,10 +750,10 @@ async def get_status_text(user_id: Optional[int] = None) -> str:
lines.append(f"<b>{_t(user_id, 'status_telemt')}:</b> v{version}")
# Config status
config = load_json(GOTELEGRAM_CONFIG)
config = load_json(PCATELEGRAM_WEB_CONFIG)
if config:
lines.append(f"<b>{_t(user_id, 'status_mode')}:</b> {html.escape(str(config.get('mode', 'unknown')))}")
# install.sh/save_gotelegram_config uses "template_id" (not "template")
# install.sh/save_pcatelegram_web_config uses "template_id" (not "template")
tpl = config.get("template_id") or config.get("template")
if tpl:
lines.append(f"<b>{_t(user_id, 'status_template')}:</b> {html.escape(template_display_name(str(tpl)))}")
@@ -795,15 +795,15 @@ async def get_traffic_stats() -> str:
await sh(
"bash",
"-lc",
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/stats.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/stats.sh; "
"stats_init >/dev/null 2>&1 || true; stats_collect >/dev/null 2>&1 || true",
timeout=15,
)
# Read current snapshot
current_file = "/run/gotelegram/stats_current.json"
history_file = "/opt/gotelegram/stats_history.csv"
current_file = "/run/pcatelegram_web/stats_current.json"
history_file = "/opt/pcatelegram_web/stats_history.csv"
try:
with open(current_file, "r") as f:
@@ -1032,7 +1032,7 @@ async def cb_lite_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
await query.answer()
config = load_json(GOTELEGRAM_CONFIG) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
current_mode = config.get("mode", "")
if current_mode != "lite":
@@ -1040,7 +1040,7 @@ async def cb_lite_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
"<b>⚠️ Установка Lite из бота пока не поддерживается</b>\n\n"
f"Выбранный домен: <code>{html.escape(domain)}</code>\n\n"
"Чтобы установить Lite, запустите на сервере:\n"
"<code>gotelegram</code> → <b>1) Прокси → 1) Установить/Обновить → Lite</b>\n\n"
"<code>pcatelegram_web</code> → <b>1) Прокси → 1) Установить/Обновить → Lite</b>\n\n"
"Существующая конфигурация <b>не была изменена</b>."
)
keyboard = InlineKeyboardMarkup(
@@ -1185,7 +1185,7 @@ async def _download_custom_git_template(url_with_branch: str) -> Tuple[bool, str
url = base
tpl_id = "custom_" + hashlib.md5(url_with_branch.encode("utf-8")).hexdigest()[:10]
target_dir = f"/opt/gotelegram/custom_templates/{tpl_id}"
target_dir = f"/opt/pcatelegram_web/custom_templates/{tpl_id}"
# Clean previous copy
if os.path.isdir(target_dir):
@@ -1403,7 +1403,7 @@ async def cb_pro_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
return
# Read current config to decide: in-place change-template vs fresh install
config = load_json(GOTELEGRAM_CONFIG) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
current_mode = config.get("mode", "")
if current_mode != "pro":
@@ -1413,7 +1413,7 @@ async def cb_pro_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
f"Выбранный шаблон: <code>{html.escape(tpl_id)}</code>\n\n"
"Pro-режим требует ввода домена, email и проверки DNS. "
"Чтобы установить Pro, запустите на сервере:\n"
"<code>gotelegram</code> → <b>1) Прокси → 1) Установить/Обновить → Pro</b>\n\n"
"<code>pcatelegram_web</code> → <b>1) Прокси → 1) Установить/Обновить → Pro</b>\n\n"
"Существующая конфигурация <b>не была изменена</b>."
)
keyboard = InlineKeyboardMarkup(
@@ -1466,7 +1466,7 @@ async def cb_pro_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
+ (f" (<code>{html.escape(err_code)}</code>)" if err_code else "")
+ "\n\n"
"Существующая конфигурация <b>не была изменена</b>. "
"Попробуйте другой шаблон или запустите <code>gotelegram</code> из консоли."
"Попробуйте другой шаблон или запустите <code>pcatelegram_web</code> из консоли."
)
keyboard = InlineKeyboardMarkup(
@@ -1766,7 +1766,7 @@ def user_traffic_history_summary(name: str) -> str:
async def get_proxy_link_for_secret(secret: str) -> Optional[str]:
"""Generate a fake-TLS proxy link for an arbitrary telemt user secret."""
config = load_json(GOTELEGRAM_CONFIG) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
if not secret:
return None
@@ -1789,7 +1789,7 @@ async def get_proxy_link_for_secret(secret: str) -> Optional[str]:
async def get_proxy_link() -> Optional[str]:
"""Generate proxy link from config. Pro-mode uses domain + fake-TLS secret."""
config = load_json(GOTELEGRAM_CONFIG)
config = load_json(PCATELEGRAM_WEB_CONFIG)
if not config:
return None
@@ -1959,7 +1959,7 @@ async def _user_detail_text(name: str, secret: str, enabled: bool = True, max_un
compact = json.dumps(data, ensure_ascii=False)[:600]
details = f"\n<pre>{html.escape(compact)}</pre>"
elif enabled:
details = "\n<i>Runtime API недоступен. Новые установки goTelegram Pro включают его автоматически.</i>"
details = "\n<i>Runtime API недоступен. Новые установки PCAtelegram_web включают его автоматически.</i>"
else:
details = "\n<i>Ключ отключён и сейчас не принимается telemt.</i>"
details += user_traffic_history_summary(name)
@@ -2033,7 +2033,7 @@ async def cb_user_qr(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None
await query.answer("Ссылка недоступна", show_alert=True)
return
qr_file = f"/tmp/gotelegram_user_qr_{hashlib.sha256(name.encode()).hexdigest()[:10]}.png"
qr_file = f"/tmp/pcatelegram_web_user_qr_{hashlib.sha256(name.encode()).hexdigest()[:10]}.png"
code, _, _ = await sh("which", "qrencode")
if code == 0:
code, _, _ = await sh("qrencode", "-o", qr_file, link)
@@ -2340,11 +2340,11 @@ def backup_schedule_state() -> Dict[str, Any]:
async def run_full_backup() -> Tuple[bool, str]:
script = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/telemt.sh; "
"source /opt/gotelegram/lib/website.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/telemt.sh; "
"source /opt/pcatelegram_web/lib/website.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
"create_backup \"\"; "
"cleanup_old_backups 30"
@@ -2358,9 +2358,9 @@ async def set_full_backup_schedule(frequency: str) -> Tuple[bool, str]:
if frequency not in {"off", "daily", "weekly", "monthly"}:
return False, "unsupported schedule"
script = (
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
f"set_backup_schedule {shlex.quote(frequency)}"
)
@@ -2373,11 +2373,11 @@ async def launch_full_restore(backup_path: str) -> None:
quoted_path = shlex.quote(backup_path)
script = (
"sleep 1; "
"source /opt/gotelegram/lib/common.sh; "
"source /opt/gotelegram/lib/i18n.sh; "
"source /opt/gotelegram/lib/telemt.sh; "
"source /opt/gotelegram/lib/website.sh; "
"source /opt/gotelegram/lib/backup.sh; "
"source /opt/pcatelegram_web/lib/common.sh; "
"source /opt/pcatelegram_web/lib/i18n.sh; "
"source /opt/pcatelegram_web/lib/telemt.sh; "
"source /opt/pcatelegram_web/lib/website.sh; "
"source /opt/pcatelegram_web/lib/backup.sh; "
"load_language \"$(detect_language 2>/dev/null || echo en)\"; "
"create_backup \"\" >/dev/null 2>&1 || true; "
f"restore_backup {quoted_path} \"\" yes; "
@@ -2431,14 +2431,14 @@ async def cb_menu_backup(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
"<b>💾 Бекапы</b>\n\n"
f"Файлов: <code>{len(backups)}</code>\n"
f"Расписание: <b>{labels.get(schedule['frequency'], schedule['frequency'])}</b>\n\n"
"В бекап входит: telemt config, настройки goTelegram, ключи, отключённые ключи, сайт, шаблоны, SSL, бот, админка и история трафика."
"В бекап входит: telemt config, настройки PCAtelegram_web, ключи, отключённые ключи, сайт, шаблоны, SSL, бот, админка и история трафика."
)
else:
text = (
"<b>💾 Backups</b>\n\n"
f"Files: <code>{len(backups)}</code>\n"
f"Schedule: <b>{labels.get(schedule['frequency'], schedule['frequency'])}</b>\n\n"
"Backups include telemt config, goTelegram settings, keys, disabled keys, site, templates, SSL, bot, admin panel and traffic history."
"Backups include telemt config, PCAtelegram_web settings, keys, disabled keys, site, templates, SSL, bot, admin panel and traffic history."
)
keyboard = InlineKeyboardMarkup(buttons)
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
@@ -2583,7 +2583,7 @@ async def cb_restore_backup(update: Update, context: ContextTypes.DEFAULT_TYPE)
if not safe_path:
text = "❌ Файл бекапа не найден" if get_user_lang(user_id) == "ru" else "❌ Backup file not found"
elif safe_path.endswith(".enc"):
text = "❌ Зашифрованный бекап пока восстанавливается через CLI: gotelegram → Восстановить." if get_user_lang(user_id) == "ru" else "❌ Encrypted backups are restored from CLI for now: gotelegram → Restore."
text = "❌ Зашифрованный бекап пока восстанавливается через CLI: pcatelegram_web → Восстановить." if get_user_lang(user_id) == "ru" else "❌ Encrypted backups are restored from CLI for now: pcatelegram_web → Restore."
else:
await launch_full_restore(safe_path)
text = (
@@ -2759,7 +2759,7 @@ async def cb_ssl_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
async def admin_web_host_hint() -> str:
config = load_json(GOTELEGRAM_CONFIG) or {}
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
domain = str(config.get("domain") or "")
if domain:
return domain
@@ -2998,7 +2998,7 @@ async def cb_menu_credits(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
text = (
f"<b> Credits & Acknowledgements</b>\n\n"
f"<b>goTelegram Pro v{GOTELEGRAM_VERSION}</b>\n\n"
f"<b>PCAtelegram_web v{PCATELEGRAM_WEB_VERSION}</b>\n\n"
f"Built with love for the Telegram community\n\n"
f"<b>Special thanks to:</b>\n\n"
f"🙏 <b>telemt</b> - MTProxy engine\n"
@@ -3010,7 +3010,7 @@ async def cb_menu_credits(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
f"🚀 <b>Start Bootstrap</b> - Bootstrap templates\n"
f" Professional design framework\n\n"
f"💬 <b>Community</b> - Your feedback & support\n\n"
f"<i>goTelegram Pro is open-source and community-driven</i>"
f"<i>PCAtelegram_web is open-source and community-driven</i>"
)
keyboard = InlineKeyboardMarkup(
@@ -3030,7 +3030,7 @@ async def cb_menu_remove(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
await query.answer()
text = (
"<b>⚠️ Remove goTelegram Pro</b>\n\n"
"<b>⚠️ Remove PCAtelegram_web</b>\n\n"
"This will completely remove the installation.\n"
"Are you sure?"
)
@@ -3048,16 +3048,16 @@ async def cb_remove_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE)
query = update.callback_query
await query.answer()
await safe_edit_message(query,"⏳ Removing goTelegram Pro...")
await safe_edit_message(query,"⏳ Removing PCAtelegram_web...")
# Stop service
await sh("systemctl", "stop", TELEMT_SERVICE)
# Remove directories
for path in ["/opt/gotelegram", WEBSITE_ROOT]:
for path in ["/opt/pcatelegram_web", WEBSITE_ROOT]:
await sh("rm", "-rf", path)
text = "goTelegram Pro removed successfully"
text = "PCAtelegram_web removed successfully"
keyboard = InlineKeyboardMarkup(
[[InlineKeyboardButton(_t(_uid(update), "btn_back"), callback_data="menu_main")]]
)
@@ -3120,7 +3120,7 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
await query.answer()
buttons = get_main_menu(user_id)
text = (
f"<b>{_tf(user_id, 'welcome_title', GOTELEGRAM_VERSION)}</b>\n\n"
f"<b>{_tf(user_id, 'welcome_title', PCATELEGRAM_WEB_VERSION)}</b>\n\n"
f"{_t(user_id, 'welcome_subtitle')}\n"
f"{_t(user_id, 'welcome_prompt')}"
)
@@ -3158,7 +3158,7 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
# Re-render main menu in the new language
buttons = get_main_menu(user_id)
text = (
f"<b>{_tf(user_id, 'welcome_title', GOTELEGRAM_VERSION)}</b>\n\n"
f"<b>{_tf(user_id, 'welcome_title', PCATELEGRAM_WEB_VERSION)}</b>\n\n"
f"{_t(user_id, 'welcome_subtitle')}\n"
f"{_t(user_id, 'welcome_prompt')}"
)
@@ -3282,12 +3282,12 @@ async def handle_text_message(update: Update, context: ContextTypes.DEFAULT_TYPE
if not ok:
await update.message.reply_text(_t(user_id, info), parse_mode="HTML")
return
# Success — record in goTelegram Pro config. Use "template_id" (canonical
# field name written by install.sh/save_gotelegram_config).
config = load_json(GOTELEGRAM_CONFIG) or {}
# Success — record in PCAtelegram_web config. Use "template_id" (canonical
# field name written by install.sh/save_pcatelegram_web_config).
config = load_json(PCATELEGRAM_WEB_CONFIG) or {}
config["template_id"] = tpl_id
config["template_source"] = url
save_json(GOTELEGRAM_CONFIG, config)
save_json(PCATELEGRAM_WEB_CONFIG, config)
if config.get("mode") == "pro" and os.path.isdir(info):
try:
os.makedirs(WEBSITE_ROOT, exist_ok=True)
@@ -3353,7 +3353,7 @@ def main() -> None:
application.add_error_handler(error_handler)
# Run the bot
logger.info(f"goTelegram Pro v{GOTELEGRAM_VERSION} bot starting...")
logger.info(f"PCAtelegram_web v{PCATELEGRAM_WEB_VERSION} bot starting...")
application.run_polling(allowed_updates=Update.ALL_TYPES)

View File

@@ -1,4 +1,4 @@
# goTelegram Pro v2.5.0 Bot Configuration
# PCAtelegram_web v2.5.0 Bot Configuration
# Copy this to .env and fill in your values
# Telegram Bot Token from @BotFather

View File

@@ -1,5 +1,5 @@
"""
goTelegram Pro v2.5.0 Bot i18n module
PCAtelegram_web v2.5.0 Bot i18n module
Provides per-user language preferences and a simple t()/tf() API.
Usage:
@@ -23,9 +23,9 @@ logger = logging.getLogger(__name__)
# ── Paths ─────────────────────────────────────────────────────────────────
_MODULE_DIR = Path(__file__).resolve().parent
LANG_DIR = _MODULE_DIR / "lang"
USER_LANG_FILE = Path("/opt/gotelegram-bot/user_langs.json")
GOTELEGRAM_CONFIG = Path("/opt/gotelegram/config.json")
GOTELEGRAM_LANG_MARKER = Path("/opt/gotelegram/.language")
USER_LANG_FILE = Path("/opt/pcatelegram_web-bot/user_langs.json")
PCATELEGRAM_WEB_CONFIG = Path("/opt/pcatelegram_web/config.json")
PCATELEGRAM_WEB_LANG_MARKER = Path("/opt/pcatelegram_web/.language")
# Supported codes; keep in sync with lang/*.json
SUPPORTED_LANGS = ("en", "ru")
@@ -34,18 +34,18 @@ SUPPORTED_LANGS = ("en", "ru")
def _detect_default_lang() -> str:
candidates = []
try:
if GOTELEGRAM_CONFIG.exists():
with open(GOTELEGRAM_CONFIG, "r", encoding="utf-8") as f:
if PCATELEGRAM_WEB_CONFIG.exists():
with open(PCATELEGRAM_WEB_CONFIG, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
candidates.extend([data.get("language"), data.get("lang")])
except Exception as e:
logger.warning("failed to read goTelegram Pro language config: %s", e)
logger.warning("failed to read PCAtelegram_web language config: %s", e)
try:
if GOTELEGRAM_LANG_MARKER.exists():
candidates.append(GOTELEGRAM_LANG_MARKER.read_text(encoding="utf-8").strip()[:2])
if PCATELEGRAM_WEB_LANG_MARKER.exists():
candidates.append(PCATELEGRAM_WEB_LANG_MARKER.read_text(encoding="utf-8").strip()[:2])
except Exception as e:
logger.warning("failed to read goTelegram Pro language marker: %s", e)
logger.warning("failed to read PCAtelegram_web language marker: %s", e)
candidates.append(os.getenv("BOT_LANG", ""))
for raw in candidates:
code = str(raw or "").strip().lower()

View File

@@ -6,7 +6,7 @@
"lang_saved": "Language saved: %s",
"lang_choose": "Choose your language:",
"welcome_title": "goTelegram Pro v%s",
"welcome_title": "PCAtelegram_web v%s",
"welcome_subtitle": "🤖 MTProxy Management Bot",
"welcome_powered": "Powered by telemt engine",
"welcome_prompt": "Select an action from the menu below:",
@@ -17,7 +17,7 @@
"btn_no": "❌ No",
"access_denied": "⛔ Access denied.\nYour ID: <code>%s</code>",
"help_title": "goTelegram Pro Bot — Commands",
"help_title": "PCAtelegram_web Bot — Commands",
"help_lines": "/start — Main menu\n/help — This help\n/status — Quick status\n/logs — Latest logs\n/lang — Change language\n/addadmin ID — Add admin\n/deladmin ID — Remove admin\n\nUse the menu buttons for other operations.",
"menu_install": "⚙️ Install",

View File

@@ -6,7 +6,7 @@
"lang_saved": "Язык сохранён: %s",
"lang_choose": "Выберите язык:",
"welcome_title": "goTelegram Pro v%s",
"welcome_title": "PCAtelegram_web v%s",
"welcome_subtitle": "🤖 Бот управления MTProxy",
"welcome_powered": "На базе движка telemt",
"welcome_prompt": "Выберите действие в меню ниже:",
@@ -17,7 +17,7 @@
"btn_no": "❌ Нет",
"access_denied": "⛔ Доступ запрещён.\nВаш ID: <code>%s</code>",
"help_title": "goTelegram Pro Bot — Команды",
"help_title": "PCAtelegram_web Bot — Команды",
"help_lines": "/start — Главное меню\n/help — Эта справка\n/status — Быстрый статус\n/logs — Последние логи\n/lang — Сменить язык\n/addadmin ID — Добавить админа\n/deladmin ID — Удалить админа\n\nИспользуйте кнопки меню для остальных операций.",
"menu_install": "⚙️ Установить",