Рефакторинг (#11)

Глобальный рефакторинг
Если что-то не работает, виноват он: AloofLibra <87386304+AloofLibra@users.noreply.github.com> 😂
This commit is contained in:
IndeecFOX 2026-01-04 18:11:38 +03:00 committed by GitHub
parent de58e3be9f
commit 602a566923
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1204 additions and 941 deletions

View file

@ -8,21 +8,38 @@ jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Полная история для корректного merge
- name: Download Data
run: |
# Качаем CSV с листа Export_TXT
curl -L "https://docs.google.com/spreadsheets/d/e/2PACX-1vRuTn6FKfGYrs1p2fjUSRTelUDeA14bDjMePXQWfSUtQEzZ4NPJc5ZqNHtkx1OMUjJSvP6ER9kkDKaO/pub?gid=2004936343&single=true&output=csv" -o recommendations.txt
# Удаляем кавычки, если Google их добавил (CSV иногда квотирует строки)
# Удаляем кавычки, если Google их добавил
sed -i 's/"//g' recommendations.txt
- name: Commit & Push
- name: Fetch and merge changes
run: |
git config user.name "Stats Bot"
git config user.email "bot@noreply.github.com"
git add recommendations.txt
# Коммитим только если есть изменения
git commit -m "Update recommendations" || exit 0
git push
# Fetch последних изменений
git fetch origin master
# Пытаемся слить изменения (игнорируем ошибки конфликтов)
git merge origin/master --no-commit --no-ff || true
- name: Force overwrite recommendations.txt
run: |
# Принудительно добавляем наш файл (игнорируя конфликты)
git add recommendations.txt --force
- name: Complete merge and push
run: |
# Завершаем merge/commit
git diff --cached --quiet || git commit -m "Update recommendations"
# Пушим изменения
git push origin master

143
lib/actions.sh Normal file
View file

@ -0,0 +1,143 @@
backup_strats() {
# Бэкап папки стратегий
if [ -d /opt/zapret/extra_strats ]; then
echo -e "${yellow}Сделать бэкап /opt/zapret/extra_strats ?${plain}"
echo -e "${yellow}5 - Да, Enter - Нет, 0 - отмена${plain}"
read -r ans
if [ "$ans" = "0" ]; then
get_menu # сигнал “отмена/в меню”
fi
if [ "$ans" = "5" ] || [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then
rm -rf /opt/extra_strats 2>/dev/null || true
cp -rf /opt/zapret/extra_strats /opt/ || true
echo -e "${green}Бэкап extra_strats сохранён в /opt/extra_strats${plain}"
fi
fi
# Бэкап листа исключений
if [ -f /opt/zapret/lists/netrogat.txt ]; then
echo -e "${yellow}Сделать бэкап /opt/zapret/lists/netrogat.txt ?${plain}"
echo -e "${yellow}5 - Да, Enter - Нет, 0 - отмена и выход в меню${plain}"
read -r ans2
if [ "$ans2" = "0" ]; then
get_menu
fi
if [ "$ans2" = "5" ] || [ "$ans2" = "y" ] || [ "$ans2" = "Y" ]; then
cp -f /opt/zapret/lists/netrogat.txt /opt/netrogat.txt || true
echo -e "${green}Бэкап netrogat.txt сохранён в /opt/netrogat.txt${plain}"
fi
fi
return 0
}
menu_action_update_config_reset() {
echo -e "${yellow}Конфиг обновлен (UTC +0): $(curl -s "https://api.github.com/repos/IndeecFOX/zapret4rocket/commits?path=config.default&per_page=1" | grep '"date"' | head -n1 | cut -d'"' -f4) ${plain}"
backup_strats
/opt/zapret/init.d/sysv/zapret stop
rm -rf /opt/zapret/lists /opt/zapret/extra_strats
rm -f /opt/zapret/files/fake/http_fake_MS.bin \
/opt/zapret/files/fake/quic_{1..7}.bin \
/opt/zapret/files/fake/syn_packet.bin \
/opt/zapret/files/fake/tls_clienthello_{1..18}.bin \
/opt/zapret/files/fake/tls_clienthello_2n.bin \
/opt/zapret/files/fake/tls_clienthello_6a.bin \
/opt/zapret/files/fake/tls_clienthello_4pda_to.bin
get_repo
# Раскомменчивание юзера под keenetic или merlin
change_user
cp -f /opt/zapret/config.default /opt/zapret/config
/opt/zapret/init.d/sysv/zapret start
# ВАЖНО: check_access_list — это по сути интерактивный тест (он сам печатает и может ждать Enter),
# поэтому лучше вызывать его из get_menu отдельным пунктом ("01"), а не тут.
# check_access_list
echo -e "${green}Config файл обновлён. Листы подбора стратегий и исключений сброшены в дефолт, если не просили сохранить. Фейк файлы обновлены.${plain}"
return 0
}
menu_action_toggle_bolvan_ports() {
if grep -Eq '^NFQWS_PORTS_UDP=.*443$' "/opt/zapret/config"; then
sed -i '76s/443$/443,1400,3478-3481,5349,50000-50099,19294-19344/' /opt/zapret/config
sed -i 's/^--skip --filter-udp=50000/--filter-udp=50000/' "/opt/zapret/config"
rm -f /opt/zapret/init.d/sysv/custom.d/50-discord-media \
/opt/zapret/init.d/sysv/custom.d/50-stun4all \
/opt/zapret/init.d/openwrt/custom.d/50-stun4all \
/opt/zapret/init.d/openwrt/custom.d/50-discord-media
echo -e "${green}Уход от скриптов bol-van. Выделены порты 50000-50099,1400,3478-3481,5349 и раскомментированы стратегии DS, WA, TG${plain}"
elif grep -q '443,1400,3478-3481,5349,50000-50099,19294-19344$' "/opt/zapret/config"; then
sed -i 's/443,1400,3478-3481,5349,50000-50099,19294-19344$/443/' /opt/zapret/config
sed -i 's/^--filter-udp=50000/--skip --filter-udp=50000/' "/opt/zapret/config"
curl -L -o /opt/zapret/init.d/sysv/custom.d/50-stun4all \
https://raw.githubusercontent.com/bol-van/zapret/master/init.d/custom.d.examples.linux/50-stun4all
curl -L -o /opt/zapret/init.d/sysv/custom.d/50-discord-media \
https://raw.githubusercontent.com/bol-van/zapret/master/init.d/custom.d.examples.linux/50-discord-media
cp -f /opt/zapret/init.d/sysv/custom.d/50-stun4all /opt/zapret/init.d/openwrt/custom.d/50-stun4all
cp -f /opt/zapret/init.d/sysv/custom.d/50-discord-media /opt/zapret/init.d/openwrt/custom.d/50-discord-media
echo -e "${green}Работа от скриптов bol-van. Вернули строку к виду NFQWS_PORTS_UDP=443 и добавили \"--skip \" в начале строк стратегии войса${plain}"
else
echo -e "${yellow}Неизвестное состояние строки NFQWS_PORTS_UDP. Проверь конфиг вручную.${plain}"
return 0
fi
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Выполнение переключений завершено.${plain}"
return 0
}
menu_action_toggle_fwtype() {
if grep -q '^FWTYPE=iptables$' "/opt/zapret/config"; then
sed -i 's/^FWTYPE=iptables$/FWTYPE=nftables/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Zapret moode: nftables.${plain}"
elif grep -q '^FWTYPE=nftables$' "/opt/zapret/config"; then
sed -i 's/^FWTYPE=nftables$/FWTYPE=iptables/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Zapret moode: iptables.${plain}"
else
echo -e "${yellow}Неизвестное состояние строки FWTYPE. Проверь конфиг вручную.${plain}"
fi
return 0
}
menu_action_toggle_udp_range() {
if grep -q '^NFQWS_PORTS_UDP=443' "/opt/zapret/config"; then
sed -i 's/^NFQWS_PORTS_UDP=443/NFQWS_PORTS_UDP=1026-65531,443/' "/opt/zapret/config"
sed -i 's/^--skip --filter-udp=1026/--filter-udp=1026/' "/opt/zapret/config"
echo -e "${green}Стратегия UDP обхода активирована. Выделены порты 1026-65531${plain}"
elif grep -q '^NFQWS_PORTS_UDP=1026-65531,443' "/opt/zapret/config"; then
sed -i 's/^NFQWS_PORTS_UDP=1026-65531,443/NFQWS_PORTS_UDP=443/' "/opt/zapret/config"
sed -i 's/^--filter-udp=1026/--skip --filter-udp=1026/' "/opt/zapret/config"
echo -e "${green}Стратегия UDP обхода ДЕактивирована. Выделенные порты 1026-65531 убраны${plain}"
else
echo -e "${yellow}Неизвестное состояние строки NFQWS_PORTS_UDP. Проверь конфиг вручную.${plain}"
return 0
fi
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Выполнение переключений завершено.${plain}"
return 0
}

59
lib/netcheck.sh Normal file
View file

@ -0,0 +1,59 @@
# Network / access checks
get_yt_cluster_domain() {
local letters_map_a="u z p k f a 5 0 v q l g b 6 1 w r m h c 7 2 x s n i d 8 3 y t o j e 9 4 -"
local letters_map_b="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z -"
cluster_codename=$(curl -s --max-time 2 "https://redirector.xn--ngstr-lra8j.com/report_mapping?di=no"| sed -n 's/.*=>[[:space:]]*\([^ (:)]*\).*/\1/p')
#Второй раз для пробития нерелевантного ответа
cluster_codename=$(curl -s --max-time 2 "https://redirector.xn--ngstr-lra8j.com/report_mapping?di=no"| sed -n 's/.*=>[[:space:]]*\([^ (:)]*\).*/\1/p')
[ -z "$cluster_codename" ] && {
echo "Не удалось получить cluster_codename. Используем тогда rr1---sn-5goeenes.googlevideo.com" >&2
echo "rr1---sn-5goeenes.googlevideo.com"
return
}
local converted_name=""
local i=0
while [ $i -lt ${#cluster_codename} ]; do
char=$(echo "$cluster_codename" | cut -c$((i+1)))
idx=1
for a in $letters_map_a; do
[ "$a" = "$char" ] && break
idx=$((idx+1))
done
b=$(echo "$letters_map_b" | cut -d' ' -f $idx)
converted_name="${converted_name}${b}"
i=$((i+1))
done
echo "rr1---sn-${converted_name}.googlevideo.com"
}
check_access() {
local TestURL="$1"
# Проверка TLS 1.2
if curl --tls-max 1.2 --max-time 1 -s -o /dev/null "$TestURL"; then
echo -e "${green}Есть ответ по TLS 1.2 (важно для ТВ и т.п.). ${yellow}Тест может быть ошибочен.${plain}"
else
echo -e "${yellow}Нет ответа по TLS 1.2 (важно для ТВ и т.п.) Таймаут 2сек. ${red}Проверьте доступность вручную. Возможно ошибка теста.${plain}"
fi
# Проверка TLS 1.3
if curl --tlsv1.3 --max-time 1 -s -o /dev/null "$TestURL"; then
echo -e "${green}Есть ответ по TLS 1.3 (важно в основном для всего современного) ${yellow}Тест может быть ошибочен.${plain}"
else
echo -e "${yellow}Нет ответа по TLS 1.3 (важно в основном для всего современного) Таймаут 2сек. ${red}Проверьте доступность вручную. Возможно ошибка теста.${plain}"
fi
}
check_access_list() {
echo "Проверка доступности youtube.com (YT TCP)"
check_access "https://www.youtube.com/"
echo "Проверка доступности $(get_yt_cluster_domain) (YT TCP)"
check_access "https://$(get_yt_cluster_domain)"
echo "Проверка доступности meduza.io (RKN list)"
check_access "https://meduza.io"
echo "Проверка доступности www.instagram.com (RKN list + нужен рабочий DNS)"
check_access "https://www.instagram.com/"
}

122
lib/premium.sh Normal file
View file

@ -0,0 +1,122 @@
# ---- ZEFEER PREMIUM (777/999) ----
# Сделано исключительно ради мемов. Практического смысла не несёт.
PREMIUM_FLAG="$CACHE_DIR/premium.enabled"
PREMIUM_TITLE_FILE="$CACHE_DIR/premium.title"
rand_from_list() {
# usage: rand_from_list "a" "b" "c"
local n=$#
(( n == 0 )) && return 1
local idx=$(( (RANDOM % n) + 1 ))
eval "echo \"\${$idx}\""
}
spinner_for_seconds() {
local seconds="${1:-2}"
local msg="${2:-Работаем}"
local frames="|/-\\"
local i=0
local end=$((SECONDS + seconds))
local _had_tput=0
if command -v tput >/dev/null 2>&1; then
_had_tput=1
tput civis
trap 'tput cnorm; trap - EXIT INT TERM' EXIT INT TERM
fi
while (( SECONDS < end )); do
i=$(( (i + 1) % 4 ))
# \r + \033[2K: в начало строки и стереть строку
printf "\r\033[2K%s... [%c]" "$msg" "${frames:$i:1}"
sleep 1
done
printf "\r\033[2K%s... [OK]\n" "$msg"
if (( _had_tput )); then
tput cnorm
trap - EXIT INT TERM
fi
}
premium_get_or_set_title() {
mkdir -p "$CACHE_DIR"
if [[ -s "$PREMIUM_TITLE_FILE" ]]; then
cat "$PREMIUM_TITLE_FILE"
return 0
fi
local title
title="$(rand_from_list \
"Граф Дезинхрона" \
"Барон QUIC'а" \
"Хранитель Hostlist'ов" \
"Лорд --new" \
"Грандмастер FakeTLS" \
"Архитектор Сплитов" \
"Повелитель RST (легальный)" \
"Смотрящий за ipset'ом" \
"Владыка TTL (ненадолго)" \
"Амбассадор «Тест не точен»" \
)"
echo "$title" > "$PREMIUM_TITLE_FILE"
echo "$title"
}
zefeer_premium_777() {
mkdir -p "$CACHE_DIR"
echo -e "${green}Специальный zeefeer premium для Valery ProD, avg97, Xoz, GeGunT, blagodarenya, mikhyan, andric62, Whoze, Necronicle, Andrei_5288515371, Nomand, Dina_turat, Nergalss, Александра, АлександраП, vecheromholodno, ЕвгенияГ, Dyadyabo, skuwakin, izzzgoy, Grigaraz, Reconnaissance, comandante1928, rudnev2028, umad, rutakote, railwayfx, vtokarev1604, Grigaraz, a40letbezurojaya и subzeero452 активирован. Наверное. Так же благодарю поддержавших проект hey_enote, VssA, vladdrazz, Alexey_Tob, Bor1sBr1tva, Azamatstd, iMLT, Qu3Bee, SasayKudasay1, alexander_novikoff, MarsKVV, porfenon123, bobrishe_dazzle, kotov38, Levonkas, DA00001, trin4ik, geodomin, I_ZNA_I и анонимов${plain}"
if [[ -f "$PREMIUM_FLAG" ]]; then
local title
title="$(premium_get_or_set_title)"
echo -e "${yellow}ZEFEER PREMIUM уже активирован.${plain}"
echo -e "Ваш титул: ${green}${title}${plain}"
return 0
fi
echo -e "${yellow}Подключаемся к платёжному шлюзу...${plain}"
spinner_for_seconds 2 "Проверяем поддержку проекта"
# Фальш-результат
local verdict
verdict="$(rand_from_list \
"Транзакция не найдена, но найден хороший человек." \
"Оплата не прошла, зато прошли вы. В сердечко." \
"Биллинг лежит. Premium — стоит." \
"Счёт не выставлялся. Списали уважение." \
"Донат не обнаружен. Обнаружена смелость нажать 777." \
)"
echo -e "${green}${verdict}${plain}"
local title
title="$(premium_get_or_set_title)"
echo -e "Premium активирован ${green}ヽ(o^ ^o)ノ ${plain}"
echo -e "Присвоен титул: ${pink}${title}${plain}"
: > "$PREMIUM_FLAG"
}
zefeer_space_999() {
echo -e "${cyan}Секретный протокол 999: попытка связи с космосом...${plain}"
spinner_for_seconds 6 "Наводим тарелку на созвездие Пакетных Потерь"
local excuse
excuse="$(rand_from_list \
"Меркурий не в том доме." \
"Вспышка на Солнце сбила сигнал." \
"Ретроградный NAT. Портал закрыт." \
"Слишком много DPI на орбите — сигнал дропнули." \
"Космос ответил RST." \
"Сигнал ушёл по QUIC, а обратно пришёл по SMTP." \
"Спутник занят: обновляет hostlist." \
"Астральный ipset переполнен." \
"Связь есть, но только с IPv6, а вы в IPv4 настроении." \
"Сбой калибровки антенны: /dev/space не найден." \
)"
echo -e "${red}Ошибка связи:${plain} ${yellow}${excuse}${plain}"
}
# ---- /ZEFEER PREMIUM ----

86
lib/provider.sh Normal file
View file

@ -0,0 +1,86 @@
# ---- Provider detector integration ----
# Используем provider.txt как основной источник правды (просто строка "Provider - City")
PROVIDER_CACHE="/opt/zapret/extra_strats/cache/provider.txt"
PROVIDER_MENU="Не определён"
PROVIDER_INIT_DONE=0
# Вспомогательная функция: делает запрос к API и пишет в файл
_detect_api_simple() {
# 1. Скачиваем ответ во временный файл (чтобы точно видеть, что пришло)
local tmp_file="/tmp/z4r_provider_debug.txt"
curl -s --max-time 10 "http://ip-api.com/line?fields=isp,city" > "$tmp_file"
# 2. Читаем построчно (без пайпов, чтобы не терять код возврата)
local p_name=$(head -n 1 "$tmp_file")
local p_city=$(head -n 2 "$tmp_file" | tail -n 1)
# 3. Чистим жестко (оставляем только латиницу, цифры и пробелы)
# Удаляем вообще все странные символы
p_name=$(echo "$p_name" | tr -cd 'a-zA-Z0-9 ._-')
p_city=$(echo "$p_city" | tr -cd 'a-zA-Z0-9 ._-')
# Убираем дублирование, если API вернул 1 строку
if [ "$p_city" = "$p_name" ]; then p_city=""; fi
# 4. Формируем результат
local res="$p_name"
if [ -n "$p_city" ]; then
res="$res - $p_city"
fi
# 5. Проверка результата перед записью
if [ -n "$res" ]; then
mkdir -p "$(dirname "$PROVIDER_CACHE")"
echo "$res" > "$PROVIDER_CACHE"
else
echo "DEBUG: Результат парсинга пустой! (Raw: $(cat $tmp_file))" >&2
fi
# Чистим за собой
rm -f "$tmp_file"
}
provider_init_once() {
[ "$PROVIDER_INIT_DONE" = "1" ] && return 0
PROVIDER_INIT_DONE=1
# Если кэша нет или он пустой — пробуем определить
if [ ! -s "$PROVIDER_CACHE" ]; then
echo "Определяем провайдера..."
_detect_api_simple
fi
# Читаем результат в переменную меню
if [ -s "$PROVIDER_CACHE" ]; then
PROVIDER_MENU="$(cat "$PROVIDER_CACHE")"
else
PROVIDER_MENU="Не определён"
fi
}
provider_force_redetect() {
echo "Обновляем данные о провайдере..."
rm -f "$PROVIDER_CACHE"
_detect_api_simple
if [ -s "$PROVIDER_CACHE" ]; then
PROVIDER_MENU="$(cat "$PROVIDER_CACHE")"
else
PROVIDER_MENU="Не удалось определить"
fi
}
provider_set_manual_menu() {
read -re -p "Провайдер (например MTS/Beeline): " p
read -re -p "Город (можно пусто): " c
local res="$p"
[ -n "$c" ] && res="$res - $c"
mkdir -p "$(dirname "$PROVIDER_CACHE")"
echo "$res" > "$PROVIDER_CACHE"
PROVIDER_MENU="$res"
}
# ---- /Provider detector integration ----

63
lib/recommendations.sh Normal file
View file

@ -0,0 +1,63 @@
# ---- Recomendations module ----
RECS_URL="https://raw.githubusercontent.com/IndeecFOX/zapret4rocket/master/recommendations.txt"
RECS_FILE="/opt/zapret/extra_strats/cache/recommendations.txt"
# 1. Функция обновления базы
update_recommendations() {
mkdir -p "$(dirname "$RECS_FILE")"
# Проверка: если файл существует И он моложе 1 дня (24 часа) - выходим.
# -mtime -1 означает "изменен менее 1 дня назад"
if [ -f "$RECS_FILE" ] && [ -n "$(find "$RECS_FILE" -mtime -1 2>/dev/null)" ]; then
# Файл свежий, обновлять не нужно
return 0
fi
# Если файла нет или он старый - качаем
curl -s --max-time 5 "$RECS_URL" -o "$RECS_FILE" || rm -f "$RECS_FILE"
return 0
}
# 2. Функция показа подсказки (Logic + UI)
show_hint() {
local strat_type="$1" # UDP, TCP, GV или RKN
local my_isp=""
# А. Узнаем провайдера
if [ -s "/opt/zapret/extra_strats/cache/provider.txt" ]; then
my_isp="$(cat "/opt/zapret/extra_strats/cache/provider.txt")"
fi
# Б. Проверяем наличие базы
if [ -z "$my_isp" ] || [ ! -f "$RECS_FILE" ]; then
return 0
fi
# В. Ищем строку (grep -F для безопасности спецсимволов)
local line
line="$(grep -F "$my_isp|" "$RECS_FILE" | head -n 1)"
[ -z "$line" ] && return 0
# Г. Парсим (актуальный формат у тебя: ISP|UDP:...|TCP:...|GV:...|RKN:...
local part=""
case "$strat_type" in
"UDP") part="$(echo "$line" | cut -d'|' -f2 | cut -d':' -f2)" ;;
"TCP") part="$(echo "$line" | cut -d'|' -f3 | cut -d':' -f2)" ;;
"GV") part="$(echo "$line" | cut -d'|' -f4 | cut -d':' -f2)" ;;
"RKN") part="$(echo "$line" | cut -d'|' -f5 | cut -d':' -f2)" ;;
*) return 0 ;;
esac
# Д. Выводим
if [ -n "$part" ] && [ "$part" != "-" ]; then
echo ""
echo -e "${cyan}💡 Подсказка:${plain} Пользователи ${green}$my_isp${plain} часто выбирают: ${yellow}$part${plain}"
echo -e "Попробуйте начать с них."
echo ""
fi
return 0
}
# ---- /Recomendations module ----

