Files
val-blog/tmp/mihomo-guard.sh
T

207 lines
5.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# mihomo-guard.sh
# 自动保活 + 自动刷新订阅 + 自动切换可用线路
#
# 目标:
# - 定期通过 mihomo 代理探测 Telegram 官方入口连通性
# - 连续失败后,自动刷新订阅并切换到候选节点
# - 若切换后仍失败,再 fallback 重启 mihomo
#
# 说明:
# - 依赖 mihomo external-controller API (9090)
# - 当前按你的环境写死了 controller / secret / provider / group 名称
# ===== 可调参数 =====
PROBE_URL="https://api.telegram.org"
PROXY_ADDR="http://127.0.0.1:7897"
PROBE_TIMEOUT=4
SLEEP_INTERVAL=15
MAX_FAIL_COUNT=5
MIHOMO_BIN="/usr/local/opt/mihomo/bin/mihomo"
MIHOMO_DIR="/usr/local/etc/mihomo"
LOG_FILE="/usr/local/var/mihomo-guard.log"
# Mihomo controller API
CONTROLLER_URL="http://127.0.0.1:9090"
CONTROLLER_SECRET="123456"
SUB_PROVIDER="my_sub"
# 代理组名称(基于你当前 API 返回)
MANUAL_GROUP="手动选择"
AI_GROUP="🤖 AI服务"
DEV_GROUP="💻 开发服务"
# 故障恢复时优先尝试的候选节点(亚洲优先)
CANDIDATE_PROXIES=(
"🇭🇰香港01-IPv6|AI"
"🇭🇰香港03-IPv6|AI"
"🇭🇰香港05-流媒体解锁|AI"
"🇸🇬新加坡01-流媒体解锁|AI"
"🇸🇬新加坡02-流媒体解锁|AI"
"🇨🇳台湾03-流媒体解锁|AI"
"🇨🇳台湾04-流媒体解锁|AI"
"🇯🇵日本03"
"🇯🇵日本01"
)
FAIL_COUNT=0
LAST_SWITCH_INDEX=-1
log() {
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$ts] $*" | tee -a "$LOG_FILE"
}
api_call() {
local method="$1"
local path="$2"
local data="${3:-}"
if [ -n "$data" ]; then
curl -sS -X "$method" \
-H "Authorization: Bearer $CONTROLLER_SECRET" \
-H "Content-Type: application/json" \
-d "$data" \
"$CONTROLLER_URL$path"
else
curl -sS -X "$method" \
-H "Authorization: Bearer $CONTROLLER_SECRET" \
"$CONTROLLER_URL$path"
fi
}
probe_once() {
curl -x "$PROXY_ADDR" -m "$PROBE_TIMEOUT" -sS "$PROBE_URL" >/dev/null
}
refresh_subscription() {
log "[ACTION] Refreshing provider: $SUB_PROVIDER"
api_call PUT "/providers/proxies/$SUB_PROVIDER" >/dev/null 2>&1
}
switch_group_to() {
local group_name="$1"
local target_proxy="$2"
log "[ACTION] Switching group [$group_name] -> [$target_proxy]"
api_call PUT "/proxies/$group_name" "{\"name\":\"$target_proxy\"}" >/dev/null 2>&1
}
try_switch_routes() {
log "[ACTION] Starting provider refresh + route switching workflow"
refresh_subscription
sleep 3
local total=${#CANDIDATE_PROXIES[@]}
local i
for ((i=1; i<=total; i++)); do
local idx=$(((LAST_SWITCH_INDEX + i) % total))
local candidate="${CANDIDATE_PROXIES[$idx]}"
log "[INFO] Trying candidate proxy: $candidate"
# 1) 手动选择 -> 指定节点
switch_group_to "$MANUAL_GROUP" "$candidate"
sleep 1
# 2) 关键业务组切到手动选择,避免继续受 URLTest 组影响
switch_group_to "$AI_GROUP" "$MANUAL_GROUP"
sleep 1
switch_group_to "$DEV_GROUP" "$MANUAL_GROUP"
sleep 1
# 给 mihomo 一点时间收敛
sleep 3
if probe_once; then
LAST_SWITCH_INDEX=$idx
log "[OK] Probe success after switching to [$candidate]"
log "[RECOVERED] 当前恢复到线路: [$candidate]"
return 0
else
log "[WARN] Probe still failed after switching to [$candidate]"
fi
done
log "[ERROR] All candidate proxies failed after refresh + switching"
return 1
}
restart_mihomo() {
log "[ACTION] Restarting mihomo..."
if pgrep -f "$MIHOMO_BIN" >/dev/null 2>&1; then
log "[INFO] Existing mihomo process found, sending TERM"
pkill -f "$MIHOMO_BIN" || log "[WARN] pkill mihomo returned non-zero"
sleep 3
else
log "[INFO] No existing mihomo process found"
fi
if [ ! -x "$MIHOMO_BIN" ]; then
log "[ERROR] mihomo binary not found or not executable: $MIHOMO_BIN"
return 1
fi
if [ ! -d "$MIHOMO_DIR" ]; then
log "[ERROR] mihomo directory not found: $MIHOMO_DIR"
return 1
fi
log "[INFO] Starting mihomo with: $MIHOMO_BIN -d $MIHOMO_DIR"
nohup "$MIHOMO_BIN" -d "$MIHOMO_DIR" >>"$LOG_FILE" 2>&1 &
sleep 3
if pgrep -f "$MIHOMO_BIN" >/dev/null 2>&1; then
log "[INFO] mihomo started successfully (pid: $(pgrep -f "$MIHOMO_BIN" | tr '\n' ' '))"
else
log "[ERROR] Failed to start mihomo"
return 1
fi
sleep 5
if probe_once; then
log "[OK] Probe success after mihomo restart"
log "[RECOVERED] 当前恢复到线路: [mihomo重启后默认线路]"
return 0
else
log "[WARN] Probe still failed after mihomo restart"
return 1
fi
}
log "=== mihomo-guard started (PROXY=$PROXY_ADDR, URL=$PROBE_URL, INTERVAL=${SLEEP_INTERVAL}s, MAX_FAIL=$MAX_FAIL_COUNT) ==="
log "=== controller=$CONTROLLER_URL provider=$SUB_PROVIDER manual_group=$MANUAL_GROUP ==="
while true; do
if probe_once; then
if [ "$FAIL_COUNT" -gt 0 ]; then
log "[OK] Probe success after $FAIL_COUNT failure(s)"
fi
FAIL_COUNT=0
else
FAIL_COUNT=$((FAIL_COUNT + 1))
log "[WARN] Probe failed ($FAIL_COUNT/$MAX_FAIL_COUNT)"
if [ "$FAIL_COUNT" -ge "$MAX_FAIL_COUNT" ]; then
log "[WARN] Consecutive probe failures reached $FAIL_COUNT, trying refresh + route switch"
if try_switch_routes; then
FAIL_COUNT=0
else
log "[WARN] Refresh + route switch failed, falling back to mihomo restart"
restart_mihomo || log "[ERROR] Restart fallback did not recover probe"
FAIL_COUNT=0
fi
fi
fi
sleep "$SLEEP_INTERVAL"
done