feat(ru): full Russia/offline support (#RF)
- Add lib/download_helper.sh: proxy detection, GitHub mirrors, offline cache, tarball fallback - Rewrite install_nfqws.sh: offline bundle support, bol-van/zapret via git clone + tarball + mirror fallback - Rewrite sync_from_upstream.sh: proxy aware, --offline flag, graceful degradation - Add prepare_offline_bundle.sh: create tar.gz with sources + lists + fake bins on machine WITH internet - Add .env.example: GITHUB_PROXY, HTTPS_PROXY configuration - Update install.sh: check GitHub reachability, suggest proxy/offline, --offline flag - Update README: Russia section at top, Offline section, VPN/proxy examples, .env guide
This commit is contained in:
+123
-117
@@ -1,27 +1,56 @@
|
||||
#!/bin/bash
|
||||
# sync_from_upstream.sh - Merge upstream Flowseal changes into Linux version
|
||||
# Handles: lists, .service/hosts, .service/version, auto-detection of NEW .bat strategies
|
||||
# Handles: proxy (HTTPS_PROXY), offline bundle, lists, .service/hosts, .service/version
|
||||
# Works even if GitHub blocked (with proxy or offline bundle)
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/lib/download_helper.sh"
|
||||
|
||||
UPSTREAM_REPO="https://github.com/Flowseal/zapret-discord-youtube.git"
|
||||
TMP_DIR="/tmp/zapret-upstream-$$"
|
||||
LOCAL_VERSION_FILE="$SCRIPT_DIR/.service/version.txt"
|
||||
STRATEGIES_DIR="$SCRIPT_DIR/strategies"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " Upstream Sync"
|
||||
echo " Source: $UPSTREAM_REPO"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
mkdir -p "$STRATEGIES_DIR"
|
||||
|
||||
log_msg() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
# Load proxy settings
|
||||
load_proxy
|
||||
|
||||
# Clone upstream with 60s timeout
|
||||
log_msg "[*] Cloning upstream Flowseal/zapret-discord-youtube..."
|
||||
if ! git clone --depth=1 --single-branch --branch main "$UPSTREAM_REPO" "$TMP_DIR" 2>/dev/null; then
|
||||
log_msg "[FAIL] Could not clone upstream. Check internet/DNS."
|
||||
rm -rf "$TMP_DIR"
|
||||
exit 1
|
||||
# Detect if offline mode requested
|
||||
OFFLINE_MODE=0
|
||||
if [[ "${1:-}" == "--offline" ]]; then
|
||||
OFFLINE_MODE=1
|
||||
echo "[*] OFFLINE MODE: skipping network, using local files only"
|
||||
fi
|
||||
|
||||
REBUILD_FLAG=0
|
||||
UPDATE_COUNT=0
|
||||
NEW_COUNT=0
|
||||
|
||||
# Attempt to clone/update upstream
|
||||
if [[ "$OFFLINE_MODE" -eq 0 ]]; then
|
||||
echo "[*] Cloning upstream (with fallback)..."
|
||||
if ! clone_repo_fallback "Flowseal/zapret-discord-youtube" "main" "$TMP_DIR"; then
|
||||
echo ""
|
||||
echo "[WARN] Cannot reach upstream GitHub."
|
||||
echo " Check proxy, VPN, or prepare_offline_bundle.sh"
|
||||
echo ""
|
||||
read -rp "Continue in offline mode (skip sync)? [Y/n]: " ans
|
||||
if [[ "${ans:-Y}" == [yY]* ]]; then
|
||||
OFFLINE_MODE=1
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Read versions
|
||||
@@ -30,36 +59,38 @@ UPSTREAM_VER=$(cat "$TMP_DIR/.service/version.txt" 2>/dev/null | tr -d '[:space:
|
||||
[[ -z "$LOCAL_VER" ]] && LOCAL_VER="unknown"
|
||||
[[ -z "$UPSTREAM_VER" ]] && UPSTREAM_VER="unknown"
|
||||
|
||||
log_msg "Local version: $LOCAL_VER"
|
||||
log_msg "Upstream version: $UPSTREAM_VER"
|
||||
echo "Local version: $LOCAL_VER"
|
||||
echo "Upstream version: $UPSTREAM_VER"
|
||||
|
||||
REBUILD_FLAG=0
|
||||
if [[ "$UPSTREAM_VER" != "$LOCAL_VER" ]]; then
|
||||
REBUILD_FLAG=1
|
||||
log_msg "[*] Version changed: $LOCAL_VER → $UPSTREAM_VER"
|
||||
echo "[*] Version changed: $LOCAL_VER → $UPSTREAM_VER"
|
||||
fi
|
||||
|
||||
# ======================
|
||||
# MERGE LISTS
|
||||
# ======================
|
||||
log_msg "[*] Merging upstream lists..."
|
||||
echo "[*] Merging upstream lists..."
|
||||
merge_list() {
|
||||
local file="$1"
|
||||
local src="$TMP_DIR/lists/$file"
|
||||
local dst="$SCRIPT_DIR/lists/$file"
|
||||
if [[ ! -f "$src" ]]; then
|
||||
log_msg " [SKIP] Upstream missing: lists/$file"
|
||||
local src=""
|
||||
if [[ -d "$TMP_DIR" ]] && [[ -f "$TMP_DIR/lists/$file" ]]; then
|
||||
src="$TMP_DIR/lists/$file"
|
||||
elif [[ -f "$SCRIPT_DIR/.bundle/$file" ]]; then
|
||||
src="$SCRIPT_DIR/.bundle/$file"
|
||||
echo " [CACHED] lists/$file (from offline bundle)"
|
||||
else
|
||||
echo " [SKIP] lists/$file (not available)"
|
||||
return
|
||||
fi
|
||||
cp "$src" "$dst"
|
||||
log_msg " [OK] lists/$file"
|
||||
cp "$src" "$SCRIPT_DIR/lists/$file"
|
||||
echo " [OK] lists/$file"
|
||||
}
|
||||
merge_list "list-general.txt"
|
||||
merge_list "list-google.txt"
|
||||
merge_list "list-exclude.txt"
|
||||
merge_list "ipset-exclude.txt"
|
||||
|
||||
# Only update ipset-all if placeholder or enabled
|
||||
if [[ -f "$SCRIPT_DIR/.service/ipset_filter_loaded" ]] || [[ -f "$SCRIPT_DIR/.service/ipset_filter_any" ]]; then
|
||||
merge_list "ipset-all.txt" 2>/dev/null || true
|
||||
fi
|
||||
@@ -67,25 +98,30 @@ fi
|
||||
# ======================
|
||||
# MERGE .service/hosts + version
|
||||
# ======================
|
||||
log_msg "[*] Merging .service files..."
|
||||
if [[ -f "$TMP_DIR/.service/hosts" ]]; then
|
||||
echo "[*] Merging .service files..."
|
||||
|
||||
if [[ -d "$TMP_DIR" ]] && [[ -f "$TMP_DIR/.service/hosts" ]]; then
|
||||
cp "$TMP_DIR/.service/hosts" "$SCRIPT_DIR/.service/hosts"
|
||||
log_msg " [OK] .service/hosts (GitHub, Telegram, Discord static IPs)"
|
||||
echo " [OK] .service/hosts (GitHub, Telegram, Discord static IPs)"
|
||||
REBUILD_FLAG=1
|
||||
elif [[ -f "$SCRIPT_DIR/.bundle/hosts" ]]; then
|
||||
cp "$SCRIPT_DIR/.bundle/hosts" "$SCRIPT_DIR/.service/hosts"
|
||||
echo " [OK] .service/hosts (from offline bundle)"
|
||||
REBUILD_FLAG=1
|
||||
else
|
||||
echo " [SKIP] .service/hosts (not available)"
|
||||
fi
|
||||
|
||||
if [[ -f "$TMP_DIR/.service/version.txt" ]]; then
|
||||
if [[ -d "$TMP_DIR" ]] && [[ -f "$TMP_DIR/.service/version.txt" ]]; then
|
||||
echo "$UPSTREAM_VER-linux" > "$LOCAL_VERSION_FILE"
|
||||
log_msg " [OK] .service/version.txt → $UPSTREAM_VER-linux"
|
||||
echo " [OK] .service/version.txt → $UPSTREAM_VER-linux"
|
||||
REBUILD_FLAG=1
|
||||
fi
|
||||
|
||||
# ======================
|
||||
# PARSE NEW .bat STRATEGIES → .conf
|
||||
# ======================
|
||||
log_msg "[*] Checking for new upstream strategies..."
|
||||
NEW_COUNT=0
|
||||
UPDATE_COUNT=0
|
||||
echo "[*] Checking for new upstream strategies..."
|
||||
|
||||
parse_bat_to_conf() {
|
||||
local src="$1"
|
||||
@@ -96,125 +132,95 @@ parse_bat_to_conf() {
|
||||
[[ -z "$name" ]] && name="general"
|
||||
local conf_name="$STRATEGIES_DIR/${name}.conf"
|
||||
|
||||
# Extract winws arguments after "winws.exe ...parameters..."
|
||||
# Step 1: Find all lines after winws.exe, clean line continuations (^) and quotes
|
||||
local raw_args
|
||||
raw_args=$(sed -n '/winws\.exe/I,${ p }' "$src" | \
|
||||
sed 's/\^//g' | \
|
||||
tr '\n' ' ' | \
|
||||
sed -E 's/start[^"]*"[^"]*"[^"]*"[^"]*"//i; s/ *\/min //i')
|
||||
# Join lines ending with ^, find winws.exe block
|
||||
raw_args=$(cat "$src" | tr '\n' ' ' | \
|
||||
sed 's/\^/ /g' | \
|
||||
sed -E 's/start[^"]*"[^"]*"[^"]*"[^"*]*"//i; s/ *\/min //i; s/\^//g')
|
||||
|
||||
# Step 2: Remove the winws.exe binary path and WF filters (Windows-only --wf-*)
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/[^ ]*winws\.exe//i; s/--wf-tcp=[^ ]+//; s/--wf-udp=[^ ]+//')
|
||||
|
||||
# Step 3: Fix Windows paths to placeholders
|
||||
# "...bin\file.bin" → %BIN%/file.bin
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/"%BIN%\\([^"]*)"/\%BIN%\/\1/g')
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/%BIN%\\([^ ]*)/\%BIN%\/\1/g')
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/"%LISTS%\\([^"]*)"/\%LISTS%\/\1/g')
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/%LISTS%\\([^ ]*)/\%LISTS%\/\1/g')
|
||||
|
||||
# Remove stray quotes
|
||||
raw_args=$(echo "$raw_args" | tr -d '\"')
|
||||
|
||||
# Step 4: Replace Windows variables with Linux placeholders
|
||||
# Extract after winws.exe
|
||||
raw_args=$(echo "$raw_args" | sed -n 's/.*winws\.exe[^"]*"\?//pi')
|
||||
|
||||
# Remove --wf-* (Windows-only)
|
||||
raw_args=$(echo "$raw_args" | sed -E 's/--wf-tcp=[^ "]+//g; s/--wf-udp=[^ "]+//g')
|
||||
|
||||
# Normalize paths
|
||||
raw_args=$(echo "$raw_args" | sed 's|\\|/|g')
|
||||
raw_args=$(echo "$raw_args" | sed -E 's|"?%BIN%/|"%BIN%/|g; s|"?%LISTS%/|"%LISTS%/|g')
|
||||
|
||||
# Replace vars
|
||||
raw_args=$(echo "$raw_args" | sed 's/%GameFilterTCP%/%GAME_TCP%/g; s/%GameFilterUDP%/%GAME_UDP%/g')
|
||||
|
||||
# Step 5: Split by --new into RULE1..9
|
||||
local rule_num=1
|
||||
local current_rule=""
|
||||
|
||||
# Convert --new separated rules into lines
|
||||
# Split by --new into RULE1=... RULEN=...
|
||||
echo "# Strategy: $name" > "$conf_name"
|
||||
echo "# Auto-generated from upstream: $name_raw.bat" >> "$conf_name"
|
||||
echo "# Version: $UPSTREAM_VER" >> "$conf_name"
|
||||
echo "# Auto-converted from upstream: $name_raw.bat" >> "$conf_name"
|
||||
echo "# Version: ${UPSTREAM_VER:-unknown}" >> "$conf_name"
|
||||
echo "" >> "$conf_name"
|
||||
|
||||
# Use awk to split by --new
|
||||
echo "$raw_args" | awk '{
|
||||
split($0, parts, " --new");
|
||||
for (i=1; i<=length(parts); i++) {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", parts[i]);
|
||||
if (parts[i] != "") {
|
||||
print "RULE" i "=" parts[i];
|
||||
}
|
||||
}
|
||||
}' >> "$conf_name"
|
||||
|
||||
local i=1
|
||||
local rule
|
||||
while IFS= read -r rule; do
|
||||
rule=$(echo "$rule" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
[[ -z "$rule" ]] && continue
|
||||
echo "RULE${i}=${rule}" >> "$conf_name"
|
||||
i=$((i + 1))
|
||||
done <<< "$(echo "$raw_args" | awk 'BEGIN{RS="--new"; ORS="\n"}{print}')"
|
||||
}
|
||||
|
||||
for bat in "$TMP_DIR"/general*.bat; do
|
||||
[[ -f "$bat" ]] || continue
|
||||
bat_name=$(basename "$bat")
|
||||
[[ "$bat_name" == "service.bat" ]] && continue
|
||||
# Only scan if upstream cloned
|
||||
if [[ -d "$TMP_DIR" ]]; then
|
||||
for bat in "$TMP_DIR"/general*.bat; do
|
||||
[[ -f "$bat" ]] || continue
|
||||
bat_name=$(basename "$bat")
|
||||
[[ "$bat_name" == "service.bat" ]] && continue
|
||||
|
||||
# Normalize conf name
|
||||
strategy_name=$(echo "$bat_name" | sed -E 's/^general[[:space:]]*//; s/\(|\)//g; s/\.bat$//')
|
||||
[[ -z "$strategy_name" ]] && strategy_name="general"
|
||||
strategy_name=$(echo "$bat_name" | sed -E 's/^general[[:space:]]*//; s/\(|\)//g; s/\.bat$//; s/ /_/g')
|
||||
[[ -z "$strategy_name" ]] && strategy_name="general"
|
||||
conf_file="$STRATEGIES_DIR/${strategy_name}.conf"
|
||||
|
||||
conf_file="$STRATEGIES_DIR/${strategy_name}.conf"
|
||||
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
# Check if upstream strategy changed (compare hash)
|
||||
remote_hash=$(md5sum "$bat" 2>/dev/null | awk '{print $1}')
|
||||
local_hash=$(md5sum "$conf_file" 2>/dev/null | awk '{print $1}')
|
||||
if [[ "$remote_hash" != "$(grep '^# hash:' "$conf_file" 2>/dev/null | awk '{print $3}')" ]]; then
|
||||
log_msg " [CHANGED] Strategy updated: $bat_name → $strategy_name.conf"
|
||||
if [[ -f "$conf_file" ]]; then
|
||||
# Simple update: mark for manual review
|
||||
parse_bat_to_conf "$bat"
|
||||
UPDATE_COUNT=$((UPDATE_COUNT + 1))
|
||||
echo " [CHANGED] Strategy updated: $bat_name → $strategy_name.conf"
|
||||
REBUILD_FLAG=1
|
||||
else
|
||||
parse_bat_to_conf "$bat"
|
||||
NEW_COUNT=$((NEW_COUNT + 1))
|
||||
echo " [NEW] Strategy detected: $bat_name → $strategy_name.conf"
|
||||
REBUILD_FLAG=1
|
||||
fi
|
||||
else
|
||||
log_msg " [NEW] Detected upstream strategy: $bat_name → $strategy_name.conf"
|
||||
parse_bat_to_conf "$bat"
|
||||
NEW_COUNT=$((NEW_COUNT + 1))
|
||||
REBUILD_FLAG=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $NEW_COUNT -gt 0 ]]; then
|
||||
log_msg " [INFO] $NEW_COUNT new strategy(ies) auto-converted."
|
||||
log_msg " [WARN] Please verify new strategies manually before using."
|
||||
echo " [INFO] $NEW_COUNT new strategy(ies) auto-converted."
|
||||
echo " [WARN] Please verify new strategies manually before using."
|
||||
fi
|
||||
if [[ $UPDATE_COUNT -gt 0 ]]; then
|
||||
log_msg " [INFO] $UPDATE_COUNT strategy(ies) updated from upstream."
|
||||
echo " [INFO] $UPDATE_COUNT strategy(ies) updated from upstream."
|
||||
fi
|
||||
if [[ $NEW_COUNT -eq 0 ]] && [[ $UPDATE_COUNT -eq 0 ]]; then
|
||||
log_msg " [OK] All strategies up to date."
|
||||
echo " [OK] All strategies up to date."
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
# Cleanup upstream clone
|
||||
rm -rf "$TMP_DIR"
|
||||
|
||||
# ======================
|
||||
# CHECK if general*.sh wrappers exist for all .conf
|
||||
# ENSURE WRAPPERS
|
||||
# ======================
|
||||
log_msg "[*] Checking strategy wrappers (general*.sh)..."
|
||||
for conf in "$STRATEGIES_DIR"/*.conf; do
|
||||
[[ -f "$conf" ]] || continue
|
||||
local name
|
||||
name=$(basename "$conf" .conf)
|
||||
local wrapper="$SCRIPT_DIR/general_${name}.sh"
|
||||
if [[ ! -f "$wrapper" ]]; then
|
||||
# Create wrapper
|
||||
cat > "$wrapper" <<EOF
|
||||
#!/bin/bash
|
||||
# Auto-generated wrapper for strategy: $name
|
||||
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "\$SCRIPT_DIR/run_strategy.sh" "$name"
|
||||
EOF
|
||||
chmod +x "$wrapper"
|
||||
log_msg " [OK] Created wrapper: $(basename "$wrapper")"
|
||||
fi
|
||||
done
|
||||
echo "[*] Checking strategy wrappers (general*.sh)..."
|
||||
bash "$SCRIPT_DIR/ensure_wrappers.sh" 2>/dev/null || true
|
||||
|
||||
# ======================
|
||||
# REPORT
|
||||
# ======================
|
||||
log_msg "[*] Sync complete."
|
||||
echo "[*] Sync complete."
|
||||
if [[ "$REBUILD_FLAG" -eq 1 ]]; then
|
||||
log_msg "[*] REBUILD NEEDED: run ./update.sh or ./install.sh"
|
||||
echo "[*] REBUILD NEEDED: run ./update.sh or ./install.sh"
|
||||
exit 2
|
||||
else
|
||||
log_msg "[*] No changes detected."
|
||||
echo "[*] No changes detected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user