|
|
@@ -0,0 +1,372 @@
|
|
|
+# shellcheck disable=SC2148
|
|
|
+# shellcheck disable=SC2034
|
|
|
+# shellcheck disable=SC2155
|
|
|
+[ -n "$BASH_VERSION" ] && set +o noglob
|
|
|
+[ -n "$ZSH_VERSION" ] && setopt glob no_nomatch
|
|
|
+
|
|
|
+URL_GH_PROXY='https://gh-proxy.com/'
|
|
|
+URL_CLASH_UI="http://board.zash.run.place"
|
|
|
+
|
|
|
+SCRIPT_BASE_DIR='./script'
|
|
|
+SCRIPT_FISH="${SCRIPT_BASE_DIR}/clashctl.fish"
|
|
|
+
|
|
|
+RESOURCES_BASE_DIR='./resources'
|
|
|
+RESOURCES_BIN_DIR="${RESOURCES_BASE_DIR}/bin"
|
|
|
+RESOURCES_CONFIG="${RESOURCES_BASE_DIR}/config.yaml"
|
|
|
+RESOURCES_CONFIG_MIXIN="${RESOURCES_BASE_DIR}/mixin.yaml"
|
|
|
+
|
|
|
+ZIP_BASE_DIR="${RESOURCES_BASE_DIR}/zip"
|
|
|
+ZIP_CLASH=$(echo ${ZIP_BASE_DIR}/clash*)
|
|
|
+ZIP_MIHOMO=$(echo ${ZIP_BASE_DIR}/mihomo*)
|
|
|
+ZIP_YQ=$(echo ${ZIP_BASE_DIR}/yq*)
|
|
|
+ZIP_SUBCONVERTER=$(echo ${ZIP_BASE_DIR}/subconverter*)
|
|
|
+ZIP_UI="${ZIP_BASE_DIR}/yacd.tar.xz"
|
|
|
+
|
|
|
+CLASH_BASE_DIR='/develop/clash'
|
|
|
+CLASH_SCRIPT_DIR="${CLASH_BASE_DIR}/$(basename $SCRIPT_BASE_DIR)"
|
|
|
+CLASH_CONFIG_URL="${CLASH_BASE_DIR}/url"
|
|
|
+CLASH_CONFIG_RAW="${CLASH_BASE_DIR}/$(basename $RESOURCES_CONFIG)"
|
|
|
+CLASH_CONFIG_RAW_BAK="${CLASH_CONFIG_RAW}.bak"
|
|
|
+CLASH_CONFIG_MIXIN="${CLASH_BASE_DIR}/$(basename $RESOURCES_CONFIG_MIXIN)"
|
|
|
+CLASH_CONFIG_RUNTIME="${CLASH_BASE_DIR}/runtime.yaml"
|
|
|
+CLASH_UPDATE_LOG="${CLASH_BASE_DIR}/clashupdate.log"
|
|
|
+
|
|
|
+_set_var() {
|
|
|
+ local user=$USER
|
|
|
+ local home=$HOME
|
|
|
+ [ -n "$SUDO_USER" ] && {
|
|
|
+ user=$SUDO_USER
|
|
|
+ home=$(awk -F: -v user="$SUDO_USER" '$1==user{print $6}' /etc/passwd)
|
|
|
+ }
|
|
|
+
|
|
|
+ [ -n "$BASH_VERSION" ] && {
|
|
|
+ _SHELL=bash
|
|
|
+ }
|
|
|
+ [ -n "$ZSH_VERSION" ] && {
|
|
|
+ _SHELL=zsh
|
|
|
+ }
|
|
|
+ [ -n "$fish_version" ] && {
|
|
|
+ _SHELL=fish
|
|
|
+ }
|
|
|
+
|
|
|
+ # rc文件路径
|
|
|
+ command -v bash >&/dev/null && {
|
|
|
+ SHELL_RC_BASH="${home}/.bashrc"
|
|
|
+ }
|
|
|
+ command -v zsh >&/dev/null && {
|
|
|
+ SHELL_RC_ZSH="${home}/.zshrc"
|
|
|
+ }
|
|
|
+ command -v fish >&/dev/null && {
|
|
|
+ SHELL_RC_FISH="${home}/.config/fish/conf.d/clashctl.fish"
|
|
|
+ }
|
|
|
+
|
|
|
+ # 定时任务路径
|
|
|
+ local os_info=$(cat /etc/os-release)
|
|
|
+ echo "$os_info" | grep -iqsE "rhel|centos|openEuler|Rocky|AlmaLinux" && CLASH_CRON_TAB="/var/spool/cron/$user"
|
|
|
+ echo "$os_info" | grep -iqsE "debian|ubuntu" && CLASH_CRON_TAB="/var/spool/cron/crontabs/$user"
|
|
|
+}
|
|
|
+_set_var
|
|
|
+
|
|
|
+# shellcheck disable=SC2120
|
|
|
+_set_bin() {
|
|
|
+ local bin_base_dir="${CLASH_BASE_DIR}/bin"
|
|
|
+ [ -n "$1" ] && bin_base_dir=$1
|
|
|
+ BIN_CLASH="${bin_base_dir}/clash"
|
|
|
+ BIN_MIHOMO="${bin_base_dir}/mihomo"
|
|
|
+ BIN_YQ="${bin_base_dir}/yq"
|
|
|
+ BIN_SUBCONVERTER_DIR="${bin_base_dir}/subconverter"
|
|
|
+ BIN_SUBCONVERTER_CONFIG="$BIN_SUBCONVERTER_DIR/pref.yml"
|
|
|
+ BIN_SUBCONVERTER_PORT="25500"
|
|
|
+ BIN_SUBCONVERTER="${BIN_SUBCONVERTER_DIR}/subconverter"
|
|
|
+ BIN_SUBCONVERTER_LOG="${BIN_SUBCONVERTER_DIR}/latest.log"
|
|
|
+
|
|
|
+ [ -f "$BIN_CLASH" ] && {
|
|
|
+ BIN_KERNEL=$BIN_CLASH
|
|
|
+ }
|
|
|
+ [ -f "$BIN_MIHOMO" ] && {
|
|
|
+ BIN_KERNEL=$BIN_MIHOMO
|
|
|
+ }
|
|
|
+ BIN_KERNEL_NAME=$(basename "$BIN_KERNEL")
|
|
|
+}
|
|
|
+_set_bin
|
|
|
+
|
|
|
+_set_rc() {
|
|
|
+ [ "$1" = "unset" ] && {
|
|
|
+ sed -i "\|$CLASH_SCRIPT_DIR|d" "$SHELL_RC_BASH" "$SHELL_RC_ZSH" 2>/dev/null
|
|
|
+ rm -f "$SHELL_RC_FISH" 2>/dev/null
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ echo "source $CLASH_SCRIPT_DIR/common.sh && source $CLASH_SCRIPT_DIR/clashctl.sh" |
|
|
|
+ tee -a "$SHELL_RC_BASH" "$SHELL_RC_ZSH" >&/dev/null
|
|
|
+ [ -n "$SHELL_RC_FISH" ] && /usr/bin/install $SCRIPT_FISH "$SHELL_RC_FISH"
|
|
|
+}
|
|
|
+
|
|
|
+# 默认集成、安装mihomo内核
|
|
|
+# 移除/删除mihomo:下载安装clash内核
|
|
|
+function _get_kernel() {
|
|
|
+ [ -f "$ZIP_CLASH" ] && {
|
|
|
+ ZIP_KERNEL=$ZIP_CLASH
|
|
|
+ BIN_KERNEL=$BIN_CLASH
|
|
|
+ }
|
|
|
+
|
|
|
+ [ -f "$ZIP_MIHOMO" ] && {
|
|
|
+ ZIP_KERNEL=$ZIP_MIHOMO
|
|
|
+ BIN_KERNEL=$BIN_MIHOMO
|
|
|
+ }
|
|
|
+
|
|
|
+ [ ! -f "$ZIP_MIHOMO" ] && [ ! -f "$ZIP_CLASH" ] && {
|
|
|
+ local arch=$(uname -m)
|
|
|
+ _failcat "${ZIP_BASE_DIR}:未检测到可用的内核压缩包"
|
|
|
+ _download_clash "$arch"
|
|
|
+ ZIP_KERNEL=$ZIP_CLASH
|
|
|
+ BIN_KERNEL=$BIN_CLASH
|
|
|
+ }
|
|
|
+
|
|
|
+ BIN_KERNEL_NAME=$(basename "$BIN_KERNEL")
|
|
|
+ _okcat "安装内核:$BIN_KERNEL_NAME"
|
|
|
+}
|
|
|
+
|
|
|
+_get_random_port() {
|
|
|
+ local randomPort=$(shuf -i 1024-65535 -n 1)
|
|
|
+ ! _is_bind "$randomPort" && { echo "$randomPort" && return; }
|
|
|
+ _get_random_port
|
|
|
+}
|
|
|
+
|
|
|
+function _get_proxy_port() {
|
|
|
+ MIXED_PORT=$(sudo "$BIN_YQ" '.mixed-port' $CLASH_CONFIG_RUNTIME)
|
|
|
+
|
|
|
+ _is_already_in_use "$MIXED_PORT" "$BIN_KERNEL_NAME" && {
|
|
|
+ local newPort=$(_get_random_port)
|
|
|
+ local msg="端口占用:${MIXED_PORT} 🎲 随机分配:$newPort"
|
|
|
+ sudo "$BIN_YQ" -i ".mixed-port = $newPort" $CLASH_CONFIG_RUNTIME
|
|
|
+ MIXED_PORT=$newPort
|
|
|
+ _failcat '🎯' "$msg"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function _get_ui_port() {
|
|
|
+ local ext_addr=$(sudo "$BIN_YQ" '.external-controller // ""' $CLASH_CONFIG_RUNTIME)
|
|
|
+ local ext_ip=${ext_addr%%:*}
|
|
|
+ EXT_IP=$ext_ip
|
|
|
+ EXT_PORT=${ext_addr##*:}
|
|
|
+ # ip route get 1.1.1.1 | grep -oP 'src \K\S+'
|
|
|
+ [ "$ext_ip" = '0.0.0.0' ] && EXT_IP=$(hostname -I | awk '{print $1}')
|
|
|
+ _is_already_in_use "$EXT_PORT" "$BIN_KERNEL_NAME" && {
|
|
|
+ local newPort=$(_get_random_port)
|
|
|
+ local msg="端口占用:${EXT_PORT} 🎲 随机分配:$newPort"
|
|
|
+ sudo "$BIN_YQ" -i ".external-controller = \"$ext_ip:$newPort\"" $CLASH_CONFIG_RUNTIME
|
|
|
+ EXT_PORT=$newPort
|
|
|
+ _failcat '🎯' "$msg"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+_get_color() {
|
|
|
+ local hex="${1#\#}"
|
|
|
+ local r=$((16#${hex:0:2}))
|
|
|
+ local g=$((16#${hex:2:2}))
|
|
|
+ local b=$((16#${hex:4:2}))
|
|
|
+ printf "\e[38;2;%d;%d;%dm" "$r" "$g" "$b"
|
|
|
+}
|
|
|
+_get_color_msg() {
|
|
|
+ local color=$(_get_color "$1")
|
|
|
+ local msg=$2
|
|
|
+ local reset="\033[0m"
|
|
|
+ printf "%b%s%b\n" "$color" "$msg" "$reset"
|
|
|
+}
|
|
|
+
|
|
|
+_get_random_val() {
|
|
|
+ cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 6
|
|
|
+}
|
|
|
+
|
|
|
+function _okcat() {
|
|
|
+ local color=#c8d6e5
|
|
|
+ local emoji=😼
|
|
|
+ [ $# -gt 1 ] && emoji=$1 && shift
|
|
|
+ local msg="${emoji} $1"
|
|
|
+ _get_color_msg "$color" "$msg" && return 0
|
|
|
+}
|
|
|
+
|
|
|
+function _failcat() {
|
|
|
+ local color=#fd79a8
|
|
|
+ local emoji=😾
|
|
|
+ [ $# -gt 1 ] && emoji=$1 && shift
|
|
|
+ local msg="${emoji} $1"
|
|
|
+ _get_color_msg "$color" "$msg" >&2 && return 1
|
|
|
+}
|
|
|
+
|
|
|
+function _quit() {
|
|
|
+ local user=root
|
|
|
+ [ -n "$SUDO_USER" ] && user=$SUDO_USER
|
|
|
+ exec sudo -u "$user" -- "$_SHELL" -i
|
|
|
+}
|
|
|
+
|
|
|
+function _error_quit() {
|
|
|
+ [ $# -gt 0 ] && {
|
|
|
+ local color=#f92f60
|
|
|
+ local emoji=📢
|
|
|
+ [ $# -gt 1 ] && emoji=$1 && shift
|
|
|
+ local msg="${emoji} $1"
|
|
|
+ _get_color_msg "$color" "$msg"
|
|
|
+ }
|
|
|
+ exec $_SHELL -i
|
|
|
+}
|
|
|
+
|
|
|
+_is_bind() {
|
|
|
+ local port=$1
|
|
|
+ { sudo ss -lnptu || sudo netstat -lnptu; } | grep ":${port}\b"
|
|
|
+}
|
|
|
+
|
|
|
+_is_already_in_use() {
|
|
|
+ local port=$1
|
|
|
+ local progress=$2
|
|
|
+ _is_bind "$port" | grep -qs -v "$progress"
|
|
|
+}
|
|
|
+
|
|
|
+function _is_root() {
|
|
|
+ [ "$(whoami)" = "root" ]
|
|
|
+}
|
|
|
+
|
|
|
+function _valid_env() {
|
|
|
+ _is_root || _error_quit "需要 root 或 sudo 权限执行"
|
|
|
+ [ "$(ps -p 1 -o comm=)" != "systemd" ] && _error_quit "系统不具备 systemd"
|
|
|
+}
|
|
|
+
|
|
|
+function _valid_config() {
|
|
|
+ [ -e "$1" ] && [ "$(wc -l <"$1")" -gt 1 ] && {
|
|
|
+ local cmd msg
|
|
|
+ cmd="sudo $BIN_KERNEL -d $(dirname "$1") -f $1 -t"
|
|
|
+ local is_dat=$(sudo "$BIN_YQ" '.geodata-mode // false' "$1")
|
|
|
+ [ "$is_dat" = "true" ] && {
|
|
|
+ sudo "$BIN_YQ" -i ".geodata-mode = false" "$1"
|
|
|
+ }
|
|
|
+ msg=$(eval "$cmd") || {
|
|
|
+ eval "$cmd"
|
|
|
+ echo "$msg" | grep -qs "unsupport proxy type" && {
|
|
|
+ local prefix="检测到订阅中包含不受支持的代理协议"
|
|
|
+ [ "$BIN_KERNEL_NAME" = "clash" ] && _error_quit "${prefix}, 推荐安装使用 mihomo 内核"
|
|
|
+ _error_quit "${prefix}, 请检查并升级内核版本"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+_download_clash() {
|
|
|
+ local arch=$1
|
|
|
+ local url sha256sum
|
|
|
+ case "$arch" in
|
|
|
+ x86_64)
|
|
|
+ url=https://downloads.clash.wiki/ClashPremium/clash-linux-amd64-2023.08.17.gz
|
|
|
+ sha256sum='92380f053f083e3794c1681583be013a57b160292d1d9e1056e7fa1c2d948747'
|
|
|
+ ;;
|
|
|
+ *86*)
|
|
|
+ url=https://downloads.clash.wiki/ClashPremium/clash-linux-386-2023.08.17.gz
|
|
|
+ sha256sum='254125efa731ade3c1bf7cfd83ae09a824e1361592ccd7c0cccd2a266dcb92b5'
|
|
|
+ ;;
|
|
|
+ armv*)
|
|
|
+ url=https://downloads.clash.wiki/ClashPremium/clash-linux-armv5-2023.08.17.gz
|
|
|
+ sha256sum='622f5e774847782b6d54066f0716114a088f143f9bdd37edf3394ae8253062e8'
|
|
|
+ ;;
|
|
|
+ aarch64)
|
|
|
+ url=https://downloads.clash.wiki/ClashPremium/clash-linux-arm64-2023.08.17.gz
|
|
|
+ sha256sum='c45b39bb241e270ae5f4498e2af75cecc0f03c9db3c0db5e55c8c4919f01afdd'
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ _error_quit "未知的架构版本:$arch,请自行下载对应版本至 ${ZIP_BASE_DIR} 目录下:https://downloads.clash.wiki/ClashPremium/"
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+
|
|
|
+ _okcat '⏳' "正在下载:clash:${arch} 架构..."
|
|
|
+ ZIP_CLASH="${ZIP_BASE_DIR}/$(basename $url)"
|
|
|
+ curl \
|
|
|
+ --progress-bar \
|
|
|
+ --show-error \
|
|
|
+ --fail \
|
|
|
+ --insecure \
|
|
|
+ --location \
|
|
|
+ --connect-timeout 5 \
|
|
|
+ --max-time 15 \
|
|
|
+ --retry 1 \
|
|
|
+ --output "$ZIP_CLASH" \
|
|
|
+ "$url"
|
|
|
+ echo $sha256sum "$ZIP_CLASH" | sha256sum -c ||
|
|
|
+ _error_quit "下载失败:请自行下载对应版本至 ${ZIP_BASE_DIR} 目录下:https://downloads.clash.wiki/ClashPremium/"
|
|
|
+}
|
|
|
+
|
|
|
+_download_raw_config() {
|
|
|
+ local dest=$1
|
|
|
+ local url=$2
|
|
|
+ local agent='clash-verge/v2.0.4'
|
|
|
+ sudo curl \
|
|
|
+ --silent \
|
|
|
+ --show-error \
|
|
|
+ --insecure \
|
|
|
+ --location \
|
|
|
+ --max-time 5 \
|
|
|
+ --retry 1 \
|
|
|
+ --user-agent "$agent" \
|
|
|
+ --output "$dest" \
|
|
|
+ "$url" ||
|
|
|
+ sudo wget \
|
|
|
+ --no-verbose \
|
|
|
+ --no-check-certificate \
|
|
|
+ --timeout 3 \
|
|
|
+ --tries 1 \
|
|
|
+ --user-agent "$agent" \
|
|
|
+ --output-document "$dest" \
|
|
|
+ "$url"
|
|
|
+}
|
|
|
+_download_convert_config() {
|
|
|
+ local dest=$1
|
|
|
+ local url=$2
|
|
|
+ _start_convert
|
|
|
+ local convert_url=$(
|
|
|
+ target='clash'
|
|
|
+ base_url="http://127.0.0.1:${BIN_SUBCONVERTER_PORT}/sub"
|
|
|
+ curl \
|
|
|
+ --get \
|
|
|
+ --silent \
|
|
|
+ --location \
|
|
|
+ --output /dev/null \
|
|
|
+ --data-urlencode "target=$target" \
|
|
|
+ --data-urlencode "url=$url" \
|
|
|
+ --write-out '%{url_effective}' \
|
|
|
+ "$base_url"
|
|
|
+ )
|
|
|
+ _download_raw_config "$dest" "$convert_url"
|
|
|
+ _stop_convert
|
|
|
+}
|
|
|
+function _download_config() {
|
|
|
+ local dest=$1
|
|
|
+ local url=$2
|
|
|
+ [ "${url:0:4}" = 'file' ] && return 0
|
|
|
+ _download_raw_config "$dest" "$url" || return 1
|
|
|
+ _okcat '🍃' '下载成功:内核验证配置...'
|
|
|
+ _valid_config "$dest" || {
|
|
|
+ _failcat '🍂' "验证失败:尝试订阅转换..."
|
|
|
+ _download_convert_config "$dest" "$url" || _failcat '🍂' "转换失败:请检查日志:$BIN_SUBCONVERTER_LOG"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+_start_convert() {
|
|
|
+ _is_already_in_use $BIN_SUBCONVERTER_PORT 'subconverter' && {
|
|
|
+ local newPort=$(_get_random_port)
|
|
|
+ _failcat '🎯' "端口占用:$BIN_SUBCONVERTER_PORT 🎲 随机分配:$newPort"
|
|
|
+ [ ! -e "$BIN_SUBCONVERTER_CONFIG" ] && {
|
|
|
+ sudo /bin/cp -f "$BIN_SUBCONVERTER_DIR/pref.example.yml" "$BIN_SUBCONVERTER_CONFIG"
|
|
|
+ }
|
|
|
+ sudo "$BIN_YQ" -i ".server.port = $newPort" "$BIN_SUBCONVERTER_CONFIG"
|
|
|
+ BIN_SUBCONVERTER_PORT=$newPort
|
|
|
+ }
|
|
|
+ local start=$(date +%s)
|
|
|
+ # 子shell运行,屏蔽kill时的输出
|
|
|
+ (sudo "$BIN_SUBCONVERTER" 2>&1 | sudo tee "$BIN_SUBCONVERTER_LOG" >/dev/null &)
|
|
|
+ while ! _is_bind "$BIN_SUBCONVERTER_PORT" >&/dev/null; do
|
|
|
+ sleep 1s
|
|
|
+ local now=$(date +%s)
|
|
|
+ [ $((now - start)) -gt 1 ] && _error_quit "订阅转换服务未启动,请检查日志:$BIN_SUBCONVERTER_LOG"
|
|
|
+ done
|
|
|
+}
|
|
|
+_stop_convert() {
|
|
|
+ sudo pkill -9 -f "$BIN_SUBCONVERTER" >&/dev/null
|
|
|
+}
|