/dev/null 2>&1
warp-cli --accept-tos proxy port "${SOCKS_PORT}" > /dev/null 2>&1
warp-cli --accept-tos connect > /dev/null 2>&1
- sleep 2
+ sleep 3
if is_warp_running; then
local wip
@@ -421,6 +445,7 @@ full_uninstall() {
echo -e "${WHITE}Будут удалены:${NC}"
echo -e " ${RED}•${NC} Пакет cloudflare-warp"
echo -e " ${RED}•${NC} Репозиторий и GPG-ключ Cloudflare"
+ echo -e " ${RED}•${NC} Telegram-бот"
echo -e " ${RED}•${NC} Конфигурация ${WHITE}/etc/warp-manager/${NC}"
echo -e " ${RED}•${NC} Команда ${WHITE}gowarp${NC}"
echo -e " ${RED}•${NC} Логи ${WHITE}/var/log/warp-manager.log${NC}"
@@ -436,30 +461,32 @@ full_uninstall() {
echo ""
echo -e "${YELLOW}Удаление WARP Manager...${NC}\n"
- echo -e " ${YELLOW}[1/6]${NC} Отключение WARP..."
+ if systemctl is-active warp-bot &>/dev/null; then
+ systemctl stop warp-bot 2>/dev/null; systemctl disable warp-bot 2>/dev/null
+ fi
+ [ -f "$BOT_PID_FILE" ] && { kill "$(cat "$BOT_PID_FILE")" 2>/dev/null; rm -f "$BOT_PID_FILE"; }
+ rm -f /etc/systemd/system/warp-bot.service
+ systemctl daemon-reload 2>/dev/null
+ echo -e " ${GREEN}✓${NC} Telegram-бот остановлен"
+
warp-cli --accept-tos disconnect > /dev/null 2>&1
echo -e " ${GREEN}✓${NC} WARP отключён"
- echo -e " ${YELLOW}[2/6]${NC} Удаление регистрации..."
warp-cli --accept-tos registration delete > /dev/null 2>&1
echo -e " ${GREEN}✓${NC} Регистрация удалена"
- echo -e " ${YELLOW}[3/6]${NC} Удаление пакета..."
apt-get remove -y cloudflare-warp > /dev/null 2>&1
apt-get autoremove -y > /dev/null 2>&1
echo -e " ${GREEN}✓${NC} Пакет удалён"
- echo -e " ${YELLOW}[4/6]${NC} Удаление репозитория и ключа..."
rm -f /etc/apt/sources.list.d/cloudflare-client.list
rm -f /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo -e " ${GREEN}✓${NC} Репозиторий и ключ удалены"
- echo -e " ${YELLOW}[5/6]${NC} Удаление конфигурации..."
rm -rf "$WARP_DIR"
rm -f "$WARP_LOG"
echo -e " ${GREEN}✓${NC} Конфигурация и логи удалены"
- echo -e " ${YELLOW}[6/6]${NC} Удаление команды gowarp..."
rm -f /usr/local/bin/gowarp
echo -e " ${GREEN}✓${NC} Команда gowarp удалена"
@@ -474,6 +501,297 @@ full_uninstall() {
exit 0
}
+# ═══════════════════════════════════════════════════════════════
+# TELEGRAM BOT
+# ═══════════════════════════════════════════════════════════════
+
+tg_api() {
+ curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/$1" \
+ -H "Content-Type: application/json" -d "$2" 2>/dev/null
+}
+
+tg_send() {
+ local chat_id="$1" text keyboard="${3:-}"
+ text=$(printf '%b' "$2")
+ local payload
+ if [ -n "$keyboard" ]; then
+ payload=$(jq -n --arg c "$chat_id" --arg t "$text" --argjson k "$keyboard" \
+ '{chat_id:$c, text:$t, parse_mode:"HTML", reply_markup:{inline_keyboard:$k}}')
+ else
+ payload=$(jq -n --arg c "$chat_id" --arg t "$text" \
+ '{chat_id:$c, text:$t, parse_mode:"HTML"}')
+ fi
+ tg_api "sendMessage" "$payload"
+}
+
+tg_edit() {
+ local chat_id="$1" msg_id="$2" text keyboard="${4:-}"
+ text=$(printf '%b' "$3")
+ local payload
+ if [ -n "$keyboard" ]; then
+ payload=$(jq -n --arg c "$chat_id" --argjson m "$msg_id" --arg t "$text" --argjson k "$keyboard" \
+ '{chat_id:$c, message_id:$m, text:$t, parse_mode:"HTML", reply_markup:{inline_keyboard:$k}}')
+ else
+ payload=$(jq -n --arg c "$chat_id" --argjson m "$msg_id" --arg t "$text" \
+ '{chat_id:$c, message_id:$m, text:$t, parse_mode:"HTML"}')
+ fi
+ tg_api "editMessageText" "$payload"
+}
+
+tg_answer_cb() {
+ tg_api "answerCallbackQuery" "{\"callback_query_id\":\"$1\",\"text\":\"${2:-}\"}"
+}
+
+# ─── Bot keyboards ───────────────────────────────────────────
+
+kbd_main() {
+ cat <<'JSON'
+[
+ [{"text":"📊 Статус","callback_data":"st"},{"text":"🌐 IP адреса","callback_data":"ip"}],
+ [{"text":"▶️ Запустить","callback_data":"on"},{"text":"⏹ Остановить","callback_data":"off"}],
+ [{"text":"🔑 Перевыпуск ключа","callback_data":"rk"}],
+ [{"text":"📋 JSON для 3X-UI","callback_data":"js"}],
+ [{"text":"💻 Система","callback_data":"sys"}],
+ [{"text":"🏢 Хостинг","callback_data":"promo"}]
+]
+JSON
+}
+
+kbd_back() { echo '[[{"text":"⬅️ Меню","callback_data":"m"}]]'; }
+
+kbd_rekey_confirm() { echo '[[{"text":"✅ Да, перевыпустить","callback_data":"rk_y"}],[{"text":"⬅️ Отмена","callback_data":"m"}]]'; }
+
+# ─── Bot handlers ────────────────────────────────────────────
+
+bot_main_menu() {
+ local chat_id="$1" msg_id="${2:-}"
+ local ws wip=""
+ ws=$(get_warp_status_text)
+ is_warp_running && wip=" | WARP IP: $(get_warp_ip)"
+ local text="WARP Manager v${WARP_VERSION}\nСервер: ${MY_IP:-N/A}\nСтатус: ${ws}${wip}\nSOCKS5: 127.0.0.1:${SOCKS_PORT}\n\nВыберите действие:"
+ local kbd
+ kbd=$(kbd_main)
+ if [ -n "$msg_id" ]; then
+ tg_edit "$chat_id" "$msg_id" "$text" "$kbd"
+ else
+ tg_send "$chat_id" "$text" "$kbd"
+ fi
+}
+
+bot_handle_callback() {
+ local chat_id="$1" msg_id="$2" cb_id="$3" data="$4"
+ [ -n "$cb_id" ] && tg_answer_cb "$cb_id" > /dev/null
+
+ case "$data" in
+ m) bot_main_menu "$chat_id" "$msg_id" ;;
+
+ st)
+ local ws wip=""
+ ws=$(get_warp_status_text)
+ is_warp_running && wip="\nWARP IP: $(get_warp_ip)"
+ local raw
+ raw=$(warp-cli --accept-tos status 2>/dev/null | head -3)
+ local text="📊 Статус WARP\n\nСтатус: ${ws}\nSOCKS5: 127.0.0.1:${SOCKS_PORT}\nСервер: ${MY_IP:-N/A}${wip}\n\n${raw}"
+ tg_edit "$chat_id" "$msg_id" "$text" "$(kbd_back)" ;;
+
+ ip)
+ local wip="N/A"
+ is_warp_running && wip=$(get_warp_ip)
+ local text="🌐 IP адреса\n\nРеальный IP: ${MY_IP:-N/A}\nWARP IP: ${wip}\nSOCKS5: 127.0.0.1:${SOCKS_PORT}"
+ tg_edit "$chat_id" "$msg_id" "$text" "$(kbd_back)" ;;
+
+ on)
+ if ! is_warp_installed; then
+ tg_edit "$chat_id" "$msg_id" "❌ WARP не установлен." "$(kbd_back)"; return
+ fi
+ if is_warp_running; then
+ tg_edit "$chat_id" "$msg_id" "✅ WARP уже подключён." "$(kbd_back)"; return
+ fi
+ tg_edit "$chat_id" "$msg_id" "⏳ Подключение WARP..." ""
+ warp-cli --accept-tos connect > /dev/null 2>&1
+ sleep 3
+ if is_warp_running; then
+ local wip
+ wip=$(get_warp_ip)
+ log_action "BOT: WARP connected"
+ tg_edit "$chat_id" "$msg_id" "✅ WARP подключён\nWARP IP: ${wip}" "$(kbd_back)"
+ else
+ tg_edit "$chat_id" "$msg_id" "❌ Не удалось подключить WARP." "$(kbd_back)"
+ fi ;;
+
+ off)
+ if ! is_warp_running; then
+ tg_edit "$chat_id" "$msg_id" "ℹ️ WARP уже отключён." "$(kbd_back)"; return
+ fi
+ warp-cli --accept-tos disconnect > /dev/null 2>&1
+ log_action "BOT: WARP disconnected"
+ tg_edit "$chat_id" "$msg_id" "⏹ WARP отключён." "$(kbd_back)" ;;
+
+ rk)
+ tg_edit "$chat_id" "$msg_id" "🔑 Перевыпуск ключа\n\nТекущая регистрация будет удалена и создана новая.\nWARP будет временно отключён.\n\nПродолжить?" "$(kbd_rekey_confirm)" ;;
+
+ rk_y)
+ tg_edit "$chat_id" "$msg_id" "⏳ Перевыпуск ключа..." ""
+ warp-cli --accept-tos disconnect > /dev/null 2>&1
+ warp-cli --accept-tos registration delete > /dev/null 2>&1
+ warp-cli --accept-tos registration new > /dev/null 2>&1
+ warp-cli --accept-tos mode proxy > /dev/null 2>&1
+ warp-cli --accept-tos proxy port "${SOCKS_PORT}" > /dev/null 2>&1
+ warp-cli --accept-tos connect > /dev/null 2>&1
+ sleep 3
+ if is_warp_running; then
+ local wip
+ wip=$(get_warp_ip)
+ log_action "BOT REKEY: warp_ip=${wip}"
+ tg_edit "$chat_id" "$msg_id" "✅ Ключ перевыпущен\nНовый WARP IP: ${wip}" "$(kbd_back)"
+ else
+ tg_edit "$chat_id" "$msg_id" "⚠️ Ключ перевыпущен, подключение не подтверждено." "$(kbd_back)"
+ fi ;;
+
+ js)
+ local text="📋 Конфигурация для 3X-UI\n\nOutbound:\n{\n \"tag\": \"warp\",\n \"protocol\": \"socks\",\n \"settings\": {\n \"servers\": [{\n \"address\": \"127.0.0.1\",\n \"port\": ${SOCKS_PORT}\n }]\n }\n}\n\nRouting rule:\n{\n \"outboundTag\": \"warp\",\n \"domain\": [\n \"geosite:openai\",\n \"geosite:netflix\"\n ]\n}"
+ tg_edit "$chat_id" "$msg_id" "$text" "$(kbd_back)" ;;
+
+ sys)
+ local s
+ s=$(get_system_stats)
+ local ws
+ ws=$(get_warp_status_text)
+ s+="\nWARP: ${ws}"
+ s+="\nSOCKS5: 127.0.0.1:${SOCKS_PORT}"
+ tg_edit "$chat_id" "$msg_id" "$s" "$(kbd_back)" ;;
+
+ promo)
+ local pt="🏢 Хостинг, который работает\n\n🌍 РФ и Европа\n👉 https://vk.cc/ct29NQ\n\nOFF60 — 60% скидка\nantenka20 — +20% (3мес)\nantenka6 — +15% (6мес)\nantenka12 — +5% (12мес)\n\n🇧🇾 Беларусь\n👉 https://vk.cc/cUxAhj\nOFF60 — 60% скидка"
+ tg_edit "$chat_id" "$msg_id" "$pt" "$(kbd_back)" ;;
+ esac
+}
+
+# ─── Bot daemon ───────────────────────────────────────────────
+
+bot_daemon() {
+ log_action "Bot daemon started (PID $$)"; echo $$ > "$BOT_PID_FILE"
+ source "$WARP_CONF"
+ [ -z "$BOT_TOKEN" ] && log_action "BOT ERROR: no token" && exit 1
+ get_my_ip
+ local offset=0
+ while true; do
+ local response
+ response=$(curl -s --max-time 35 \
+ "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?offset=${offset}&timeout=30" 2>/dev/null)
+ [ -z "$response" ] && sleep 2 && continue
+ local ok
+ ok=$(echo "$response" | jq -r '.ok // "false"')
+ [ "$ok" != "true" ] && sleep 5 && continue
+ local cnt
+ cnt=$(echo "$response" | jq '.result | length')
+ for (( i=0; i /dev/null && continue
+ bot_handle_callback "$cci" "$cmi" "$cbi" "$cbd"
+ else
+ local mci mtx
+ mci=$(echo "$upd" | jq -r '.message.chat.id // empty')
+ mtx=$(echo "$upd" | jq -r '.message.text // empty')
+ if [ -n "$mci" ] && [ -n "$mtx" ]; then
+ [ -n "$BOT_CHAT_ID" ] && [ "$mci" != "$BOT_CHAT_ID" ] && \
+ tg_send "$mci" "⛔ Нет доступа.\nChat ID: $mci" "" > /dev/null && continue
+ if [ "$mtx" = "/start" ] || [ "$mtx" = "/menu" ]; then
+ bot_main_menu "$mci"
+ else
+ tg_send "$mci" "Используйте /start или /menu" "" > /dev/null
+ fi
+ fi
+ fi
+ done
+ done
+}
+
+start_bot() {
+ source "$WARP_CONF"
+ [ -z "$BOT_TOKEN" ] && echo -e "${RED}Задайте BOT_TOKEN!${NC}" && return
+ [ -f "$BOT_PID_FILE" ] && kill -0 "$(cat "$BOT_PID_FILE")" 2>/dev/null && echo -e "${YELLOW}Уже запущен.${NC}" && return
+ cat > /etc/systemd/system/warp-bot.service < /dev/null 2>&1
+ systemctl start warp-bot
+ sleep 1
+ systemctl is-active warp-bot &>/dev/null && echo -e "${GREEN}[OK] Бот запущен.${NC}" && log_action "Bot started" \
+ || echo -e "${RED}[ERROR] journalctl -u warp-bot${NC}"
+}
+
+stop_bot() {
+ systemctl stop warp-bot 2>/dev/null
+ systemctl disable warp-bot 2>/dev/null
+ rm -f "$BOT_PID_FILE"
+ echo -e "${GREEN}[OK] Бот остановлен.${NC}"
+ log_action "Bot stopped"
+}
+
+bot_menu() {
+ while true; do
+ clear; source "$WARP_CONF" 2>/dev/null
+ local bs="${RED}Выкл${NC}"
+ [ -f "$BOT_PID_FILE" ] && kill -0 "$(cat "$BOT_PID_FILE" 2>/dev/null)" 2>/dev/null && bs="${GREEN}Вкл ($(cat "$BOT_PID_FILE"))${NC}"
+ local td="нет"
+ [ -n "${BOT_TOKEN:-}" ] && td="***${BOT_TOKEN: -6}"
+
+ echo -e "${CYAN}━━━ Telegram Bot ━━━${NC}"
+ echo -e "Статус: $bs"
+ echo -e "Токен: ${YELLOW}$td${NC}"
+ echo -e "Chat ID: ${YELLOW}${BOT_CHAT_ID:-нет}${NC}"
+ echo ""
+ echo -e "1) Токен бота"
+ echo -e "2) Chat ID (авто)"
+ echo -e "3) Chat ID (вручную)"
+ echo -e "4) ${GREEN}Запустить${NC}"
+ echo -e "5) ${RED}Остановить${NC}"
+ echo -e "0) Назад"
+ read -p "Выбор: " ch
+ case $ch in
+ 1) echo "Токен:"; read -p "> " t
+ [ -n "$t" ] && save_config_val "BOT_TOKEN" "$t" && BOT_TOKEN="$t" && echo -e "${GREEN}OK${NC}"
+ read -p "Enter..." ;;
+ 2) [ -z "${BOT_TOKEN:-}" ] && echo -e "${RED}Сначала задайте токен!${NC}" && read -p "" && continue
+ echo -e "${YELLOW}Отправьте боту сообщение, затем нажмите Enter.${NC}"; read -p ""
+ local c
+ c=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?limit=1&offset=-1" | \
+ jq -r '.result[0].message.chat.id // empty')
+ [ -n "$c" ] && save_config_val "BOT_CHAT_ID" "$c" && BOT_CHAT_ID="$c" && echo -e "${GREEN}Chat ID: $c${NC}" \
+ || echo -e "${RED}Не удалось получить Chat ID.${NC}"
+ read -p "Enter..." ;;
+ 3) echo "Chat ID:"; read -p "> " c
+ [ -n "$c" ] && save_config_val "BOT_CHAT_ID" "$c" && BOT_CHAT_ID="$c" && echo -e "${GREEN}OK${NC}"
+ read -p "Enter..." ;;
+ 4) start_bot; read -p "Enter..." ;;
+ 5) stop_bot; read -p "Enter..." ;;
+ 0) return ;;
+ esac
+ done
+}
+
# ═══════════════════════════════════════════════════════════════
# PROMO
# ═══════════════════════════════════════════════════════════════
@@ -510,14 +828,6 @@ show_info() {
echo -e " Ваш VPS получает «чистый» IP-адрес Cloudflare, который"
echo -e " не заблокирован популярными сервисами.${NC}"
echo ""
- echo -e "${CYAN}═══ ЗАЧЕМ НУЖЕН WARP + 3X-UI ═══${NC}"
- echo ""
- echo -e "${WHITE} Многие сайты блокируют IP дешёвых VPS:"
- echo -e " ChatGPT, Netflix, Disney+, Spotify и другие."
- echo -e ""
- echo -e " WARP решает эту проблему: трафик из 3X-UI идёт через"
- echo -e " Cloudflare и получает доверенный IP.${NC}"
- echo ""
echo -e "${CYAN}═══ СХЕМА РАБОТЫ ═══${NC}"
echo ""
echo -e "${WHITE} Клиент → 3X-UI (Xray) → SOCKS5 (WARP) → Cloudflare → Интернет${NC}"
@@ -532,7 +842,7 @@ show_info() {
echo -e " ${GREEN}✓${NC} Разблокировка ChatGPT, Netflix, Disney+, Spotify"
echo -e " ${GREEN}✓${NC} Чистый IPv4/IPv6 от Cloudflare"
echo -e " ${GREEN}✓${NC} Стабильный маршрут через сеть Cloudflare"
- echo -e " ${GREEN}✓${NC} Гибкая маршрутизация: часть сайтов через WARP, остальные напрямую"
+ echo -e " ${GREEN}✓${NC} Управление через Telegram-бот"
echo -e " ${GREEN}✓${NC} Бесплатно (Cloudflare WARP — бесплатный сервис)"
echo ""
echo -e "${MAGENTA}──────────────────────────────────────────────────────────────${NC}"
@@ -568,9 +878,10 @@ show_menu() {
echo -e " 5) 📋 ${CYAN}JSON для 3X-UI${NC}"
echo -e " 6) 🔑 ${YELLOW}Перевыпуск ключа${NC}"
echo -e " 7) 🔧 ${WHITE}Изменить порт SOCKS5${NC}"
- echo -e " 8) ${YELLOW}PROMO${NC}"
- echo -e " 9) ${MAGENTA}📚 Инструкция${NC}"
- echo -e "10) ${RED}⚠ Полное удаление${NC}"
+ echo -e " 8) 🤖 ${CYAN}Telegram Bot${NC}"
+ echo -e " 9) ${YELLOW}PROMO${NC}"
+ echo -e "10) ${MAGENTA}📚 Инструкция${NC}"
+ echo -e "11) ${RED}⚠ Полное удаление${NC}"
echo -e " 0) Выход"
echo -e "------------------------------------------------------"
read -p "Выбор: " ch
@@ -582,9 +893,10 @@ show_menu() {
5) show_xui_json ;;
6) rekey_warp ;;
7) change_port ;;
- 8) show_promo ;;
- 9) show_info ;;
- 10) full_uninstall ;;
+ 8) bot_menu ;;
+ 9) show_promo ;;
+ 10) show_info ;;
+ 11) full_uninstall ;;
0) exit 0 ;;
esac
done
@@ -595,11 +907,11 @@ show_menu() {
# ═══════════════════════════════════════════════════════════════
run_startup() {
- local total=5 s=0
+ local total=6 s=0
clear; echo ""
echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════════╗${NC}"
- echo -e "${MAGENTA}║ WARP Manager v${WARP_VERSION} — Загрузка ║${NC}"
+ echo -e "${MAGENTA}║ WARP Manager v${WARP_VERSION} — Загрузка ║${NC}"
echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════════╝${NC}"
echo ""
@@ -613,6 +925,11 @@ run_startup() {
init_config
printf "\r ${CYAN}[%d/%d]${NC} ${GREEN}✓${NC} Конфигурация загружена \n" "$s" "$total"
+ ((s++))
+ printf " ${CYAN}[%d/%d]${NC} ${YELLOW}⏳${NC} Проверка зависимостей..." "$s" "$total"
+ check_deps
+ printf "\r ${CYAN}[%d/%d]${NC} ${GREEN}✓${NC} Зависимости на месте \n" "$s" "$total"
+
((s++))
printf " ${CYAN}[%d/%d]${NC} ${YELLOW}⏳${NC} Установка gowarp..." "$s" "$total"
if [ "$(readlink -f "$0" 2>/dev/null)" != "/usr/local/bin/gowarp" ]; then
@@ -649,4 +966,7 @@ run_startup() {
# ENTRY POINT
# ═══════════════════════════════════════════════════════════════
-run_startup
+case "${1:-}" in
+ --bot-daemon) init_config; bot_daemon ;;
+ *) run_startup ;;
+esac