- Switched container to --network host to eliminate NAT-related UDP call issues. - Fixed availability by explicitly binding to 0.0.0.0 instead of hybrid :: address. - Retained smart port selection logic and ad-free environment. - Verified as the most stable configuration for MTProxy calls.
315 lines
15 KiB
Bash
315 lines
15 KiB
Bash
#!/bin/bash
|
||
# 🚀 SwiftGram MTProxy v1.0.3 — Финальный фикс звонков (UDP)
|
||
# Режим: Network Host + IPv4 Explicit Binding (100% Calls Compatibility)
|
||
|
||
# ── Настройки репозитория ────────────────────────────────────────────────────
|
||
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"
|
||
} >> /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 настроен (TCP/UDP $port)"
|
||
}
|
||
|
||
# ── Установка зависимостей ───────────────────────────────────────────────────
|
||
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"
|
||
}
|
||
|
||
# ── Показать данные подключения ──────────────────────────────────────────────
|
||
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 " IP: ${WHITE}$IP4${NC}"
|
||
[ -n "$IP6" ] && echo -e " IPv6: ${WHITE}$IP6${NC}"
|
||
echo -e " Порт: ${WHITE}$PORT${NC} (Direct UDP Calls Ready)"
|
||
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}║ УСТАНОВКА SWIFTGRAM MTPROXY ║${NC}"
|
||
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||
|
||
local domains=("google.com" "wikipedia.org" "github.com" "habr.com" "microsoft.com" "stackoverflow.com" "lenta.ru" "rbc.ru")
|
||
echo -e "\nВыберите домен для маскировки (Fake TLS):"
|
||
for i in "${!domains[@]}"; do
|
||
printf " ${YELLOW}%2d)${NC} %-20s" "$((i+1))" "${domains[$i]}"
|
||
[[ $(( (i+1) % 2 )) -eq 0 ]] && echo ""
|
||
done
|
||
echo -e "\n ${CYAN}9)${NC} Ввести свой домен"
|
||
read -p "Выбор: " d_idx
|
||
if [ "$d_idx" = "9" ]; then read -p "Введите домен: " DOMAIN; else DOMAIN=${domains[$((d_idx-1))]}; fi
|
||
DOMAIN=${DOMAIN:-google.com}
|
||
|
||
local PORT=$(find_smart_port)
|
||
echo -e " ${GREEN}✓${NC} Итоговый порт: ${WHITE}$PORT${NC}"
|
||
|
||
optimize_system
|
||
fix_firewall "$PORT"
|
||
|
||
spinner_start "Запуск контейнера (UDP Calls Optimized)..."
|
||
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
|
||
|
||
# РЕШЕНИЕ: --network host + привязка к IPv4 (0.0.0.0).
|
||
# Это убирает NAT докера, который ломает звонки, но оставляет доступность.
|
||
docker run -d --name "$CONTAINER_NAME" --restart always \
|
||
--network host \
|
||
nineseconds/mtg:2 simple-run \
|
||
-n 1.1.1.1 -t 1.0.0.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
|
||
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
|
||
read -p "Удалить SwiftGram полностью? (y/N): " yn
|
||
[[ "$yn" != "y" ]] && 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 (v1.0.3) ║${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 ${RED}5)${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"; sleep 1 ;; 5) menu_remove ;; 0) exit 0 ;;
|
||
esac
|
||
done |