203
lib/strategies.sh Normal file
View file

@ -0,0 +1,203 @@
# Функция определяет номер активной стратегии в указанной папке
# Использование: get_active_strat_num "/path/to/folder" MAX_COUNT
get_active_strat_num() {
local folder="$1"
local max="$2"
local i
# Перебираем файлы от 1 до MAX
for ((i=1; i<=max; i++)); do
if [ -s "${folder}/${i}.txt" ]; then
echo "$i"
return
fi
done
# Если ничего не найдено - 0
echo "0"
}
# Функция для генерации строки статуса стратегий
get_current_strategies_info() {
local s_udp=$(get_active_strat_num "/opt/zapret/extra_strats/UDP/YT" 8)
local s_tcp=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/YT" 17)
local s_gv=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/GV" 17)
local s_rkn=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/RKN" 17)
# Формируем красивую строку. Цвета можно менять.
# Функция для окраски: 0 - серый, >0 - зеленый
colorize_num() {
if [ "$1" == "0" ]; then
echo "${gray}Def${plain}"
else
echo "${green}$1${plain}"
fi
}
echo -e "YT_UDP:$(colorize_num "$s_udp") YT_TCP:$(colorize_num "$s_tcp") YT_GV:$(colorize_num "$s_gv") RKN:$(colorize_num "$s_rkn")"
}
#Функция для функции подбора стратегий
try_strategies() {
local count="$1"
local base_path="$2"
local list_file="$3"
local final_action="$4"
read -re -p "Введите номер стратегии к которой перейти или Enter: " strat_num
if (( strat_num < 1 || strat_num > count )); then
echo "Введено значение не из диапазона. Начинаем с 1 стратегии"
strat_num=1
fi
# Предварительная очистка всех файлов стратегий в папке
for ((clr_txt=1; clr_txt<=count; clr_txt++)); do
echo -n > "$base_path/${clr_txt}.txt"
done
# Основной цикл перебора
for ((strat_num=strat_num; strat_num<=count; strat_num++)); do
# Очищаем файл предыдущей стратегии (чтобы не было дублей)
if [[ $strat_num -ge 2 ]]; then
prev=$((strat_num - 1))
echo -n > "$base_path/${prev}.txt"
fi
# Запись в файл текущей стратегии
if [[ "$list_file" != "/dev/null" ]]; then
# Режим списка (копируем весь файл)
cp "$list_file" "$base_path/${strat_num}.txt"
else
# Режим одного домена
echo "$user_domain" > "$base_path/${strat_num}.txt"
fi
echo "Стратегия номер $strat_num активирована"
# Блок проверки доступности (curl)
# Работает только для TCP стратегий
if [[ "$count" == "17" ]]; then
local TestURL=""
# ЛОГИКА ВЫБОРА ДОМЕНА ДЛЯ ПРОВЕРКИ
if [[ "$user_domain" == "googlevideo.com" ]]; then
# 1. Если это GVideo - ищем живой кластер для проверки видеопотока
local cluster
cluster=$(get_yt_cluster_domain)
TestURL="https://$cluster"
echo "Проверка доступности (кластер): $cluster"
elif [[ -z "$user_domain" ]]; then
# 2. Если домен пустой (обычный режим YT) - проверяем доступ к самому сайту
TestURL="https://www.youtube.com"
else
# 3. Для кастомных доменов и RKN проверяем сам введенный домен
TestURL="https://$user_domain"
fi
check_access "$TestURL"
fi
read -re -p "Проверьте работу (1 - сохранить, 0 - отмена, Enter - далее): " answer_strat
if [[ "$answer_strat" == "1" ]]; then
echo "Стратегия $strat_num сохранена."
send_stats # Отправка телеметрии (если включена)
# Если передано дополнительное действие (final_action), выполняем его
if [[ -n "$final_action" ]]; then
eval "$final_action"
fi
return
elif [[ "$answer_strat" == "0" ]]; then
# Сброс текущей стратегии при отмене
echo -n > "$base_path/${strat_num}.txt"
echo "Изменения отменены."
return
fi
done
# Если цикл закончился, а пользователь ничего не выбрал
echo -n > "$base_path/${count}.txt"
echo "Все стратегии испробованы. Ничего не подошло."
pause_enter
return
}
#Сама функция подбора стратегий
Strats_Tryer() {
local mode_domain="$1"
local answer_strat_mode=""
local user_domain=""
# ВАЖНО: теперь Strats_Tryer не рисует меню и не спрашивает режим сам.
# Режим выбирается снаружи (strategies_submenu), а сюда приходит либо:
# - "1".."4" (режим)
# - или строка-домен (режим кастомного домена)
case "$mode_domain" in
"1"|"2"|"3"|"4")
answer_strat_mode="$mode_domain"
;;
*)
# Если аргумент не похож на режим — считаем, что это домен
answer_strat_mode="5"
user_domain="$mode_domain"
;;
esac
case "$answer_strat_mode" in
"1")
echo "Подбор для хост-листа YouTube с видеопотоком (UDP QUIC - браузеры, моб. приложения). Ранее заданная стратегия этого листа сброшена в дефолт."
#вывод подсказки
show_hint "UDP"
try_strategies 8 "/opt/zapret/extra_strats/UDP/YT" "/opt/zapret/extra_strats/UDP/YT/List.txt" ""
;;
"2")
echo "Подбор для хост-листа YouTube (TCP - сам интерфейс. Без видео-домена). Ранее заданная стратегия этого листа сброшена в дефолт."
#вывод подсказки
show_hint "TCP"
try_strategies 17 "/opt/zapret/extra_strats/TCP/YT" "/opt/zapret/extra_strats/TCP/YT/List.txt" ""
;;
"3")
echo "Подбор для googlevideo.com (Видеопоток YouTube). Ранее заданная стратегия этого листа сброшена в дефолт."
#на всякий случай убираем GV из листа YT
[ -f "/opt/zapret/extra_strats/TCP/YT/List.txt" ] && \
sed -i '/googlevideo.com/d' "/opt/zapret/extra_strats/TCP/YT/List.txt"
user_domain="googlevideo.com"
#вывод подсказки
show_hint "GV"
try_strategies 17 "/opt/zapret/extra_strats/TCP/GV" "/dev/null" ""
;;
"4")
echo "Подбор для хост-листа основных доменов блока RKN. Проверка доступности задана на домен meduza.io. Ранее заданная стратегия этого листа сброшена в дефолт."
for numRKN in {1..17}; do
echo -n > "/opt/zapret/extra_strats/TCP/RKN/${numRKN}.txt"
done
user_domain="meduza.io"
#вывод подсказки
show_hint "RKN"
try_strategies 17 "/opt/zapret/extra_strats/TCP/RKN" "/opt/zapret/extra_strats/TCP/RKN/List.txt" ""
;;
"5")
echo "Режим ручного указания домена"
# раньше домен спрашивался тут, но теперь ввод домена делается в сабменю
if [ -z "$user_domain" ]; then
echo "Домен не задан. Отмена."
return 0
fi
echo "Введён домен: $user_domain"
try_strategies 17 "/opt/zapret/extra_strats/TCP/temp" "/dev/null" \
"echo -n > \"/opt/zapret/extra_strats/TCP/temp/\${strat_num}.txt\"; \
echo \"$user_domain\" >> \"/opt/zapret/extra_strats/TCP/User/\${strat_num}.txt\""
;;
*)
echo "Пропуск подбора альтернативной стратегии"
return 0
;;
esac
}

