Files
swiftgram/install.sh
2026-04-05 23:59:51 +03:00

368 lines
18 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 🚀 SwiftGram MTProxy — Smart Modular Manager
# v1.0.5 — Final Working Version (TCP + UDP Calls)
# Полный функционал, без рекламы, исправлена логика портов.
# ── Настройки репозитория ────────────────────────────────────────────────────
REPO_RAW_URL="https://git.bargcraft.top/kobalt/swiftgram/raw/branch/main"
# ── Цвета ────────────────────────────────────────────────────────────────────
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'
NC='\033[0m'
# ── Конфиг ───────────────────────────────────────────────────────────────────
CONTAINER_NAME="swiftgram-proxy"
BOT_DIR="/opt/swiftgram"
SERVICE_NAME="swiftgram-bot"
# ── Спиннер и прогресс-бар ────────────────────────────────────────────────────
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.12
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_progress() {
local label="$1"; shift
spinner_start "$label"
"$@" >/dev/null 2>&1
local rc=$?
spinner_stop
if [ $rc -eq 0 ]; then
echo -e " ${GREEN}${NC} $label"
else
echo -e " ${RED}${NC} $label ${RED}(ошибка)${NC}"
fi
return $rc
}
# ── Проверки системы ─────────────────────────────────────────────────────────
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Запустите с sudo / root.${NC}"
exit 1
fi
install_pkg() {
if command -v apt-get &>/dev/null; then
apt-get update -qq && apt-get install -y -qq "$@"
elif command -v dnf &>/dev/null; then
dnf install -y "$@" 2>/dev/null
elif command -v yum &>/dev/null; then
yum install -y "$@"
fi
}
# ── Оптимизация Сети (BBR) ───────────────────────────────────────────────────
optimize_system() {
if ! sysctl net.ipv4.tcp_congestion_control | grep -q "bbr"; then
spinner_start "Оптимизация сетевого стека (BBR)..."
{
echo "net.core.default_qdisc=fq"
echo "net.ipv4.tcp_congestion_control=bbr"
echo "net.ipv4.ip_local_port_range=1024 65535"
echo "net.core.somaxconn=65535"
} >> /etc/sysctl.conf
sysctl -p >/dev/null 2>&1
spinner_stop
echo -e " ${GREEN}${NC} BBR ускорение включено"
fi
}
# ── Firewall (Фикс звонков) ──────────────────────────────────────────────────
fix_firewall() {
local port="$1"
if command -v ufw &>/dev/null && ufw status | grep -q "active"; then
ufw allow "$port"/tcp >/dev/null 2>&1
ufw allow "$port"/udp >/dev/null 2>&1
elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-port="$port"/tcp >/dev/null 2>&1
firewall-cmd --permanent --add-port="$port"/udp >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
fi
echo -e " ${GREEN}${NC} Firewall: порты $port (TCP/UDP) открыты"
}
# ── Установка зависимостей ───────────────────────────────────────────────────
install_base_deps() {
local steps=0 total=4
progress_bar $steps $total "Проверка зависимостей..."
if ! command -v curl &>/dev/null; then run_with_progress "Установка curl" install_pkg curl; fi
steps=$((steps+1)); progress_bar $steps $total "curl"
if ! command -v docker &>/dev/null; then
spinner_start "Установка Docker..."
curl -fsSL https://get.docker.com | sh >/dev/null 2>&1
systemctl enable --now docker >/dev/null 2>&1
spinner_stop
fi
steps=$((steps+1)); progress_bar $steps $total "docker"
if ! command -v qrencode &>/dev/null; then run_with_progress "Установка qrencode" install_pkg qrencode; fi
steps=$((steps+1)); progress_bar $steps $total "qrencode"
if ! docker info &>/dev/null 2>&1; then systemctl start docker 2>/dev/null; sleep 2; fi
steps=$((steps+1)); progress_bar $steps $total "Готово"
echo ""
}
# ── Утилиты IP и Портов ──────────────────────────────────────────────────────
get_ip4() { curl -s -4 --max-time 5 https://api.ipify.org || echo "0.0.0.0"; }
get_ip6() { curl -s -6 --max-time 5 https://api6.ipify.org || echo ""; }
check_port() {
local port="$1"
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"; then
local hp=$(docker inspect "$CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}} {{end}}' 2>/dev/null)
for p in $hp; do [ "$p" = "$port" ] && return 1; done
fi
local line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
[ -n "$line" ] && { echo "$line"; return 0; }
return 1
}
find_smart_port() {
local port=443
if ss -tlnp | grep -qE ":${port}\b"; then
echo -e " ${YELLOW} Порт 443 занят (Hiddify/Nginx). Пробую 8443...${NC}" >&2
port=8443
if ss -tlnp | grep -qE ":${port}\b"; then
port=$(( (RANDOM % 10000) + 20000 ))
echo -e " ${YELLOW} Порт 8443 тоже занят. Выбран случайный: $port${NC}" >&2
fi
fi
echo "$port"
}
# ── Интеллектуальный анализ домена ───────────────────────────────────────────
analyze_best_domain() {
spinner_start "Анализ доменов для Fake TLS..."
local test_domains=("google.com" "wikipedia.org" "github.com" "habr.com" "microsoft.com")
local best="google.com"
local min=999
for d in "${test_domains[@]}"; do
local t=$(ping -c 1 -W 1 "$d" 2>/dev/null | grep 'time=' | awk -F'time=' '{print $2}' | awk '{print $1}')
if [ -n "$t" ] && (( $(echo "$t < $min" | bc -l 2>/dev/null || [ ${t%.*} -lt ${min%.*} ]) )); then
min=$t; best=$d
fi
done
spinner_stop
echo "$best"
}
# ── Показать данные подключения ──────────────────────────────────────────────
show_config() {
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
echo -e "${RED}Прокси не запущен!${NC}"; return
fi
local DATA=$(cat "$BOT_DIR/proxy.json" 2>/dev/null)
local PORT=$(echo "$DATA" | grep -oP '(?<="port": ")[^"]*')
local SECRET=$(echo "$DATA" | grep -oP '(?<="secret": ")[^"]*')
local IP4=$(get_ip4)
local IP6=$(get_ip6)
local LINK="tg://proxy?server=$IP4&port=$PORT&secret=$SECRET"
echo -e "\n${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ ДАННЫЕ ПОДКЛЮЧЕНИЯ ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
echo -e " IPv4: ${WHITE}$IP4${NC}"
[ -n "$IP6" ] && echo -e " IPv6: ${WHITE}$IP6${NC}"
echo -e " Порт: ${WHITE}$PORT${NC} (TCP + UDP)"
echo -e " Secret: ${WHITE}$SECRET${NC}"
echo -e "\n Ссылка: ${BLUE}$LINK${NC}"
echo ""
qrencode -t ANSIUTF8 "$LINK"
}
# ── 1) Установка MTProxy ─────────────────────────────────────────────────────
menu_install() {
clear
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Выберите домен для маскировки (Fake TLS) ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
local domains=(
"google.com" "wikipedia.org" "habr.com" "github.com"
"coursera.org" "udemy.com" "medium.com" "stackoverflow.com"
"bbc.com" "cnn.com" "reuters.com" "nytimes.com"
"lenta.ru" "rbc.ru" "ria.ru" "kommersant.ru"
"stepik.org" "duolingo.com" "khanacademy.org" "ted.com"
)
local best_suggest=$(analyze_best_domain)
echo -e " ${GREEN}→ Рекомендуемый домен (лучший пинг): $best_suggest${NC}\n"
for i in "${!domains[@]}"; do
printf " ${YELLOW}%2d)${NC} %-22s" "$((i+1))" "${domains[$i]}"
[[ $(( (i+1) % 2 )) -eq 0 ]] && echo ""
done
echo -e "\n ${CYAN}21)${NC} Ввести свой домен"
local d_idx DOMAIN
read -p "Ваш выбор [1-21]: " d_idx
if [ "$d_idx" = "21" ]; then
read -p " Введите домен: " DOMAIN
DOMAIN=$(echo "$DOMAIN" | tr -d '[:space:]')
else
DOMAIN=${domains[$((d_idx-1))]}
fi
DOMAIN=${DOMAIN:-$best_suggest}
local PORT=$(find_smart_port)
echo -e " ${GREEN}${NC} Итоговый порт: ${WHITE}$PORT${NC}"
optimize_system
fix_firewall "$PORT"
spinner_start "Запуск контейнера (команда оригинала)..."
docker pull nineseconds/mtg:2 >/dev/null 2>&1
local SECRET=$(docker run --rm nineseconds/mtg:2 generate-secret --hex "$DOMAIN" 2>/dev/null)
docker stop "$CONTAINER_NAME" &>/dev/null
docker rm "$CONTAINER_NAME" &>/dev/null
# ИСПОЛЬЗУЕМ ТОЧНУЮ КОМАНДУ, КОТОРАЯ РАБОТАЛА В ОРИГИНАЛЕ
docker run -d --name "$CONTAINER_NAME" --restart always \
-p "$PORT":"$PORT"/tcp \
-p "$PORT":"$PORT"/udp \
nineseconds/mtg:2 simple-run \
-n 1.1.1.1 -i prefer-ipv4 \
0.0.0.0:"$PORT" "$SECRET" > /dev/null 2>&1
sleep 2
spinner_stop
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
mkdir -p "$BOT_DIR"
echo "{\"domain\": \"$DOMAIN\", \"port\": \"$PORT\", \"secret\": \"$SECRET\"}" > "$BOT_DIR/proxy.json"
echo -e "\n${GREEN}✓ SwiftGram успешно запущен!${NC}"
show_config
else
echo -e "\n${RED}✗ Ошибка запуска. Проверьте: docker logs $CONTAINER_NAME${NC}"
fi
read -p "Нажмите Enter..."
}
# ── 3) Настройка бота ────────────────────────────────────────────────────────
menu_setup_bot() {
clear
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ НАСТРОЙКА TELEGRAM БОТА ║${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
if ! command -v python3 &>/dev/null; then run_with_progress "Установка Python3" install_pkg python3 python3-pip python3-venv; fi
mkdir -p "$BOT_DIR"
cd "$BOT_DIR"
spinner_start "Загрузка модулей бота..."
curl -sL "$REPO_RAW_URL/requirements.txt" -o "requirements.txt"
curl -sL "$REPO_RAW_URL/bot.py" -o "bot.py"
spinner_stop
[ ! -d "venv" ] && python3 -m venv venv >/dev/null 2>&1
./venv/bin/pip install --upgrade pip -q
./venv/bin/pip install -r requirements.txt -q
echo -e "\n${YELLOW}Введите BOT_TOKEN:${NC}"
read -r TOKEN
echo -e "${YELLOW}Ваш Telegram ID:${NC}"
read -r ADMIN_ID
{
echo "BOT_TOKEN=$TOKEN"
echo "ALLOWED_IDS=$ADMIN_ID"
echo "CONTAINER_NAME=$CONTAINER_NAME"
echo "CONFIG_PATH=$BOT_DIR/proxy.json"
} > .env
chmod 600 .env
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
[Unit]
Description=SwiftGram Bot Service
After=network.target docker.service
[Service]
Type=simple
WorkingDirectory=$BOT_DIR
ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now "$SERVICE_NAME"
systemctl restart "$SERVICE_NAME"
echo -e "\n${GREEN}✓ Бот запущен!${NC}"
read -p "Нажмите Enter..."
}
# ── 7) Удаление ──────────────────────────────────────────────────────────────
menu_remove() {
clear
echo -e "${RED}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${RED}║ УДАЛЕНИЕ SWIFTGRAM ║${NC}"
echo -e "${RED}╚══════════════════════════════════════════════════════════════╝${NC}"
read -p " Удалить полностью? (y/N): " yn
[[ "$yn" != "y" ]] && return
local words=("УДАЛИТЬ" "SWIFTGRAM" "ОЧИСТКА")
local confirm_word="${words[$((RANDOM % ${#words[@]}))]}"
echo -e " Введите слово: ${WHITE}$confirm_word${NC}"
read -p " >>> " input_word
[[ "$input_word" != "$confirm_word" ]] && return
spinner_start "Очистка..."
docker stop "$CONTAINER_NAME" &>/dev/null; docker rm "$CONTAINER_NAME" &>/dev/null
systemctl stop "$SERVICE_NAME" 2>/dev/null; systemctl disable "$SERVICE_NAME" 2>/dev/null
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"; rm -rf "$BOT_DIR"; rm -f /usr/local/bin/swiftgram
spinner_stop
echo -e "${GREEN}✓ Система очищена.${NC}"; read -p "Enter..."
}
# ── Главный цикл ─────────────────────────────────────────────────────────────
install_base_deps
SELF="$(realpath "$0")"
[ "$SELF" != "/usr/local/bin/swiftgram" ] && { cp "$SELF" /usr/local/bin/swiftgram; chmod +x /usr/local/bin/swiftgram; }
while true; do
clear
echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${MAGENTA}║ SWIFTGRAM MANAGER (Professional) ║${NC}"
echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════════╝${NC}"
docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" && echo -e " Прокси: ${GREEN}РАБОТАЕТ${NC}" || echo -e " Прокси: ${RED}ВЫКЛЮЧЕН${NC}"
systemctl is-active --quiet "$SERVICE_NAME" && echo -e " Бот: ${GREEN}РАБОТАЕТ${NC}" || echo -e " Бот: ${YELLOW}НЕ НАСТРОЕН${NC}"
echo -e "\n ${GREEN}1)${NC} Установить / Обновить прокси\n ${GREEN}2)${NC} Показать данные (QR)\n ${CYAN}3)${NC} Настроить Telegram-бота\n ${GREEN}4)${NC} Перезапустить прокси\n ${GREEN}5)${NC} Логи прокси\n ${RED}6)${NC} Удалить SwiftGram\n ${WHITE}0)${NC} Выход"
read -p "Пункт: " m_idx
case $m_idx in
1) menu_install ;; 2) clear; show_config; read -p "Нажмите Enter..." ;; 3) menu_setup_bot ;;
4) docker restart "$CONTAINER_NAME"; echo "Готово"; sleep 1 ;;
5) docker logs --tail 50 "$CONTAINER_NAME"; read -p "Enter..." ;;
6) menu_remove ;; 0) exit 0 ;;
esac
done