From 6ccb9ff14547c7c75bd0f49b6be8a733008ec4fa Mon Sep 17 00:00:00 2001 From: BROBIRD <7692707+BROBIRD@users.noreply.github.com> Date: Tue, 2 Jun 2026 02:36:46 +0800 Subject: [PATCH] fix some bugs --- .github/workflows/update.yml | 5 +- scripts/gfwlist_parser.py | 114 ++++++++++++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index a8251cc..f428df7 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -82,8 +82,8 @@ jobs: return 1 } - # 处理我们生成的分离文件 - for file in Clash/Providers/*_domain.yaml Clash/Providers/*_ip.yaml; do + # 处理所有分离文件(包括生成的和已存在的) + for file in Clash/Providers/*_domain.yaml Clash/Providers/*_ip.yaml Clash/Providers/Ruleset/*_domain.yaml Clash/Providers/Ruleset/*_ip.yaml; do if [ -f "$file" ]; then filename=$(basename "$file") if [[ "$filename" == *"_domain.yaml" ]]; then @@ -113,6 +113,7 @@ jobs: # 清理临时文件 rm -f Clash/Providers/*_domain.yaml Clash/Providers/*_ip.yaml + rm -f Clash/Providers/Ruleset/*_domain.yaml Clash/Providers/Ruleset/*_ip.yaml rm -f Clash/Ruleset/*_domain.list Clash/Ruleset/*_ip.list # 清理 mihomo 二进制文件 rm -f mihomo diff --git a/scripts/gfwlist_parser.py b/scripts/gfwlist_parser.py index 75407b6..7034abb 100644 --- a/scripts/gfwlist_parser.py +++ b/scripts/gfwlist_parser.py @@ -3,6 +3,7 @@ import base64 import re import os import sys +from glob import glob def is_ip(s): if not s: @@ -187,6 +188,115 @@ def format_ip_cidr_acl_rules(ip_rules): rules.append(ip) return rules +def parse_yaml_rule_line(line): + line = line.strip() + if not line: + return None, None + rule = None + if line.startswith('- '): + rule = line[2:] + elif line.startswith(' - '): + rule = line[4:] + else: + return None, None + if ',' in rule: + return rule.split(',', 1) + return rule, '' + +def parse_list_rule_line(line): + line = line.strip() + if not line or line.startswith('#'): + return None, None + if ',' in line: + return line.split(',', 1) + return line, '' + +def is_domain_rule_type(rule_type): + return rule_type in ('DOMAIN', 'DOMAIN-SUFFIX', 'DOMAIN-KEYWORD', 'DOMAIN-REGEX') + +def is_ip_rule_type(rule_type): + return rule_type in ('IP-CIDR', 'IP-CIDR6', 'IP-SUFFIX', 'IP-ASN') + +def split_existing_rule_files(): + print("Splitting existing rule files for mrs conversion...") + + for filepath in glob('Clash/Providers/*.yaml'): + if '_domain' in filepath or '_ip' in filepath: + continue + split_yaml_file(filepath) + + for filepath in glob('Clash/Providers/Ruleset/*.yaml'): + if '_domain' in filepath or '_ip' in filepath: + continue + split_yaml_file(filepath) + + for filepath in glob('Clash/Ruleset/*.list'): + if '_domain' in filepath or '_ip' in filepath: + continue + split_list_file(filepath) + +def split_yaml_file(filepath): + domains = [] + ips = [] + try: + with open(filepath, 'r', encoding='utf-8') as f: + for line in f: + rule_type, rule_value = parse_yaml_rule_line(line) + if not rule_type or not rule_value: + continue + if is_domain_rule_type(rule_type): + domains.append((rule_type, rule_value.strip())) + elif is_ip_rule_type(rule_type): + ips.append((rule_type, rule_value.strip())) + except Exception as e: + print(f"Warning: Failed to read {filepath}: {e}") + return + + base_name = os.path.splitext(filepath)[0] + if domains: + unique_domains = sorted(set(domains)) + domain_yaml = "payload:\n" + for rule_type, d in unique_domains: + domain_yaml += f" - {rule_type},{d}\n" + write_file(f"{base_name}_domain.yaml", domain_yaml) + if ips: + unique_ips = sorted(set(ips)) + ip_yaml = "payload:\n" + for rule_type, ip in unique_ips: + ip_yaml += f" - {rule_type},{ip}\n" + write_file(f"{base_name}_ip.yaml", ip_yaml) + +def split_list_file(filepath): + domains = [] + ips = [] + try: + with open(filepath, 'r', encoding='utf-8') as f: + for line in f: + rule_type, rule_value = parse_list_rule_line(line) + if not rule_type or not rule_value: + continue + if is_domain_rule_type(rule_type): + domains.append((rule_type, rule_value.strip())) + elif is_ip_rule_type(rule_type): + ips.append((rule_type, rule_value.strip())) + except Exception as e: + print(f"Warning: Failed to read {filepath}: {e}") + return + + base_name = os.path.splitext(filepath)[0] + if domains: + unique_domains = sorted(set(domains)) + domain_text = "" + for rule_type, d in unique_domains: + domain_text += f"{rule_type},{d}\n" + write_file(f"{base_name}_domain.list", domain_text) + if ips: + unique_ips = sorted(set(ips)) + ip_text = "" + for rule_type, ip in unique_ips: + ip_text += f"{rule_type},{ip}\n" + write_file(f"{base_name}_ip.list", ip_text) + def generate_acl_file(domain_list, ip_list, filename, title="GFWList Rules"): header = f"""#********************************************************************** # {title} @@ -336,5 +446,7 @@ def main(): generate_clash_ruleset_list(domain_whitelist, ip_whitelist, 'Clash/Ruleset/UnBan.list', 'GFWList 白名单') print("Generated: Clash/Ruleset/UnBan.list") + split_existing_rule_files() + if __name__ == "__main__": - main() \ No newline at end of file + main()