199
lib/submenus.sh Normal file
View file

@ -0,0 +1,199 @@
# submenus.sh
# Единый стиль: loop + return на 0/Enter
#функция меню "1. Сменить стратегии"
strategies_submenu() {
while true; do
local strategies_status
strategies_status=$(get_current_strategies_info)
clear
echo -e "${cyan}--- Управление стратегиями ---${plain}"
echo -e "${yellow}Подобрать стратегию? (1-5 для подбора, 0 или Enter для отмены)${plain}"
echo -e " Текущие стратегии [${strategies_status}]"
echo -e
submenu_item " 1" "YouTube с видеопотоком (UDP QUIC)." "8 вариантов"
submenu_item " 2" "YouTube (TCP. Интерфейс)." "17 вариантов"
submenu_item " 3" "YouTube (TCP. Видеопоток/GV домен)." "17 вариантов"
submenu_item " 4" "RKN (Популярные блокированные сайты. Дискорд в т.ч.)." "17 вариантов"
submenu_item " 5" "Отдельный домен." "17 вариантов"
submenu_item " 0" "Назад"
echo ""
read -re -p "Ваш выбор: " ans
case "$ans" in
"1"|"2"|"3"|"4")
Strats_Tryer "$ans"
pause_enter
;;
"5")
local user_domain=""
read -re -p "Введите домен (например mydomain.com) или Enter для выхода: " user_domain
[ -z "$user_domain" ] && continue
Strats_Tryer "$user_domain"
pause_enter
;;
"0"|"")
return
;;
*)
echo -e "${yellow}Неверный ввод.${plain}"
sleep 1
;;
esac
done
}
flowoffload_submenu() {
while true; do
clear
echo -e "${cyan}--- FLOWOFFLOAD ---${plain}"
echo "Текущее состояние: $(grep '^FLOWOFFLOAD=' /opt/zapret/config 2>/dev/null)"
echo ""
submenu_item "1" "software (программное ускорение)"
submenu_item "2" "hardware (аппаратное NAT)"
submenu_item "3" "none (отключено)"
submenu_item "4" "donttouch (дефолт)"
submenu_item "0" "Назад"
echo ""
read -re -p "Ваш выбор: " ans
case "$ans" in
"1")
sed -i 's/^FLOWOFFLOAD=.*/FLOWOFFLOAD=software/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}FLOWOFFLOAD=software применён.${plain}"
pause_enter
;;
"2")
sed -i 's/^FLOWOFFLOAD=.*/FLOWOFFLOAD=hardware/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}FLOWOFFLOAD=hardware применён.${plain}"
pause_enter
;;
"3")
sed -i 's/^FLOWOFFLOAD=.*/FLOWOFFLOAD=none/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}FLOWOFFLOAD=none применён.${plain}"
pause_enter
;;
"4")
sed -i 's/^FLOWOFFLOAD=.*/FLOWOFFLOAD=donttouch/' "/opt/zapret/config"
/opt/zapret/install_prereq.sh
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}FLOWOFFLOAD=donttouch применён.${plain}"
pause_enter
;;
"0"|"")
return
;;
*)
echo -e "${yellow}Неверный ввод.${plain}"
sleep 1
;;
esac
done
}
tcp443_submenu() {
while true; do
clear
num=$(sed -n '112,128p' /opt/zapret/config | grep -n '^--filter-tcp=443 --hostlist-domains= --' | head -n1 | cut -d: -f1)
echo -e "${yellow}Безразборный режим по стратегии: ${plain}$((num ? num : 0))"
echo -e "\033[33mС каким номером применить стратегию? (1-17, 0 - отключение безразборного режима, Enter - выход) \033[31mПри активации кастомно подобранные домены будут очищены:${plain}"
read -re -p " " answer_bezr
case "$answer_bezr" in
"" )
return
;;
*)
if echo "$answer_bezr" | grep -Eq '^[0-9]+$' && [ "$answer_bezr" -ge 0 ] && [ "$answer_bezr" -le 17 ]; then
#Отключение
for i in $(seq 112 128); do
if sed -n "${i}p" /opt/zapret/config | grep -Fq -- '--filter-tcp=443 --hostlist-domains= --h'; then
sed -i "${i}s#--filter-tcp=443 --hostlist-domains= --h#--filter-tcp=443 --hostlist-domains=none.dom --h#" /opt/zapret/config
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Выполнена команда перезапуска zapret${plain}"
echo "Безразборный режим отключен"
break
fi
done
if [ "$answer_bezr" -ge 1 ] && [ "$answer_bezr" -le 17 ]; then
for f_clear in $(seq 1 17); do
echo -n > "/opt/zapret/extra_strats/TCP/User/$f_clear.txt"
echo -n > "/opt/zapret/extra_strats/TCP/temp/$f_clear.txt"
done
sed -i "$((111 + answer_bezr))s/--hostlist-domains=none\.dom/--hostlist-domains=/" /opt/zapret/config
/opt/zapret/init.d/sysv/zapret restart
echo -e "${green}Выполнена команда перезапуска zapret. ${yellow}Безразборный режим активирован на $answer_bezr стратегии для TCP-443. Проверка доступа к meduza.io${plain}"
check_access_list
fi
read -n 1 -s -r -p "Нажмите любую клавишу для продолжения..."
else
echo -e "${yellow}Неверный ввод.${plain}"
sleep 1
pause_enter
fi
;;
esac
done
}
provider_submenu() {
provider_init_once
while true; do
clear
echo -e "${cyan}--- Провайдер / подсказки ---${plain}"
echo -e "Текущий провайдер: ${green}${PROVIDER_MENU}${plain}"
echo ""
submenu_item "1" "Указать провайдера вручную"
submenu_item "2" "Определить провайдера заново (сбросить кэш)"
submenu_item "3" "Обновить базу рекомендаций (подсказки)"
submenu_item "0" "Назад"
echo ""
read -re -p "Ваш выбор: " ans
case "$ans" in
"1")
provider_set_manual_menu
sleep 1
pause_enter
;;
"2")
provider_force_redetect
sleep 1
pause_enter
;;
"3")
echo "Обновляем базу рекомендаций..."
rm -f "$RECS_FILE"
update_recommendations
if [ -s "$RECS_FILE" ]; then
echo -e "${green}База успешно обновлена!${plain}"
else
echo -e "${red}Ошибка обновления базы.${plain}"
fi
sleep 1
pause_enter
;;
"0"|"")
return
;;
*)
echo -e "${yellow}Неверный ввод.${plain}"
sleep 1
;;
esac
done
}

