parser.py

public ⁨2⁩ ⁨files⁩ 2023-12-21 13:39:59 UTC

readme.txt

Raw
请求:GET http://localhost:8000/?source={base64过的URL}&parser={base64过的URL}
例如:GET http://localhost:8000/?source=aHR0cHM6Ly9zdWIuaWQ5LmNjL3N1Yj90YXJnZXQ9Y2xhc2gmbmV3X25hbWU9dHJ1ZSZ1cmw9dGVzdCZpbnNlcnQ9ZmFsc2UmY29uZmlnPWh0dHBzJTNBJTJGJTJGcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSUyRkFDTDRTU1IlMkZBQ0w0U1NSJTJGbWFzdGVyJTJGQ2xhc2glMkZjb25maWclMkZBQ0w0U1NSX09ubGluZS5pbmk=&parser={base64过的URL}
 
source 传入需要修改的 Clash yaml 如订阅转换后的地址,例如:
aHR0cHM6Ly9zdWIuaWQ5LmNjL3N1Yj90YXJnZXQ9Y2xhc2gmbmV3X25hbWU9dHJ1ZSZ1cmw9dGVzdCZpbnNlcnQ9ZmFsc2UmY29uZmlnPWh0dHBzJTNBJTJGJTJGcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSUyRkFDTDRTU1IlMkZBQ0w0U1NSJTJGbWFzdGVyJTJGQ2xhc2glMkZjb25maWclMkZBQ0w0U1NSX09ubGluZS5pbmk=


parser 传入 CFW 的 yaml 版规则预处理 yaml
说明:https://docs.gtk.pw/contents/parser.html#%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82
例如:aHR0cHM6Ly9leGFtcGxlLmNvbS9jZndfcGFyc2VyLnlhbWw=
 
Command 功能支持不完全,大概只支持下面例子里的用法。
 
 
parser 需要以 yaml 开头,例如:
```
yaml:
  commands:
    # 向 策略组 内添加节点
    - proxy-groups.🇪🇺 欧盟节点.proxies=[]proxyNames|^(?=.*(?:奥地利|Austria|🇦🇹|比利时|Belgium|🇧🇪|保加利亚|Bulgaria|🇧🇬|克罗地亚|Croatia|🇭🇷|塞浦路斯|Cyprus|🇨🇾|捷克共和国|Czech Republic|🇨🇿|丹麦|Denmark|🇩🇰|爱沙尼亚|Estonia|🇪🇪|芬兰|Finland|🇫🇮|法国|France|🇫🇷|德国|Germany|🇩🇪|希腊|Greece|🇬🇷|匈牙利|Hungary|🇭🇺|爱尔兰|Ireland|🇮🇪|意大利|Italy|🇮🇹|拉脱维亚|Latvia|🇱🇻|立陶宛|Lithuania|🇱🇹|卢森堡|Luxembourg|🇱🇺|马耳他|Malta|🇲🇹|荷兰|Netherlands|🇳🇱|波兰|Poland|🇵🇱|葡萄牙|Portugal|🇵🇹|罗马尼亚|Romania|🇷🇴|斯洛伐克|Slovakia|🇸🇰|斯洛文尼亚|Slovenia|🇸🇮|西班牙|Spain|🇪🇸|瑞典|Sweden|🇸🇪)).*$
    - proxy-groups.🚀 节点选择.proxies.0+🚰 大流量
    - proxy-groups.🚀 节点选择.proxies.0+🚰 大流量负载均衡
  prepend-rules: 
    - DOMAIN-SUFFIX,s.team,🚀 节点选择
    - DOMAIN,fanatical.com,🚀 节点选择
    - DOMAIN,humblebundle.com,🚀 节点选择
    - DOMAIN,playartifact.com,🚀 节点选择
    - DOMAIN-SUFFIX,steam-chat.com,🚀 节点选择
    - DOMAIN-SUFFIX,steamcommunity.com,🚀 节点选择
    - DOMAIN-SUFFIX,steamgames.com,🚀 节点选择
    - DOMAIN-SUFFIX,steamstatic.com,🚀 节点选择
    - DOMAIN-SUFFIX,steamusercontent.com,🚀 节点选择
    - DOMAIN-SUFFIX,underlords.com,🚀 节点选择
    - DOMAIN-SUFFIX,valvesoftware.com,🚀 节点选择
    - DOMAIN,api.steampowered.com,🚀 节点选择
    - DOMAIN,store.steampowered.com,🚀 节点选择
    - DOMAIN-SUFFIX,steamserver.net,DIRECT
    - DOMAIN-SUFFIX,test.com,🚰 大流量
  mix-proxy-providers:  #添加自定义规则组
    hightraffic: 
      type: http 
      path: ./Proxy/hightraffic.yaml 
      url: https://example.com/clash.yaml
      interval: 21600 
      health-check:  
          enable: true
          url: http://www.gstatic.com/generate_204
          interval: 300
```

parser.py

Raw
import base64
import re
 
