#!/usr/bin/env bash set -euo pipefail #### 配置 Rime 输入法引擎 #### # 支持鼠须管或小企鹅 # 例如 "fcitx5" # 例如 "squirrel" ENGINE="squirrel" ######### 配置结束 ######### # 全局变量 CNB_API="https://cnb.cool/amzxyz/rime-wanxiang/-/releases" SCHEMA_API="https://api.github.com/repos/amzxyz/rime_wanxiang/releases" GRAM_API="https://api.github.com/repos/amzxyz/RIME-LMDG/releases" TOOLS_API="https://api.github.com/repos/rimeinn/rime-wanxiang-update-tools/releases" FUZHU_LIST=("base" "flypy" "hanxin" "moqi" "tiger" "wubi" "zrm") TEMP_DIR=$(mktemp -d /tmp/wanxiang-update-XXXXXX) UPDATE_TOOLS_VERSION="v6.1.5" # 日志与错误处理 log() { local red="\033[0;31m" green="\033[0;32m" yellow="\033[0;33m" nc="\033[0m" local level="$1" color="$nc" case "$level" in INFO) color="$green" ;; WARN) color="$yellow" ;; ERROR) color="$red" ;; esac shift printf "${color}[%s] %s${nc}\n" "$level" "$*" } # 获取当前脚本名称 script_name=$(basename $0) script_dir=$(pwd) engine_check() { # 输入法引擎检测 if [ -z "$ENGINE" ]; then log ERROR "当前未配置输入法引擎" log WARN "如果使用Fcitx5(小企鹅)输入法,请复制以下语句并按回车执行,结束后请重新运行脚本:" echo "sed -i '' 's/ENGINE=\"\"/ENGINE=\"fcitx5\"/g' ${script_dir}/${script_name}" log WARN "如果使用Squirrel(鼠须管)输入法,请复制以下语句并按回车执行,结束后请重新运行脚本:" echo "sed -i '' 's/ENGINE=\"\"/ENGINE=\"squirrel\"/g' ${script_dir}/${script_name}" exit elif [ "$ENGINE" == "fcitx5" ]; then log INFO "当前使用Fcitx5(小企鹅)输入法" read -rp "按回车继续,M 键更改: " if_modify if [ "$if_modify" == "M" ]; then log WARN "请复制以下语句并按回车执行,结束后请重新运行脚本:" echo "sed -i '' 's/ENGINE=\"fcitx5\"/ENGINE=\"squirrel\"/g' ${script_dir}/${script_name}" exit fi elif [ "$ENGINE" == "squirrel" ]; then log INFO "当前使用squirrel(鼠须管)输入法" read -rp "按回车继续,M 键更改: " if_modify if [ "$if_modify" == "M" ]; then log WARN "请复制以下语句并按回车执行,结束后请重新运行脚本:" echo "sed -i '' 's/ENGINE=\"squirrel\"/ENGINE=\"fcitx5\"/g' ${script_dir}/${script_name}" exit fi fi } error_exit() { log ERROR "$*" cleanup exit 1 } cleanup() { if [[ -d "$TEMP_DIR" ]]; then rm -rf "$TEMP_DIR" || log WARN "清理缓存文件失败" fi } deps_check() { for _cmd in curl jq unzip; do command -v "$_cmd" >/dev/null || error_exit "缺少必要依赖:$_cmd" done } fuzhu_check() { local fuzhu_check="$1" for _fuzhu in "${FUZHU_LIST[@]}"; do if [[ "$fuzhu_check" == "$_fuzhu" ]]; then return 0 fi done return 1 } script_check() { local mirror="$1" if [[ "$UPDATE_TOOLS_VERSION" =~ ^"DEFAULT" ]]; then log WARN "您似乎正在使用源文件!" log WARN "请从 Release 页面下载正式版!" error_exit "终止操作" fi log INFO "工具当前版本 $UPDATE_TOOLS_VERSION" if [[ "$mirror" == "github" ]]; then # 检查 GitHub 连接状态 log INFO "正在检查 GitHub 连接状态" if ! curl -sL --connect-timeout 5 "https://api.github.com" >/dev/null; then error_exit "您似乎无法连接到 GitHub API, 请检查您的网络" elif ! curl -sL --connect-timeout 5 "https://github.com" >/dev/null; then error_exit "您似乎无法连接到 GitHub, 请检查您的网络" fi log INFO "正在检查本工具是否存在更新" local local_version remote_version local_version="$UPDATE_TOOLS_VERSION" remote_version=$( curl -sL --connect-timeout 10 $TOOLS_API | jq -r '.[].tag_name' | grep -vE "rc" | sort -rV | head -n 1 ) if [[ "$remote_version" > "$local_version" ]]; then log WARN "检测到工具最新版本为: $remote_version, 建议更新后继续" log WARN "https://github.com/rimeinn/rime-wanxiang-update-tools/releases/download/$remote_version/rime-wanxiang-update-macos.sh" else log INFO "工具已是最新版本" fi elif [[ "$mirror" == "cnb" ]]; then log WARN "由于您正在使用镜像,无法检查本工具是否存在更新" fi } get_info() { local mirror="$1" version="$2" name="$3" info if [[ "$mirror" == "github" ]]; then info=$( jq -r --arg version "$version" --arg name "$name" '.[] | select( .tag_name == $version ) | .assets.[] | select( .name | test( $name ) )' "$TEMP_DIR/github_$name.json" ) echo "$info" elif [[ "$mirror" == "cnb" ]]; then info=$( jq -r --arg version "refs/tags/$version" --arg name "$name" '.releases.[] | select( .tag_ref == $version ) | .assets[] | select( .name | test( $name ) )' "$TEMP_DIR/cnb_$name.json" ) echo "$info" fi } # 排除文件检查 # 函数:检查文件是否存在,如果不存在则创建并写入指定内容 create_exclude_file() { local file="${DEPLOY_DIR}/custom/user_exclude_file.txt" if [[ -z "$file" ]]; then error_exit "错误:必须指定排除文件路径" fi if [[ -f "$file" ]]; then echo "排除文件已存在:$file" else echo "排除文件不存在,正在创建:$file" # 确保目录存在 mkdir -p "$(dirname "$file")" # 创建并写入内容 cat > "$file" <"$TEMP_DIR/github_$fuzhu.json"; then error_exit "连接到 GitHub API 失败,您可能需要检查网络" fi fi elif [[ "$mirror" == "cnb" ]]; then if [[ ! -f "$TEMP_DIR/cnb_$fuzhu.json" ]]; then if ! curl -sL -H "accept: application/vnd.cnb.web+json" \ --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_$fuzhu.json"; then error_exit "连接到 CNB 失败,您可能需要检查网络" fi fi fi # 获取本地版本号 local local_version remote_version if [[ -f "$DEPLOY_DIR/lua/wanxiang.lua" ]]; then local_version=$(grep "wanxiang.version" "$DEPLOY_DIR/lua/wanxiang.lua" | awk -F '"' '{print $2}') [[ "$local_version" == v* ]] || local_version="v$local_version" else local_version="v0" fi # 获取远程版本号 if [[ "$mirror" == "github" ]]; then remote_version=$( jq -r '.[].tag_name' "$TEMP_DIR/github_$fuzhu.json" | grep -vE "dict-nightly" | sort -rV | head -n 1 ) elif [[ "$mirror" == "cnb" ]]; then remote_version=$( jq -r '.releases.[].tag_ref' \ "$TEMP_DIR/cnb_$fuzhu.json" | grep -vE "model" | sort -rV | head -n 1 ) remote_version="${remote_version#"refs/tags/"}" fi [[ "$remote_version" == v* ]] || remote_version="v$remote_version" if [[ "$remote_version" > "$local_version" ]]; then log INFO "远程方案文件版本号为 $remote_version, 以下内容为更新日志" local changelog if [[ "$mirror" == "github" ]]; then changelog=$( jq -r --arg version "$remote_version" '.[] | select( .tag_name == $version ) | .body' "$TEMP_DIR/github_$fuzhu.json" ) elif [[ "$mirror" == "cnb" ]]; then changelog=$( jq -r --arg version "refs/tags/$remote_version" '.releases.[] | select( .tag_ref == $version ) | .body' "$TEMP_DIR/cnb_$fuzhu.json" ) fi echo -e "$changelog" | sed -n '/## 📝 更新日志/,/## 🚀 下载引导/p' | sed '$d' sleep 3 log INFO "开始更新方案文件,正在下载文件" local schemaurl schemaname local_size remote_size if [[ "$mirror" == "github" ]]; then schemaurl=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.browser_download_url') elif [[ "$mirror" == "cnb" ]]; then schemaurl=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.path') schemaurl="https://cnb.cool$schemaurl" fi schemaname=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.name') curl -L --connect-timeout 10 -o "$TEMP_DIR/$schemaname" "$schemaurl" log INFO "正在验证文件完整性" local_size=$(stat -f %z "$TEMP_DIR/$schemaname") if [[ "$mirror" == "github" ]]; then remote_size=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.size') elif [[ "$mirror" == "cnb" ]]; then remote_size=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.size_in_byte') fi if [[ "$local_size" != "$remote_size" ]]; then log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size" error_exit "方案文件下载出错,请重试!" fi log INFO "验证成功,开始更新方案文件" unzip -q "$TEMP_DIR/$schemaname" -d "$TEMP_DIR/${schemaname%.zip}" for _file in "简纯+.trime.yaml" "custom_phrase.txt" "squirrel.yaml" "weasel.yaml"; do if [[ -f "$TEMP_DIR/${schemaname%.zip}/$_file" ]]; then rm -r "$TEMP_DIR/${schemaname%.zip}/${_file:?}" fi done local exclude_file while IFS= read -r _line; do if [[ "$_line" != \#* ]]; then exclude_file="$_line" # if [[ ! -e "$DEPLOY_DIR/$exclude_file" ]]; then # log WARN "项目 $DEPLOY_DIR/$exclude_file 不存在,跳过备份!" # else # cp -rf "$DEPLOY_DIR/$exclude_file" "$TEMP_DIR/${schemaname%.zip}/$exclude_file" # fi if [[ -e "$TEMP_DIR/$exclude_file" ]]; then log WARN "项目 $TEMP_DIR/$exclude_file 为排除文件不更新" rm -rf "$TEMP_DIR/$exclude_file" fi fi done <"$DEPLOY_DIR/custom/user_exclude_file.txt" # 单独处理语法模型 # [[ "$gram" == "true" ]] || cp -rf "$DEPLOY_DIR/wanxiang-lts-zh-hans.gram" \ # "$TEMP_DIR/${schemaname%.zip}/wanxiang-lts-zh-hans.gram" # rm -rf "${DEPLOY_DIR:?}" # cp -rf "$TEMP_DIR/${schemaname%.zip}" "$DEPLOY_DIR" # 应用更新 apply "$TEMP_DIR/${schemaname%.zip}" log INFO "方案文件更新成功" return 0 else log INFO "远程方案文件版本号为 $remote_version" log INFO "本地方案文件版本号为 $local_version, 您目前无需更新它" return 1 fi } update_dict() { local mirror="$1" fuzhu="$2" # 缓存 API 响应 if [[ "$mirror" == "github" ]]; then if [[ ! -f "$TEMP_DIR/github_$fuzhu.json" ]]; then if ! curl -sL -H "Accept: application/vnd.github.v3+json" \ --connect-timeout 10 "$SCHEMA_API" >"$TEMP_DIR/github_$fuzhu.json"; then error_exit "连接到 GitHub API 失败,您可能需要检查网络" fi fi elif [[ "$mirror" == "cnb" ]]; then if [[ ! -f "$TEMP_DIR/cnb_$fuzhu.json" ]]; then if ! curl -sL -H "accept: application/vnd.cnb.web+json" \ --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_$fuzhu.json"; then error_exit "连接到 CNB 失败,您可能需要检查网络" fi fi fi local local_date remote_date if [[ -f "$DEPLOY_DIR/dicts/chengyu.txt" ]]; then local_date=$(stat -f %c "$DEPLOY_DIR/dicts/chengyu.txt") else local_date=0 fi if [[ "$mirror" == "github" ]]; then remote_date=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.updated_at') elif [[ "$mirror" == "cnb" ]]; then remote_date=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.updated_at') fi remote_date=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$remote_date" +%s) if [[ $remote_date -gt $local_date ]]; then log INFO "正在下载最新词典文件" local dicturl dictname local_size remote_size if [[ "$mirror" == "github" ]]; then dicturl=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.browser_download_url') dictname=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.name') elif [[ "$mirror" == "cnb" ]]; then dicturl=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.path') dicturl="https://cnb.cool$dicturl" dictname=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.name') fi curl -L --connect-timeout 10 -o "$TEMP_DIR/$dictname" "$dicturl" log INFO "正在验证文件完整性" local_size=$(stat -f %z "$TEMP_DIR/$dictname") if [[ "$mirror" == "github" ]]; then remote_size=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.size') elif [[ "$mirror" == "cnb" ]]; then remote_size=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.size_in_byte') fi if [[ "$local_size" != "$remote_size" ]]; then log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size" error_exit "词典文件下载出错,请重试!" fi log INFO "验证成功,开始更新词典文件" unzip -q "$TEMP_DIR/$dictname" -d "$TEMP_DIR" dictname="${dictname:2}" && dictname="${dictname%.zip}" cp -rf "$TEMP_DIR/$dictname"/* "$DEPLOY_DIR/dicts" log INFO "词典文件更新成功" return 0 else remote_date=$(date -r "$remote_date" +"%Y-%m-%d %H:%M:%S") log INFO "远程词典文件最后更新于 $remote_date" local_date=$(date -r "$local_date" +"%Y-%m-%d %H:%M:%S") log INFO "本地词典文件最后更新于 $local_date, 您目前无需更新它" return 1 fi } update_gram() { local mirror="$1" # 缓存 API 响应 if [[ "$mirror" == "github" ]]; then if [[ ! -f "$TEMP_DIR/github_gram.json" ]]; then if ! curl -sL -H "Accept: application/vnd.github.v3+json" \ --connect-timeout 10 "$GRAM_API" >"$TEMP_DIR/github_gram.json"; then error_exit "连接到 GitHub API 失败,您可能需要检查网络" fi fi elif [[ "$mirror" == "cnb" ]]; then if [[ ! -f "$TEMP_DIR/cnb_gram.json" ]]; then headers=$(curl -sL -D - -o /dev/null -H "accept: application/vnd.cnb.web+json" "$CNB_API") X_CNB_TOTAL=$(echo "$headers" | awk -F': ' '/[Xx]-[Cc]nb-[Tt]otal:/ {gsub(/ /,"",$2); print $2}') X_CNB_PAGE_SIZE=$(echo "$headers" | awk -F': ' '/[Xx]-[Cc]nb-[Pp]age-[Ss]ize:/ {gsub(/ /,"",$2); print $2}') # 防止为空 X_CNB_TOTAL=${X_CNB_TOTAL:-0} X_CNB_PAGE_SIZE=${X_CNB_PAGE_SIZE:-1} # 确保是数字 X_CNB_TOTAL=$(echo "$X_CNB_TOTAL" | tr -d -c 0-9) X_CNB_PAGE_SIZE=$(echo "$X_CNB_PAGE_SIZE" | tr -d -c 0-9) # 获取最后一页 last_page=$(( (X_CNB_TOTAL + X_CNB_PAGE_SIZE - 1) / X_CNB_PAGE_SIZE )) if ! curl -G -sL -H "accept: application/vnd.cnb.web+json" \ --data-urlencode "page=${last_page}" \ --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_gram.json"; then error_exit "连接到 CNB 失败,您可能需要检查网络" fi fi fi local local_date remote_date gramname="wanxiang-lts-zh-hans.gram" if [[ -f "$DEPLOY_DIR/$gramname" ]]; then local_date=$(stat -f %c "$DEPLOY_DIR/$gramname") else local_date=0 fi if [[ "$mirror" == "github" ]]; then remote_date=$(get_info "$mirror" "LTS" "gram" | jq -r '.updated_at') elif [[ "$mirror" == "cnb" ]]; then remote_date=$(get_info "$mirror" "model" "gram" | jq -r '.updated_at') fi remote_date=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$remote_date" +%s) if [[ $remote_date -gt $local_date ]]; then log INFO "正在下载最新语法模型" local gramurl local_size remote_size if [[ "$mirror" == "github" ]]; then gramurl=$(get_info "$mirror" "LTS" "gram" | jq -r '.browser_download_url') elif [[ "$mirror" == "cnb" ]]; then gramurl=$(get_info "$mirror" "model" "gram" | jq -r '.path') gramurl="https://cnb.cool$gramurl" fi curl -L --connect-timeout 10 -o "$TEMP_DIR/$gramname" "$gramurl" log INFO "正在验证文件完整性" local_size=$(stat -f %z "$TEMP_DIR/$gramname") if [[ "$mirror" == "github" ]]; then remote_size=$(get_info "$mirror" "LTS" "gram" | jq -r '.size') elif [[ "$mirror" == "cnb" ]]; then remote_size=$(get_info "$mirror" "model" "gram" | jq -r '.size_in_byte') fi if [[ "$local_size" != "$remote_size" ]]; then log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size" error_exit "语法模型下载出错,请重试!" fi log INFO "验证成功,开始更新语法模型" cp -rf "$TEMP_DIR/$gramname" "${DEPLOY_DIR}/$gramname" log INFO "语法模型更新成功" return 0 else remote_date=$(date -r "$remote_date" +"%Y-%m-%d %H:%M:%S") log INFO "远程语法模型最后更新于 $remote_date" local_date=$(date -r "$local_date" +"%Y-%m-%d %H:%M:%S") log INFO "本地语法模型最后更新于 $local_date, 您目前无需更新它" return 1 fi } # 部属函数 deploy() { local deploy_executable="$1" shift # 移除第一个参数,后续所有参数都是要传给可执行文件的 if [ -x "$deploy_executable" ]; then echo "正在触发重新部署配置" if output_and_error=$("$deploy_executable" "$@" 2>&1); then [[ -n "$output_and_error" ]] && echo "输出: $output_and_error" echo "重新部署成功" else echo "重新部署失败" [[ -n "$output_and_error" ]] && echo "错误信息: $output_and_error" fi else echo "找不到可执行文件: $deploy_executable" echo "请手动部署" fi } show_help() { cat <