Revisions for ⁨parser.py⁩

View the changes made to this paste.

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

parser.py

@@ -0,0 +1,163 @@

+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()
\ No newline at end of file

readme.txt

@@ -0,0 +1,49 @@

+请求: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
+```
\ No newline at end of file