import yaml
from http.server import BaseHTTPRequestHandler, HTTPServer
import requests
 
 
class YAMLModifierHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # 解析查询参数
        query_components = self.parse_query_parameters(self.path)
 
        # 提取并解码 URL
        source_url = base64.b64decode(query_components.get('source', [''])[0]).decode()
        parser_url = base64.b64decode(query_components.get('parser', [''])[0]).decode()
 
        # 获取并解析 YAML 文件
        source_yaml = self.fetch_yaml(source_url)
        parser_yaml = self.fetch_yaml(parser_url)
 
        if source_yaml and parser_yaml:
            # 使用 parser_yaml 中的规则修改 source_yaml
            modified_yaml = self.modify_yaml(source_yaml, parser_yaml)
 
            # 发送响应
            self.send_response(200)
            self.send_header('Content-type', 'text/yaml')
            self.end_headers()
            self.wfile.write(
                yaml.dump(modified_yaml, allow_unicode=True, default_flow_style=False, sort_keys=False).encode())
        else:
            # 发送错误响应
            self.send_error(404, "Unable to fetch or parse YAML files")
 
    @staticmethod
    def parse_query_parameters(path):
        # 解析 URL 中的查询参数
        from urllib.parse import urlparse, parse_qs
        query = urlparse(path).query
        return parse_qs(query)
 
    @staticmethod
    def fetch_yaml(url):
        # 从给定的 URL 获取并解析 YAML
        try:
            response = requests.get(url)
            response.raise_for_status()
            return yaml.safe_load(response.content)
        except Exception as e:
            print(f"Error fetching YAML from {url}: {e}")
            return None
 
    def modify_yaml(self, source_yaml, parser_yaml):
        # 根据 parser_yaml 中的规则修改 source_yaml
        for key, operations in parser_yaml.get('yaml', {}).items():
            if key.startswith('append-') or key.startswith('prepend-'):
                self.modify_list(source_yaml, key, operations)
            elif key.startswith('mix-'):
                self.mix_object(source_yaml, key, operations)
 
        # 执行 commands
        for command in parser_yaml.get('yaml', {}).get('commands', []):
            self.apply_command(source_yaml, command)
 
        return source_yaml
 
    def modify_list(self, source_yaml, key, operations):
        # 修改 source_yaml 中的列表
        list_name = key.split('-', 1)[1]
        source_list = source_yaml.get(list_name, [])
 
        # Prepend or append operations
        # 根据操作类型(附加 Prepend 或前置 append)进行修改
        if key.startswith('append-'):
            source_list.extend(operations)
        elif key.startswith('prepend-'):
            source_list = operations + source_list
 
        source_yaml[list_name] = source_list
 
    def mix_object(self, source_yaml, key, operations):
        # 确定 source_yaml 中的目标对象
        object_name = key.split('-', 1)[1]
        source_object = source_yaml.get(object_name, {})
 
        # 合并对象
        source_object.update(operations)
        source_yaml[object_name] = source_object
 
    def apply_command(self, source_yaml, command):
        path, operation, value = self.parse_command(command)
 
        # 查找并处理目标策略组
        target_group = self.find_proxy_group(source_yaml, path)
        if target_group is None:
            return  # 如果没有找到目标策略组,则跳过此命令
 
        if value.startswith('[]'):
            regex_pattern = value[2:]
            self.apply_regex_to_proxies(source_yaml, target_group, regex_pattern)
 
        if operation == '+':
            # 插入代理到特定位置
            index = int(path[2]) if path[2].isdigit() else None
            if index is not None and index < len(target_group['proxies']):
                target_group['proxies'].insert(index, value)
            else:
                target_group['proxies'].append(value)  # 如果索引无效,则默认添加到末尾
 
    def find_proxy_group(self, source_yaml, path):
        # 检查路径长度是否足够
        if len(path) < 2:
            return None
        # 查找指定的策略组
        group_name = path[1]
        for group in source_yaml.get('proxy-groups', []):
            if group['name'] == group_name:
                return group
        return None
 
    def apply_regex_to_proxies(self, source_yaml, target_group, regex_pattern):
        # 使用正则表达式筛选代理
        regex = re.compile(regex_pattern)
        matched_proxies = [proxy['name'] for proxy in source_yaml.get('proxies', []) if regex.search(proxy['name'])]
 
        # 将匹配的代理添加到目标策略组
        target_group['proxies'].extend(matched_proxies)
 
    def parse_command(self, command):
        # 首先尝试以 '+' 为分隔符分割命令
        if '+' in command:
            parts = command.split('+', 1)
            operation = '+'
        # 如果没有 '+', 则尝试以 '=' 为分隔符
        elif '=' in command:
            parts = command.split('=', 1)
            operation = '='
        else:
            raise ValueError("Invalid command format")
 
        if len(parts) != 2:
            raise ValueError("Invalid command format")
 
        path = parts[0].split('.')
        value = parts[1]
 
        return path, operation, value
 
    def log_message(self, format, *args):
        # 覆盖以避免记录每个请求
        pass
 
 
def run_server(server_class=HTTPServer, handler_class=YAMLModifierHTTPRequestHandler, port=8000):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print(f"Starting HTTP server on port {port}")
    httpd.serve_forever()
 
 
if __name__ == '__main__':
    run_server()