feat: initial commit of clean multi-protocol singbox core script
This commit is contained in:
+328
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# 模块:多协议分享链接生成、聚合订阅与多客户端配置导出
|
||||
# ==============================================================================
|
||||
|
||||
show_node_info() {
|
||||
if [[ ! -f "${META_FILE}" ]]; then
|
||||
log_error "未检测到已安装的节点配置元数据,请先执行安装!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local uuid server_ip
|
||||
uuid=$(jq -r '.uuid' "${META_FILE}")
|
||||
server_ip=$(get_public_ip)
|
||||
|
||||
local share_links=()
|
||||
local clash_proxies=""
|
||||
local sb_outbounds=()
|
||||
local summary_text=""
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 1. VLESS-Reality
|
||||
# --------------------------------------------------------------------------
|
||||
if [[ $(jq -r '.protocols.vless_reality.enabled // false' "${META_FILE}") == "true" ]]; then
|
||||
local vl_port vl_sni vl_pub vl_sid vl_name vl_link
|
||||
vl_port=$(jq -r '.protocols.vless_reality.port' "${META_FILE}")
|
||||
vl_sni=$(jq -r '.protocols.vless_reality.sni' "${META_FILE}")
|
||||
vl_pub=$(jq -r '.protocols.vless_reality.public_key' "${META_FILE}")
|
||||
vl_sid=$(jq -r '.protocols.vless_reality.short_id' "${META_FILE}")
|
||||
vl_name="VLESS-Reality-${server_ip}"
|
||||
|
||||
vl_link="vless://${uuid}@${server_ip}:${vl_port}?security=reality&encryption=none&pbk=${vl_pub}&headerType=none&fp=chrome&type=tcp&flow=xtls-rprx-vision&sni=${vl_sni}&sid=${vl_sid}#${vl_name}"
|
||||
share_links+=("$vl_link")
|
||||
|
||||
summary_text+="[VLESS-Reality-Vision] 端口: ${vl_port}/tcp | SNI: ${vl_sni} | PublicKey: ${vl_pub} | ShortID: ${vl_sid}\n${vl_link}\n\n"
|
||||
|
||||
clash_proxies+=$(cat <<EOF
|
||||
|
||||
- name: "${vl_name}"
|
||||
type: vless
|
||||
server: ${server_ip}
|
||||
port: ${vl_port}
|
||||
uuid: ${uuid}
|
||||
network: tcp
|
||||
tls: true
|
||||
udp: true
|
||||
flow: xtls-rprx-vision
|
||||
servername: ${vl_sni}
|
||||
reality-opts:
|
||||
public-key: ${vl_pub}
|
||||
short-id: ${vl_sid}
|
||||
client-fingerprint: chrome
|
||||
EOF
|
||||
)
|
||||
sb_outbounds+=("$(cat <<EOF
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "${vl_name}",
|
||||
"server": "${server_ip}",
|
||||
"server_port": ${vl_port},
|
||||
"uuid": "${uuid}",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "${vl_sni}",
|
||||
"utls": { "enabled": true, "fingerprint": "chrome" },
|
||||
"reality": { "enabled": true, "public_key": "${vl_pub}", "short_id": "${vl_sid}" }
|
||||
},
|
||||
"packet_encoding": "xudp"
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 2. Hysteria 2
|
||||
# --------------------------------------------------------------------------
|
||||
if [[ $(jq -r '.protocols.hysteria2.enabled // false' "${META_FILE}") == "true" ]]; then
|
||||
local hy_port hy_sni hy_name hy_link
|
||||
hy_port=$(jq -r '.protocols.hysteria2.port' "${META_FILE}")
|
||||
hy_sni=$(jq -r '.protocols.hysteria2.sni' "${META_FILE}")
|
||||
hy_name="Hysteria2-${server_ip}"
|
||||
|
||||
hy_link="hy2://${uuid}@${server_ip}:${hy_port}?insecure=1&sni=${hy_sni}#${hy_name}"
|
||||
share_links+=("$hy_link")
|
||||
|
||||
summary_text+="[Hysteria-2] 端口: ${hy_port}/udp | SNI: ${hy_sni} | 密码: ${uuid}\n${hy_link}\n\n"
|
||||
|
||||
clash_proxies+=$(cat <<EOF
|
||||
|
||||
- name: "${hy_name}"
|
||||
type: hysteria2
|
||||
server: ${server_ip}
|
||||
port: ${hy_port}
|
||||
password: ${uuid}
|
||||
sni: ${hy_sni}
|
||||
skip-cert-verify: true
|
||||
alpn:
|
||||
- h3
|
||||
EOF
|
||||
)
|
||||
sb_outbounds+=("$(cat <<EOF
|
||||
{
|
||||
"type": "hysteria2",
|
||||
"tag": "${hy_name}",
|
||||
"server": "${server_ip}",
|
||||
"server_port": ${hy_port},
|
||||
"password": "${uuid}",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "${hy_sni}",
|
||||
"insecure": true,
|
||||
"alpn": ["h3"]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 3. TUIC v5
|
||||
# --------------------------------------------------------------------------
|
||||
if [[ $(jq -r '.protocols.tuic.enabled // false' "${META_FILE}") == "true" ]]; then
|
||||
local tu_port tu_sni tu_name tu_link
|
||||
tu_port=$(jq -r '.protocols.tuic.port' "${META_FILE}")
|
||||
tu_sni=$(jq -r '.protocols.tuic.sni' "${META_FILE}")
|
||||
tu_name="TUIC5-${server_ip}"
|
||||
|
||||
tu_link="tuic://${uuid}:${uuid}@${server_ip}:${tu_port}?congestion_control=bbr&alpn=h3&sni=${tu_sni}&allow_insecure=1#${tu_name}"
|
||||
share_links+=("$tu_link")
|
||||
|
||||
summary_text+="[TUIC-v5] 端口: ${tu_port}/udp | SNI: ${tu_sni} | UUID/密码: ${uuid}\n${tu_link}\n\n"
|
||||
|
||||
clash_proxies+=$(cat <<EOF
|
||||
|
||||
- name: "${tu_name}"
|
||||
type: tuic
|
||||
server: ${server_ip}
|
||||
port: ${tu_port}
|
||||
uuid: ${uuid}
|
||||
password: ${uuid}
|
||||
congestion-controller: bbr
|
||||
skip-cert-verify: true
|
||||
sni: ${tu_sni}
|
||||
alpn:
|
||||
- h3
|
||||
EOF
|
||||
)
|
||||
sb_outbounds+=("$(cat <<EOF
|
||||
{
|
||||
"type": "tuic",
|
||||
"tag": "${tu_name}",
|
||||
"server": "${server_ip}",
|
||||
"server_port": ${tu_port},
|
||||
"uuid": "${uuid}",
|
||||
"password": "${uuid}",
|
||||
"congestion_control": "bbr",
|
||||
"udp_relay_mode": "native",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "${tu_sni}",
|
||||
"insecure": true,
|
||||
"alpn": ["h3"]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 4. VMess-WS
|
||||
# --------------------------------------------------------------------------
|
||||
if [[ $(jq -r '.protocols.vmess_ws.enabled // false' "${META_FILE}") == "true" ]]; then
|
||||
local vm_port vm_path vm_name vm_json vm_b64 vm_link
|
||||
vm_port=$(jq -r '.protocols.vmess_ws.port' "${META_FILE}")
|
||||
vm_path=$(jq -r '.protocols.vmess_ws.path' "${META_FILE}")
|
||||
vm_name="VMess-WS-${server_ip}"
|
||||
|
||||
vm_json="{\"v\":\"2\",\"ps\":\"${vm_name}\",\"add\":\"${server_ip}\",\"port\":\"${vm_port}\",\"id\":\"${uuid}\",\"aid\":\"0\",\"scy\":\"auto\",\"net\":\"ws\",\"type\":\"none\",\"host\":\"\",\"path\":\"${vm_path}\",\"tls\":\"\"}"
|
||||
vm_b64=$(echo -n "$vm_json" | base64 | tr -d '\r\n ')
|
||||
vm_link="vmess://${vm_b64}"
|
||||
share_links+=("$vm_link")
|
||||
|
||||
summary_text+="[VMess-WS] 端口: ${vm_port}/tcp | Path: ${vm_path} | UUID: ${uuid}\n${vm_link}\n\n"
|
||||
|
||||
clash_proxies+=$(cat <<EOF
|
||||
|
||||
- name: "${vm_name}"
|
||||
type: vmess
|
||||
server: ${server_ip}
|
||||
port: ${vm_port}
|
||||
uuid: ${uuid}
|
||||
alterId: 0
|
||||
cipher: auto
|
||||
udp: true
|
||||
network: ws
|
||||
ws-opts:
|
||||
path: "${vm_path}"
|
||||
EOF
|
||||
)
|
||||
sb_outbounds+=("$(cat <<EOF
|
||||
{
|
||||
"type": "vmess",
|
||||
"tag": "${vm_name}",
|
||||
"server": "${server_ip}",
|
||||
"server_port": ${vm_port},
|
||||
"uuid": "${uuid}",
|
||||
"security": "auto",
|
||||
"transport": {
|
||||
"type": "ws",
|
||||
"path": "${vm_path}"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 5. AnyTLS
|
||||
# --------------------------------------------------------------------------
|
||||
if [[ $(jq -r '.protocols.anytls.enabled // false' "${META_FILE}") == "true" ]]; then
|
||||
local an_port an_sni an_name an_link
|
||||
an_port=$(jq -r '.protocols.anytls.port' "${META_FILE}")
|
||||
an_sni=$(jq -r '.protocols.anytls.sni' "${META_FILE}")
|
||||
an_name="AnyTLS-${server_ip}"
|
||||
|
||||
an_link="anytls://${uuid}@${server_ip}:${an_port}?sni=${an_sni}&insecure=1#${an_name}"
|
||||
share_links+=("$an_link")
|
||||
|
||||
summary_text+="[AnyTLS] 端口: ${an_port}/tcp | SNI: ${an_sni} | 密码: ${uuid}\n${an_link}\n\n"
|
||||
|
||||
clash_proxies+=$(cat <<EOF
|
||||
|
||||
- name: "${an_name}"
|
||||
type: anytls
|
||||
server: ${server_ip}
|
||||
port: ${an_port}
|
||||
password: ${uuid}
|
||||
sni: ${an_sni}
|
||||
skip-cert-verify: true
|
||||
EOF
|
||||
)
|
||||
sb_outbounds+=("$(cat <<EOF
|
||||
{
|
||||
"type": "anytls",
|
||||
"tag": "${an_name}",
|
||||
"server": "${server_ip}",
|
||||
"server_port": ${an_port},
|
||||
"password": "${uuid}",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "${an_sni}",
|
||||
"insecure": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 导出客户端配置文件
|
||||
# --------------------------------------------------------------------------
|
||||
local sb_outbounds_joined
|
||||
sb_outbounds_joined=$(IFS=,; echo "${sb_outbounds[*]}")
|
||||
|
||||
local all_links_raw=""
|
||||
for link in "${share_links[@]}"; do
|
||||
all_links_raw+="${link}"$'\n'
|
||||
done
|
||||
local sub_base64
|
||||
sub_base64=$(echo -n "$all_links_raw" | base64 | tr -d '\r\n ')
|
||||
|
||||
local info_file="${INSTALL_DIR}/node_info.txt"
|
||||
local clash_file="${INSTALL_DIR}/clash_meta.yaml"
|
||||
local sb_client_file="${INSTALL_DIR}/singbox_client.json"
|
||||
local b64_file="${INSTALL_DIR}/sub_base64.txt"
|
||||
|
||||
echo "$sub_base64" > "$b64_file"
|
||||
|
||||
cat > "$clash_file" <<EOF
|
||||
# Clash-Meta / Mihomo 节点配置片段
|
||||
proxies:${clash_proxies}
|
||||
EOF
|
||||
|
||||
cat > "$sb_client_file" <<EOF
|
||||
{
|
||||
"outbounds": [
|
||||
${sb_outbounds_joined}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > "$info_file" <<EOF
|
||||
================================================================================
|
||||
Sing-box 多协议节点与订阅信息汇总
|
||||
================================================================================
|
||||
服务器 IP : ${server_ip}
|
||||
统一 UUID : ${uuid}
|
||||
--------------------------------------------------------------------------------
|
||||
$(echo -e "$summary_text")--------------------------------------------------------------------------------
|
||||
【聚合 Base64 订阅内容 (可整段复制导入 v2rayN / Nekobox / Shadowrocket)】:
|
||||
${sub_base64}
|
||||
================================================================================
|
||||
EOF
|
||||
|
||||
chmod 600 "$info_file" "$clash_file" "$sb_client_file" "$b64_file"
|
||||
|
||||
# 终端高亮打印
|
||||
echo ""
|
||||
echo -e "${CLR_GREEN}================================================================================${CLR_RESET}"
|
||||
echo -e "${CLR_BOLD}${CLR_GREEN} Sing-box 核心代理节点与订阅信息汇总${CLR_RESET}"
|
||||
echo -e "${CLR_GREEN}================================================================================${CLR_RESET}"
|
||||
echo -e "${CLR_BOLD}服务器 IP${CLR_RESET} : ${CLR_YELLOW}${server_ip}${CLR_RESET}"
|
||||
echo -e "${CLR_BOLD}统一 UUID${CLR_RESET} : ${CLR_BLUE}${uuid}${CLR_RESET}"
|
||||
echo -e "${CLR_GREEN}--------------------------------------------------------------------------------${CLR_RESET}"
|
||||
echo -e "$summary_text"
|
||||
echo -e "${CLR_GREEN}--------------------------------------------------------------------------------${CLR_RESET}"
|
||||
echo -e "${CLR_BOLD}${CLR_YELLOW}【聚合 Base64 订阅串】(一键复制导入客户端即可识别所有已装协议):${CLR_RESET}"
|
||||
echo -e "${CLR_PURPLE}${sub_base64}${CLR_RESET}"
|
||||
echo -e "${CLR_GREEN}--------------------------------------------------------------------------------${CLR_RESET}"
|
||||
echo -e "${CLR_BOLD}本地导出的配置文件路径:${CLR_RESET}"
|
||||
echo -e " • 节点详情汇总 : ${CLR_BLUE}${info_file}${CLR_RESET}"
|
||||
echo -e " • Clash/Mihomo : ${CLR_BLUE}${clash_file}${CLR_RESET}"
|
||||
echo -e " • Sing-box客户端: ${CLR_BLUE}${sb_client_file}${CLR_RESET}"
|
||||
echo -e " • Base64 订阅串 : ${CLR_BLUE}${b64_file}${CLR_RESET}"
|
||||
echo -e "${CLR_GREEN}================================================================================${CLR_RESET}"
|
||||
echo ""
|
||||
}
|
||||
Reference in New Issue
Block a user