rime-wanxiang-update-macos.sh 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. #### 配置 Rime 输入法引擎 ####
  4. # 支持鼠须管或小企鹅
  5. # 例如 "fcitx5"
  6. # 例如 "squirrel"
  7. ENGINE="squirrel"
  8. ######### 配置结束 #########
  9. # 全局变量
  10. CNB_API="https://cnb.cool/amzxyz/rime-wanxiang/-/releases"
  11. SCHEMA_API="https://api.github.com/repos/amzxyz/rime_wanxiang/releases"
  12. GRAM_API="https://api.github.com/repos/amzxyz/RIME-LMDG/releases"
  13. TOOLS_API="https://api.github.com/repos/rimeinn/rime-wanxiang-update-tools/releases"
  14. FUZHU_LIST=("base" "flypy" "hanxin" "moqi" "tiger" "wubi" "zrm")
  15. TEMP_DIR=$(mktemp -d /tmp/wanxiang-update-XXXXXX)
  16. UPDATE_TOOLS_VERSION="v6.1.5"
  17. # 日志与错误处理
  18. log() {
  19. local red="\033[0;31m" green="\033[0;32m" yellow="\033[0;33m" nc="\033[0m"
  20. local level="$1" color="$nc"
  21. case "$level" in
  22. INFO) color="$green" ;;
  23. WARN) color="$yellow" ;;
  24. ERROR) color="$red" ;;
  25. esac
  26. shift
  27. printf "${color}[%s] %s${nc}\n" "$level" "$*"
  28. }
  29. # 获取当前脚本名称
  30. script_name=$(basename $0)
  31. script_dir=$(pwd)
  32. engine_check() {
  33. # 输入法引擎检测
  34. if [ -z "$ENGINE" ]; then
  35. log ERROR "当前未配置输入法引擎"
  36. log WARN "如果使用Fcitx5(小企鹅)输入法,请复制以下语句并按回车执行,结束后请重新运行脚本:"
  37. echo "sed -i '' 's/ENGINE=\"\"/ENGINE=\"fcitx5\"/g' ${script_dir}/${script_name}"
  38. log WARN "如果使用Squirrel(鼠须管)输入法,请复制以下语句并按回车执行,结束后请重新运行脚本:"
  39. echo "sed -i '' 's/ENGINE=\"\"/ENGINE=\"squirrel\"/g' ${script_dir}/${script_name}"
  40. exit
  41. elif [ "$ENGINE" == "fcitx5" ]; then
  42. log INFO "当前使用Fcitx5(小企鹅)输入法"
  43. read -rp "按回车继续,M 键更改: " if_modify
  44. if [ "$if_modify" == "M" ]; then
  45. log WARN "请复制以下语句并按回车执行,结束后请重新运行脚本:"
  46. echo "sed -i '' 's/ENGINE=\"fcitx5\"/ENGINE=\"squirrel\"/g' ${script_dir}/${script_name}"
  47. exit
  48. fi
  49. elif [ "$ENGINE" == "squirrel" ]; then
  50. log INFO "当前使用squirrel(鼠须管)输入法"
  51. read -rp "按回车继续,M 键更改: " if_modify
  52. if [ "$if_modify" == "M" ]; then
  53. log WARN "请复制以下语句并按回车执行,结束后请重新运行脚本:"
  54. echo "sed -i '' 's/ENGINE=\"squirrel\"/ENGINE=\"fcitx5\"/g' ${script_dir}/${script_name}"
  55. exit
  56. fi
  57. fi
  58. }
  59. error_exit() {
  60. log ERROR "$*"
  61. cleanup
  62. exit 1
  63. }
  64. cleanup() {
  65. if [[ -d "$TEMP_DIR" ]]; then
  66. rm -rf "$TEMP_DIR" || log WARN "清理缓存文件失败"
  67. fi
  68. }
  69. deps_check() {
  70. for _cmd in curl jq unzip; do
  71. command -v "$_cmd" >/dev/null || error_exit "缺少必要依赖:$_cmd"
  72. done
  73. }
  74. fuzhu_check() {
  75. local fuzhu_check="$1"
  76. for _fuzhu in "${FUZHU_LIST[@]}"; do
  77. if [[ "$fuzhu_check" == "$_fuzhu" ]]; then
  78. return 0
  79. fi
  80. done
  81. return 1
  82. }
  83. script_check() {
  84. local mirror="$1"
  85. if [[ "$UPDATE_TOOLS_VERSION" =~ ^"DEFAULT" ]]; then
  86. log WARN "您似乎正在使用源文件!"
  87. log WARN "请从 Release 页面下载正式版!"
  88. error_exit "终止操作"
  89. fi
  90. log INFO "工具当前版本 $UPDATE_TOOLS_VERSION"
  91. if [[ "$mirror" == "github" ]]; then
  92. # 检查 GitHub 连接状态
  93. log INFO "正在检查 GitHub 连接状态"
  94. if ! curl -sL --connect-timeout 5 "https://api.github.com" >/dev/null; then
  95. error_exit "您似乎无法连接到 GitHub API, 请检查您的网络"
  96. elif ! curl -sL --connect-timeout 5 "https://github.com" >/dev/null; then
  97. error_exit "您似乎无法连接到 GitHub, 请检查您的网络"
  98. fi
  99. log INFO "正在检查本工具是否存在更新"
  100. local local_version remote_version
  101. local_version="$UPDATE_TOOLS_VERSION"
  102. remote_version=$(
  103. curl -sL --connect-timeout 10 $TOOLS_API |
  104. jq -r '.[].tag_name' | grep -vE "rc" | sort -rV | head -n 1
  105. )
  106. if [[ "$remote_version" > "$local_version" ]]; then
  107. log WARN "检测到工具最新版本为: $remote_version, 建议更新后继续"
  108. log WARN "https://github.com/rimeinn/rime-wanxiang-update-tools/releases/download/$remote_version/rime-wanxiang-update-macos.sh"
  109. else
  110. log INFO "工具已是最新版本"
  111. fi
  112. elif [[ "$mirror" == "cnb" ]]; then
  113. log WARN "由于您正在使用镜像,无法检查本工具是否存在更新"
  114. fi
  115. }
  116. get_info() {
  117. local mirror="$1" version="$2" name="$3" info
  118. if [[ "$mirror" == "github" ]]; then
  119. info=$(
  120. jq -r --arg version "$version" --arg name "$name" '.[] |
  121. select( .tag_name == $version ) | .assets.[] |
  122. select( .name | test( $name ) )' "$TEMP_DIR/github_$name.json"
  123. )
  124. echo "$info"
  125. elif [[ "$mirror" == "cnb" ]]; then
  126. info=$(
  127. jq -r --arg version "refs/tags/$version" --arg name "$name" '.releases.[] |
  128. select( .tag_ref == $version ) | .assets[] |
  129. select( .name | test( $name ) )' "$TEMP_DIR/cnb_$name.json"
  130. )
  131. echo "$info"
  132. fi
  133. }
  134. # 排除文件检查
  135. # 函数:检查文件是否存在,如果不存在则创建并写入指定内容
  136. create_exclude_file() {
  137. local file="${DEPLOY_DIR}/custom/user_exclude_file.txt"
  138. if [[ -z "$file" ]]; then
  139. error_exit "错误:必须指定排除文件路径"
  140. fi
  141. if [[ -f "$file" ]]; then
  142. echo "排除文件已存在:$file"
  143. else
  144. echo "排除文件不存在,正在创建:$file"
  145. # 确保目录存在
  146. mkdir -p "$(dirname "$file")"
  147. # 创建并写入内容
  148. cat > "$file" <<EOF
  149. # 排除文件本身(请勿删除)
  150. custom/user_exclude_file.txt
  151. # 用户数据库
  152. lua/sequence.userdb
  153. lua/sequence.txt
  154. lua/input_stats.lua
  155. zc.userdb
  156. # 同步
  157. installation.yaml
  158. user.yaml
  159. # custom文件
  160. default.custom.yaml
  161. wanxiang_pro.custom.yaml
  162. wanxiang_reverse.custom.yaml
  163. wanxiang_mixedcode.custom.yaml
  164. # ##############以上内容请在了解万象方案机制后自行更改,否则请不要更改##############
  165. EOF
  166. fi
  167. }
  168. apply() {
  169. local source_dir="$1"
  170. local base_dir="${2:-$source_dir}" # 基准目录,默认为第一个参数,$2和$source_dir哪个不为空就取哪个
  171. # 计算目标路径
  172. local relative_path="${source_dir#$base_dir}" # 从source_dir删除开头的base_dir部分
  173. local target_path="$DEPLOY_DIR/${relative_path:+$relative_path/}" # relative_path不为空则使用relative_path,否则使用空值
  174. # 确保目标目录存在
  175. mkdir -p "$target_path"
  176. # 处理当前目录的文件和子目录
  177. for item in "$source_dir"/*; do
  178. if [[ -f "$item" ]]; then
  179. # 复制解压出来的文件到目标路径
  180. cp -f "$item" "$target_path"
  181. elif [[ -d "$item" ]]; then
  182. # 处理子目录
  183. apply "$item" "$base_dir"
  184. fi
  185. done
  186. }
  187. update_schema() {
  188. local mirror="$1" fuzhu="$2" gram="$3"
  189. # 缓存 API 响应
  190. if [[ "$mirror" == "github" ]]; then
  191. if [[ ! -f "$TEMP_DIR/github_$fuzhu.json" ]]; then
  192. if ! curl -sL -H "Accept: application/vnd.github.v3+json" \
  193. --connect-timeout 10 "$SCHEMA_API" >"$TEMP_DIR/github_$fuzhu.json"; then
  194. error_exit "连接到 GitHub API 失败,您可能需要检查网络"
  195. fi
  196. fi
  197. elif [[ "$mirror" == "cnb" ]]; then
  198. if [[ ! -f "$TEMP_DIR/cnb_$fuzhu.json" ]]; then
  199. if ! curl -sL -H "accept: application/vnd.cnb.web+json" \
  200. --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_$fuzhu.json"; then
  201. error_exit "连接到 CNB 失败,您可能需要检查网络"
  202. fi
  203. fi
  204. fi
  205. # 获取本地版本号
  206. local local_version remote_version
  207. if [[ -f "$DEPLOY_DIR/lua/wanxiang.lua" ]]; then
  208. local_version=$(grep "wanxiang.version" "$DEPLOY_DIR/lua/wanxiang.lua" | awk -F '"' '{print $2}')
  209. [[ "$local_version" == v* ]] || local_version="v$local_version"
  210. else
  211. local_version="v0"
  212. fi
  213. # 获取远程版本号
  214. if [[ "$mirror" == "github" ]]; then
  215. remote_version=$(
  216. jq -r '.[].tag_name' "$TEMP_DIR/github_$fuzhu.json" |
  217. grep -vE "dict-nightly" | sort -rV | head -n 1
  218. )
  219. elif [[ "$mirror" == "cnb" ]]; then
  220. remote_version=$(
  221. jq -r '.releases.[].tag_ref' \
  222. "$TEMP_DIR/cnb_$fuzhu.json" | grep -vE "model" | sort -rV | head -n 1
  223. )
  224. remote_version="${remote_version#"refs/tags/"}"
  225. fi
  226. [[ "$remote_version" == v* ]] || remote_version="v$remote_version"
  227. if [[ "$remote_version" > "$local_version" ]]; then
  228. log INFO "远程方案文件版本号为 $remote_version, 以下内容为更新日志"
  229. local changelog
  230. if [[ "$mirror" == "github" ]]; then
  231. changelog=$(
  232. jq -r --arg version "$remote_version" '.[] |
  233. select( .tag_name == $version ) | .body' "$TEMP_DIR/github_$fuzhu.json"
  234. )
  235. elif [[ "$mirror" == "cnb" ]]; then
  236. changelog=$(
  237. jq -r --arg version "refs/tags/$remote_version" '.releases.[] |
  238. select( .tag_ref == $version ) | .body' "$TEMP_DIR/cnb_$fuzhu.json"
  239. )
  240. fi
  241. echo -e "$changelog" | sed -n '/## 📝 更新日志/,/## 🚀 下载引导/p' | sed '$d'
  242. sleep 3
  243. log INFO "开始更新方案文件,正在下载文件"
  244. local schemaurl schemaname local_size remote_size
  245. if [[ "$mirror" == "github" ]]; then
  246. schemaurl=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.browser_download_url')
  247. elif [[ "$mirror" == "cnb" ]]; then
  248. schemaurl=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.path')
  249. schemaurl="https://cnb.cool$schemaurl"
  250. fi
  251. schemaname=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.name')
  252. curl -L --connect-timeout 10 -o "$TEMP_DIR/$schemaname" "$schemaurl"
  253. log INFO "正在验证文件完整性"
  254. local_size=$(stat -f %z "$TEMP_DIR/$schemaname")
  255. if [[ "$mirror" == "github" ]]; then
  256. remote_size=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.size')
  257. elif [[ "$mirror" == "cnb" ]]; then
  258. remote_size=$(get_info "$mirror" "$remote_version" "$fuzhu" | jq -r '.size_in_byte')
  259. fi
  260. if [[ "$local_size" != "$remote_size" ]]; then
  261. log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size"
  262. error_exit "方案文件下载出错,请重试!"
  263. fi
  264. log INFO "验证成功,开始更新方案文件"
  265. unzip -q "$TEMP_DIR/$schemaname" -d "$TEMP_DIR/${schemaname%.zip}"
  266. for _file in "简纯+.trime.yaml" "custom_phrase.txt" "squirrel.yaml" "weasel.yaml"; do
  267. if [[ -f "$TEMP_DIR/${schemaname%.zip}/$_file" ]]; then
  268. rm -r "$TEMP_DIR/${schemaname%.zip}/${_file:?}"
  269. fi
  270. done
  271. local exclude_file
  272. while IFS= read -r _line; do
  273. if [[ "$_line" != \#* ]]; then
  274. exclude_file="$_line"
  275. # if [[ ! -e "$DEPLOY_DIR/$exclude_file" ]]; then
  276. # log WARN "项目 $DEPLOY_DIR/$exclude_file 不存在,跳过备份!"
  277. # else
  278. # cp -rf "$DEPLOY_DIR/$exclude_file" "$TEMP_DIR/${schemaname%.zip}/$exclude_file"
  279. # fi
  280. if [[ -e "$TEMP_DIR/$exclude_file" ]]; then
  281. log WARN "项目 $TEMP_DIR/$exclude_file 为排除文件不更新"
  282. rm -rf "$TEMP_DIR/$exclude_file"
  283. fi
  284. fi
  285. done <"$DEPLOY_DIR/custom/user_exclude_file.txt"
  286. # 单独处理语法模型
  287. # [[ "$gram" == "true" ]] || cp -rf "$DEPLOY_DIR/wanxiang-lts-zh-hans.gram" \
  288. # "$TEMP_DIR/${schemaname%.zip}/wanxiang-lts-zh-hans.gram"
  289. # rm -rf "${DEPLOY_DIR:?}"
  290. # cp -rf "$TEMP_DIR/${schemaname%.zip}" "$DEPLOY_DIR"
  291. # 应用更新
  292. apply "$TEMP_DIR/${schemaname%.zip}"
  293. log INFO "方案文件更新成功"
  294. return 0
  295. else
  296. log INFO "远程方案文件版本号为 $remote_version"
  297. log INFO "本地方案文件版本号为 $local_version, 您目前无需更新它"
  298. return 1
  299. fi
  300. }
  301. update_dict() {
  302. local mirror="$1" fuzhu="$2"
  303. # 缓存 API 响应
  304. if [[ "$mirror" == "github" ]]; then
  305. if [[ ! -f "$TEMP_DIR/github_$fuzhu.json" ]]; then
  306. if ! curl -sL -H "Accept: application/vnd.github.v3+json" \
  307. --connect-timeout 10 "$SCHEMA_API" >"$TEMP_DIR/github_$fuzhu.json"; then
  308. error_exit "连接到 GitHub API 失败,您可能需要检查网络"
  309. fi
  310. fi
  311. elif [[ "$mirror" == "cnb" ]]; then
  312. if [[ ! -f "$TEMP_DIR/cnb_$fuzhu.json" ]]; then
  313. if ! curl -sL -H "accept: application/vnd.cnb.web+json" \
  314. --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_$fuzhu.json"; then
  315. error_exit "连接到 CNB 失败,您可能需要检查网络"
  316. fi
  317. fi
  318. fi
  319. local local_date remote_date
  320. if [[ -f "$DEPLOY_DIR/dicts/chengyu.txt" ]]; then
  321. local_date=$(stat -f %c "$DEPLOY_DIR/dicts/chengyu.txt")
  322. else
  323. local_date=0
  324. fi
  325. if [[ "$mirror" == "github" ]]; then
  326. remote_date=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.updated_at')
  327. elif [[ "$mirror" == "cnb" ]]; then
  328. remote_date=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.updated_at')
  329. fi
  330. remote_date=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$remote_date" +%s)
  331. if [[ $remote_date -gt $local_date ]]; then
  332. log INFO "正在下载最新词典文件"
  333. local dicturl dictname local_size remote_size
  334. if [[ "$mirror" == "github" ]]; then
  335. dicturl=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.browser_download_url')
  336. dictname=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.name')
  337. elif [[ "$mirror" == "cnb" ]]; then
  338. dicturl=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.path')
  339. dicturl="https://cnb.cool$dicturl"
  340. dictname=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.name')
  341. fi
  342. curl -L --connect-timeout 10 -o "$TEMP_DIR/$dictname" "$dicturl"
  343. log INFO "正在验证文件完整性"
  344. local_size=$(stat -f %z "$TEMP_DIR/$dictname")
  345. if [[ "$mirror" == "github" ]]; then
  346. remote_size=$(get_info "$mirror" "dict-nightly" "$fuzhu" | jq -r '.size')
  347. elif [[ "$mirror" == "cnb" ]]; then
  348. remote_size=$(get_info "$mirror" "v1.0.0" "$fuzhu" | jq -r '.size_in_byte')
  349. fi
  350. if [[ "$local_size" != "$remote_size" ]]; then
  351. log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size"
  352. error_exit "词典文件下载出错,请重试!"
  353. fi
  354. log INFO "验证成功,开始更新词典文件"
  355. unzip -q "$TEMP_DIR/$dictname" -d "$TEMP_DIR"
  356. dictname="${dictname:2}" && dictname="${dictname%.zip}"
  357. cp -rf "$TEMP_DIR/$dictname"/* "$DEPLOY_DIR/dicts"
  358. log INFO "词典文件更新成功"
  359. return 0
  360. else
  361. remote_date=$(date -r "$remote_date" +"%Y-%m-%d %H:%M:%S")
  362. log INFO "远程词典文件最后更新于 $remote_date"
  363. local_date=$(date -r "$local_date" +"%Y-%m-%d %H:%M:%S")
  364. log INFO "本地词典文件最后更新于 $local_date, 您目前无需更新它"
  365. return 1
  366. fi
  367. }
  368. update_gram() {
  369. local mirror="$1"
  370. # 缓存 API 响应
  371. if [[ "$mirror" == "github" ]]; then
  372. if [[ ! -f "$TEMP_DIR/github_gram.json" ]]; then
  373. if ! curl -sL -H "Accept: application/vnd.github.v3+json" \
  374. --connect-timeout 10 "$GRAM_API" >"$TEMP_DIR/github_gram.json"; then
  375. error_exit "连接到 GitHub API 失败,您可能需要检查网络"
  376. fi
  377. fi
  378. elif [[ "$mirror" == "cnb" ]]; then
  379. if [[ ! -f "$TEMP_DIR/cnb_gram.json" ]]; then
  380. headers=$(curl -sL -D - -o /dev/null -H "accept: application/vnd.cnb.web+json" "$CNB_API")
  381. X_CNB_TOTAL=$(echo "$headers" | awk -F': ' '/[Xx]-[Cc]nb-[Tt]otal:/ {gsub(/ /,"",$2); print $2}')
  382. X_CNB_PAGE_SIZE=$(echo "$headers" | awk -F': ' '/[Xx]-[Cc]nb-[Pp]age-[Ss]ize:/ {gsub(/ /,"",$2); print $2}')
  383. # 防止为空
  384. X_CNB_TOTAL=${X_CNB_TOTAL:-0}
  385. X_CNB_PAGE_SIZE=${X_CNB_PAGE_SIZE:-1}
  386. # 确保是数字
  387. X_CNB_TOTAL=$(echo "$X_CNB_TOTAL" | tr -d -c 0-9)
  388. X_CNB_PAGE_SIZE=$(echo "$X_CNB_PAGE_SIZE" | tr -d -c 0-9)
  389. # 获取最后一页
  390. last_page=$(( (X_CNB_TOTAL + X_CNB_PAGE_SIZE - 1) / X_CNB_PAGE_SIZE ))
  391. if ! curl -G -sL -H "accept: application/vnd.cnb.web+json" \
  392. --data-urlencode "page=${last_page}" \
  393. --connect-timeout 10 "$CNB_API" >"$TEMP_DIR/cnb_gram.json"; then
  394. error_exit "连接到 CNB 失败,您可能需要检查网络"
  395. fi
  396. fi
  397. fi
  398. local local_date remote_date gramname="wanxiang-lts-zh-hans.gram"
  399. if [[ -f "$DEPLOY_DIR/$gramname" ]]; then
  400. local_date=$(stat -f %c "$DEPLOY_DIR/$gramname")
  401. else
  402. local_date=0
  403. fi
  404. if [[ "$mirror" == "github" ]]; then
  405. remote_date=$(get_info "$mirror" "LTS" "gram" | jq -r '.updated_at')
  406. elif [[ "$mirror" == "cnb" ]]; then
  407. remote_date=$(get_info "$mirror" "model" "gram" | jq -r '.updated_at')
  408. fi
  409. remote_date=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$remote_date" +%s)
  410. if [[ $remote_date -gt $local_date ]]; then
  411. log INFO "正在下载最新语法模型"
  412. local gramurl local_size remote_size
  413. if [[ "$mirror" == "github" ]]; then
  414. gramurl=$(get_info "$mirror" "LTS" "gram" | jq -r '.browser_download_url')
  415. elif [[ "$mirror" == "cnb" ]]; then
  416. gramurl=$(get_info "$mirror" "model" "gram" | jq -r '.path')
  417. gramurl="https://cnb.cool$gramurl"
  418. fi
  419. curl -L --connect-timeout 10 -o "$TEMP_DIR/$gramname" "$gramurl"
  420. log INFO "正在验证文件完整性"
  421. local_size=$(stat -f %z "$TEMP_DIR/$gramname")
  422. if [[ "$mirror" == "github" ]]; then
  423. remote_size=$(get_info "$mirror" "LTS" "gram" | jq -r '.size')
  424. elif [[ "$mirror" == "cnb" ]]; then
  425. remote_size=$(get_info "$mirror" "model" "gram" | jq -r '.size_in_byte')
  426. fi
  427. if [[ "$local_size" != "$remote_size" ]]; then
  428. log ERROR "期望文件大小: $remote_size, 实际文件大小: $local_size"
  429. error_exit "语法模型下载出错,请重试!"
  430. fi
  431. log INFO "验证成功,开始更新语法模型"
  432. cp -rf "$TEMP_DIR/$gramname" "${DEPLOY_DIR}/$gramname"
  433. log INFO "语法模型更新成功"
  434. return 0
  435. else
  436. remote_date=$(date -r "$remote_date" +"%Y-%m-%d %H:%M:%S")
  437. log INFO "远程语法模型最后更新于 $remote_date"
  438. local_date=$(date -r "$local_date" +"%Y-%m-%d %H:%M:%S")
  439. log INFO "本地语法模型最后更新于 $local_date, 您目前无需更新它"
  440. return 1
  441. fi
  442. }
  443. # 部属函数
  444. deploy() {
  445. local deploy_executable="$1"
  446. shift # 移除第一个参数,后续所有参数都是要传给可执行文件的
  447. if [ -x "$deploy_executable" ]; then
  448. echo "正在触发重新部署配置"
  449. if output_and_error=$("$deploy_executable" "$@" 2>&1); then
  450. [[ -n "$output_and_error" ]] && echo "输出: $output_and_error"
  451. echo "重新部署成功"
  452. else
  453. echo "重新部署失败"
  454. [[ -n "$output_and_error" ]] && echo "错误信息: $output_and_error"
  455. fi
  456. else
  457. echo "找不到可执行文件: $deploy_executable"
  458. echo "请手动部署"
  459. fi
  460. }
  461. show_help() {
  462. cat <<EOF
  463. Usage: $0 [OPTIONS]
  464. 选项:
  465. --mirror [github|cnb] 选择下载源 (默认: github)
  466. --engine [fcitx5|squirrel] 设置输入法引擎 (必需,也可在脚本中设置对应变量)
  467. --schema [base|pro] 更新方案类型
  468. --fuzhu SCHEMA 更新辅助码表 (base|flypy|hanxin|moqi|tiger|wubi|zrm)
  469. --dict 更新词典
  470. --gram 更新语法模型
  471. --help 显示此帮助信息
  472. 示例:
  473. $0 --engine squirrel --schema base --fuzhu base --dict
  474. $0 --mirror cnb --engine squirrel --schema pro --fuzhu flypy --gram
  475. 注意:
  476. 必须至少指定一个更新项目: --schema, --dict 或 --gram
  477. 使用 --schema 或 --dict 时必须同时使用 --fuzhu
  478. EOF
  479. }
  480. main() {
  481. # 脚本退出清理临时目录
  482. trap cleanup EXIT
  483. # 欢迎语
  484. log INFO "欢迎使用万象方案更新助手"
  485. # 检查是否为root用户
  486. if [[ "$EUID" -eq 0 ]]; then
  487. error_exit "请不要使用 root 身份运行该脚本!"
  488. fi
  489. # 检查必要的依赖
  490. deps_check
  491. # 处理用户输入
  492. local mirror="" schema="" fuzhu="" dict="false" gram="false"
  493. # 解析命令行参数
  494. while [[ "$#" -gt 0 ]]; do
  495. case $1 in
  496. --mirror)
  497. if [[ -n "$mirror" ]]; then
  498. error_exit "选项 mirror 需要参数!"
  499. else
  500. shift
  501. fi
  502. if [[ "$1" != "cnb" ]]; then
  503. error_exit "选项 mirror 的参数目前只能为 cnb"
  504. else
  505. mirror="$1"
  506. fi
  507. ;;
  508. --engine)
  509. if [[ -n "$ENGINE" ]]; then
  510. error_exit "选项 engine 已指定!"
  511. fi
  512. shift
  513. if [[ -z "$1" || "$1" == --* ]]; then
  514. error_exit "选项 engine 需要参数!"
  515. fi
  516. if [[ "$1" != "fcitx5" && "$1" != "squirrel" ]]; then
  517. error_exit "选项 engine 的参数只能为 fcitx5 或 squirrel"
  518. fi
  519. ENGINE="$1"
  520. ;;
  521. --schema)
  522. if [[ -n "$schema" ]]; then
  523. error_exit "选项 schema 需要参数!"
  524. else
  525. shift
  526. fi
  527. if [[ "$1" != "base" && "$1" != "pro" ]]; then
  528. error_exit "选项 schema 的参数只能为 base 或 pro"
  529. else
  530. schema="$1"
  531. fi
  532. ;;
  533. --fuzhu)
  534. if [[ -n "$fuzhu" ]]; then
  535. error_exit "选项 fuzhu 需要参数!"
  536. else
  537. shift
  538. fi
  539. if fuzhu_check "$1"; then
  540. fuzhu="$1"
  541. else
  542. error_exit "选项 fuzhu 的参数只能为 ${FUZHU_LIST[*]} 其中之一"
  543. fi
  544. ;;
  545. --dict)
  546. dict="true"
  547. ;;
  548. --gram)
  549. gram="true"
  550. ;;
  551. --help)
  552. show_help
  553. exit 0
  554. ;;
  555. *)
  556. log WARN "未知参数: $1"
  557. log WARN "使用 --help 查看帮助信息"
  558. error_exit "参数输入错误: $1"
  559. ;;
  560. esac
  561. shift
  562. done
  563. engine_check
  564. # 获取输入法配置路径
  565. if [ "$ENGINE" = "fcitx5" ]; then
  566. DEPLOY_DIR="$HOME/.local/share/fcitx5/rime"
  567. else
  568. DEPLOY_DIR="$HOME/Library/Rime"
  569. fi
  570. # 判断是否设置了部署目录
  571. if [[ -n "$DEPLOY_DIR" ]]; then
  572. if [[ ! -d "$DEPLOY_DIR" ]]; then
  573. log WARN "部署目录 $DEPLOY_DIR 不存在,您要创建它吗?"
  574. read -rp "请输入 YES 或 NO (区分大小写) " _check
  575. if [[ "$_check" == "YES" ]]; then
  576. log WARN "您真的要创建该目录吗?您确定您的设置正确吗?"
  577. read -rp "请输入 YES 或 NO (区分大小写) " _check_again
  578. [[ "$_check_again" == "YES" ]] || error_exit "用户终止操作"
  579. mkdir -p "$DEPLOY_DIR"
  580. else
  581. error_exit "用户终止操作"
  582. fi
  583. fi
  584. else
  585. error_exit "请设置部署目录!"
  586. fi
  587. # 排除项目列表文件是否存在
  588. if [[ -f "$DEPLOY_DIR/user_exclude_file.txt" ]]; then
  589. mv "$DEPLOY_DIR/user_exclude_file.txt" "$DEPLOY_DIR/custom/user_exclude_file.txt"
  590. sed -i 's/user_exclude_file\.txt/custom\/user_exclude_file\.txt/g' \
  591. "$DEPLOY_DIR/custom/user_exclude_file.txt"
  592. fi
  593. if [[ ! -f "$DEPLOY_DIR/custom/user_exclude_file.txt" ]]; then
  594. log WARN "您没有设置排除项目列表!"
  595. log WARN "将为您自动创建包含部分排除项目列表文件: $DEPLOY_DIR/custom/user_exclude_file.txt"
  596. # 生成排除文件
  597. create_exclude_file
  598. log INFO "排除项目列表文件已创建"
  599. log WARN "您还可以在该文件中写入您需要排除的项目,每行一个"
  600. read -rp "按回车继续,M 键更改: " if_modify
  601. if [ "$if_modify" == "M" ]; then
  602. log WARN "请修改排除项目列表文件: $DEPLOY_DIR/custom/user_exclude_file.txt"
  603. log WARN "保存后重新运行该脚本"
  604. open "$DEPLOY_DIR/custom/user_exclude_file.txt"
  605. exit
  606. fi
  607. fi
  608. # 检查 schema 和 fuzhu 是否同时存在
  609. if [[ -n "$schema" && -z "$fuzhu" ]]; then
  610. error_exit "选项 schema 与选项 fuzhu 必须同时使用"
  611. fi
  612. # 检查 dict 和 fuzhu 是否同时存在
  613. if [[ "$dict" == "true" && -z "$fuzhu" ]]; then
  614. error_exit "选项 dict 与选项 fuzhu 必须同时使用"
  615. fi
  616. # 检查当 schema 为 base 时,fuzhu 是否也为 base
  617. if [[ "$schema" == "base" && "$fuzhu" != "base" ]]; then
  618. error_exit "当选项 schema 为 base 时,选项 fuzhu 必须为 base"
  619. fi
  620. [[ -n "$mirror" ]] || mirror="github"
  621. # 脚本自检
  622. script_check "$mirror"
  623. # 开始更新
  624. updated=false
  625. [[ -z "$schema" ]] || {
  626. update_schema "$mirror" "$fuzhu" "$gram" && updated=true
  627. }
  628. [[ "$dict" == "false" ]] || {
  629. update_dict "$mirror" "$fuzhu" && updated=true
  630. }
  631. [[ "$gram" == "false" ]] || {
  632. update_gram "$mirror" && updated=true
  633. }
  634. # 自动部署
  635. if [ "$updated" = true ]; then
  636. if [ "$ENGINE" = "squirrel" ]; then
  637. DEPLOY_EXECUTABLE="/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel"
  638. deploy "$DEPLOY_EXECUTABLE" --reload
  639. else
  640. DEPLOY_EXECUTABLE="/Library/Input Methods/Fcitx5.app/Contents/bin/fcitx5-curl"
  641. deploy "$DEPLOY_EXECUTABLE" /config/addon/rime/deploy -X POST -d '{}'
  642. fi
  643. fi
  644. }
  645. main "$@"