106
lib/telemetry.sh Normal file
View file

@ -0,0 +1,106 @@
# ---- Telemetry module integration ----
# Настройки Google Forms
STATS_FORM_ID="1FAIpQLScrUf7Pybm0n61aK8aZuxuAR8KhyNYZ-X0xjSUS8K72SmEhPw"
ENTRY_UUID="entry.1346249141"
ENTRY_ISP="entry.2008245653"
ENTRY_UDP="entry.592144534"
ENTRY_TCP="entry.1826276405"
ENTRY_GV="entry.1549076812"
ENTRY_RKN="entry.1527830884"
# 2. Пути к файлам (используем простые форматы)
CACHE_DIR="/opt/zapret/extra_strats/cache"
TELEMETRY_CFG="$CACHE_DIR/telemetry.config"
PROVIDER_TXT="$CACHE_DIR/provider.txt"
# Функция инициализации (Спрашивает пользователя один раз)
init_telemetry() {
mkdir -p "$CACHE_DIR"
local tel_enabled=""
local tel_uuid=""
# 1. Загружаем конфиг, если он есть
[ -f "$TELEMETRY_CFG" ] && source "$TELEMETRY_CFG"
# 2. Если статус еще не задан — спрашиваем
if [ -z "$tel_enabled" ]; then
echo ""
echo -e "${green}Хотите отправлять анонимную статистику (Провайдер + Стратегии)?${plain}"
echo -e "Это поможет понять, какие стратегии работают лучше всего."
read -p "Разрешить? (y/n): " stats_yn
case "$stats_yn" in
[Yy]*) tel_enabled="1" ;;
*) tel_enabled="0" ;;
esac
# Сразу сохраняем выбор
echo "tel_enabled=$tel_enabled" > "$TELEMETRY_CFG"
echo "tel_uuid=$tel_uuid" >> "$TELEMETRY_CFG"
if [ "$tel_enabled" == "1" ]; then
echo -e "${green}Спасибо! Статистика включена.${plain}"
else
echo -e "${red}Статистика отключена.${plain}"
fi
sleep 1
fi
# 3. Генерация UUID (если включено и его нет)
if [ "$tel_enabled" == "1" ] && [ -z "$tel_uuid" ]; then
# Пытаемся взять системный UUID или генерируем md5 от времени
if [ -f /proc/sys/kernel/random/uuid ]; then
tel_uuid=$(cat /proc/sys/kernel/random/uuid | cut -c1-8)
else
tel_uuid=$(date +%s%N | md5sum | head -c 8)
fi
# Перезаписываем конфиг с новым UUID
echo "tel_enabled=$tel_enabled" > "$TELEMETRY_CFG"
echo "tel_uuid=$tel_uuid" >> "$TELEMETRY_CFG"
fi
}
# Функция отправки статистики
send_stats() {
# Если конфига нет, значит init_telemetry не запускался — выходим
[ ! -f "$TELEMETRY_CFG" ] && return 0
# Читаем переменные (tel_enabled, tel_uuid)
source "$TELEMETRY_CFG"
# Если пользователь запретил — выходим
if [ "$tel_enabled" != "1" ]; then
return 0
fi
# 1. Провайдер (Читаем из provider.txt, который создает Provider detector)
local my_isp="Unknown"
if [ -s "$PROVIDER_TXT" ]; then
my_isp=$(cat "$PROVIDER_TXT")
else
# Фолбек: если provider.txt еще нет, пробуем быстро узнать
my_isp=$(curl -s --max-time 3 "http://ip-api.com/line?fields=org" | tr -cd '[:alnum:] ._-')
fi
# Обрезаем до 60 символов и ставим заглушку если пусто
my_isp=$(echo "$my_isp" | head -c 60)
[ -z "$my_isp" ] && my_isp="Unknown"
# 2. Определяем номера стратегий
local s_udp=$(get_active_strat_num "/opt/zapret/extra_strats/UDP/YT" 8)
local s_tcp=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/YT" 17)
local s_gv=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/GV" 17)
local s_rkn=$(get_active_strat_num "/opt/zapret/extra_strats/TCP/RKN" 17)
# 3. Отправка в Google Forms (Тихий режим, в фоне &)
curl -sL --max-time 10 \
-d "$ENTRY_UUID=$tel_uuid" \
-d "$ENTRY_ISP=$my_isp" \
-d "$ENTRY_UDP=$s_udp" \
-d "$ENTRY_TCP=$s_tcp" \
-d "$ENTRY_GV=$s_gv" \
-d "$ENTRY_RKN=$s_rkn" \
"https://docs.google.com/forms/d/e/$STATS_FORM_ID/formResponse" > /dev/null 2>&1 &
}
# ---- /Telemetry module integration ----

14
lib/ui.sh Normal file
View file

@ -0,0 +1,14 @@
# UI helpers
pause_enter() {
read -re -p "Enter для продолжения" _
}
submenu_item() {
echo -e "${green}$1. $2${plain} $3"
}
# Совместимость со старым кодом меню
exit_to_menu() {
pause_enter
}

1115
z4r.sh

File diff suppressed because it is too large Load diff