mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 11:26:03 +00:00
v2.2.1 hotfix: fix telemt download grep pattern (grep -iE), add QR cleanup, bump version to 2.2.1
This commit is contained in:
912
lib/common.sh
912
lib/common.sh
@@ -1,456 +1,456 @@
|
||||
#!/bin/bash
|
||||
# GoTelegram v2.2 — Общие утилиты
|
||||
# Цвета, логирование, спиннер, системные функции, совместимость с v1
|
||||
|
||||
# ── Версия ────────────────────────────────────────────────────────────────────
|
||||
GOTELEGRAM_VERSION="2.2.0"
|
||||
GOTELEGRAM_NAME="GoTelegram"
|
||||
|
||||
# ── Пути ──────────────────────────────────────────────────────────────────────
|
||||
GOTELEGRAM_DIR="/opt/gotelegram"
|
||||
GOTELEGRAM_CONFIG="$GOTELEGRAM_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"
|
||||
|
||||
# ── V1 совместимость ─────────────────────────────────────────────────────────
|
||||
V1_CONTAINER_NAME="mtproto-proxy"
|
||||
V1_CONFIG_FILE="/opt/gotelegram-bot/proxy.json"
|
||||
V1_SERVICE_NAME="gotelegram-bot"
|
||||
|
||||
# ── Цвета ────────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
YELLOW='\033[1;33m'
|
||||
MAGENTA='\033[0;35m'
|
||||
BLUE='\033[0;34m'
|
||||
WHITE='\033[1;37m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ── Логирование ──────────────────────────────────────────────────────────────
|
||||
log_info() { echo -e " ${CYAN}ℹ${NC} $*"; }
|
||||
log_success() { echo -e " ${GREEN}✓${NC} $*"; }
|
||||
log_warning() { echo -e " ${YELLOW}⚠${NC} $*"; }
|
||||
log_error() { echo -e " ${RED}✗${NC} $*"; }
|
||||
log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}"; }
|
||||
log_dim() { echo -e " ${DIM}$*${NC}"; }
|
||||
|
||||
log_to_file() {
|
||||
local ts; ts=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$ts] $*" >> "$LOG_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
# ── Спиннер ──────────────────────────────────────────────────────────────────
|
||||
_spin_pid=""
|
||||
spinner_start() {
|
||||
local msg="${1:-Подождите...}"
|
||||
(
|
||||
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||
local i=0
|
||||
while true; do
|
||||
printf "\r ${CYAN}${frames[$i]}${NC} ${msg}" >&2
|
||||
i=$(( (i+1) % ${#frames[@]} ))
|
||||
sleep 0.1
|
||||
done
|
||||
) &
|
||||
_spin_pid=$!
|
||||
}
|
||||
|
||||
spinner_stop() {
|
||||
[ -n "$_spin_pid" ] && kill "$_spin_pid" 2>/dev/null && wait "$_spin_pid" 2>/dev/null
|
||||
_spin_pid=""
|
||||
printf "\r\033[K" >&2
|
||||
}
|
||||
|
||||
# ── Прогресс-бар ─────────────────────────────────────────────────────────────
|
||||
progress_bar() {
|
||||
local current="$1" total="$2" label="${3:-}"
|
||||
local pct=$(( current * 100 / total ))
|
||||
local filled=$(( pct / 2 ))
|
||||
local empty=$(( 50 - filled ))
|
||||
local bar=""
|
||||
for ((i=0; i<filled; i++)); do bar+="█"; done
|
||||
for ((i=0; i<empty; i++)); do bar+="░"; done
|
||||
printf "\r ${GREEN}[${bar}]${NC} ${pct}%% ${label}" >&2
|
||||
[ "$current" -eq "$total" ] && echo "" >&2
|
||||
}
|
||||
|
||||
# ── Выполнение с индикатором ─────────────────────────────────────────────────
|
||||
run_with_spinner() {
|
||||
local label="$1"; shift
|
||||
local err_file="/tmp/.gotelegram_spinner_err_$$"
|
||||
spinner_start "$label"
|
||||
"$@" >/dev/null 2>"$err_file"
|
||||
local rc=$?
|
||||
spinner_stop
|
||||
if [ $rc -eq 0 ]; then
|
||||
log_success "$label"
|
||||
else
|
||||
log_error "$label ${RED}(ошибка, код: $rc)${NC}"
|
||||
if [ -s "$err_file" ]; then
|
||||
log_dim " $(head -3 "$err_file")"
|
||||
fi
|
||||
fi
|
||||
rm -f "$err_file"
|
||||
return $rc
|
||||
}
|
||||
|
||||
# ── Баннер ───────────────────────────────────────────────────────────────────
|
||||
show_banner() {
|
||||
echo ""
|
||||
echo -e "${CYAN}╔══════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║${NC} ${BOLD}${WHITE}🚀 GoTelegram v${GOTELEGRAM_VERSION}${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}║${NC} ${DIM}MTProxy на ядре telemt (Rust + Tokio)${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}║${NC} ${DIM}Anti-DPI • Fake TLS • TCP Splice • JA3/JA4${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}╚══════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Благодарности ────────────────────────────────────────────────────────────
|
||||
show_credits() {
|
||||
echo ""
|
||||
echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${BOLD}Благодарности / Credits${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}╟──────────────────────────────────────────────────────────╢${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}telemt${NC} — MTProxy engine (Rust) ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}github.com/telemt/telemt${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}HTML5 UP${NC} — адаптивные HTML/CSS шаблоны ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}html5up.net • CC BY 3.0 • @ajlkn${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}learning-zone${NC} — 150+ HTML5 шаблонов ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}github.com/learning-zone/website-templates${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}Start Bootstrap${NC} — MIT лицензия ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}startbootstrap.com${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Системные утилиты ────────────────────────────────────────────────────────
|
||||
_valid_ip() {
|
||||
# Validate that each octet is 0-255
|
||||
local ip="$1"
|
||||
local IFS='.'
|
||||
read -ra octets <<< "$ip"
|
||||
[ ${#octets[@]} -ne 4 ] && return 1
|
||||
for octet in "${octets[@]}"; do
|
||||
[[ "$octet" =~ ^[0-9]+$ ]] || return 1
|
||||
[ "$octet" -gt 255 ] && return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
get_server_ip() {
|
||||
local ip raw
|
||||
for url in "https://api.ipify.org" "https://icanhazip.com" "https://ifconfig.me"; do
|
||||
raw=$(curl -s -4 --max-time 5 "$url" 2>/dev/null)
|
||||
ip=$(echo "$raw" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
if [ -n "$ip" ] && _valid_ip "$ip"; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "0.0.0.0"
|
||||
return 1
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Запустите скрипт с sudo / от root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_os() {
|
||||
if [ ! -f /etc/os-release ]; then
|
||||
log_error "Не удалось определить ОС. Требуется Linux."
|
||||
return 1
|
||||
fi
|
||||
# Validate os-release before sourcing (reject command injection: ;, backticks, $())
|
||||
if grep -qE '(;|`|\$\(|\$\{)' /etc/os-release 2>/dev/null; then
|
||||
log_warning "/etc/os-release содержит подозрительные строки, пропускаем"
|
||||
return 0
|
||||
fi
|
||||
. /etc/os-release
|
||||
case "$ID" in
|
||||
ubuntu|debian|centos|rocky|almalinux|fedora|rhel)
|
||||
log_dim "ОС: $PRETTY_NAME"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
log_warning "ОС $ID может быть несовместима. Поддерживаются: Ubuntu, Debian, CentOS, Rocky."
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_arch() {
|
||||
local arch
|
||||
arch=$(uname -m)
|
||||
case "$arch" in
|
||||
x86_64|amd64) echo "amd64" ;;
|
||||
aarch64|arm64) echo "arm64" ;;
|
||||
armv7*|armhf) echo "armv7" ;;
|
||||
*) echo "$arch" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_pkg_manager() {
|
||||
if command -v apt-get &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v yum &>/dev/null; then echo "yum"
|
||||
else echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
install_pkg() {
|
||||
local pkg="$1"
|
||||
case "$(get_pkg_manager)" in
|
||||
apt) apt-get install -y -qq "$pkg" ;;
|
||||
dnf) dnf install -y -q "$pkg" ;;
|
||||
yum) yum install -y -q "$pkg" ;;
|
||||
*) log_error "Неизвестный пакетный менеджер"; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_deps() {
|
||||
local missing=()
|
||||
for cmd in curl jq openssl git; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
log_step "Установка зависимостей: ${missing[*]}"
|
||||
case "$(get_pkg_manager)" in
|
||||
apt) apt-get update -qq && apt-get install -y -qq "${missing[@]}" ;;
|
||||
dnf) dnf install -y -q "${missing[@]}" ;;
|
||||
yum) yum install -y -q "${missing[@]}" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
check_port() {
|
||||
local port="$1"
|
||||
local line
|
||||
line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
|
||||
[ -z "$line" ] && line=$(netstat -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
|
||||
if [ -n "$line" ]; then
|
||||
echo "$line"
|
||||
return 0 # порт занят
|
||||
fi
|
||||
return 1 # свободен
|
||||
}
|
||||
|
||||
check_disk_space() {
|
||||
local min_mb="${1:-500}"
|
||||
local avail_mb
|
||||
avail_mb=$(df -m / | awk 'NR==2 {print $4}')
|
||||
if [ "$avail_mb" -lt "$min_mb" ]; then
|
||||
log_error "Мало места на диске: ${avail_mb}MB (нужно ${min_mb}MB+)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Конфигурация GoTelegram (JSON) ──────────────────────────────────────────
|
||||
save_gotelegram_config() {
|
||||
mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")"
|
||||
cat > "$GOTELEGRAM_CONFIG" << EOJSON
|
||||
{
|
||||
"version": "$GOTELEGRAM_VERSION",
|
||||
"engine": "${1:-telemt}",
|
||||
"mode": "${2:-quick}",
|
||||
"port": ${3:-443},
|
||||
"secret": "${4:-}",
|
||||
"mask_host": "${5:-google.com}",
|
||||
"domain": "${6:-}",
|
||||
"template_id": "${7:-}",
|
||||
"installed_at": "$(date -Iseconds)",
|
||||
"updated_at": "$(date -Iseconds)"
|
||||
}
|
||||
EOJSON
|
||||
chmod 600 "$GOTELEGRAM_CONFIG"
|
||||
}
|
||||
|
||||
load_gotelegram_config() {
|
||||
if [ -f "$GOTELEGRAM_CONFIG" ]; then
|
||||
cat "$GOTELEGRAM_CONFIG"
|
||||
return 0
|
||||
fi
|
||||
echo "{}"
|
||||
return 1
|
||||
}
|
||||
|
||||
config_get() {
|
||||
local key="$1"
|
||||
if [ ! -f "$GOTELEGRAM_CONFIG" ]; then
|
||||
log_dim "Конфиг не найден: $GOTELEGRAM_CONFIG" >&2
|
||||
return 2 # file missing
|
||||
fi
|
||||
local val
|
||||
val=$(jq -r ".$key // empty" "$GOTELEGRAM_CONFIG" 2>/dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
log_dim "Ошибка чтения JSON: $GOTELEGRAM_CONFIG" >&2
|
||||
return 3 # invalid JSON
|
||||
fi
|
||||
if [ -z "$val" ]; then
|
||||
return 1 # key missing or empty
|
||||
fi
|
||||
echo "$val"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── V1 совместимость ─────────────────────────────────────────────────────────
|
||||
detect_v1_installation() {
|
||||
# Проверяем наличие mtg Docker контейнера (v1)
|
||||
if command -v docker &>/dev/null; then
|
||||
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${V1_CONTAINER_NAME}$"; then
|
||||
return 0 # v1 обнаружена
|
||||
fi
|
||||
fi
|
||||
# Проверяем наличие конфига v1
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
get_v1_config() {
|
||||
# Извлекаем данные из работающего v1 контейнера
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "{}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local running
|
||||
running=$(docker ps --format '{{.Names}}' 2>/dev/null | grep "^${V1_CONTAINER_NAME}$")
|
||||
|
||||
if [ -z "$running" ]; then
|
||||
# Пробуем из сохранённого конфига
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
cat "$V1_CONFIG_FILE"
|
||||
return 0
|
||||
fi
|
||||
echo "{}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Достаём из Docker
|
||||
local cmd_str port secret ip
|
||||
cmd_str=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range .Config.Cmd}}{{.}} {{end}}' 2>/dev/null)
|
||||
secret=$(echo "$cmd_str" | awk '{print $NF}')
|
||||
port=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}}{{end}}' 2>/dev/null)
|
||||
ip=$(get_server_ip)
|
||||
|
||||
jq -n \
|
||||
--arg secret "$secret" \
|
||||
--arg port "${port:-443}" \
|
||||
--arg ip "$ip" \
|
||||
'{secret: $secret, port: ($port | tonumber), ip: $ip, engine: "mtg"}'
|
||||
}
|
||||
|
||||
migrate_v1_to_v2() {
|
||||
log_step "Миграция с v1 (mtg) на v2 (telemt)"
|
||||
|
||||
local v1_config
|
||||
v1_config=$(get_v1_config)
|
||||
|
||||
local old_port old_secret
|
||||
old_port=$(echo "$v1_config" | jq -r '.port // 443')
|
||||
old_secret=$(echo "$v1_config" | jq -r '.secret // empty')
|
||||
|
||||
if [ -z "$old_secret" ]; then
|
||||
log_warning "Не удалось извлечь secret из v1. Будет создан новый."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${WHITE}Найдена установка v1 (mtg):${NC}"
|
||||
echo -e " Порт: ${CYAN}${old_port}${NC}"
|
||||
echo -e " Secret: ${CYAN}${old_secret:0:16}...${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Внимание:${NC} секрет mtg НЕ совместим с telemt напрямую."
|
||||
echo -e " Клиентам потребуется новая ссылка."
|
||||
echo ""
|
||||
echo -ne " Остановить v1 контейнер и перейти на v2? [Y/n]: "
|
||||
read -r ans
|
||||
if [[ "$ans" =~ ^[Nn] ]]; then
|
||||
log_info "Миграция отменена. v1 оставлен без изменений."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Останавливаем v1
|
||||
log_info "Остановка v1 контейнера..."
|
||||
docker stop "$V1_CONTAINER_NAME" 2>/dev/null
|
||||
docker rm "$V1_CONTAINER_NAME" 2>/dev/null
|
||||
|
||||
# Бекапим v1 конфиг
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
mkdir -p "$GOTELEGRAM_DIR"
|
||||
cp "$V1_CONFIG_FILE" "$GOTELEGRAM_DIR/v1_backup_proxy.json" 2>/dev/null
|
||||
log_success "Конфиг v1 сохранён в $GOTELEGRAM_DIR/v1_backup_proxy.json"
|
||||
fi
|
||||
|
||||
log_success "v1 остановлен. Порт $old_port освобождён."
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Подтверждение ────────────────────────────────────────────────────────────
|
||||
confirm() {
|
||||
local msg="${1:-Продолжить?}"
|
||||
echo -ne " ${msg} [Y/n]: "
|
||||
read -r ans
|
||||
[[ ! "$ans" =~ ^[Nn] ]]
|
||||
}
|
||||
|
||||
# ── Выбор из списка ──────────────────────────────────────────────────────────
|
||||
select_option() {
|
||||
local title="$1"
|
||||
shift
|
||||
local options=("$@")
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${WHITE}${title}${NC}"
|
||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||
local i=1
|
||||
for opt in "${options[@]}"; do
|
||||
echo -e " ${CYAN}${i})${NC} ${opt}"
|
||||
((i++))
|
||||
done
|
||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||
echo -ne " ${WHITE}Выбор:${NC} "
|
||||
read -r choice
|
||||
echo "$choice"
|
||||
}
|
||||
|
||||
# ── Генерация случайного hex ─────────────────────────────────────────────────
|
||||
generate_hex() {
|
||||
local len="${1:-32}"
|
||||
openssl rand -hex "$((len/2))" 2>/dev/null || head -c "$((len/2))" /dev/urandom | xxd -p | tr -d '\n'
|
||||
}
|
||||
|
||||
# ── Проверка домена ──────────────────────────────────────────────────────────
|
||||
validate_domain() {
|
||||
local domain="$1"
|
||||
if echo "$domain" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Init: создание директорий ────────────────────────────────────────────────
|
||||
init_dirs() {
|
||||
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null
|
||||
touch "$LOG_FILE" 2>/dev/null
|
||||
}
|
||||
#!/bin/bash
|
||||
# GoTelegram v2.2 — Общие утилиты
|
||||
# Цвета, логирование, спиннер, системные функции, совместимость с v1
|
||||
|
||||
# ── Версия ────────────────────────────────────────────────────────────────────
|
||||
GOTELEGRAM_VERSION="2.2.1"
|
||||
GOTELEGRAM_NAME="GoTelegram"
|
||||
|
||||
# ── Пути ──────────────────────────────────────────────────────────────────────
|
||||
GOTELEGRAM_DIR="/opt/gotelegram"
|
||||
GOTELEGRAM_CONFIG="$GOTELEGRAM_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"
|
||||
|
||||
# ── V1 совместимость ─────────────────────────────────────────────────────────
|
||||
V1_CONTAINER_NAME="mtproto-proxy"
|
||||
V1_CONFIG_FILE="/opt/gotelegram-bot/proxy.json"
|
||||
V1_SERVICE_NAME="gotelegram-bot"
|
||||
|
||||
# ── Цвета ────────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
YELLOW='\033[1;33m'
|
||||
MAGENTA='\033[0;35m'
|
||||
BLUE='\033[0;34m'
|
||||
WHITE='\033[1;37m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ── Логирование ──────────────────────────────────────────────────────────────
|
||||
log_info() { echo -e " ${CYAN}ℹ${NC} $*"; }
|
||||
log_success() { echo -e " ${GREEN}✓${NC} $*"; }
|
||||
log_warning() { echo -e " ${YELLOW}⚠${NC} $*"; }
|
||||
log_error() { echo -e " ${RED}✗${NC} $*"; }
|
||||
log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}"; }
|
||||
log_dim() { echo -e " ${DIM}$*${NC}"; }
|
||||
|
||||
log_to_file() {
|
||||
local ts; ts=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$ts] $*" >> "$LOG_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
# ── Спиннер ──────────────────────────────────────────────────────────────────
|
||||
_spin_pid=""
|
||||
spinner_start() {
|
||||
local msg="${1:-Подождите...}"
|
||||
(
|
||||
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||
local i=0
|
||||
while true; do
|
||||
printf "\r ${CYAN}${frames[$i]}${NC} ${msg}" >&2
|
||||
i=$(( (i+1) % ${#frames[@]} ))
|
||||
sleep 0.1
|
||||
done
|
||||
) &
|
||||
_spin_pid=$!
|
||||
}
|
||||
|
||||
spinner_stop() {
|
||||
[ -n "$_spin_pid" ] && kill "$_spin_pid" 2>/dev/null && wait "$_spin_pid" 2>/dev/null
|
||||
_spin_pid=""
|
||||
printf "\r\033[K" >&2
|
||||
}
|
||||
|
||||
# ── Прогресс-бар ─────────────────────────────────────────────────────────────
|
||||
progress_bar() {
|
||||
local current="$1" total="$2" label="${3:-}"
|
||||
local pct=$(( current * 100 / total ))
|
||||
local filled=$(( pct / 2 ))
|
||||
local empty=$(( 50 - filled ))
|
||||
local bar=""
|
||||
for ((i=0; i<filled; i++)); do bar+="█"; done
|
||||
for ((i=0; i<empty; i++)); do bar+="░"; done
|
||||
printf "\r ${GREEN}[${bar}]${NC} ${pct}%% ${label}" >&2
|
||||
[ "$current" -eq "$total" ] && echo "" >&2
|
||||
}
|
||||
|
||||
# ── Выполнение с индикатором ─────────────────────────────────────────────────
|
||||
run_with_spinner() {
|
||||
local label="$1"; shift
|
||||
local err_file="/tmp/.gotelegram_spinner_err_$$"
|
||||
spinner_start "$label"
|
||||
"$@" >/dev/null 2>"$err_file"
|
||||
local rc=$?
|
||||
spinner_stop
|
||||
if [ $rc -eq 0 ]; then
|
||||
log_success "$label"
|
||||
else
|
||||
log_error "$label ${RED}(ошибка, код: $rc)${NC}"
|
||||
if [ -s "$err_file" ]; then
|
||||
log_dim " $(head -3 "$err_file")"
|
||||
fi
|
||||
fi
|
||||
rm -f "$err_file"
|
||||
return $rc
|
||||
}
|
||||
|
||||
# ── Баннер ───────────────────────────────────────────────────────────────────
|
||||
show_banner() {
|
||||
echo ""
|
||||
echo -e "${CYAN}╔══════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║${NC} ${BOLD}${WHITE}🚀 GoTelegram v${GOTELEGRAM_VERSION}${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}║${NC} ${DIM}MTProxy на ядре telemt (Rust + Tokio)${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}║${NC} ${DIM}Anti-DPI • Fake TLS • TCP Splice • JA3/JA4${NC} ${CYAN}║${NC}"
|
||||
echo -e "${CYAN}╚══════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Благодарности ────────────────────────────────────────────────────────────
|
||||
show_credits() {
|
||||
echo ""
|
||||
echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${BOLD}Благодарности / Credits${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}╟──────────────────────────────────────────────────────────╢${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}telemt${NC} — MTProxy engine (Rust) ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}github.com/telemt/telemt${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}HTML5 UP${NC} — адаптивные HTML/CSS шаблоны ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}html5up.net • CC BY 3.0 • @ajlkn${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}learning-zone${NC} — 150+ HTML5 шаблонов ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}github.com/learning-zone/website-templates${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${WHITE}Start Bootstrap${NC} — MIT лицензия ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}║${NC} ${DIM}startbootstrap.com${NC} ${MAGENTA}║${NC}"
|
||||
echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ── Системные утилиты ────────────────────────────────────────────────────────
|
||||
_valid_ip() {
|
||||
# Validate that each octet is 0-255
|
||||
local ip="$1"
|
||||
local IFS='.'
|
||||
read -ra octets <<< "$ip"
|
||||
[ ${#octets[@]} -ne 4 ] && return 1
|
||||
for octet in "${octets[@]}"; do
|
||||
[[ "$octet" =~ ^[0-9]+$ ]] || return 1
|
||||
[ "$octet" -gt 255 ] && return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
get_server_ip() {
|
||||
local ip raw
|
||||
for url in "https://api.ipify.org" "https://icanhazip.com" "https://ifconfig.me"; do
|
||||
raw=$(curl -s -4 --max-time 5 "$url" 2>/dev/null)
|
||||
ip=$(echo "$raw" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
if [ -n "$ip" ] && _valid_ip "$ip"; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo "0.0.0.0"
|
||||
return 1
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Запустите скрипт с sudo / от root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_os() {
|
||||
if [ ! -f /etc/os-release ]; then
|
||||
log_error "Не удалось определить ОС. Требуется Linux."
|
||||
return 1
|
||||
fi
|
||||
# Validate os-release before sourcing (reject command injection: ;, backticks, $())
|
||||
if grep -qE '(;|`|\$\(|\$\{)' /etc/os-release 2>/dev/null; then
|
||||
log_warning "/etc/os-release содержит подозрительные строки, пропускаем"
|
||||
return 0
|
||||
fi
|
||||
. /etc/os-release
|
||||
case "$ID" in
|
||||
ubuntu|debian|centos|rocky|almalinux|fedora|rhel)
|
||||
log_dim "ОС: $PRETTY_NAME"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
log_warning "ОС $ID может быть несовместима. Поддерживаются: Ubuntu, Debian, CentOS, Rocky."
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_arch() {
|
||||
local arch
|
||||
arch=$(uname -m)
|
||||
case "$arch" in
|
||||
x86_64|amd64) echo "amd64" ;;
|
||||
aarch64|arm64) echo "arm64" ;;
|
||||
armv7*|armhf) echo "armv7" ;;
|
||||
*) echo "$arch" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_pkg_manager() {
|
||||
if command -v apt-get &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v yum &>/dev/null; then echo "yum"
|
||||
else echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
install_pkg() {
|
||||
local pkg="$1"
|
||||
case "$(get_pkg_manager)" in
|
||||
apt) apt-get install -y -qq "$pkg" ;;
|
||||
dnf) dnf install -y -q "$pkg" ;;
|
||||
yum) yum install -y -q "$pkg" ;;
|
||||
*) log_error "Неизвестный пакетный менеджер"; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
ensure_deps() {
|
||||
local missing=()
|
||||
for cmd in curl jq openssl git; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
log_step "Установка зависимостей: ${missing[*]}"
|
||||
case "$(get_pkg_manager)" in
|
||||
apt) apt-get update -qq && apt-get install -y -qq "${missing[@]}" ;;
|
||||
dnf) dnf install -y -q "${missing[@]}" ;;
|
||||
yum) yum install -y -q "${missing[@]}" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
check_port() {
|
||||
local port="$1"
|
||||
local line
|
||||
line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
|
||||
[ -z "$line" ] && line=$(netstat -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
|
||||
if [ -n "$line" ]; then
|
||||
echo "$line"
|
||||
return 0 # порт занят
|
||||
fi
|
||||
return 1 # свободен
|
||||
}
|
||||
|
||||
check_disk_space() {
|
||||
local min_mb="${1:-500}"
|
||||
local avail_mb
|
||||
avail_mb=$(df -m / | awk 'NR==2 {print $4}')
|
||||
if [ "$avail_mb" -lt "$min_mb" ]; then
|
||||
log_error "Мало места на диске: ${avail_mb}MB (нужно ${min_mb}MB+)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Конфигурация GoTelegram (JSON) ──────────────────────────────────────────
|
||||
save_gotelegram_config() {
|
||||
mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")"
|
||||
cat > "$GOTELEGRAM_CONFIG" << EOJSON
|
||||
{
|
||||
"version": "$GOTELEGRAM_VERSION",
|
||||
"engine": "${1:-telemt}",
|
||||
"mode": "${2:-quick}",
|
||||
"port": ${3:-443},
|
||||
"secret": "${4:-}",
|
||||
"mask_host": "${5:-google.com}",
|
||||
"domain": "${6:-}",
|
||||
"template_id": "${7:-}",
|
||||
"installed_at": "$(date -Iseconds)",
|
||||
"updated_at": "$(date -Iseconds)"
|
||||
}
|
||||
EOJSON
|
||||
chmod 600 "$GOTELEGRAM_CONFIG"
|
||||
}
|
||||
|
||||
load_gotelegram_config() {
|
||||
if [ -f "$GOTELEGRAM_CONFIG" ]; then
|
||||
cat "$GOTELEGRAM_CONFIG"
|
||||
return 0
|
||||
fi
|
||||
echo "{}"
|
||||
return 1
|
||||
}
|
||||
|
||||
config_get() {
|
||||
local key="$1"
|
||||
if [ ! -f "$GOTELEGRAM_CONFIG" ]; then
|
||||
log_dim "Конфиг не найден: $GOTELEGRAM_CONFIG" >&2
|
||||
return 2 # file missing
|
||||
fi
|
||||
local val
|
||||
val=$(jq -r ".$key // empty" "$GOTELEGRAM_CONFIG" 2>/dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
log_dim "Ошибка чтения JSON: $GOTELEGRAM_CONFIG" >&2
|
||||
return 3 # invalid JSON
|
||||
fi
|
||||
if [ -z "$val" ]; then
|
||||
return 1 # key missing or empty
|
||||
fi
|
||||
echo "$val"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── V1 совместимость ─────────────────────────────────────────────────────────
|
||||
detect_v1_installation() {
|
||||
# Проверяем наличие mtg Docker контейнера (v1)
|
||||
if command -v docker &>/dev/null; then
|
||||
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${V1_CONTAINER_NAME}$"; then
|
||||
return 0 # v1 обнаружена
|
||||
fi
|
||||
fi
|
||||
# Проверяем наличие конфига v1
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
get_v1_config() {
|
||||
# Извлекаем данные из работающего v1 контейнера
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "{}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local running
|
||||
running=$(docker ps --format '{{.Names}}' 2>/dev/null | grep "^${V1_CONTAINER_NAME}$")
|
||||
|
||||
if [ -z "$running" ]; then
|
||||
# Пробуем из сохранённого конфига
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
cat "$V1_CONFIG_FILE"
|
||||
return 0
|
||||
fi
|
||||
echo "{}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Достаём из Docker
|
||||
local cmd_str port secret ip
|
||||
cmd_str=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range .Config.Cmd}}{{.}} {{end}}' 2>/dev/null)
|
||||
secret=$(echo "$cmd_str" | awk '{print $NF}')
|
||||
port=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}}{{end}}' 2>/dev/null)
|
||||
ip=$(get_server_ip)
|
||||
|
||||
jq -n \
|
||||
--arg secret "$secret" \
|
||||
--arg port "${port:-443}" \
|
||||
--arg ip "$ip" \
|
||||
'{secret: $secret, port: ($port | tonumber), ip: $ip, engine: "mtg"}'
|
||||
}
|
||||
|
||||
migrate_v1_to_v2() {
|
||||
log_step "Миграция с v1 (mtg) на v2 (telemt)"
|
||||
|
||||
local v1_config
|
||||
v1_config=$(get_v1_config)
|
||||
|
||||
local old_port old_secret
|
||||
old_port=$(echo "$v1_config" | jq -r '.port // 443')
|
||||
old_secret=$(echo "$v1_config" | jq -r '.secret // empty')
|
||||
|
||||
if [ -z "$old_secret" ]; then
|
||||
log_warning "Не удалось извлечь secret из v1. Будет создан новый."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${WHITE}Найдена установка v1 (mtg):${NC}"
|
||||
echo -e " Порт: ${CYAN}${old_port}${NC}"
|
||||
echo -e " Secret: ${CYAN}${old_secret:0:16}...${NC}"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Внимание:${NC} секрет mtg НЕ совместим с telemt напрямую."
|
||||
echo -e " Клиентам потребуется новая ссылка."
|
||||
echo ""
|
||||
echo -ne " Остановить v1 контейнер и перейти на v2? [Y/n]: "
|
||||
read -r ans
|
||||
if [[ "$ans" =~ ^[Nn] ]]; then
|
||||
log_info "Миграция отменена. v1 оставлен без изменений."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Останавливаем v1
|
||||
log_info "Остановка v1 контейнера..."
|
||||
docker stop "$V1_CONTAINER_NAME" 2>/dev/null
|
||||
docker rm "$V1_CONTAINER_NAME" 2>/dev/null
|
||||
|
||||
# Бекапим v1 конфиг
|
||||
if [ -f "$V1_CONFIG_FILE" ]; then
|
||||
mkdir -p "$GOTELEGRAM_DIR"
|
||||
cp "$V1_CONFIG_FILE" "$GOTELEGRAM_DIR/v1_backup_proxy.json" 2>/dev/null
|
||||
log_success "Конфиг v1 сохранён в $GOTELEGRAM_DIR/v1_backup_proxy.json"
|
||||
fi
|
||||
|
||||
log_success "v1 остановлен. Порт $old_port освобождён."
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Подтверждение ────────────────────────────────────────────────────────────
|
||||
confirm() {
|
||||
local msg="${1:-Продолжить?}"
|
||||
echo -ne " ${msg} [Y/n]: "
|
||||
read -r ans
|
||||
[[ ! "$ans" =~ ^[Nn] ]]
|
||||
}
|
||||
|
||||
# ── Выбор из списка ──────────────────────────────────────────────────────────
|
||||
select_option() {
|
||||
local title="$1"
|
||||
shift
|
||||
local options=("$@")
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}${WHITE}${title}${NC}"
|
||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||
local i=1
|
||||
for opt in "${options[@]}"; do
|
||||
echo -e " ${CYAN}${i})${NC} ${opt}"
|
||||
((i++))
|
||||
done
|
||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||
echo -ne " ${WHITE}Выбор:${NC} "
|
||||
read -r choice
|
||||
echo "$choice"
|
||||
}
|
||||
|
||||
# ── Генерация случайного hex ─────────────────────────────────────────────────
|
||||
generate_hex() {
|
||||
local len="${1:-32}"
|
||||
openssl rand -hex "$((len/2))" 2>/dev/null || head -c "$((len/2))" /dev/urandom | xxd -p | tr -d '\n'
|
||||
}
|
||||
|
||||
# ── Проверка домена ──────────────────────────────────────────────────────────
|
||||
validate_domain() {
|
||||
local domain="$1"
|
||||
if echo "$domain" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Init: создание директорий ────────────────────────────────────────────────
|
||||
init_dirs() {
|
||||
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null
|
||||
touch "$LOG_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
610
lib/telemt.sh
610
lib/telemt.sh
@@ -1,305 +1,305 @@
|
||||
#!/bin/bash
|
||||
# GoTelegram v2.2 — Управление telemt binary
|
||||
# Скачивание, обновление, запуск, остановка через systemd
|
||||
|
||||
TELEMT_GITHUB="telemt/telemt"
|
||||
TELEMT_RELEASE_API="https://api.github.com/repos/${TELEMT_GITHUB}/releases/latest"
|
||||
TELEMT_USER="telemt"
|
||||
TELEMT_GROUP="telemt"
|
||||
|
||||
# ── Получение последней версии ───────────────────────────────────────────────
|
||||
get_latest_telemt_version() {
|
||||
local resp
|
||||
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
|
||||
if [ $? -ne 0 ] || [ -z "$resp" ]; then
|
||||
log_error "Не удалось получить информацию о релизах telemt"
|
||||
return 1
|
||||
fi
|
||||
echo "$resp" | jq -r '.tag_name // empty'
|
||||
}
|
||||
|
||||
get_telemt_download_url() {
|
||||
local arch
|
||||
arch=$(get_arch)
|
||||
local resp
|
||||
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
|
||||
if [ -z "$resp" ]; then return 1; fi
|
||||
|
||||
local pattern
|
||||
case "$arch" in
|
||||
amd64) pattern="linux.*amd64\|linux.*x86_64" ;;
|
||||
arm64) pattern="linux.*arm64\|linux.*aarch64" ;;
|
||||
armv7) pattern="linux.*armv7\|linux.*arm" ;;
|
||||
*) pattern="linux.*${arch}" ;;
|
||||
esac
|
||||
|
||||
echo "$resp" | jq -r ".assets[].browser_download_url" 2>/dev/null | grep -i "$pattern" | head -1
|
||||
}
|
||||
|
||||
# ── Установленная версия ─────────────────────────────────────────────────────
|
||||
get_installed_telemt_version() {
|
||||
if [ -x "$TELEMT_BIN" ]; then
|
||||
"$TELEMT_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
is_telemt_installed() {
|
||||
[ -x "$TELEMT_BIN" ]
|
||||
}
|
||||
|
||||
# ── Скачивание и установка ───────────────────────────────────────────────────
|
||||
download_telemt() {
|
||||
local url
|
||||
url=$(get_telemt_download_url)
|
||||
if [ -z "$url" ]; then
|
||||
log_error "Не найден бинарник telemt для архитектуры $(get_arch)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tmp_file="/tmp/telemt_download_$$"
|
||||
log_info "Скачивание: $url"
|
||||
|
||||
if ! curl -L -s --max-time 120 -o "$tmp_file" "$url"; then
|
||||
log_error "Ошибка скачивания telemt"
|
||||
rm -f "$tmp_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Определяем тип файла и распаковываем
|
||||
local mime
|
||||
mime=$(file -b --mime-type "$tmp_file" 2>/dev/null)
|
||||
|
||||
case "$mime" in
|
||||
application/gzip|application/x-gzip)
|
||||
tar xzf "$tmp_file" -C /tmp/ 2>/dev/null
|
||||
local extracted
|
||||
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
|
||||
if [ -z "$extracted" ]; then
|
||||
# Может быть просто gzip без tar
|
||||
gunzip -c "$tmp_file" > /tmp/telemt_bin_$$ 2>/dev/null
|
||||
extracted="/tmp/telemt_bin_$$"
|
||||
fi
|
||||
;;
|
||||
application/x-tar)
|
||||
tar xf "$tmp_file" -C /tmp/ 2>/dev/null
|
||||
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
|
||||
;;
|
||||
application/zip)
|
||||
unzip -o "$tmp_file" -d /tmp/telemt_extract_$$ 2>/dev/null
|
||||
extracted=$(find /tmp/telemt_extract_$$ -name "telemt" -type f 2>/dev/null | head -1)
|
||||
;;
|
||||
application/octet-stream|application/x-executable)
|
||||
# Уже бинарник
|
||||
extracted="$tmp_file"
|
||||
;;
|
||||
*)
|
||||
# Пробуем как бинарник
|
||||
extracted="$tmp_file"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$extracted" ] || [ ! -f "$extracted" ]; then
|
||||
log_error "Не удалось извлечь бинарник telemt"
|
||||
rm -f "$tmp_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Устанавливаем
|
||||
cp "$extracted" "$TELEMT_BIN"
|
||||
chmod 755 "$TELEMT_BIN"
|
||||
rm -f "$tmp_file"
|
||||
rm -rf /tmp/telemt_extract_$$ /tmp/telemt_bin_$$
|
||||
|
||||
# Проверяем
|
||||
if "$TELEMT_BIN" --version &>/dev/null; then
|
||||
log_success "telemt $(get_installed_telemt_version) установлен в $TELEMT_BIN"
|
||||
return 0
|
||||
else
|
||||
log_error "Бинарник telemt не запускается"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Системный пользователь ───────────────────────────────────────────────────
|
||||
create_telemt_user() {
|
||||
if ! id "$TELEMT_USER" &>/dev/null; then
|
||||
useradd -r -s /usr/sbin/nologin -d /etc/telemt "$TELEMT_USER" 2>/dev/null
|
||||
log_dim "Создан системный пользователь: $TELEMT_USER"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Systemd сервис ───────────────────────────────────────────────────────────
|
||||
install_telemt_service() {
|
||||
local config_path="${1:-$TELEMT_CONFIG}"
|
||||
|
||||
cat > "/etc/systemd/system/${TELEMT_SERVICE}.service" << EOF
|
||||
[Unit]
|
||||
Description=GoTelegram MTProxy (telemt engine)
|
||||
Documentation=https://github.com/telemt/telemt
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=$TELEMT_BIN run $config_path
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=65535
|
||||
|
||||
# Безопасность
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/etc/telemt /var/log
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
log_success "Systemd сервис $TELEMT_SERVICE создан"
|
||||
}
|
||||
|
||||
# ── Управление сервисом ──────────────────────────────────────────────────────
|
||||
start_telemt() {
|
||||
systemctl start "$TELEMT_SERVICE" 2>/dev/null
|
||||
sleep 2
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE"; then
|
||||
log_success "telemt запущен"
|
||||
return 0
|
||||
else
|
||||
log_error "telemt не запустился"
|
||||
journalctl -u "$TELEMT_SERVICE" --no-pager -n 10 2>/dev/null
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_telemt() {
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
systemctl stop "$TELEMT_SERVICE"
|
||||
log_success "telemt остановлен"
|
||||
else
|
||||
log_dim "telemt уже остановлен"
|
||||
fi
|
||||
}
|
||||
|
||||
restart_telemt() {
|
||||
systemctl restart "$TELEMT_SERVICE" 2>/dev/null
|
||||
sleep 2
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE"; then
|
||||
log_success "telemt перезапущен"
|
||||
return 0
|
||||
else
|
||||
log_error "telemt не перезапустился"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
enable_telemt() {
|
||||
systemctl enable "$TELEMT_SERVICE" 2>/dev/null
|
||||
}
|
||||
|
||||
telemt_status() {
|
||||
if ! is_telemt_installed; then
|
||||
echo "not_installed"
|
||||
return
|
||||
fi
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
echo "running"
|
||||
elif systemctl is-enabled --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
echo "stopped"
|
||||
else
|
||||
echo "disabled"
|
||||
fi
|
||||
}
|
||||
|
||||
telemt_logs() {
|
||||
local lines="${1:-40}"
|
||||
journalctl -u "$TELEMT_SERVICE" --no-pager -n "$lines" 2>/dev/null
|
||||
}
|
||||
|
||||
telemt_uptime() {
|
||||
local started
|
||||
started=$(systemctl show "$TELEMT_SERVICE" --property=ActiveEnterTimestamp --value 2>/dev/null)
|
||||
if [ -n "$started" ] && [ "$started" != "" ]; then
|
||||
echo "$started"
|
||||
else
|
||||
echo "N/A"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Обновление ───────────────────────────────────────────────────────────────
|
||||
check_telemt_update() {
|
||||
local current latest
|
||||
current=$(get_installed_telemt_version)
|
||||
latest=$(get_latest_telemt_version)
|
||||
|
||||
if [ -z "$current" ] || [ -z "$latest" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$current" != "$latest" ]; then
|
||||
echo "$latest"
|
||||
return 0 # есть обновление
|
||||
fi
|
||||
return 1 # актуально
|
||||
}
|
||||
|
||||
update_telemt() {
|
||||
local latest
|
||||
latest=$(check_telemt_update)
|
||||
if [ $? -ne 0 ]; then
|
||||
log_info "telemt уже последней версии ($(get_installed_telemt_version))"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Доступно обновление: $(get_installed_telemt_version) → $latest"
|
||||
if ! confirm "Обновить telemt?"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
stop_telemt
|
||||
if download_telemt; then
|
||||
start_telemt
|
||||
log_success "telemt обновлён до $latest"
|
||||
else
|
||||
start_telemt # запускаем старую версию обратно
|
||||
log_error "Обновление не удалось"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Полная установка telemt ──────────────────────────────────────────────────
|
||||
install_telemt_full() {
|
||||
log_step "Установка telemt"
|
||||
|
||||
# Создаём директории
|
||||
mkdir -p /etc/telemt
|
||||
|
||||
# Скачиваем бинарник
|
||||
run_with_spinner "Скачивание telemt" download_telemt || return 1
|
||||
|
||||
# Устанавливаем systemd сервис
|
||||
install_telemt_service
|
||||
|
||||
# Включаем автозапуск
|
||||
enable_telemt
|
||||
|
||||
log_success "telemt готов к работе"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Удаление telemt ──────────────────────────────────────────────────────────
|
||||
remove_telemt() {
|
||||
stop_telemt
|
||||
systemctl disable "$TELEMT_SERVICE" 2>/dev/null
|
||||
rm -f "/etc/systemd/system/${TELEMT_SERVICE}.service"
|
||||
systemctl daemon-reload
|
||||
rm -f "$TELEMT_BIN"
|
||||
rm -rf /etc/telemt
|
||||
log_success "telemt полностью удалён"
|
||||
}
|
||||
#!/bin/bash
|
||||
# GoTelegram v2.2 — Управление telemt binary
|
||||
# Скачивание, обновление, запуск, остановка через systemd
|
||||
|
||||
TELEMT_GITHUB="telemt/telemt"
|
||||
TELEMT_RELEASE_API="https://api.github.com/repos/${TELEMT_GITHUB}/releases/latest"
|
||||
TELEMT_USER="telemt"
|
||||
TELEMT_GROUP="telemt"
|
||||
|
||||
# ── Получение последней версии ───────────────────────────────────────────────
|
||||
get_latest_telemt_version() {
|
||||
local resp
|
||||
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
|
||||
if [ $? -ne 0 ] || [ -z "$resp" ]; then
|
||||
log_error "Не удалось получить информацию о релизах telemt"
|
||||
return 1
|
||||
fi
|
||||
echo "$resp" | jq -r '.tag_name // empty'
|
||||
}
|
||||
|
||||
get_telemt_download_url() {
|
||||
local arch
|
||||
arch=$(get_arch)
|
||||
local resp
|
||||
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
|
||||
if [ -z "$resp" ]; then return 1; fi
|
||||
|
||||
local pattern
|
||||
case "$arch" in
|
||||
amd64) pattern="linux.*(amd64|x86_64)" ;;
|
||||
arm64) pattern="linux.*(arm64|aarch64)" ;;
|
||||
armv7) pattern="linux.*(armv7|arm)" ;;
|
||||
*) pattern="linux.*${arch}" ;;
|
||||
esac
|
||||
|
||||
echo "$resp" | jq -r ".assets[].browser_download_url" 2>/dev/null | grep -iE "$pattern" | head -1
|
||||
}
|
||||
|
||||
# ── Установленная версия ─────────────────────────────────────────────────────
|
||||
get_installed_telemt_version() {
|
||||
if [ -x "$TELEMT_BIN" ]; then
|
||||
"$TELEMT_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
is_telemt_installed() {
|
||||
[ -x "$TELEMT_BIN" ]
|
||||
}
|
||||
|
||||
# ── Скачивание и установка ───────────────────────────────────────────────────
|
||||
download_telemt() {
|
||||
local url
|
||||
url=$(get_telemt_download_url)
|
||||
if [ -z "$url" ]; then
|
||||
log_error "Не найден бинарник telemt для архитектуры $(get_arch)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tmp_file="/tmp/telemt_download_$$"
|
||||
log_info "Скачивание: $url"
|
||||
|
||||
if ! curl -L -s --max-time 120 -o "$tmp_file" "$url"; then
|
||||
log_error "Ошибка скачивания telemt"
|
||||
rm -f "$tmp_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Определяем тип файла и распаковываем
|
||||
local mime
|
||||
mime=$(file -b --mime-type "$tmp_file" 2>/dev/null)
|
||||
|
||||
case "$mime" in
|
||||
application/gzip|application/x-gzip)
|
||||
tar xzf "$tmp_file" -C /tmp/ 2>/dev/null
|
||||
local extracted
|
||||
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
|
||||
if [ -z "$extracted" ]; then
|
||||
# Может быть просто gzip без tar
|
||||
gunzip -c "$tmp_file" > /tmp/telemt_bin_$$ 2>/dev/null
|
||||
extracted="/tmp/telemt_bin_$$"
|
||||
fi
|
||||
;;
|
||||
application/x-tar)
|
||||
tar xf "$tmp_file" -C /tmp/ 2>/dev/null
|
||||
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
|
||||
;;
|
||||
application/zip)
|
||||
unzip -o "$tmp_file" -d /tmp/telemt_extract_$$ 2>/dev/null
|
||||
extracted=$(find /tmp/telemt_extract_$$ -name "telemt" -type f 2>/dev/null | head -1)
|
||||
;;
|
||||
application/octet-stream|application/x-executable)
|
||||
# Уже бинарник
|
||||
extracted="$tmp_file"
|
||||
;;
|
||||
*)
|
||||
# Пробуем как бинарник
|
||||
extracted="$tmp_file"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$extracted" ] || [ ! -f "$extracted" ]; then
|
||||
log_error "Не удалось извлечь бинарник telemt"
|
||||
rm -f "$tmp_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Устанавливаем
|
||||
cp "$extracted" "$TELEMT_BIN"
|
||||
chmod 755 "$TELEMT_BIN"
|
||||
rm -f "$tmp_file"
|
||||
rm -rf /tmp/telemt_extract_$$ /tmp/telemt_bin_$$
|
||||
|
||||
# Проверяем
|
||||
if "$TELEMT_BIN" --version &>/dev/null; then
|
||||
log_success "telemt $(get_installed_telemt_version) установлен в $TELEMT_BIN"
|
||||
return 0
|
||||
else
|
||||
log_error "Бинарник telemt не запускается"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Системный пользователь ───────────────────────────────────────────────────
|
||||
create_telemt_user() {
|
||||
if ! id "$TELEMT_USER" &>/dev/null; then
|
||||
useradd -r -s /usr/sbin/nologin -d /etc/telemt "$TELEMT_USER" 2>/dev/null
|
||||
log_dim "Создан системный пользователь: $TELEMT_USER"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Systemd сервис ───────────────────────────────────────────────────────────
|
||||
install_telemt_service() {
|
||||
local config_path="${1:-$TELEMT_CONFIG}"
|
||||
|
||||
cat > "/etc/systemd/system/${TELEMT_SERVICE}.service" << EOF
|
||||
[Unit]
|
||||
Description=GoTelegram MTProxy (telemt engine)
|
||||
Documentation=https://github.com/telemt/telemt
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=$TELEMT_BIN run $config_path
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=65535
|
||||
|
||||
# Безопасность
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/etc/telemt /var/log
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
log_success "Systemd сервис $TELEMT_SERVICE создан"
|
||||
}
|
||||
|
||||
# ── Управление сервисом ──────────────────────────────────────────────────────
|
||||
start_telemt() {
|
||||
systemctl start "$TELEMT_SERVICE" 2>/dev/null
|
||||
sleep 2
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE"; then
|
||||
log_success "telemt запущен"
|
||||
return 0
|
||||
else
|
||||
log_error "telemt не запустился"
|
||||
journalctl -u "$TELEMT_SERVICE" --no-pager -n 10 2>/dev/null
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_telemt() {
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
systemctl stop "$TELEMT_SERVICE"
|
||||
log_success "telemt остановлен"
|
||||
else
|
||||
log_dim "telemt уже остановлен"
|
||||
fi
|
||||
}
|
||||
|
||||
restart_telemt() {
|
||||
systemctl restart "$TELEMT_SERVICE" 2>/dev/null
|
||||
sleep 2
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE"; then
|
||||
log_success "telemt перезапущен"
|
||||
return 0
|
||||
else
|
||||
log_error "telemt не перезапустился"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
enable_telemt() {
|
||||
systemctl enable "$TELEMT_SERVICE" 2>/dev/null
|
||||
}
|
||||
|
||||
telemt_status() {
|
||||
if ! is_telemt_installed; then
|
||||
echo "not_installed"
|
||||
return
|
||||
fi
|
||||
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
echo "running"
|
||||
elif systemctl is-enabled --quiet "$TELEMT_SERVICE" 2>/dev/null; then
|
||||
echo "stopped"
|
||||
else
|
||||
echo "disabled"
|
||||
fi
|
||||
}
|
||||
|
||||
telemt_logs() {
|
||||
local lines="${1:-40}"
|
||||
journalctl -u "$TELEMT_SERVICE" --no-pager -n "$lines" 2>/dev/null
|
||||
}
|
||||
|
||||
telemt_uptime() {
|
||||
local started
|
||||
started=$(systemctl show "$TELEMT_SERVICE" --property=ActiveEnterTimestamp --value 2>/dev/null)
|
||||
if [ -n "$started" ] && [ "$started" != "" ]; then
|
||||
echo "$started"
|
||||
else
|
||||
echo "N/A"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Обновление ───────────────────────────────────────────────────────────────
|
||||
check_telemt_update() {
|
||||
local current latest
|
||||
current=$(get_installed_telemt_version)
|
||||
latest=$(get_latest_telemt_version)
|
||||
|
||||
if [ -z "$current" ] || [ -z "$latest" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$current" != "$latest" ]; then
|
||||
echo "$latest"
|
||||
return 0 # есть обновление
|
||||
fi
|
||||
return 1 # актуально
|
||||
}
|
||||
|
||||
update_telemt() {
|
||||
local latest
|
||||
latest=$(check_telemt_update)
|
||||
if [ $? -ne 0 ]; then
|
||||
log_info "telemt уже последней версии ($(get_installed_telemt_version))"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Доступно обновление: $(get_installed_telemt_version) → $latest"
|
||||
if ! confirm "Обновить telemt?"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
stop_telemt
|
||||
if download_telemt; then
|
||||
start_telemt
|
||||
log_success "telemt обновлён до $latest"
|
||||
else
|
||||
start_telemt # запускаем старую версию обратно
|
||||
log_error "Обновление не удалось"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Полная установка telemt ──────────────────────────────────────────────────
|
||||
install_telemt_full() {
|
||||
log_step "Установка telemt"
|
||||
|
||||
# Создаём директории
|
||||
mkdir -p /etc/telemt
|
||||
|
||||
# Скачиваем бинарник
|
||||
run_with_spinner "Скачивание telemt" download_telemt || return 1
|
||||
|
||||
# Устанавливаем systemd сервис
|
||||
install_telemt_service
|
||||
|
||||
# Включаем автозапуск
|
||||
enable_telemt
|
||||
|
||||
log_success "telemt готов к работе"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Удаление telemt ──────────────────────────────────────────────────────────
|
||||
remove_telemt() {
|
||||
stop_telemt
|
||||
systemctl disable "$TELEMT_SERVICE" 2>/dev/null
|
||||
rm -f "/etc/systemd/system/${TELEMT_SERVICE}.service"
|
||||
systemctl daemon-reload
|
||||
rm -f "$TELEMT_BIN"
|
||||
rm -rf /etc/telemt
|
||||
log_success "telemt полностью удалён"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user