🌈 Sync 2026-08-11 02:29:57

This commit is contained in:
github-actions[bot] committed 2026-08-11 02:29:57 +08:00
1 parent f8bdc4459f
commit 412c262edf
73 files changed
+9791 -5915

No files matched your search

@@ -22,19 +22,19 @@ module YAML
end
def self.load_file(filename, *args, **kwargs)
yaml_content = File.read(filename, mode: "r:bom|utf-8")
secret = nil
if kwargs.key?(:secret)
secret = kwargs.delete(:secret)
end
if yaml_content.include?("BEGIN AGE ENCRYPTED FILE")
header = File.binread(filename, 512).to_s
if header.include?("BEGIN AGE ENCRYPTED FILE")
yaml_content = File.read(filename, mode: "r:bom|utf-8")
if secret && secret.to_s.strip != ""
decrypted = decrypt_content_with_secret(secret.to_s, yaml_content)
if decrypted && !decrypted.empty? && !decrypted.include?("BEGIN AGE ENCRYPTED FILE")
processed = fix_short_id_quotes(decrypted)
return load(processed, *args, **kwargs)
return fix_and_load(decrypted, *args, **kwargs)
else
raise "Decrypted content empty or still encrypted: [#{filename}]"
end
@@ -46,8 +46,7 @@ module YAML
begin
decrypted = decrypt_content_with_secret(sec, yaml_content)
if decrypted && !decrypted.empty? && !decrypted.include?("BEGIN AGE ENCRYPTED FILE")
processed = fix_short_id_quotes(decrypted)
return load(processed, *args, **kwargs)
return fix_and_load(decrypted, *args, **kwargs)
end
rescue => e
last_error = e.message
@@ -58,13 +57,51 @@ module YAML
raise "Encrypted file: decryption failed for [#{filename}]: [#{detail}]"
end
processed_content = fix_short_id_quotes(yaml_content)
load(processed_content, *args, **kwargs)
base64, short_id, protocol_param = File.open(filename, "r:bom|utf-8") do |io|
scan_for_fixes(io)
end
if base64 || protocol_param
fix_and_load(File.read(filename, mode: "r:bom|utf-8"), *args, **kwargs)
elsif short_id
fixed = File.open(filename, "r:bom|utf-8") { |io| fix_short_id_text(io) }
begin
load(fixed, *args, **kwargs)
rescue => e
raise "fix short-id values type failed: #{e.message}"
end
else
result = File.open(filename, "r:bom|utf-8") do |io|
load(io, *args, **kwargs)
end
if result.nil? || result == false
result = fix_and_load(File.read(filename, mode: "r:bom|utf-8"), *args, **kwargs)
end
result
end
end
def self.dump(obj, io = nil, **options)
yaml_content = original_dump(obj, **options)
processed = fix_short_id_quotes(yaml_content)
if obj.nil? || obj == false
target = ""
if io.is_a?(String)
target = " [#{io}]"
elsif io && io.respond_to?(:path)
target = " [#{io.path}]"
elsif options.key?(:filename)
target = " [#{options[:filename]}]"
end
raise "YAML.dump: refusing to write nil/false config content#{target} (previous load may have failed)"
end
if io.is_a?(String)
dump_to_path(obj, io, **options)
else
dump_to_io(obj, io, **options)
end
end
def self.dump_to_io(obj, io = nil, **options)
public_key = nil
fname = nil
if options.key?(:public)
@@ -86,7 +123,11 @@ module YAML
end
end
needs_fix = contains_short_id?(obj)
if public_key && public_key.to_s.strip != ""
yaml_content = original_dump(obj, **options)
processed = needs_fix ? fix_short_id_quotes(yaml_content) : yaml_content
begin
encrypted = encrypt_content_with_public(public_key.to_s, processed)
if encrypted && !encrypted.empty?
@@ -108,15 +149,40 @@ module YAML
end
if io.nil?
processed
yaml_content = original_dump(obj, **options)
needs_fix ? fix_short_id_quotes(yaml_content) : yaml_content
elsif io.respond_to?(:write)
io.write(processed)
if needs_fix
fix_short_id_text(original_dump(obj, **options), io)
else
original_dump(obj, io, **options)
end
io
else
processed
yaml_content = original_dump(obj, **options)
needs_fix ? fix_short_id_quotes(yaml_content) : yaml_content
end
end
def self.dump_to_path(obj, path, **options)
real = File.symlink?(path) ? File.realpath(path) : path
dir = File.dirname(real)
tmp = File.join(dir, ".#{File.basename(real)}.tmp#{Process.pid}.#{rand(1000)}")
mode = File.exist?(real) ? File.stat(real).mode & 07777 : nil
begin
File.open(tmp, 'w') { |f| dump_to_io(obj, f, **options.merge(filename: real)) }
File.chmod(mode, tmp) if mode
File.rename(tmp, real)
rescue Exception
begin
File.unlink(tmp) if File.exist?(tmp)
rescue
end
raise
end
path
end
def self.popen_stream(cmd, input, chunk_size: 64 * 1024)
output = String.new
IO.popen(cmd, 'r+', err: [:child, :out]) do |io|
@@ -229,75 +295,162 @@ module YAML
# Input: short-id: "1600e237" -> Output: short-id: "1600e237"
# Input: short-id: null -> Output: short-id: ""
def self.fix_short_id_quotes(yaml_content)
def self.fix_and_load(yaml_content, *args, **kwargs)
yaml_content = decode64(yaml_content)
return yaml_content unless yaml_content.include?('short-id:')
# Fix bare protocol-param values that break YAML parsing (e.g. "1.2.3.4:8080#test")
yaml_content.gsub!(/^(\s*protocol-param:\s+)([^\s"'][^"\n\r]*[#:][^"\n\r]*)$/, '\1"\2"')
return load(yaml_content, *args, **kwargs) unless yaml_content.include?('short-id:')
begin
stream = Psych.parse_stream(yaml_content)
traverse = lambda do |node|
case node
when Psych::Nodes::Mapping
children = node.children || []
i = 0
while i < children.length
key = children[i]
val = children[i + 1]
if key.is_a?(Psych::Nodes::Scalar) && key.value == 'short-id'
if val.is_a?(Psych::Nodes::Scalar)
is_null_scalar = (val.tag == 'tag:yaml.org,2002:null') || (val.tag == '!!null') || (val.value =~ /^\s*(~|null|NULL|Null)\s*$/)
unless is_null_scalar
val.tag = nil
val.style = defined?(Psych::Nodes::Scalar::DOUBLE_QUOTED) ? Psych::Nodes::Scalar::DOUBLE_QUOTED : 2
end
elsif val.is_a?(Psych::Nodes::Sequence)
val.children.each do |child|
if child.is_a?(Psych::Nodes::Scalar)
is_null_child = (child.tag == 'tag:yaml.org,2002:null') || (child.tag == '!!null') || (child.value =~ /^\s*(~|null|NULL|Null)\s*$/)
unless is_null_child
child.tag = nil
child.style = defined?(Psych::Nodes::Scalar::DOUBLE_QUOTED) ? Psych::Nodes::Scalar::DOUBLE_QUOTED : 2
end
end
end
end
else
traverse.call(key) if key.respond_to?(:children)
traverse.call(val) if val.respond_to?(:children)
end
i += 2
end
when Psych::Nodes::Sequence
(node.children || []).each { |c| traverse.call(c) }
when Psych::Nodes::Document, Psych::Nodes::Stream
(node.children || []).each { |c| traverse.call(c) }
else
if node.respond_to?(:children)
(node.children || []).each { |c| traverse.call(c) }
end
end
end
stream.children.each do |doc_node|
if doc_node.is_a?(Psych::Nodes::Document)
traverse.call(doc_node.root) if doc_node.root
end
end
if stream.respond_to?(:to_yaml)
processed_yaml = stream.to_yaml
processed_yaml = processed_yaml.gsub(/^([ \t]*short-id:\s*)!\s*/, "\\1")
processed_yaml
else
yaml_content
end
load(fix_short_id_text(yaml_content), *args, **kwargs)
rescue => e
raise "fix short-id values type failed: #{e.message}"
end
end
def self.quote_short_id_scalar(value)
v = value.strip
return value if v.empty?
return '""' if v =~ /\A(?:~|null|NULL|Null)\z/
if v.start_with?("'")
if (m = v.match(/\A'((?:[^']|'')*)'(\s+#.*)?\z/))
inner = m[1].gsub("''", "'")
return "\"#{inner.gsub(/["\\]/) { |c| "\\#{c}" }}\"#{m[2]}"
end
return value
end
return value if v.start_with?('"')
return value if v =~ /[:{}\[\],|>]/
if (m = v.match(/\A(\S+)(\s+#.*)?\z/))
"\"#{m[1].gsub('"', '\\"')}\"#{m[2]}"
else
"\"#{v.gsub('"', '\\"')}\""
end
end
def self.fix_short_id_text(yaml_content, output = nil)
out = output || String.new
in_seq = false
seq_indent = -1
in_block = false
block_indent = -1
pending = nil
yaml_content.each_line do |line|
if in_block
if line.strip.empty? || (line =~ /^(\s*)/ && Regexp.last_match(1).length > block_indent)
out << line
next
else
in_block = false
end
end
if pending
if (m = line.match(/^(\s*)-(\s*)(.*?)(\r?\n)?\z/)) && m[1].length >= seq_indent
out << pending
in_seq = true
else
out << pending.sub(/^(\s*)short-id:.*?(\r?\n)?\z/, '\1short-id: ""\2')
in_seq = false
end
pending = nil
end
if (m = line.match(/^(\s*)short-id:(\s*)(.*?)(\r?\n)?\z/))
indent = m[1]
rest = m[3].to_s.strip
if rest.empty? || rest.start_with?('#')
in_seq = true
seq_indent = indent.length
pending = line
else
in_seq = false
out << indent + "short-id:" + m[2] + quote_short_id_scalar(rest) + m[4].to_s
end
elsif (m = line.match(/^(\s*)[^:\s][^:]*:\s*[|>](\s*.*)?$/))
in_block = true
in_seq = false
block_indent = m[1].length
out << line
elsif line.strip.empty?
out << line
elsif in_seq && (m = line.match(/^(\s*)-(\s*)(.*?)(\r?\n)?\z/)) && m[1].length >= seq_indent
out << m[1] + "-" + m[2] + quote_short_id_scalar(m[3]) + m[4].to_s
else
in_seq = false
out << line
end
end
if pending
out << pending.sub(/^(\s*)short-id:.*?(\r?\n)?\z/, '\1short-id: ""\2')
end
out
end
def self.fix_short_id_quotes(yaml_content)
begin
fix_short_id_text(yaml_content)
rescue => e
raise "fix short-id values type failed: #{e.message}"
end
end
def self.scan_for_fixes(io)
base64 = false
short_id = false
protocol_param = false
first_nonempty_seen = false
buffer = String.new
chunk_size = 64 * 1024
while (chunk = io.read(chunk_size))
buffer << chunk
unless first_nonempty_seen
if (idx = buffer.index("\n"))
stripped = buffer[0...idx].strip
if !stripped.empty?
first_nonempty_seen = true
base64 = stripped.match?(/\A[A-Za-z0-9+\/=]+\z/)
end
end
end
short_id ||= buffer.include?('short-id:')
protocol_param ||= buffer.include?('protocol-param:')
if first_nonempty_seen && buffer.bytesize > chunk_size + 4096
buffer = buffer[-4096, 4096]
end
break if base64 || short_id || protocol_param
end
unless first_nonempty_seen
stripped = buffer.strip
base64 = !stripped.empty? && stripped.match?(/\A[A-Za-z0-9+\/=]+\z/)
end
[base64, short_id, protocol_param]
end
def self.contains_short_id?(obj, depth = 0)
return false if depth > 64
case obj
when Hash
return true if obj.key?('short-id') || obj.key?(:"short-id")
obj.each_value { |v| return true if contains_short_id?(v, depth + 1) }
false
when Array
obj.any? { |v| contains_short_id?(v, depth + 1) }
else
false
end
end
def self.overwrite(base, override)
return override if base.nil?
return base if override.nil?
@@ -1,48 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 884>"/tmp/lock/openclash_clash_version.lock" 2>/dev/null
flock -x 884 2>/dev/null
}
del_lock() {
flock -u 884 2>/dev/null
rm -rf "/tmp/lock/openclash_clash_version.lock" 2>/dev/null
}
set_lock
DOWNLOAD_FILE="/tmp/clash_last_version"
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
CORE_TYPE=$(uci_get_config "core_type")
OIX_TOKEN=$(uci_get_config "oix_token")
if [ -n "$1" ]; then
github_address_mod="$1"
fi
if [ "$CORE_TYPE" = "Oix" ] || [ -n "$OIX_TOKEN" ]; then
OIX_VERSION_URL="https://github.com/vernesong/mihomo-oix/releases/download/Pre-Alpha/version.txt"
OIX_VERSION_P_URL="https://dl.dler.io/mihomo-oix/version.txt?tag=Pre-Alpha"
if [ "$github_address_mod" != "0" ] && [ "$github_address_mod" != "https://cdn.jsdelivr.net/" ] && [ "$github_address_mod" != "https://fastly.jsdelivr.net/" ] && [ "$github_address_mod" != "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}${OIX_VERSION_URL}"
else
DOWNLOAD_URL="$OIX_VERSION_P_URL"
fi
else
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/vernesong/OpenClash@core/${RELEASE_BRANCH}/core_version"
else
DOWNLOAD_URL="${github_address_mod}https://raw.githubusercontent.com/vernesong/OpenClash/core/${RELEASE_BRANCH}/core_version"
fi
else
DOWNLOAD_URL="https://raw.githubusercontent.com/vernesong/OpenClash/core/${RELEASE_BRANCH}/core_version"
fi
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "$DOWNLOAD_FILE" "$DOWNLOAD_FILE"
del_lock
@@ -6,7 +6,7 @@ LOG_FILE="/tmp/openclash.log"
LOG_OUT()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Info] ${1}" >> $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Info] ${1}" >> $LOG_FILE
fi
}
@@ -14,7 +14,7 @@ LOG_OUT()
LOG_TIP()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Tip] ${1}" >> $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Tip] ${1}" >> $LOG_FILE
fi
}
@@ -22,7 +22,7 @@ LOG_TIP()
LOG_WARN()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Warning] ${1}" >> $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Warning] ${1}" >> $LOG_FILE
fi
}
@@ -30,7 +30,7 @@ LOG_WARN()
LOG_ERROR()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Error] ${1}" >> $START_LOG
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Error] ${1}" >> $LOG_FILE
fi
}
@@ -48,14 +48,3 @@ LOG_WATCHDOG()
echo -e "$(date "+%Y-%m-%d %H:%M:%S") [Watchdog] ${1}" >> $LOG_FILE
fi
}
LOG_ALERT()
{
echo -e "$(tail -n 20 $LOG_FILE |grep -E 'level=fatal|level=error' |awk 'END {print}')" > $START_LOG
sleep 3
}
SLOG_CLEAN()
{
echo "##FINISH##" > $START_LOG
}
@@ -67,6 +67,24 @@ config_test()
fi
}
yaml_sub_validate()
{
rm -f /tmp/yaml_sub_status 2>/dev/null
ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
begin
Value = YAML.load_file('$CFG_FILE')
unless Value.key?('proxies') || Value.key?('proxy-providers')
File.open('/tmp/yaml_sub_status', 'w') { |f| f.puts 'NO_CONTENT' }
end
rescue Exception => e
YAML.LOG_ERROR('Unable To Parse Config File,【' + e.message + '】')
system 'rm -rf ${CFG_FILE} 2>/dev/null'
end
" 2>/dev/null >> $LOG_FILE
YAML_SUB_STATUS=$(cat /tmp/yaml_sub_status 2>/dev/null)
rm -f /tmp/yaml_sub_status 2>/dev/null
}
config_download()
{
LOG_TIP "Config File【$name】Downloading User-Agent【$sub_ua】..."
@@ -152,7 +170,7 @@ config_cus_up()
YAML.LOG_ERROR('Filter Proxies Failed,【' + e.message + '】');
ensure
begin
File.open('$CFG_FILE','w') {|f| YAML.dump(Value, f)};
YAML.dump(Value, '$CFG_FILE');
rescue Exception => e
YAML.LOG_ERROR('Write file failed:【%s】' % [e.message])
end
@@ -222,8 +240,6 @@ config_download_direct()
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "$CFG_FILE" ]; then
#prevent ruby unexpected error
sed -i -E 's/protocol-param: ([^,'"'"'"''}( *#)\n\r]+)/protocol-param: "\1"/g' "$CFG_FILE" 2>/dev/null
sed -i '/^ \{0,\}enhanced-mode:/d' "$CFG_FILE" >/dev/null 2>&1
config_test
if [ $? -ne 0 ]; then
LOG_ERROR "Config File Tested Failed, Please Check The Log Infos!"
@@ -231,14 +247,7 @@ config_download_direct()
config_error
return
fi
ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
begin
YAML.load_file('$CFG_FILE');
rescue Exception => e
YAML.LOG_ERROR('Unable To Parse Config File,【' + e.message + '】');
system 'rm -rf ${CFG_FILE} 2>/dev/null'
end
" 2>/dev/null >> $LOG_FILE
yaml_sub_validate
if [ $? -ne 0 ]; then
LOG_ERROR "Ruby Works Abnormally, Please Check The Ruby Library Depends!"
only_download=1
@@ -248,7 +257,7 @@ config_download_direct()
LOG_OUT "Config File Format Validation Failed..."
change_dns
config_error
elif ! "$(ruby_read "$CFG_FILE" ".key?('proxies')")" && ! "$(ruby_read "$CFG_FILE" ".key?('proxy-providers')")" ; then
elif [ "$YAML_SUB_STATUS" = "NO_CONTENT" ]; then
LOG_ERROR "Updated Config【$name】Has No Proxy Field, Update Exit..."
change_dns
config_error
@@ -434,7 +443,6 @@ sub_info_get()
config_download
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "$CFG_FILE" ]; then
#prevent ruby unexpected error
sed -i -E 's/protocol-param: ([^,'"'"'"''}( *#)\n\r]+)/protocol-param: "\1"/g' "$CFG_FILE" 2>/dev/null
config_test
if [ $? -ne 0 ]; then
LOG_ERROR "Config File Tested Failed, Please Check The Log Infos!"
@@ -442,14 +450,7 @@ sub_info_get()
config_download_direct
return
fi
ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
begin
YAML.load_file('$CFG_FILE');
rescue Exception => e
YAML.LOG_ERROR('Unable To Parse Config File,【' + e.message + '】');
system 'rm -rf ${CFG_FILE} 2>/dev/null'
end
" 2>/dev/null >> $LOG_FILE
yaml_sub_validate
if [ $? -ne 0 ]; then
LOG_ERROR "Ruby Works Abnormally, Please Check The Ruby Library Depends!"
only_download=1
@@ -457,7 +458,7 @@ sub_info_get()
elif [ ! -f "$CFG_FILE" ]; then
LOG_OUT "Config File Format Validation Failed, Trying To Download Without Agent..."
config_download_direct
elif ! "$(ruby_read "$CFG_FILE" ".key?('proxies')")" && ! "$(ruby_read "$CFG_FILE" ".key?('proxy-providers')")" ; then
elif [ "$YAML_SUB_STATUS" = "NO_CONTENT" ]; then
LOG_ERROR "Updated Config【$name】Has No Proxy Field, Trying To Download Without Agent..."
config_download_direct
else
@@ -474,6 +475,5 @@ sub_info_get()
#分别获取订阅信息进行处理
config_load "openclash"
config_foreach sub_info_get "config_subscribe" "$1"
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -115,6 +115,5 @@ fi
rm -rf /tmp/china_ip*_route* >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -19,40 +19,54 @@ set_lock
inc_job_counter
restart=0
UPDATE_SUCCESS=0
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
if [ "$github_address_mod" = "0" ] && [ -z "$(echo $2 2>/dev/null |grep -E 'http|one_key_update')" ] && [ -z "$(echo $3 2>/dev/null |grep 'http')" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
DIRECT_CORE_URL=""
if [ -n "$2" ] && echo "$2" | grep -qE '^https?://'; then
DIRECT_CORE_URL="$2"
fi
if [ -n "$3" ] && [ "$2" = "one_key_update" ]; then
github_address_mod="$3"
if [ "$github_address_mod" = "0" ] && [ -z "$DIRECT_CORE_URL" ] && [ -z "$(echo $2 2>/dev/null |grep -E 'http|one_key_update')" ] && [ -z "$(echo $3 2>/dev/null |grep 'http')" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - GitHub Address Proxy Options"
fi
if [ -n "$2" ] && [ "$2" = "one_key_update" ] && [ -z "$3" ]; then
github_address_mod=0
fi
if [ -n "$2" ] && [ "$2" != "one_key_update" ]; then
github_address_mod="$2"
if [ -z "$DIRECT_CORE_URL" ]; then
if [ -n "$3" ] && [ "$2" = "one_key_update" ]; then
github_address_mod="$3"
fi
if [ -n "$2" ] && [ "$2" = "one_key_update" ] && [ -z "$3" ]; then
github_address_mod=0
fi
if [ -n "$2" ] && [ "$2" != "one_key_update" ]; then
github_address_mod="$2"
fi
if echo "$github_address_mod" | grep -q "raw\.githubusercontent\.com"; then
github_address_mod=0
fi
fi
CORE_TYPE="$1"
C_CORE_TYPE=$(uci_get_config "core_type")
SMART_ENABLE=$(uci_get_config "smart_enable" || echo 0)
OIX_TOKEN=$(uci_get_config "oix_token")
[ "$SMART_ENABLE" -eq 1 ] && CORE_TYPE="Smart"
[ "$CORE_TYPE" = "Oix" ] || [ -n "$OIX_TOKEN" ] && CORE_TYPE="Oix"
[ -n "$OIX_TOKEN" ] && CORE_TYPE="Oix"
[ -z "$CORE_TYPE" ] && CORE_TYPE="Meta"
small_flash_memory=$(uci_get_config "small_flash_memory")
CPU_MODEL=$(uci_get_config "core_version")
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
if [ "$github_address_mod" != "0" ]; then
/usr/share/openclash/clash_version.sh "$github_address_mod" 2>/dev/null
else
/usr/share/openclash/clash_version.sh 2>/dev/null
fi
if [ ! -f "/tmp/clash_last_version" ]; then
LOG_ERROR "【"$CORE_TYPE"】Core Version Check Error, Please Try Again Later..."
SLOG_CLEAN
del_lock
exit 0
if [ -z "$DIRECT_CORE_URL" ]; then
lua /usr/share/openclash/openclash_version.lua "$github_address_mod" 2>/dev/null
if [ "$CORE_TYPE" = "Oix" ]; then
CORE_LV=$(jsonfilter -i /tmp/openclash_version_history.json -e "@.oix.ver" 2>/dev/null)
elif [ "$CORE_TYPE" = "Smart" ]; then
CORE_LV=$(jsonfilter -i /tmp/openclash_version_history.json -e "@.${RELEASE_BRANCH}.latest.core_smart" 2>/dev/null)
else
CORE_LV=$(jsonfilter -i /tmp/openclash_version_history.json -e "@.${RELEASE_BRANCH}.latest.core_meta" 2>/dev/null)
fi
if [ -z "$CORE_LV" ]; then
LOG_ERROR "【"$CORE_TYPE"】Core Version Check Error, Please Try Again Later..."
del_lock
exit 0
fi
fi
if [ "$small_flash_memory" != "1" ]; then
@@ -70,23 +84,23 @@ TMP_FILE="${TARGET_CORE_PATH}.new.$$"
if [ "$CORE_TYPE" = "Oix" ]; then
CORE_URL_PATH=""
DOWNLOAD_FILE="/tmp/clash_meta.gz"
CORE_LV=$(sed -n 1p /tmp/clash_last_version 2>/dev/null)
elif [ "$CORE_TYPE" = "Smart" ]; then
CORE_URL_PATH="$RELEASE_BRANCH/smart"
DOWNLOAD_FILE="/tmp/clash_meta.tar.gz"
CORE_LV=$(sed -n 2p /tmp/clash_last_version 2>/dev/null)
else
CORE_URL_PATH="$RELEASE_BRANCH/meta"
DOWNLOAD_FILE="/tmp/clash_meta.tar.gz"
CORE_LV=$(sed -n 1p /tmp/clash_last_version 2>/dev/null)
fi
[ "$C_CORE_TYPE" != "$CORE_TYPE" ] || [ -z "$C_CORE_TYPE" ] && restart=1
if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
if [ -n "$DIRECT_CORE_URL" ] || [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
if [ "$CPU_MODEL" != 0 ]; then
LOG_TIP "【$CORE_TYPE】Core Downloading, Please Try to Download and Upload Manually If Fails"
if [ "$CORE_TYPE" = "Oix" ]; then
LOG_TIP "【"$CORE_TYPE"】Core Downloading, Please Try to Download and Upload Manually If Fails"
# If $2 is a full download URL, use it directly
if [ -n "$2" ] && echo "$2" | grep -qE '^https?://'; then
DOWNLOAD_URL="$2"
elif [ "$CORE_TYPE" = "Oix" ]; then
OIX_CORE_URL="https://github.com/vernesong/mihomo-oix/releases/download/Pre-Alpha/mihomo-${CPU_MODEL}-${CORE_LV}.gz"
OIX_CORE_P_URL="https://dl.dler.io/mihomo-oix/mihomo-${CPU_MODEL}-${CORE_LV}.gz?tag=Pre-Alpha"
if [ "$github_address_mod" != "0" ] && [ "$github_address_mod" != "https://cdn.jsdelivr.net/" ] && [ "$github_address_mod" != "https://fastly.jsdelivr.net/" ] && [ "$github_address_mod" != "https://testingcf.jsdelivr.net/" ]; then
@@ -118,91 +132,85 @@ if [ "$CORE_CV" != "$CORE_LV" ] || [ -z "$CORE_CV" ]; then
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ]; then
gzip -t "$DOWNLOAD_FILE" >/dev/null 2>&1
gzip_test_err=$(gzip -t "$DOWNLOAD_FILE" 2>&1)
if [ "$?" -eq 0 ]; then
LOG_TIP "【"$CORE_TYPE"】Core Download Successful, Start Update..."
extract_success=true
extract_err=""
[ -s "$DOWNLOAD_FILE" ] && {
if [ "$CORE_TYPE" = "Oix" ]; then
gzip -dc "$DOWNLOAD_FILE" > "$TMP_FILE" 2>/dev/null || extract_success=false
extract_err=$(gzip -dc "$DOWNLOAD_FILE" > "$TMP_FILE" 2>&1) || extract_success=false
else
tar zxvfo "$DOWNLOAD_FILE" -C /tmp >/dev/null 2>&1 || extract_success=false
mv /tmp/clash "$TMP_FILE" >/dev/null 2>&1 || extract_success=false
extract_err=$(tar zxvfo "$DOWNLOAD_FILE" -C /tmp 2>&1) || extract_success=false
[ "$extract_success" = "true" ] && { extract_err=$(mv /tmp/clash "$TMP_FILE" 2>&1) || extract_success=false; }
fi
rm -rf "$DOWNLOAD_FILE" >/dev/null 2>&1
chmod 4755 "$TMP_FILE" >/dev/null 2>&1 || extract_success=false
"$TMP_FILE" -v >/dev/null 2>&1 || extract_success=false
[ "$extract_success" = "true" ] && { extract_err=$(chmod 4755 "$TMP_FILE" 2>&1) || extract_success=false; }
[ "$extract_success" = "true" ] && { extract_err=$("$TMP_FILE" -v 2>&1) || extract_success=false; }
}
if [ "$extract_success" != "true" ]; then
if [ "$retry_count" -lt "$max_retries" ]; then
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Update Failed..."
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Update Failed:【$(echo "$extract_err" | tr '\n' ' ' | head -c 300)】..."
rm -rf "$TMP_FILE" >/dev/null 2>&1
sleep 2
continue
else
LOG_ERROR "【"$CORE_TYPE"】Core Update Failed, Please Make Sure Enough Flash Memory Space or Selected Correct Core Platform And Try Again!"
LOG_ERROR "【"$CORE_TYPE"】Core Update Failed:【$(echo "$extract_err" | tr '\n' ' ' | head -c 300)】..."
rm -rf "$TMP_FILE" >/dev/null 2>&1
SLOG_CLEAN
del_lock
exit 0
break
fi
fi
mv -f "$TMP_FILE" "$TARGET_CORE_PATH" >/dev/null 2>&1
mv_err=$(mv -f "$TMP_FILE" "$TARGET_CORE_PATH" 2>&1)
if [ "$?" == "0" ]; then
LOG_TIP "【"$CORE_TYPE"】Core Update Successful!"
SLOG_CLEAN
LOG_TIP "【"$CORE_TYPE"】Core Update Successful"
UPDATE_SUCCESS=1
restart=1
break
else
if [ "$retry_count" -lt "$max_retries" ]; then
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Update Failed..."
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Move Failed:【$(echo "$mv_err" | tr '\n' ' ' | head -c 300)】"
sleep 2
continue
else
LOG_ERROR "【"$CORE_TYPE"】Core Update Failed, Please Make Sure Enough Flash Memory Space And Try Again!"
SLOG_CLEAN
LOG_ERROR "【"$CORE_TYPE"】Core Move Failed:【$(echo "$mv_err" | tr '\n' ' ' | head -c 300)】"
break
fi
fi
else
if [ "$retry_count" -lt "$max_retries" ]; then
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Update Failed..."
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Verification Failed:【$(echo "$gzip_test_err" | tr '\n' ' ' | head -c 300)】"
sleep 2
continue
else
LOG_ERROR "【"$CORE_TYPE"】Core Update Failed, Please Check The Network or Try Again Later!"
SLOG_CLEAN
LOG_ERROR "【"$CORE_TYPE"】Core Verification Failed:【$(echo "$gzip_test_err" | tr '\n' ' ' | head -c 300)】"
break
fi
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_TIP "【"$CORE_TYPE"】Core Has Not Been Updated, Stop Continuing Operation!"
SLOG_CLEAN
LOG_TIP "【"$CORE_TYPE"】Core Has Not Been Updated, Stop Continuing Operation"
else
if [ "$retry_count" -lt "$max_retries" ]; then
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Download Failed..."
LOG_ERROR "【$retry_count/$max_retries】【"$CORE_TYPE"】Core Download Failed, Please Check The Network or Try Again Later..."
sleep 2
continue
else
LOG_ERROR "【"$CORE_TYPE"】Core Download Failed, Please Check The Network or Try Again Later!"
SLOG_CLEAN
LOG_ERROR "【"$CORE_TYPE"】Core Download Failed, Please Check The Network or Try Again Later"
break
fi
fi
done
else
LOG_WARN "No Compiled Version Selected, Please Select In Update Page And Try Again!"
SLOG_CLEAN
fi
else
LOG_TIP "【"$CORE_TYPE"】Core Has Not Been Updated, Stop Continuing Operation!"
SLOG_CLEAN
LOG_TIP "【"$CORE_TYPE"】Core Has Not Been Updated, Stop Continuing Operation"
fi
rm -rf "$TMP_FILE" >/dev/null 2>&1
[ "$UPDATE_SUCCESS" = "1" ] && restart=1 || restart=0
dec_job_counter_and_restart "$restart"
del_lock
@@ -54,7 +54,7 @@ EOF
if [ "$SHOW_DOWNLOAD_PROGRESS" = "1" ] || [ "$SHOW_DOWNLOAD_PROGRESS" = "true" ]; then
TEMP_LOG="/tmp/curl_log_$$"
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - 0%】"
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - 0%】..."
(
if [ -n "$SECRET_KEY" ] && [ -n "$ETAG_HEADER" ]; then
@@ -101,7 +101,7 @@ EOF
if [ -n "$PROGRESS" ] && [ "$PROGRESS" -ne "$LAST_PROGRESS" ]; then
if [ "$PROGRESS" -gt "$LAST_PROGRESS" ]; then
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - ${PROGRESS}%】"
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - ${PROGRESS}%】..."
LAST_PROGRESS="$PROGRESS"
fi
fi
@@ -114,7 +114,7 @@ EOF
HTTP_CODE=$(grep -i "^HTTP" "$HEADER_TMP" 2>/dev/null | tail -1 | cut -d' ' -f2)
if [ "$EXIR_CODE" -eq 0 ] && [ "$LAST_PROGRESS" -ne 100 ]; then
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - 100%】"
LOG_OUT "Downloading:【$(basename "$DOWNLOAD_PATH") - 100%】..."
fi
if [ "$EXIR_CODE" -ne 0 ]; then
@@ -132,7 +132,6 @@ EOF
OUTPUT=$(DOWNLOAD_FAILURE_OUTPUT "$EXIR_CODE" "$HTTP_CODE" "${OUTPUT:-}")
LOG_OUT "【${DOWNLOAD_PATH}】Download Failed:【${OUTPUT}】"
rm -f "$HEADER_TMP" "$DOWNLOAD_TMP"
SLOG_CLEAN
return 1
fi
else
@@ -188,7 +187,6 @@ EOF
OUTPUT=$(DOWNLOAD_FAILURE_OUTPUT "$EXIR_CODE" "$HTTP_CODE" "$OUTPUT")
LOG_OUT "【${DOWNLOAD_PATH}】Download Failed:【${OUTPUT}】"
rm -f "$HEADER_TMP" "$DOWNLOAD_TMP"
SLOG_CLEAN
return 1
fi
fi
@@ -196,7 +194,6 @@ EOF
if ! mv -f "$DOWNLOAD_TMP" "$DOWNLOAD_PATH"; then
LOG_OUT "【${DOWNLOAD_PATH}】Download Failed:【Unable to save download file】"
rm -f "$HEADER_TMP" "$DOWNLOAD_TMP"
SLOG_CLEAN
return 1
fi
NEW_ETAG=$(grep -i "^etag:" "$HEADER_TMP" 2>/dev/null | tail -1 | cut -d' ' -f2- | tr -d '\r\n' | sed 's/^"//;s/"$//')
@@ -794,7 +794,9 @@ EOF
sed -i -E 's/(([0-9]{1,3}\.){2})[0-9]{1,3}\.[0-9]{1,3}/\1*\.*/g' "$DEBUG_LOG" 2>/dev/null
sed -i -E 's/(:[0-9a-fA-F]{1,4}){3}/:*:*:*/' "$DEBUG_LOG" 2>/dev/null
sed -i -E 's/::[0-9a-fA-F]{1,4}:(([0-9]{1,3}\.){3}[0-9]{1,3})/::*:\1/g' "$DEBUG_LOG" 2>/dev/null
sed -i -E 's/([0-9a-fA-F]{1,4}):[0-9a-fA-F]{1,4}:[0-9a-fA-F]{1,4}:[0-9a-fA-F]{1,4}:[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){3}/\1:*:*:*:*\2/g' "$DEBUG_LOG" 2>/dev/null
sed -i -E 's/([0-9a-fA-F]{1,4})(:[0-9a-fA-F]{1,4}){0,5}::([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4})*)?/\1:*::*/g' "$DEBUG_LOG" 2>/dev/null
sed -i 's/Downloading URL【[^】]*】/Downloading URL【*】/g' "$DEBUG_LOG" 2>/dev/null
@@ -9,16 +9,6 @@ local json = require "luci.jsonc"
local datatype = require "luci.cbi.datatypes"
local addr = arg[1]
local function s(e)
local t=0
local a={' KB',' MB',' GB',' TB',' PB'}
repeat
e=e/1024
t=t+1
until(e<=1024)
return string.format("%.1f",e)..a[t]
end
local function debug_getcon()
local info, ip, host, diag_info
ip = fs.lanip()
@@ -30,6 +20,7 @@ local function debug_getcon()
info = json.parse(info)
end
if info then
local conn_lines = {}
for i = 1, #(info.connections) do
if info.connections[i].metadata.host == "" then
host = "Empty"
@@ -37,13 +28,20 @@ local function debug_getcon()
host = info.connections[i].metadata.host
end
if not addr then
luci.sys.exec(string.format('echo "%s. SourceIP:【%s】 - Host:【%s】 - DestinationIP:【%s】 - Network:【%s】 - RulePayload:【%s】 - Lastchain:【%s】" >> /tmp/openclash_debug.log', i, (info.connections[i].metadata.sourceIP), host, (info.connections[i].metadata.destinationIP), (info.connections[i].metadata.network), (info.connections[i].rulePayload),(info.connections[i].chains[1])))
conn_lines[#conn_lines + 1] = string.format("%d. SourceIP:【%s】 - Host:【%s】 - DestinationIP:【%s】 - Network:【%s】 - RulePayload:【%s】 - Lastchain:【%s】\n",
i,
tostring(info.connections[i].metadata.sourceIP),
tostring(host),
tostring(info.connections[i].metadata.destinationIP),
tostring(info.connections[i].metadata.network),
tostring(info.connections[i].rulePayload),
tostring(info.connections[i].chains and info.connections[i].chains[1]))
else
if datatype.hostname(addr) and string.lower(addr) == host or datatype.ipaddr(addr) and addr == (info.connections[i].metadata.destinationIP) then
print("id: "..(info.connections[i].id))
print("start: "..(info.connections[i].start))
print("download: "..s(info.connections[i].download))
print("upload: "..s(info.connections[i].upload))
print("download: "..fs.filesize(info.connections[i].download))
print("upload: "..fs.filesize(info.connections[i].upload))
print("rule: "..(info.connections[i].rule))
print("rulePayload: "..(info.connections[i].rulePayload))
print("chains: ")
@@ -62,6 +60,10 @@ local function debug_getcon()
end
end
end
if not addr and #conn_lines > 0 then
local existing = fs.readfile("/tmp/openclash_debug.log") or ""
fs.writefile("/tmp/openclash_debug.log", existing .. table.concat(conn_lines))
end
end
end
os.exit(0)
@@ -57,7 +57,7 @@ EOF
}
log_unzip_error() {
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Unzip Error!" && SLOG_CLEAN
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Unzip Error!"
cleanup_dashboard_tmp
del_lock
exit 2
@@ -121,7 +121,7 @@ EOF
fi
if mv "$NEW_FILE_DIR" "$TARGET_FILE_DIR" >/dev/null 2>&1 && validate_dashboard_dir "$TARGET_FILE_DIR"; then
cleanup_dashboard_tmp
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Successful!" && SLOG_CLEAN
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Successful!"
del_lock
exit 0
else
@@ -137,7 +137,7 @@ EOF
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
if validate_dashboard_dir "$UNPACK_FILE_DIR"; then
cleanup_dashboard_tmp
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Successful!" && SLOG_CLEAN
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Successful!"
del_lock
exit 0
else
@@ -145,7 +145,7 @@ EOF
fi
else
cleanup_dashboard_tmp
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Error!" && SLOG_CLEAN
LOG_OUT "Control Panel【$DASH_NAME - $DASH_TYPE】Download Error!"
del_lock
exit 1
fi
@@ -174,6 +174,5 @@ case "$1" in
;;
esac
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -56,6 +56,5 @@ fi
rm -rf /tmp/Model.bin >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -14,23 +14,29 @@ del_lock() {
rm -rf "/tmp/lock/openclash_update.lock" 2>/dev/null
}
early_exit() {
dec_job_counter_and_restart "0"
del_lock
exit "${1:-0}"
}
set_lock
inc_job_counter
if [ -n "$1" ] && [ "$1" != "one_key_update" ]; then
/usr/share/openclash/openclash_version.sh "$1" 2>/dev/null
elif [ -n "$2" ]; then
/usr/share/openclash/openclash_version.sh "$2" 2>/dev/null
else
/usr/share/openclash/openclash_version.sh 2>/dev/null
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
PLUGIN_DIRECT=0
if [ -n "$3" ] && echo "$3" | grep -qE '^https?://'; then
PLUGIN_DIRECT=1
fi
if [ ! -f "/tmp/openclash_last_version" ]; then
lua /usr/share/openclash/openclash_version.lua "$2" 2>/dev/null
PLUGIN_LATEST=$(jsonfilter -i /tmp/openclash_version_history.json -e "@.${RELEASE_BRANCH}.latest.plugin" 2>/dev/null)
if [ "$PLUGIN_DIRECT" -eq 0 ] && [ -z "$PLUGIN_LATEST" ]; then
LOG_ERROR "Failed to get version information, please try again later..."
SLOG_CLEAN
dec_job_counter_and_restart "0"
del_lock
exit 0
early_exit
fi
version_compare() {
@@ -59,7 +65,7 @@ run_with_timeout() {
(
sleep "$timeout_sec"
kill $_pid 2>/dev/null
sleep 0.5
sleep 1
kill -9 $_pid 2>/dev/null
) &
local _watchdog=$!
@@ -70,36 +76,65 @@ run_with_timeout() {
return $_ret
}
LAST_OPVER="/tmp/openclash_last_version"
LAST_VER=$(sed -n 1p "$LAST_OPVER" 2>/dev/null |sed "s/^v//g" |tr -d "\n")
LAST_VER=$(echo "$PLUGIN_LATEST" |sed "s/^v//g" |tr -d "\n")
if [ -x "/bin/opkg" ]; then
OP_CV=$(rm -f /var/lock/opkg.lock && opkg status luci-app-openclash 2>/dev/null |grep 'Version' |awk -F 'Version: ' '{print $2}' 2>/dev/null)
elif [ -x "/usr/bin/apk" ]; then
OP_CV=$(rm -f /lib/apk/db/lock && apk list luci-app-openclash 2>/dev/null|grep "installed" | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
fi
OP_LV=$(sed -n 1p "$LAST_OPVER" 2>/dev/null |sed "s/^v//g" |tr -d "\n")
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
OP_LV="$LAST_VER"
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
#一键更新
if [ "$1" = "one_key_update" ]; then
if [ "$github_address_mod" = "0" ] && [ -z "$2" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
fi
if [ -n "$2" ]; then
/usr/share/openclash/openclash_core.sh "Meta" "$1" "$2" >/dev/null 2>&1
github_address_mod="$2"
else
/usr/share/openclash/openclash_core.sh "Meta" "$1" >/dev/null 2>&1
github_address_mod=0
fi
if [ "$PLUGIN_DIRECT" -eq 0 ]; then
if [ "$github_address_mod" = "0" ] && [ -z "$2" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - GitHub Address Proxy Options"
fi
if [ -n "$2" ]; then
github_address_mod="$2"
else
github_address_mod=0
fi
if echo "$github_address_mod" | grep -q "raw\.githubusercontent\.com"; then
github_address_mod=0
fi
fi
elif [ "$1" = "plugin_update" ]; then
if [ "$PLUGIN_DIRECT" -eq 0 ]; then
if [ "$github_address_mod" = "0" ] && [ -z "$2" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - GitHub Address Proxy Options"
fi
if [ -n "$2" ]; then
github_address_mod="$2"
fi
if echo "$github_address_mod" | grep -q "raw\.githubusercontent\.com"; then
github_address_mod=0
fi
fi
else
if [ "$github_address_mod" = "0" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - GitHub Address Proxy Options"
fi
fi
if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [ -f "$LAST_OPVER" ]; then
if [ "$PLUGIN_DIRECT" -eq 1 ]; then
# Direct download URL (historical version / downgrade) — skip version compare
if [ -x "/bin/opkg" ]; then
DOWNLOAD_URL="$3"
DOWNLOAD_PATH="/tmp/openclash.ipk"
elif [ -x "/usr/bin/apk" ]; then
DOWNLOAD_URL="$3"
DOWNLOAD_PATH="/tmp/openclash.apk"
fi
LAST_VER=$(echo "$DOWNLOAD_URL" | grep -oE 'luci-app-openclash[_-][0-9]+(\.[0-9]+)*' | head -1 | sed 's/^luci-app-openclash[_-]//')
LOG_TIP "Start downloading【OpenClash - v$LAST_VER】..."
elif [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [ -n "$PLUGIN_LATEST" ]; then
LOG_TIP "Start downloading【OpenClash - v$LAST_VER】..."
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
@@ -128,6 +163,16 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
DOWNLOAD_PATH="/tmp/openclash.apk"
fi
fi
else
if [ -z "$PLUGIN_LATEST" ] || [ -z "$OP_CV" ] || [ -z "$OP_LV" ]; then
LOG_ERROR "Failed to get version information, please try again later..."
else
LOG_TIP "OpenClash has not been updated, stop continuing!"
fi
early_exit
fi
if [ -n "$DOWNLOAD_URL" ]; then
retry_count=0
max_retries=3
@@ -154,22 +199,25 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
max_update_retry=2
while [ $update_retry -lt $max_update_retry ]; do
update_retry=$((update_retry + 1))
run_with_timeout 30 opkg update >/dev/null 2>&1
opkg_update_err=$(run_with_timeout 30 opkg update 2>&1)
opkg_ret=$?
rm -f /var/lock/opkg.lock
if [ $opkg_ret -eq 0 ]; then
break
fi
if [ $update_retry -lt $max_update_retry ]; then
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】opkg update failed or timed out, retrying..."
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】opkg update failed or timed out:【$(echo "$opkg_update_err" | tr '\n' ' ' | head -c 300)】, retrying..."
sleep 2
else
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】opkg update failed, trying pre update test..."
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】opkg update failed:【$(echo "$opkg_update_err" | tr '\n' ' ' | head -c 300)】, trying pre update test..."
fi
done
if [ -s "/tmp/openclash.ipk" ]; then
if [ -n "$(opkg install /tmp/openclash.ipk --noaction 2>/dev/null |grep 'Upgrading luci-app-openclash on root' 2>/dev/null)" ]; then
opkg_test_err=$(opkg --force-downgrade install /tmp/openclash.ipk --noaction 2>&1)
if [ -n "$(echo "$opkg_test_err" | grep -E '(Upgrading|Downgrading) luci-app-openclash on root')" ]; then
pre_test_success=true
else
LOG_ERROR "【$retry_count/$max_retries】【OpenClash - v$LAST_VER】Pre update test failed:【$(echo "$opkg_test_err" | tr '\n' ' ' | head -c 500)】..."
fi
fi
elif [ -x "/usr/bin/apk" ]; then
@@ -177,23 +225,25 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
max_update_retry=2
while [ $update_retry -lt $max_update_retry ]; do
update_retry=$((update_retry + 1))
run_with_timeout 30 apk update >/dev/null 2>&1
apk_update_err=$(run_with_timeout 30 apk update 2>&1)
apk_ret=$?
rm -f /lib/apk/db/lock
if [ $apk_ret -eq 0 ]; then
break
fi
if [ $update_retry -lt $max_update_retry ]; then
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】apk update failed or timed out, retrying..."
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】apk update failed or timed out:【$(echo "$apk_update_err" | tr '\n' ' ' | head -c 300)】, retrying..."
sleep 2
else
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】apk update failed, trying pre update test..."
LOG_ERROR "【$update_retry/$max_update_retry】【OpenClash - v$LAST_VER】apk update failed:【$(echo "$apk_update_err" | tr '\n' ' ' | head -c 300)】, trying pre update test..."
fi
done
if [ -s "/tmp/openclash.apk" ]; then
apk add -s -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk >/dev/null 2>&1
apk_test_err=$(apk add -s -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk 2>&1)
if [ $? -eq 0 ]; then
pre_test_success=true
else
LOG_ERROR "【$retry_count/$max_retries】【OpenClash - v$LAST_VER】Pre update test failed:【$(echo "$apk_test_err" | tr '\n' ' ' | head -c 500)】..."
fi
fi
fi
@@ -208,14 +258,11 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
continue
else
if [ -x "/bin/opkg" ]; then
LOG_ERROR "【OpenClash - v$LAST_VER】Pre update test failed after 3 attempts, the file is saved in /tmp/openclash.ipk, please try to update manually with【opkg install /tmp/openclash.ipk】"
LOG_ERROR "【OpenClash - v$LAST_VER】Pre update test failed after 3 attempts, the file is saved in /tmp/openclash.ipk, please try to update manually with【opkg --force-downgrade install /tmp/openclash.ipk】"
elif [ -x "/usr/bin/apk" ]; then
LOG_ERROR "【OpenClash - v$LAST_VER】Pre update test failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】"
fi
SLOG_CLEAN
dec_job_counter_and_restart "0"
del_lock
exit 0
early_exit
fi
fi
else
@@ -227,10 +274,7 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
LOG_ERROR "【OpenClash - v$LAST_VER】Download Failed after 3 attempts, please check the network or try again later!"
rm -rf /tmp/openclash.ipk >/dev/null 2>&1
rm -rf /tmp/openclash.apk >/dev/null 2>&1
dec_job_counter_and_restart "0"
SLOG_CLEAN
del_lock
exit 0
early_exit
fi
fi
done
@@ -299,9 +343,9 @@ install_missing_packages() {
while [ $retry_count -lt $max_retries ]; do
retry_count=$((retry_count + 1))
if [ -x "/bin/opkg" ]; then
opkg install "$pkg"
pkg_err=$(opkg install "$pkg" 2>&1)
elif [ -x "/usr/bin/apk" ]; then
apk add "$pkg"
pkg_err=$(apk add "$pkg" 2>&1)
fi
if [ $? -eq 0 ]; then
break
@@ -309,7 +353,7 @@ install_missing_packages() {
if [ $retry_count -lt $max_retries ]; then
sleep 2
else
LOG_ERROR "【$pkg】failed to install, please try to install it manually..."
LOG_ERROR "【$pkg】failed to install:【$(echo "$pkg_err" | tr '\n' ' ' | head -c 300)】, please try to install it manually..."
fi
fi
done
@@ -333,14 +377,16 @@ while [ $install_retry_count -lt $max_install_retries ]; do
installed_before="$installed_before $pkg"
fi
done
opkg install /tmp/openclash.ipk
opkg --force-downgrade install /tmp/openclash.ipk >/dev/null 2>&1
install_err=$(cat /tmp/openclash_install.log 2>/dev/null)
elif [ -x "/usr/bin/apk" ]; then
for pkg in $packages_to_check; do
if apk list "$pkg" |grep "installed" >/dev/null 2>&1; then
installed_before="$installed_before $pkg"
fi
done
apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk
apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk >/tmp/openclash_install.log 2>&1
install_err=$(cat /tmp/openclash_install.log 2>/dev/null)
fi
sleep 2
@@ -350,7 +396,7 @@ while [ $install_retry_count -lt $max_install_retries ]; do
install_missing_packages "$installed_before"
break
else
LOG_ERROR "【$install_retry_count/$max_install_retries】Installation failed..."
LOG_ERROR "【$install_retry_count/$max_install_retries】Installation failed:【$(echo "$install_err" | tr '\n' ' ' | head -c 500)】..."
if [ $install_retry_count -lt $max_install_retries ]; then
sleep 3
fi
@@ -365,13 +411,12 @@ if [ "$install_success" = true ]; then
fi
else
if [ -x "/bin/opkg" ]; then
LOG_ERROR "OpenClash update failed after 3 attempts, the file is saved in /tmp/openclash.ipk, please try to update manually with【opkg install /tmp/openclash.ipk】"
LOG_ERROR "OpenClash update failed after 3 attempts, the file is saved in /tmp/openclash.ipk, please try to update manually with【opkg --force-downgrade install /tmp/openclash.ipk】【$(echo "$install_err" | tr '\n' ' ' | head -c 500)】"
elif [ -x "/usr/bin/apk" ]; then
LOG_ERROR "OpenClash update failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】"
LOG_ERROR "OpenClash update failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】【$(echo "$install_err" | tr '\n' ' ' | head -c 500)】"
fi
fi
dec_job_counter_and_restart "0"
SLOG_CLEAN
del_update_lock
EOF
chmod 4755 /tmp/openclash_update.sh
@@ -379,10 +424,7 @@ EOF
if [ ! -f "/tmp/openclash_update.sh" ] || [ ! -s "/tmp/openclash_update.sh" ] || [ ! -x "/tmp/openclash_update.sh" ]; then
LOG_ERROR "Failed to create update script!"
rm -rf /tmp/openclash_update.sh
dec_job_counter_and_restart "0"
SLOG_CLEAN
del_lock
exit 1
early_exit 1
fi
retry_count=0
@@ -413,14 +455,6 @@ EOF
fi
(sleep 15; rm -f /tmp/openclash_update.sh) &
else
if [ ! -f "$LAST_OPVER" ] || [ -z "$OP_CV" ] || [ -z "$OP_LV" ]; then
LOG_ERROR "Failed to get version information, please try again later..."
else
LOG_TIP "OpenClash has not been updated, stop continuing!"
fi
dec_job_counter_and_restart "0"
SLOG_CLEAN
fi
del_lock
@@ -0,0 +1,644 @@
#!/usr/bin/lua
require "nixio"
require "luci.model.uci"
local fs = require "luci.openclash"
local json = require "luci.jsonc"
local M = {}
local VERSION_CACHE_FILE = "/tmp/openclash_version_history.json"
local CDN_CACHE_FILE = "/tmp/openclash_cdn_info.json"
local function trim(s)
if not s then return "" end
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end
function M.is_valid_version(s)
if not s or s == "" then return false end
s = trim(s)
if s == "" then return false end
if s:match("^<") then return false end
return true
end
function M.is_oix_mode()
local oix_token = fs.uci_get_config("config", "oix_token") or ""
return oix_token ~= ""
end
function try_read(fd, maxlen)
local pfds = {
{ fd = fd, events = nixio.poll_flags("in", "hup", "err") }
}
local nfds = nixio.poll(pfds, 0)
if nfds and nfds > 0 then
local buf = fd:read(maxlen or 4096)
if buf and #buf > 0 then
return buf
end
end
return nil
end
local function read_json(path)
if not fs.access(path) then return nil end
local raw = fs.readfile(path)
if not raw or raw == "" then return nil end
local ok, parsed = pcall(json.parse, raw)
if ok and parsed and type(parsed) == "table" then
return parsed
end
return nil
end
local function write_json(path, data)
local tmp = path .. ".tmp"
fs.writefile(tmp, json.stringify(data))
os.rename(tmp, path)
end
local function read_version_cache()
return read_json(VERSION_CACHE_FILE)
end
local function update_version_cache(updater)
local parsed = read_version_cache() or {}
updater(parsed)
write_json(VERSION_CACHE_FILE, parsed)
end
local DEFAULT_CDN_LIST = {
"https://ghfast.top/",
"https://github.dpik.top/",
"https://gh-proxy.com/",
"https://git.yylx.win/"
}
local function cdn_list()
local list = fs.cdn_list()
if #list > 0 then return list end
return DEFAULT_CDN_LIST
end
local function raw_url(path)
return "https://raw.githubusercontent.com/vernesong/OpenClash/" .. path
end
local function build_fetch_urls(mod, path)
if mod == "0" or mod == "" then
local urls = { raw_url(path) }
for _, cdn in ipairs(cdn_list()) do
urls[#urls + 1] = cdn .. raw_url(path)
end
return urls
end
if mod == "https://cdn.jsdelivr.net/" or mod == "https://fastly.jsdelivr.net/" or mod == "https://testingcf.jsdelivr.net/" then
return { mod .. "gh/vernesong/OpenClash@" .. path }
end
return { mod .. raw_url(path) }
end
local function fork_curl(url, timeout)
local fdi, fdo = nixio.pipe()
if not fdi or not fdo then return nil end
local cmd = string.format('curl -sL -m %d "%s" 2>/dev/null', timeout or 5, url)
local child = nixio.fork()
if child > 0 then
fdo:close()
return { pid = child, fdi = fdi, buf = "", done = false }
elseif child == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exec("/bin/sh", "-c", cmd)
else
if fdi then fdi:close() end
if fdo then fdo:close() end
return nil
end
end
local MAX_URL_BATCH = 3
local function try_fetch(urls, validator)
if not urls or #urls == 0 then return "" end
local timeout = (#urls > 1) and 5 or 10
local delay = 50000000
local max_wait = 100
local jobs = {}
local active = 0
local idx = 1
local function launch_batch()
local n = 0
while idx <= #urls and n < MAX_URL_BATCH do
local job = fork_curl(urls[idx], timeout)
if job then
jobs[#jobs + 1] = job
active = active + 1
end
idx = idx + 1
n = n + 1
end
end
launch_batch()
for _ = 1, max_wait do
local winner = ""
for _, job in ipairs(jobs) do
if not job.done then
local ok_r, buf = pcall(try_read, job.fdi, 4096)
if ok_r and buf then job.buf = job.buf .. buf end
local ok_w, wpid = pcall(nixio.waitpid, job.pid, "nohang")
if ok_w and wpid then
while true do
local ok_b, b = pcall(try_read, job.fdi, 4096)
if not ok_b or not b then break end
job.buf = job.buf .. b
end
pcall(job.fdi.close, job.fdi)
job.done = true
active = active - 1
if winner == "" and job.buf ~= "" and (not validator or validator(job.buf)) then
winner = job.buf
end
end
end
end
if winner ~= "" then
for _, job in ipairs(jobs) do
if not job.done then
pcall(job.fdi.close, job.fdi)
nixio.kill(job.pid, 9)
end
end
return trim(winner)
end
if active == 0 then
if idx <= #urls then
launch_batch()
else
break
end
end
nixio.nanosleep(0, delay)
delay = math.min(delay * 2, 200000000)
end
for _, job in ipairs(jobs) do
if not job.done then
pcall(job.fdi.close, job.fdi)
nixio.kill(job.pid, 9)
end
end
return ""
end
function M.prepare_oix_cdn_data(force)
if not M.is_oix_mode() then
return false, "", ""
end
local parsed = read_version_cache()
if parsed and parsed.oix and parsed.oix.cached_at then
local age = os.time() - parsed.oix.cached_at
local ttl = parsed.oix.cache_ttl or 300
if age < 5 or (not force and age < ttl) then
return true, parsed.oix.ver or "", parsed.oix.error or ""
end
end
local function fork_oix_version(url)
local fdi, fdo = nixio.pipe()
if not fdi or not fdo then return nil end
local cmd = string.format('curl -sL -m 3 --connect-timeout 3 "%s" 2>/dev/null', url)
local child = nixio.fork()
if child > 0 then
fdo:close()
return { pid = child, fdi = fdi, buf = "" }
elseif child == 0 then
nixio.dup(fdo, nixio.stdout)
fdi:close()
fdo:close()
nixio.exec("/bin/sh", "-c", cmd)
else
if fdi then fdi:close() end
if fdo then fdo:close() end
return nil
end
end
local jobs = {}
local dl_job = fork_oix_version("https://dl.dler.io/mihomo-oix/version.txt?tag=Pre-Alpha")
if dl_job then jobs[#jobs + 1] = dl_job end
local gh_job = fork_oix_version("https://github.com/vernesong/mihomo-oix/releases/download/Pre-Alpha/version.txt")
if gh_job then jobs[#jobs + 1] = gh_job end
if #jobs == 0 then
return true, "", "error"
end
local ver = ""
local delay = 50000000
local max_wait = 40
for _ = 1, max_wait do
for _, job in ipairs(jobs) do
if not job.done then
local ok_r, buf = pcall(try_read, job.fdi, 4096)
if ok_r and buf then job.buf = job.buf .. buf end
local ok_w, wpid = pcall(nixio.waitpid, job.pid, "nohang")
if ok_w and wpid then
while true do
local ok_b, b = pcall(try_read, job.fdi, 4096)
if not ok_b or not b then break end
job.buf = job.buf .. b
end
pcall(job.fdi.close, job.fdi)
job.done = true
end
end
end
for _, job in ipairs(jobs) do
if job.done and ver == "" then
local candidate = trim(job.buf:gsub("[\n\r]+", ""))
if M.is_valid_version(candidate) then
ver = candidate
end
end
end
if ver ~= "" then
for _, job in ipairs(jobs) do
if not job.done then
pcall(job.fdi.close, job.fdi)
nixio.kill(job.pid, 9)
end
end
update_version_cache(function(p) p.oix = { ver = ver, error = "", cache_ttl = 300, cached_at = os.time() } end)
return true, ver, ""
end
local all_done = true
for _, job in ipairs(jobs) do
if not job.done then all_done = false break end
end
if all_done then break end
nixio.nanosleep(0, delay)
delay = math.min(delay * 2, 200000000)
end
for _, job in ipairs(jobs) do
if not job.done then
pcall(job.fdi.close, job.fdi)
nixio.kill(job.pid, 9)
end
end
update_version_cache(function(p) p.oix = { ver = "", error = "error", cache_ttl = 5, cached_at = os.time() } end)
return true, "", "error"
end
local function fork_file_fetch(sha, file_path, mod)
local urls = build_fetch_urls(mod, sha .. "/" .. file_path)
if #urls == 0 then return nil end
return { children = {}, idx = 1, buf = "", done = false, launched = false, sha = sha, urls = urls }
end
local function launch_file_batch(job)
local timeout = (#job.urls > 1) and 5 or 10
local n = 0
while job.idx <= #job.urls and n < MAX_URL_BATCH do
local child = fork_curl(job.urls[job.idx], timeout)
if child then job.children[#job.children + 1] = child end
job.idx = job.idx + 1
n = n + 1
end
end
local function collect_fork_results(jobs, max_jobs)
local results = {}
local active = 0
local delay = 50000000
local max_iter = 250
for _ = 1, max_iter do
while active < max_jobs do
local launched = false
for _, job in ipairs(jobs) do
if not job.launched then
job.launched = true
active = active + 1
launched = true
launch_file_batch(job)
if #job.children == 0 then
job.done = true
active = active - 1
end
break
end
end
if not launched then break end
end
for _, job in ipairs(jobs) do
if job.launched and not job.done then
local winner = ""
local all_children_done = true
for _, child in ipairs(job.children) do
if not child.done then
all_children_done = false
local ok_r, buf = pcall(try_read, child.fdi, 4096)
if ok_r and buf then child.buf = child.buf .. buf end
local ok_w, wpid = pcall(nixio.waitpid, child.pid, "nohang")
if ok_w and wpid then
while true do
local ok_b, b = pcall(try_read, child.fdi, 4096)
if not ok_b or not b then break end
child.buf = child.buf .. b
end
pcall(child.fdi.close, child.fdi)
child.done = true
if winner == "" and child.buf ~= "" then
winner = child.buf
end
end
end
end
if winner ~= "" then
for _, child in ipairs(job.children) do
if not child.done then
pcall(child.fdi.close, child.fdi)
nixio.kill(child.pid, 9)
end
end
job.done = true
active = active - 1
results[job.sha] = trim(winner)
elseif all_children_done then
if job.idx <= #job.urls then
launch_file_batch(job)
else
job.done = true
active = active - 1
end
end
end
end
local all_done = true
for _, job in ipairs(jobs) do
if not job.done then all_done = false break end
end
if all_done then break end
nixio.nanosleep(0, delay)
delay = math.min(delay * 2, 200000000)
end
for _, job in ipairs(jobs) do
if not job.done then
if job.children then
for _, child in ipairs(job.children) do
if not child.done then
pcall(child.fdi.close, child.fdi)
nixio.kill(child.pid, 9)
end
end
end
job.done = true
end
end
return results
end
local function html_unescape(s)
if not s then return "" end
s = s:gsub("&amp;", "&")
s = s:gsub("&lt;", "<")
s = s:gsub("&gt;", ">")
s = s:gsub("&quot;", '"')
s = s:gsub("&#39;", "'")
s = s:gsub("&#x27;", "'")
return s
end
local function build_feed_urls(mod, path)
local feed = "https://github.com/vernesong/OpenClash/commits/" .. path .. ".atom"
if mod == "0" or mod == "" then
local urls = { feed }
for _, cdn in ipairs(cdn_list()) do
urls[#urls + 1] = cdn .. feed
end
return urls
end
if mod == "https://cdn.jsdelivr.net/" or mod == "https://fastly.jsdelivr.net/" or mod == "https://testingcf.jsdelivr.net/" then
return { feed }
end
return { mod .. feed, feed }
end
local function fetch_commit_feed(mod, path)
return try_fetch(build_feed_urls(mod, path), function(buf)
return buf:match("<entry") ~= nil
end)
end
local function parse_commit_feed(raw, max_count)
local commits = {}
if raw and raw ~= "" then
local n = 0
for entry in raw:gmatch("<entry>(.-)</entry>") do
n = n + 1
if n > max_count then break end
local sha = entry:match("<id>[^<]*/([0-9a-f]+)</id>")
if sha then
local date = entry:match("<updated>(.-)</updated>") or ""
local title = entry:match("<title>(.-)</title>") or ""
title = trim(title)
commits[#commits + 1] = { sha = sha, date = date, message = html_unescape(title) }
end
end
end
return commits
end
function M.fetch_version_history(branch, force, cdn, latest_only)
local result = { plugin = {}, core_meta = {}, core_smart = {}, latest = nil, error = nil, oix_ver = "" }
local cur_oix = M.is_oix_mode()
local skip_core = cur_oix
local github_address_mod = fs.uci_get_config("config", "github_address_mod") or "0"
if cdn and cdn ~= "" then
github_address_mod = cdn
if github_address_mod:match("raw%.githubusercontent%.com") then
github_address_mod = "0"
end
end
if not force then
local parsed = read_version_cache()
if parsed and parsed[branch] and parsed[branch].cached_at then
local blk = parsed[branch]
local ttl = blk.cache_ttl or 300
if os.time() - blk.cached_at < ttl and blk.oix == cur_oix then
if latest_only then
local lt = blk.latest
if lt and lt.plugin and lt.plugin ~= "" and (cur_oix or (lt.core_meta and lt.core_meta ~= "")) then
return blk
end
elseif blk.plugin and #blk.plugin > 0 then
return blk
end
end
end
end
if latest_only then
local plugin_latest = ""
local core_meta_latest = ""
local core_smart_latest = ""
local plugin_raw = try_fetch(build_fetch_urls(github_address_mod, "package/" .. branch .. "/version"))
if plugin_raw and plugin_raw ~= "" then
plugin_latest = trim(plugin_raw:match("^[^\n\r]*") or "")
end
if not cur_oix then
local core_raw = try_fetch(build_fetch_urls(github_address_mod, "core/" .. branch .. "/core_version"))
if core_raw and core_raw ~= "" then
core_meta_latest = trim(core_raw:match("^[^\n\r]*") or "")
local after = core_raw:match("[\n\r]+(.*)")
if after then
core_smart_latest = trim(after:match("^[^\n\r]*") or "")
end
end
end
result.latest = { plugin = plugin_latest, core_meta = core_meta_latest, core_smart = core_smart_latest }
if plugin_latest == "" then
result.error = "network_error"
end
if cur_oix then
M.prepare_oix_cdn_data(force)
end
result.cache_ttl = (plugin_latest == "") and 5 or 300
result.cached_at = os.time()
result.oix = cur_oix
update_version_cache(function(parsed)
local blk = parsed[branch] or {}
blk.latest = result.latest
blk.error = result.error
blk.cache_ttl = result.cache_ttl
blk.cached_at = result.cached_at
blk.oix = cur_oix
blk.plugin = nil
blk.core_meta = nil
blk.core_smart = nil
blk.oix_ver = nil
parsed[branch] = blk
end)
return result
end
local plugin_feed = fetch_commit_feed(github_address_mod, "package/" .. branch .. "/version")
local plugin_commits = parse_commit_feed(plugin_feed, 5)
if #plugin_commits > 0 then
local file_jobs = {}
for _, c in ipairs(plugin_commits) do
local job = fork_file_fetch(c.sha, branch .. "/version", github_address_mod)
if job then file_jobs[#file_jobs + 1] = job end
end
local file_results = collect_fork_results(file_jobs, 2)
for _, c in ipairs(plugin_commits) do
local raw = file_results[c.sha]
local ver
if raw and raw ~= "" then
ver = trim(raw:match("^[^\n\r]*") or "")
if not M.is_valid_version(ver) then ver = nil end
end
result.plugin[#result.plugin + 1] = {
version = ver,
date = c.date,
sha = c.sha,
message = c.message
}
end
end
if not skip_core then
local core_feed = fetch_commit_feed(github_address_mod, "core/" .. branch .. "/core_version")
local core_commits = parse_commit_feed(core_feed, 5)
if #core_commits > 0 then
local file_jobs = {}
for _, c in ipairs(core_commits) do
local job = fork_file_fetch(c.sha, branch .. "/core_version", github_address_mod)
if job then file_jobs[#file_jobs + 1] = job end
end
local file_results = collect_fork_results(file_jobs, 2)
for _, c in ipairs(core_commits) do
local content = file_results[c.sha]
if content and content ~= "" then
local meta_ver = content:match("^[^\n\r]*")
local after_first = content:match("[\n\r]+(.*)")
local smart_ver = after_first and after_first:match("^[^\n\r]*") or nil
if meta_ver then
meta_ver = trim(meta_ver)
if M.is_valid_version(meta_ver) then
result.core_meta[#result.core_meta + 1] = { version = meta_ver, date = c.date, sha = c.sha }
end
end
if smart_ver then
smart_ver = trim(smart_ver)
if M.is_valid_version(smart_ver) then
result.core_smart[#result.core_smart + 1] = { version = smart_ver, date = c.date, sha = c.sha }
end
end
end
end
end
end
result.latest = {
plugin = (result.plugin[1] and result.plugin[1].version) or "",
core_meta = (result.core_meta[1] and result.core_meta[1].version) or "",
core_smart = (result.core_smart[1] and result.core_smart[1].version) or ""
}
if cur_oix then
local _, oix_ver = M.prepare_oix_cdn_data(force)
if oix_ver and oix_ver ~= "" then
result.oix_ver = oix_ver
end
end
local cache_ttl = 300
if #result.plugin == 0 then
if not plugin_feed or plugin_feed == "" then
result.error = "network_error"
cache_ttl = 5
else
result.error = "parse_error"
cache_ttl = 5
end
end
result.cache_ttl = cache_ttl
result.cached_at = os.time()
result.oix = cur_oix
update_version_cache(function(parsed)
parsed[branch] = result
end)
return result
end
if arg and arg[0] and arg[0]:match("openclash_version%.lua$") then
local branch = fs.uci_get_config("config", "release_branch") or "master"
M.fetch_version_history(branch, arg[2], arg[1], arg[3] ~= "0")
end
return M
@@ -1,67 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 869>"/tmp/lock/openclash_version.lock" 2>/dev/null
flock -x 869 2>/dev/null
}
del_lock() {
flock -u 869 2>/dev/null
rm -rf "/tmp/lock/openclash_version.lock" 2>/dev/null
}
set_lock
version_compare() {
local current_ver="$1"
local latest_ver="$2"
if echo "1.0.0" | sort -V >/dev/null 2>&1; then
if [ "$(printf '%s\n%s\n' "$current_ver" "$latest_ver" | sort -V | head -n1)" = "$current_ver" ] && [ "$current_ver" != "$latest_ver" ]; then
return 0
fi
else
local cv_num=$(echo "$current_ver" | awk -F '.' '{print $2$3}' 2>/dev/null)
local lv_num=$(echo "$latest_ver" | awk -F '.' '{print $2$3}' 2>/dev/null)
if [ -n "$cv_num" ] && [ -n "$lv_num" ] && [ "$(expr "$lv_num" \> "$cv_num")" -eq 1 ]; then
return 0
fi
fi
return 1
}
DOWNLOAD_FILE="/tmp/openclash_last_version"
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
if [ -x "/bin/opkg" ]; then
OP_CV=$(rm -f /var/lock/opkg.lock && opkg status luci-app-openclash 2>/dev/null |grep 'Version' |awk -F 'Version: ' '{print $2}' 2>/dev/null)
elif [ -x "/usr/bin/apk" ]; then
OP_CV=$(rm -f /lib/apk/db/lock && apk list luci-app-openclash 2>/dev/null|grep 'installed' | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
fi
OP_LV=$(sed -n 1p "$DOWNLOAD_FILE" 2>/dev/null |sed "s/^v//g" |tr -d "\n")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
if [ -n "$1" ]; then
github_address_mod="$1"
fi
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/vernesong/OpenClash@package/${RELEASE_BRANCH}/version"
else
DOWNLOAD_URL="${github_address_mod}https://raw.githubusercontent.com/vernesong/OpenClash/package/${RELEASE_BRANCH}/version"
fi
else
DOWNLOAD_URL="https://raw.githubusercontent.com/vernesong/OpenClash/package/${RELEASE_BRANCH}/version"
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "$DOWNLOAD_FILE" "$DOWNLOAD_FILE"
if [ "$?" -eq 0 ]; then
OP_LV=$(sed -n 1p $DOWNLOAD_FILE 2>/dev/null |awk -F 'v' '{print $2}' |awk -F '.' '{print $2$3}' 2>/dev/null)
if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [ -f "$DOWNLOAD_FILE" ]; then
sed -i '/^https:/,$d' $DOWNLOAD_FILE
fi
fi
del_lock
@@ -244,7 +244,7 @@ fi
LOG_WATCHDOG "Setting Firewall For Rules Order..."
/etc/init.d/openclash reload "firewall"
let FIREWALL_RELOAD++
elif [ -n "$(ip tuntap list |grep utun)" ] && [ -z "$(ip route list table 354)" ]; then
elif [ -n "$(ip link show utun 2>/dev/null)" ] && [ -z "$(ip route list table 354)" ]; then
## 路由表检查
LOG_WATCHDOG "Setting Firewall For IP Rules Table Recreate..."
/etc/init.d/openclash reload "firewall"
@@ -470,6 +470,5 @@ fi
LOG_ERROR "Streaming Unlock Could not Work Because of Router-Self Proxy Disabled, Exiting..."
fi
SLOG_CLEAN
sleep 60
done 2>/dev/null
@@ -0,0 +1,13 @@
# CDN proxy address list (one URL per line, "#" starts a comment)
# Comment out a line with "#" to temporarily disable it
https://ghfast.top/
https://git.yylx.win/
https://cdn.akaere.online/
https://gh-proxy.com/
https://gh.jasonzeng.dev/
https://ghproxy.monkeyray.net/
https://down.mxw.xx.kg/
https://github.tbap.top/
https://ghm.078465.xyz/
https://gh.monlor.com/
https://gh.idayer.com/
@@ -35,6 +35,10 @@ ACL4SSR 规则 WithChinaIp WithGFW,ACL4SSR_WithChinaIp_WithGFW.ini,https://raw.g
ACL4SSR 规则 WithGFW,ACL4SSR_WithGFW.ini,https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_WithGFW.ini
eHpo1 规则,ehpo1_main.ini,https://gist.githubusercontent.com/tindy2013/1fa08640a9088ac8652dbd40c5d2715b/raw/ehpo1_main.ini
Aethersailor 规则 标准版 Custom_Clash,Custom_Clash.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash.ini
Aethersailor 规则 标准版 故障转移,Custom_Clash_Fallback.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_Fallback.ini
Aethersailor 规则 轻量版 Custom_Clash_Lite,Custom_Clash_Lite.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_Lite.ini
Aethersailor 规则 轻量版 故障转移,Custom_Clash_Lite_Fallback.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_Lite_Fallback.ini
Aethersailor 规则 极简版(GFW) Custom_Clash_GFW,Custom_Clash_GFW.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_GFW.ini
Aethersailor 规则 极简版(GFW) 故障转移,Custom_Clash_GFW_Fallback.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_GFW_Fallback.ini
Aethersailor 规则 重度分流版 Custom_Clash_Full,Custom_Clash_Full.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_Full.ini
Aethersailor 规则 重度分流版 故障转移,Custom_Clash_Full_Fallback.ini,https://raw.githubusercontent.com/Aethersailor/Custom_OpenClash_Rules/refs/heads/main/cfg/Custom_Clash_Full_Fallback.ini
@@ -92,7 +92,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); Value$2=$3; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); Value$2=$3; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -107,7 +107,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not '$4'.empty? then Value$2=Value_1['$4']; else Value$2=Value_1 end else if not '$4'.empty? then Value.delete('$4'); end; end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not '$4'.empty? then Value$2=Value_1['$4']; else Value$2=Value_1 end else if not '$4'.empty? then Value.delete('$4'); end; end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -122,7 +122,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not Value$2 then Value$2 = {}; end; if Value_1$4 && Value_1$4.is_a?(Hash) then Value$2.merge!(Value_1$4) end end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not Value$2 then Value$2 = {}; end; if Value_1$4 && Value_1$4.is_a?(Hash) then Value$2.merge!(Value_1$4) end end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -137,7 +137,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 then Value$2=Value$2.uniq; end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 then Value$2=Value$2.uniq; end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -152,7 +152,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 then Value$2 = {}; end; Value$2.merge!({$3}); File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 then Value$2 = {}; end; Value$2.merge!({$3}); YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -167,7 +167,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$4') then Value_1 = YAML.load_file('$4'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if Value_1$5 && Value_1$5.is_a?(Array) then idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value_1$5.reverse.each{|x| Value$2.insert(idx,x); idx += 1}; Value$2=Value$2.uniq end end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$4') then Value_1 = YAML.load_file('$4'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if Value_1$5 && Value_1$5.is_a?(Array) then idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value_1$5.reverse.each{|x| Value$2.insert(idx,x); idx += 1}; Value$2=Value$2.uniq end end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -182,7 +182,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if Value_1$4 && Value_1$4.is_a?(Array) then Value$2=(Value_1$4+Value$2).uniq else Value$2=Value$2.uniq end end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if File::exist?('$3') then Value_1 = YAML.load_file('$3'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if Value_1$4 && Value_1$4.is_a?(Array) then Value$2=(Value_1$4+Value$2).uniq else Value$2=Value$2.uniq end end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -197,7 +197,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value$2=Value$2.insert(idx,'$4').uniq; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value$2=Value$2.insert(idx,'$4').uniq; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -212,7 +212,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value$2=Value$2.insert(idx,$4).uniq; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; Value$2=Value$2.insert(idx,$4).uniq; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -227,7 +227,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if $4.is_a?(Array) then idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; $4.reverse.each{|x| Value$2=Value$2.insert(idx,x); idx += 1}; Value$2=Value$2.uniq end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if not Value$2 or Value$2.nil? then Value$2 = []; end; if $4.is_a?(Array) then idx = [$3.to_i, 0].max; idx = [idx, Value$2.length].min; $4.reverse.each{|x| Value$2=Value$2.insert(idx,x); idx += 1}; Value$2=Value$2.uniq end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -247,9 +247,9 @@ if openclash_custom_overwrite; then
return
fi
if [ -z "$2" ]; then
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); Value.delete('$3'); File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); Value.delete('$3'); YAML.dump(Value, '$1')"
else
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 then if Value$2.is_a?(Hash) then Value$2.delete('$3') elsif Value$2.is_a?(Array) then Value$2.delete('$3') end end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 then if Value$2.is_a?(Hash) then Value$2.delete('$3') elsif Value$2.is_a?(Array) then Value$2.delete('$3') end end; YAML.dump(Value, '$1')"
fi
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -265,7 +265,7 @@ if openclash_custom_overwrite; then
write_ruby_part "$RUBY_YAML_PARSE"
return
fi
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Hash) then if Value$2['$3'] && Value$2['$3'].is_a?(Hash) then Value$2['$3']$4 = '$5' end end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Hash) then if Value$2['$3'] && Value$2['$3'].is_a?(Hash) then Value$2['$3']$4 = '$5' end end; YAML.dump(Value, '$1')"
run_ruby_part "$RUBY_YAML_PARSE"
}
@@ -288,9 +288,9 @@ if openclash_custom_overwrite; then
return
fi
if [ -n "$3" ] && [ -n "$4" ] && [ -n "$5" ] && [ -n "$6" ]; then
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Array) then Value$2.map!{|x| if x.is_a?(Hash) && x$3 == '$4' then x$5='$6' end; x}; Value$2.uniq! end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Array) then Value$2.map!{|x| if x.is_a?(Hash) && x$3 == '$4' then x$5='$6' end; x}; Value$2.uniq! end; YAML.dump(Value, '$1')"
elif [ -z "$3" ] && [ -n "$4" ] && [ -z "$5" ] && [ -n "$6" ]; then
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Array) then Value$2.map!{|x| if x == '$4' then '$6' else x end}; Value$2.uniq! end; File.open('$1','w') {|f| YAML.dump(Value, f)}"
RUBY_YAML_PARSE="Value = YAML.load_file('$1'); if Value$2 && Value$2.is_a?(Array) then Value$2.map!{|x| if x == '$4' then '$6' else x end}; Value$2.uniq! end; YAML.dump(Value, '$1')"
else
return
fi
@@ -166,6 +166,7 @@ sys_dns_append()
PROXY_GROUPS=$(ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
begin
Value = YAML.load_file('$CONFIG_FILE')
File.open('/tmp/yaml_change_marshal', 'wb') { |f| Marshal.dump(Value, f) }
if Value.key?('proxy-groups') && Value['proxy-groups'].is_a?(Array)
Value['proxy-groups'].each { |x| puts x['name'] if x.key?('name') }
end
@@ -328,7 +329,15 @@ end
begin
config_file = '$5'
Value = YAML.load_file(config_file)
if File.exist?('/tmp/yaml_change_marshal')
begin
Value = Marshal.load(File.open('/tmp/yaml_change_marshal', 'rb') { |f| f.read })
rescue
Value = YAML.load_file(config_file)
end
else
Value = YAML.load_file(config_file)
end
rescue Exception => e
YAML.LOG_ERROR('Load File Failed,【%s】' % [e.message])
exit
@@ -811,9 +820,10 @@ rescue Exception => e
YAML.LOG_ERROR('Config File Overwrite Failed,【%s】' % [e.message])
ensure
begin
File.open(config_file, 'w') { |f| YAML.dump(Value, f) }
YAML.dump(Value, config_file)
rescue Exception => e
YAML.LOG_ERROR('Write file failed:【%s】' % [e.message])
end
File.delete('/tmp/yaml_change_marshal') rescue nil
end
" 2>/dev/null >> $LOG_FILE
@@ -1617,5 +1617,4 @@ ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
" >> $LOG_FILE
LOG_OUT "Config File【$CONFIG_NAME】Read Successful!"
SLOG_CLEAN
del_lock
@@ -1852,12 +1852,33 @@ rm -rf $servers_name
LOG_OUT "Proxies, Proxy-providers, Groups Edited Successful, Updating Config File【$CONFIG_NAME】..."
config_hash=$(ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "Value = YAML.load_file('$CONFIG_FILE'); puts Value" 2>/dev/null)
if [ "$config_hash" != "false" ] && [ -n "$config_hash" ]; then
ruby_cover "$CONFIG_FILE" "['proxies']" "$SERVER_FILE" "proxies"
ruby_cover "$CONFIG_FILE" "['proxy-providers']" "$PROXY_PROVIDER_FILE" "proxy-providers"
ruby_cover "$CONFIG_FILE" "['proxy-groups']" "/tmp/yaml_groups.yaml" "proxy-groups"
else
config_hash=$(ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
begin
Value = YAML.load_file('$CONFIG_FILE')
{
'proxies' => '$SERVER_FILE',
'proxy-providers' => '$PROXY_PROVIDER_FILE',
'proxy-groups' => '/tmp/yaml_groups.yaml'
}.each do |key, src_file|
begin
if File.exist?(src_file)
src = YAML.load_file(src_file)
Value[key] = src[key]
else
Value.delete(key)
end
rescue Exception => e
YAML.LOG_ERROR('Merge [' + key + '] Failed: ' + e.message)
end
end
YAML.dump(Value, '$CONFIG_FILE')
puts 'OK'
rescue Exception => e
YAML.LOG_ERROR('Update Config File Failed: ' + e.message)
end
" 2>/dev/null)
if [ "$config_hash" != "OK" ]; then
cat "$SERVER_FILE" "$PROXY_PROVIDER_FILE" "/tmp/yaml_groups.yaml" > "$CONFIG_FILE" 2>/dev/null
fi
@@ -1868,5 +1889,4 @@ rm -rf /tmp/Proxy_Server 2>/dev/null
rm -rf /tmp/Proxy_Provider 2>/dev/null
LOG_OUT "Config File【$CONFIG_NAME】Write Successful!"
SLOG_CLEAN
del_lock
@@ -387,27 +387,29 @@ yml_other_set()
# smart auto switch
begin
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1') and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1' or '${14}' != '0') and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if '${8}' == '1' and ['url-test', 'load-balance'].include?(group['type']) then
group['type'] = 'smart';
group['uselightgbm'] = true if '${12}' == '1';
group['collectdata'] = true if '${9}' == '1';
group['sample-rate'] = '${10}'.to_f if '${9}' == '1';
end;
if '${9}' == '1' and group['type'] == 'smart' then
group['collectdata'] = true;
group['sample-rate'] = '${10}'.to_f;
end;
if '${11}' != '0' and group['type'] == 'smart' then
group['policy-priority'] = '${11}';
end;
if '${12}' == '1' and group['type'] == 'smart' then
group['uselightgbm'] = true;
end;
if '${13}' == '1' and group['type'] == 'smart' then
group['prefer-asn'] = true;
if group['type'] == 'smart' then
if '${9}' == '1' then
group['collectdata'] = true;
group['sample-rate'] = '${10}'.to_f;
end;
if '${11}' != '0'then
group['policy-priority'] = '${11}';
end;
if '${12}' == '1' then
group['uselightgbm'] = true;
end;
if '${13}' == '1' then
group['prefer-asn'] = true;
end;
if '${14}' != '0' then
group['tolerance'] = '${14}'.to_i;
end;
end;
};
};
@@ -425,11 +427,11 @@ yml_other_set()
YAML.LOG_ERROR('Config File Overwrite Failed,【%s】' % [e.message])
ensure
begin
File.open('$2','w') {|f| YAML.dump(Value, f)};
YAML.dump(Value, '$2');
rescue Exception => e
YAML.LOG_ERROR('Write file failed:【%s】' % [e.message])
end
end" 2>/dev/null >> $LOG_FILE
}
yml_other_set "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}"
yml_other_set "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "${10}" "${11}" "${12}" "${13}" "${14}"