Plugin_update #129
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Plugin_update | |
on: | |
workflow_dispatch: | |
schedule: | |
- cron: "0 20 * * *" | |
push: | |
branches: | |
- main | |
paths: | |
- 'scripts/**' | |
- 'loon/plugin/**' | |
jobs: | |
Plugin_update: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Set up time zone | |
run: sudo timedatectl set-timezone Asia/Shanghai | |
- name: Checkout repository | |
uses: actions/checkout@main | |
- name: Get last commit files | |
if: github.event_name == 'push' | |
id: last_commit | |
run: | | |
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
- name: Get changed files | |
if: github.event_name == 'push' | |
id: file_conf | |
run: | | |
COMMIT=${{ env.sha }} | |
URL="https://api.github.com/repos/${{ github.repository }}/compare/${COMMIT}^...${COMMIT}" | |
conf_File=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" $URL | jq -r '.files[] | select(.filename | test("^scripts/.*\\.conf$")) | .filename' | tr '\n' ' ') | |
echo "conf_File=${conf_File}" >> $GITHUB_OUTPUT | |
- name: Update Plugin | |
run: | | |
script_folder="scripts" #存放配置和脚本目录 | |
loon_plugin_folder="loon/plugin" #存放生成Loon插件目录 | |
base_url="https://raw.githubusercontent.com/$GITHUB_REPOSITORY/${GITHUB_REF##*/}/" | |
current_year=$(date +'%Y') | |
current_date=$(date "+%Y-%m-%d %H:%M:%S") | |
previous_year_1=$((current_year - 1)) | |
previous_year_2=$((current_year - 2)) | |
echo 'user_agent = "Loon/699 CFNetwork/1496.0.7 Darwin/23.5.0"' >> ~/.wgetrc | |
mkdir -p "$script_folder" "$script_folder" "$loon_plugin_folder" tmp | |
extract_value() { | |
grep -i "$1" "$2" | awk -F '=' '{print $2}' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/%0D//' | |
} | |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [ "${{ github.event_name }}" = "schedule" ]; then | |
conf_File=($(find "$script_folder" -name '*.conf')) | |
else | |
conf_File="${{ steps.file_conf.outputs.conf_File }}" && IFS=' ' read -r -a conf_File <<< "$conf_File" | |
[ ${#conf_File[@]} -eq 0 ] && conf_File=($(find "$script_folder" -name '*.conf')) | |
fi | |
for conf_file in "${conf_File[@]}"; do | |
if [ -e "$conf_file" ]; then | |
remote_url=$(extract_value "^remote_url" $conf_file) | |
script_name=$(extract_value "^script_name" $conf_file) | |
[ -z "$script_name" ] && script_name=$(basename "$remote_url" | sed 's/\.[^.]*$//' | awk '{print tolower($0)}') | |
if [[ $remote_url == http* ]]; then | |
download_file="$script_folder/$script_name/files/$(basename "$remote_url" | awk '{print tolower($0)}')" | |
mkdir -p "$script_folder/$script_name/files/" && wget -O "$download_file" "$remote_url" --tries=2 | |
name=$(extract_value "^#!name" $conf_file) | |
[ -z "$name" ] && name=$(extract_value "^#!name" $download_file) | |
[ -z "$name" ] && name=$script_name | |
desc=$(extract_value "^#!desc" $conf_file) | |
[ -z "$desc" ] && desc=$(extract_value "^#!desc" $download_file) | |
[ -z "$desc" ] && desc="仅供学习和个人使用,不得用于商业目的或其他非法用途" | |
openurl=$(extract_value "^#!openUrl" $conf_file) | |
[ -z "$openurl" ] && openurl=$(extract_value "^#!openUrl" $download_file) | |
author=$(extract_value "^#!author" "$conf_file") | |
[ -z "$author" ] && author=$(extract_value "^#!author" "$download_file") | |
homepage=$(extract_value "^#!homepage" $conf_file) | |
[ -z "$homepage" ] && homepage=$(extract_value "^#!homepage" $download_file) | |
icon=$(extract_value "^#!icon" $conf_file) | |
[ -z "$icon" ] && icon=$(extract_value "^#!icon" $download_file) | |
select=$(extract_value "^#!select" $conf_file) | |
[ -z "$select" ] && select=$(extract_value "^#!select" $download_file) | |
input=$(extract_value "^#!input" $conf_file) | |
[ -z "$input" ] && input=$(extract_value "^#!input" $download_file) | |
date=$(extract_value "^#!date" $conf_file) | |
[ -z "$date" ] && date=$(awk '/\/\*/,/\*\// || /#/' "$download_file" | grep -oEm 1 "$current_year[-/年.][[:digit:]]{1,2}[-/年.][[:digit:]]{1,2}|$previous_year_1[-/年.][[:digit:]]{1,2}[-/年.][[:digit:]]{1,2}|$previous_year_2[-/年.][[:digit:]]{1,2}[-/年.][[:digit:]]{1,2}" || extract_value "^#!date" $download_file) | |
[ -z "$date" ] && { date="$current_date"; grep -q '^#!date' "$conf_file" && sed -i "s/^#!date.*/#!date = $current_date/" "$conf_file" || echo -e "\n#!date = $current_date" >> "$conf_file"; } | |
date=$(echo "$date" | sed 's/[\/年.]/-/g; /[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}/! s/$/ 00:00:00/') | |
mkdir -p "tmp/$loon_plugin_folder/" | |
plugin_file="tmp/$loon_plugin_folder/$script_name.plugin" | |
[[ "$remote_url" == *.plugin ]] && echo -e "/*\n插件引用"$remote_url"\n*/" > "$plugin_file" | |
echo "#!name = $name" >> "$plugin_file" | |
echo "#!desc = $desc" >> "$plugin_file" | |
[ -n "$openurl" ] && echo "#!openUrl = $openurl" >> "$plugin_file" | |
[ -n "$author" ] && echo "#!author = $author" >> "$plugin_file" | |
[ -n "$homepage" ] && echo "#!homepage = $homepage" >> "$plugin_file" | |
if [ -n "$icon" ]; then | |
icon_file="$script_folder/$script_name/icon/$script_name.png" | |
[ ! -e "$icon_file" ] && mkdir -p "$script_folder/$script_name/icon" && wget -O "$icon_file" "$icon" --tries=2 | |
echo "#!icon = $base_url$icon_file" >> "$plugin_file" | |
fi | |
[ -n "$select" ] && echo "$select" | sed 's/^/#!select = /' >> "$plugin_file" | |
[ -n "$input" ] && echo "$input" | sed 's/^/#!input = /' >> "$plugin_file" | |
echo "#!date = $date" >> "$plugin_file" | |
mitm=$(grep -i "^hostname" $conf_file | awk -F '=' '{print $2}' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/%0D//; s/,$//; s/,\([^ ]\)/, \1/g') | |
[ -z "$mitm" ] &&mitm=$(grep -i "^hostname" $download_file | awk -F '=' '{print $2}' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/%0D//; s/,$//; s/,\([^ ]\)/, \1/g' | sed 's/%INSERT% //; s/%APPEND% //') | |
script=$(sed -n '/\[Script\]/,/^\[/p' "$conf_file" | sed '/^\[/d; /^[[:space:]]*$/d') | |
[ -z "$script" ] && script=$(sed -n '/\[Script\]/,/^\[/p;/\[rewrite_local\]/,/^\[/p' "$download_file" | sed '/^\[/d; /^[[:space:]]*$/d') | |
rewrite=$(sed -n '/\[Rewrite\]/,/^\[/p' "$conf_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
[ -z "$rewrite" ] && rewrite=$(sed -n '/\[.*Rewrite\]/,/^\[/p' "$download_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
general=$(sed -n '/\[General\]/,/^\[/p' "$conf_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
[ -z "$general" ] && general=$(sed -n '/\[General\]/,/^\[/p' "$download_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d' | sed '/always-raw-tcp-hosts/d') | |
rule=$(sed -n '/\[Rule\]/,/^\[/p' "$conf_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
[ -z "$rule" ] && rule=$(sed -n '/\[Rule\]/,/^\[/p' "$download_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
host=$(sed -n '/\[Host\]/,/^\[/p' "$conf_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
[ -z "$host" ] && host=$(sed -n '/\[Host\]/,/^\[/p' "$download_file" | sed '/^\[/d; /^[[:space:]]*$/d; /^#/d') | |
reject=$(echo "$script" | awk '/ reject/ {print}' | sed 's/url reject/reject/g') | |
[ -n "$reject" ] && [ -n "$rewrite" ] && rewrite=$(printf "%s\n%s" "$rewrite" "$reject") ; script=$(echo "$script" | sed '/ reject/d') | |
[ -n "$reject" ] && [ -z "$rewrite" ] && rewrite="$reject" ; script=$(echo "$script" | sed '/ reject/d') | |
[ -n "$general" ] && echo -e "\n[General]\n$general" >> "$plugin_file" | |
[ -n "$rewrite" ] && echo -e "\n[Rewrite]\n$rewrite" >> "$plugin_file" | |
[ -n "$host" ] && echo -e "\n[Host]\n$host" >> "$plugin_file" | |
[ -n "$rule" ] && echo -e "\n[Rule]\n$rule" >> "$plugin_file" | |
[ -n "$script" ] && echo -e "\n[Script]\n$script" >> "$plugin_file" | |
[ -n "$mitm" ] && echo -e "\n[Mitm]\nhostname = $mitm" >> "$plugin_file" | |
# http-response | |
sed -i '/script-response-body/ s/^/http-response /' "$plugin_file" | |
sed -i '/script-response-body/ s/$/, requires-body = true, tag =/' "$plugin_file" | |
sed -i 's/url script-response-body/script-path =/' "$plugin_file" | |
# http-request | |
sed -i '/script-request-\(header\|body\)/ s/^/http-request /' "$plugin_file" | |
sed -i '/script-request-header/ s/$/, tag =/' "$plugin_file" | |
sed -i '/script-request-body/ s/$/, requires-body = true, tag =/' "$plugin_file" | |
sed -i 's/url script-request-\(header\|body\)/script-path =/' "$plugin_file" | |
#tag= | |
for keyword in 'http-response' 'http-request'; do | |
line_numbers=$(grep -n "$keyword" "$plugin_file" | cut -d: -f1) | |
for line_number in $line_numbers; do | |
if [ "$line_number" ]; then | |
current_line=$(sed -n "${line_number}p" "$plugin_file") | |
if [[ "$current_line" =~ [=]$ || "$current_line" =~ [=][[:space:]]$ ]]; then | |
previous_line_number=$((line_number - 1)) | |
previous_line=$(sed -n "${previous_line_number}p" "$plugin_file") | |
if [[ "$previous_line" == "#"* ]]; then | |
comment_value=$(echo "$previous_line" | sed 's/^# *//; s/^> *//') | |
sed -i -e "${line_number}s/$/ $comment_value/" "$plugin_file" | |
else | |
name_value=$(echo "$previous_line" | grep -o 'tag = [^ ]*' | sed 's/tag = //') | |
[ -z "$name_value" ] && name_value=$name | |
sed -i -e "${line_number}s/$/ $name_value/" "$plugin_file" | |
fi | |
fi | |
fi | |
done | |
done | |
sed -i '/^# /d' "$plugin_file" | |
# script下载 | |
mkdir -p "tmp/$script_folder/$script_name/script" "$script_folder/$script_name/script" | |
while IFS= read -r line; do | |
if [[ "$line" == *"script-path"* ]]; then | |
script_download_url=$(echo "$line" | sed -n 's/^.*script-path\s*=\s*\([^,]*\).*$/\1/p') | |
script_basename=$(basename "$script_download_url" | awk '{print tolower($0)}') | |
script_tmp_path="tmp/$script_folder/$script_name/script/$script_basename" | |
script_folder_path="$script_folder/$script_name/script/$script_basename" | |
wget -O "$script_tmp_path" "$script_download_url" --tries=2 | |
if head -n 5 "$script_tmp_path" | awk '/来源|引用/ && /http/ {exit 1}'; then | |
sed -i '1i\/*\n脚本引用'"$script_download_url"'\n*/' "$script_tmp_path" | |
fi | |
# 对比script内容更新date值 | |
if [ -f "$script_tmp_path" ] && [ -f "$script_folder_path" ]; then | |
if ! cmp -s "$script_tmp_path" "$script_folder_path"; then | |
sed -i "s/#!date.*/#!date = $current_date/" "$plugin_file" | |
grep -q '^#!date' "$conf_file" && sed -i "s/^#!date.*/#!date = $current_date/" "$conf_file" || echo -e "\n#!date = $current_date" >> "$conf_file" | |
fi | |
fi | |
full_url="$base_url$script_folder_path" | |
sed -i "s#$(echo "$script_download_url" | sed 's/[&/\]/\\&/g')#$full_url#" "$plugin_file" | |
mv -f "$script_tmp_path" "$script_folder_path" | |
fi | |
done < "$plugin_file" | |
# 规范文件格式 | |
#sed -i '1,/^#!date/{/^$/d}' $conf_file | |
#sed -i 's/\([^[:space:]]\)\s*=\s*\([^[:space:]]\)/\1 = \2/g' $plugin_file | |
#sed -i -E '/( script-path| requires-body| tag| timeout)\s*=/ s/([^[:space:]])\s*=\s*([^[:space:]])/\1 = \2/g' $plugin_file | |
sed -i '/^hostname/s/,\([^ ]\)/, \1/g' $plugin_file | |
# 对比插件内容更新date值 | |
grep -v '^#!date' "$plugin_file" > "plugin_new.tmp" | |
if [ -f "$loon_plugin_folder/$script_name.plugin" ]; then | |
grep -v '^#!date' "$loon_plugin_folder/$script_name.plugin" > "plugin_old.tmp" | |
fi | |
if [ -f "plugin_new.tmp" ] && [ -f "plugin_old.tmp" ]; then | |
if ! cmp -s "plugin_new.tmp" "plugin_old.tmp"; then | |
sed -i "s/#!date.*/#!date = $current_date/" "$plugin_file" | |
grep -q '^#!date' "$conf_file" && sed -i "s/^#!date.*/#!date = $current_date/" "$conf_file" || echo -e "\n#!date = $current_date" >> "$conf_file" | |
fi | |
fi | |
rm -f "plugin_new.tmp" "plugin_old.tmp" | |
mv -f "$plugin_file" "$loon_plugin_folder/" | |
#实例生成conf | |
script_file="$script_folder/$script_name/$script_name.conf" | |
[ ! -e "$script_file" ] && mkdir -p "$script_folder/$script_name" && mv "$conf_file" "$script_file" | |
fi | |
rm -fr tmp | |
fi | |
done | |
if [ -n "$(find "$loon_plugin_folder" -name '*.plugin' | head -n 1)" ]; then | |
echo '开始更新readme.md' | |
process_plugin_file() { | |
local file="$1" | |
plugin_name=$(extract_value "^#!name" "$file") | |
plugin_date=$(grep -i "^#!date" "$file" | awk -F '=' '{print $2}' | sed 's/^ *//;s/ *$//' | while read date; do date -d "$date" +%Y/%m/%d; done) | |
if [ -n "$plugin_name" ]; then | |
install_link="https://www.nsloon.com/openloon/import?plugin=$base_url$file" | |
raw_link="$base_url$file" | |
echo "| ✅[$plugin_name]($raw_link) | $plugin_date | [导入]($install_link) |" >> "$readme_file" | |
fi | |
} | |
files_with_dates=$(find "$loon_plugin_folder" -maxdepth 1 -type f -name "*.plugin" -exec grep -H '#!date' {} +) | |
plugin_files=($(echo "$files_with_dates" | awk -F'[=]' '{print $NF,$0}' | sort -r -k1 | cut -d' ' -f4- | awk -F ':' '{print $1}')) | |
readme_file="$loon_plugin_folder/readme.md" | |
touch "$readme_file" && echo -e "## 🎈Loon插件\n| ⚙插件名称 | 📌更新时间 | 链接 |\n| - | - | - |" > "$readme_file" | |
for file in "${plugin_files[@]}"; do | |
file_base_name=$(basename "$file" .plugin) | |
conf_file="$script_folder/$file_base_name/$file_base_name.conf" | |
if [ -f "$conf_file" ] && grep -qE "^readme_tag\s*=\s*(常用插件|)$" "$conf_file"; then | |
process_plugin_file "$file" | |
fi | |
done | |
echo -e "#### 🔓解锁插件:\n<details>\n<summary>👆︎点击查看</summary>\n<ul>\n\n| 🔓插件名称 | 📌更新时间 | 链接 |\n| - | - | - |" >> "$readme_file" | |
for file in "${plugin_files[@]}"; do | |
file_base_name=$(basename "$file" .plugin) | |
conf_file="$script_folder/$file_base_name/$file_base_name.conf" | |
if [ -f "$conf_file" ] && grep -qE "^readme_tag\s*=\s*.*解锁.*$" "$conf_file"; then | |
process_plugin_file "$file" | |
fi | |
done | |
echo -e "</ul>\n</details>\n\n## ⚠️免责声明" >> "$readme_file" | |
sed -n '/^## ⚠️免责声明$/,/^##/{/^##/!p}' readme.md >> "$readme_file" | |
sed -i '/## 🎈Loon插件/,$d' loon/readme.md ; cat $readme_file >> loon/readme.md | |
fi | |
if [ ! -f "$script_folder/script.conf" ]; then | |
echo '创建script示例' | |
cat <<EOF > "$script_folder/script.conf" | |
## 用于 Loon 插件定时下载到仓库,以防止原作者删除库的脚本配置文件。 | |
# 支持将 Quantumult X 脚本转换为 Loon 插件,并覆盖原插件中的参数。 | |
# 注意:仅供学习自用,不保证使用过程中的问题。 | |
# 下载地址,支持 Loon 插件和 Quantumult X 脚本(必填) | |
remote_url = | |
# 用于创建的文件名(可选,默认使用下载地址的文件名) | |
script_name = | |
# 用于 readme.md 中插件分类(可选) | |
readme_tag = | |
# 使用方法: | |
# 1. 填写下载地址(remote_url)和可选的文件名(script_name)。 | |
# 2. 必要时填写插件分类标签(readme_tag)。 | |
# 3. 在下方需要覆盖默认参数的部分添加相应的配置。 | |
# 4. 保存该文件会自动运行工作流,生成插件和 conf 配置文件,便于后续修改。 | |
# ============================= 可选 - 下方添加需要覆盖的默认参数 ============================= | |
# 例如: | |
# #!name = xxx | |
# #!icon = xxx | |
# [Rule] | |
# DOMAIN, xxx, PROXY | |
# [Mitm] | |
# hostname = xxx | |
EOF | |
fi | |
- name: Commit and push | |
run: | | |
git config --global user.email "[email protected]" && git config --global user.name "Bot" | |
git add . && git commit -m "插件更新$(date +'%Y-%m-%d %H:%M')" || exit 0 | |
git push | |
- name: Cleanup Workflow | |
uses: Mattraks/delete-workflow-runs@main | |
with: | |
retain_days: 1 | |
keep_minimum_runs: 2 |