├── .github └── workflows │ ├── merge.yml │ └── serv.yml ├── .gitignore ├── README.md ├── merge.py ├── meta_merge.py ├── requirements.txt ├── sub ├── merged_proxies_new.yaml ├── merged_warp_proxies_new.yaml ├── ssr └── ssr64 ├── templates ├── clash_template.yaml └── clash_warp_template.yaml └── urls ├── clash_urls.txt ├── hysteria2_urls.txt ├── hysteria_urls.txt ├── naiverproxy_urls.txt ├── sb_urls.txt ├── ss_urls.txt ├── v2_urls.txt └── xray_urls.txt /.github/workflows/merge.yml: -------------------------------------------------------------------------------- 1 | name: Merge Script 2 | 3 | on: 4 | # push: 5 | # branches: 6 | # - main # 替换为你的默认分支 7 | schedule: 8 | - cron: '0 1 * * *' 9 | workflow_dispatch: # 触发手动事件 10 | jobs: 11 | merge: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: 3.11 22 | 23 | - name: Set timezone 24 | run: sudo timedatectl set-timezone 'Asia/Shanghai' 25 | 26 | - name: Install dependencies 27 | run: pip install -r requirements.txt 28 | 29 | - name: Run clash merge script 30 | run: python meta_merge.py 31 | 32 | - name: Run shadowrocket merge script 33 | run: python merge.py 34 | 35 | - name: Commit Changes 36 | run: | 37 | git config core.ignorecase false 38 | git config --local user.email "actions@github.com" 39 | git config --local user.name "GitHub Action" 40 | git add . 41 | git commit -m "Updated at $(date '+%Y-%m-%d %H:%M:%S')" 42 | git push 43 | -------------------------------------------------------------------------------- /.github/workflows/serv.yml: -------------------------------------------------------------------------------- 1 | name: Parallel URL Fetch 2 | 3 | on: 4 | #如要使用定时运行,请把下面两行开头的#符号删除 5 | schedule: 6 | - cron: '0 1 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | fetch-urls: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v2 16 | 17 | - name: Set timezone 18 | run: sudo timedatectl set-timezone 'Asia/Shanghai' 19 | 20 | - name: Fetch URLs in parallel 21 | # secrets设置,变量名称:http 22 | # 变量值要求:每个保活/up网页或每个重启/re网页之间用空格或者,或者,间隔开,网页前带http:// 23 | # 变量值填写示例:http://保活或重启网页1 http://保活或重启网页2 http://保活或重启网页3 ……… 24 | run: | 25 | IFS=$',, ' read -r -a http <<< "${{ secrets.http }}" 26 | echo "*****************************************************" 27 | for url in "${http[@]}"; do 28 | response=$(curl -sk "$url" || true) 29 | if [[ "$response" == *"网页保活启动"* ]]; then 30 | echo "🎉恭喜!$url ✅运行正常,成功拉起一次保活" 31 | elif [[ "$response" == *"主程序重启成功"* ]]; then 32 | echo "🎉恭喜!$url ✅运行正常,成功重启一次主程序" 33 | else 34 | echo "💥杯具!$url ❌运行失败,⚠️网页变量是否填写正确?或者Serv00炸了" 35 | fi 36 | done 37 | wait 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | sub/ssr -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Removed according to regulations. -------------------------------------------------------------------------------- /merge.py: -------------------------------------------------------------------------------- 1 | import base64, json 2 | import urllib.request 3 | import yaml, codecs, logging, random, uuid 4 | myID = uuid 5 | # 提取节点 6 | def process_urls(url_file, processor): 7 | try: 8 | with open(url_file, 'r') as file: 9 | urls = file.read().splitlines() 10 | 11 | for index, url in enumerate(urls): 12 | try: 13 | response = urllib.request.urlopen(url) 14 | data = response.read().decode('utf-8') 15 | processor(data, index) 16 | except Exception as e: 17 | logging.error(f"Error processing URL {url}: {e}") 18 | except Exception as e: 19 | logging.error(f"Error reading file {url_file}: {e}") 20 | 21 | #clash 22 | def process_clash(data, index): 23 | # 解析YAML格式的内容 24 | content = yaml.safe_load(data) 25 | 26 | # 提取proxies部分并合并到merged_proxies中 27 | proxies = content.get('proxies', []) 28 | 29 | for proxy in proxies: 30 | # 如果类型是vless 31 | if proxy['type'] == 'vless' : 32 | name = proxy.get("name", "") 33 | server = proxy.get("server", "") 34 | port = int(proxy.get("port", 443)) 35 | udp = proxy.get("udp", "") 36 | uuid = proxy.get("uuid", "") 37 | network = proxy.get("network", "") 38 | tls = int(proxy.get("tls", 0)) 39 | xudp = proxy.get("xudp", "") 40 | sni = proxy.get("servername", "") 41 | flow = proxy.get("flow", "") 42 | publicKey = proxy.get('reality-opts', {}).get('public-key', '') 43 | short_id = proxy.get('reality-opts', {}).get('short-id', '') 44 | fp = proxy.get("client-fingerprint", "") 45 | insecure = int(proxy.get("skip-cert-verify", 0)) 46 | grpc_serviceName = proxy.get('grpc-opts', {}).get('grpc-service-name', '') 47 | 48 | ws_path = proxy.get('ws-opts', {}).get('path', '') 49 | ws_headers_host = proxy.get('ws-opts', {}).get('headers', {}).get('Host', '') 50 | if tls == 0: 51 | security = 'none' 52 | elif tls == 1 and publicKey != '': 53 | security = 'reality' 54 | else: 55 | security = 'tls' 56 | vless_meta = f"vless://{uuid}@{server}:{port}?security={security}&allowInsecure={insecure}&flow={flow}&type={network}&fp={fp}&pbk={publicKey}&sid={short_id}&sni={sni}&serviceName={grpc_serviceName}&path={ws_path}&host={ws_headers_host}" + f"#vless_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 57 | 58 | merged_proxies.append(vless_meta) 59 | 60 | if proxy['type'] == 'vmess' : 61 | name = proxy.get("name", "") 62 | server = proxy.get("server", "") 63 | port = int(proxy.get("port", 443)) 64 | uuid = proxy.get("uuid", "") 65 | insecure = int(proxy.get("skip-cert-verify", 0)) 66 | fp = proxy.get("client-fingerprint", "") 67 | alterId = proxy.get("alterId", "") 68 | network = proxy.get("network", "") 69 | scy = proxy.get("scy","") 70 | tls = int(proxy.get("tls", 0)) 71 | if tls == 0: 72 | security = "none" 73 | elif tls == 1: 74 | security = "tls" 75 | sni = proxy.get("servername", "") 76 | ws_path = proxy.get('ws-opts', {}).get('path', '') 77 | ws_headers_host = proxy.get('ws-opts', {}).get('headers', {}).get('Host', '') 78 | 79 | vmess_format = {"add":server,"aid":alterId,"alpn":"","host":ws_headers_host,"id":uuid,"net":network,"path":ws_path,"port":port,"ps":"vmess_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + name.strip('dongtaiwang.com_'),"scy":scy,"sni":sni,"tls":security} 80 | dataJson = json.dumps(vmess_format) 81 | vmess_meta = "vmess://" + base64.b64encode(dataJson.encode('utf-8')).decode('utf-8') 82 | merged_proxies.append(vmess_meta) 83 | 84 | elif proxy['type'] == 'tuic': 85 | server = proxy.get("server", "") 86 | port = int(proxy.get("port", 443)) 87 | uuid = proxy.get("uuid", "") 88 | password = proxy.get("password", "") 89 | sni = proxy.get("sni", "") 90 | insecure = int(proxy.get("skip-cert-verify", 0)) 91 | udp_relay_mode = proxy.get("udp-relay-mode", "naive") 92 | congestion = proxy.get("congestion-controller", "bbr") 93 | alpn = proxy.get("alpn", [])[0] if proxy.get("alpn") and len(proxy["alpn"]) > 0 else None 94 | #tuic_meta_neko = f"tuic://{server}:{port}?uuid={uuid}&version=5&password={password}&insecure={insecure}&alpn={alpn}&mode={udp_relay_mode}" 95 | tuic_meta = f"tuic://{uuid}:{password}@{server}:{port}?sni={sni}&congestion_control={congestion}&udp_relay_mode={udp_relay_mode}&alpn={alpn}&allow_insecure={insecure}" + f"#tuic_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 96 | merged_proxies.append(tuic_meta) 97 | 98 | elif proxy['type'] == "hysteria2": 99 | server = proxy.get("server", "") 100 | port = int(proxy.get("port", 443)) 101 | auth = proxy.get("password", "") 102 | obfs = proxy.get("obfs", "") 103 | obfs_password = proxy.get("obfs-password","") 104 | sni = proxy.get("sni", "") 105 | insecure = int(proxy.get("skip-cert-verify", 0)) 106 | hy2_meta = f"hysteria2://{auth}@{server}:{port}?insecure={insecure}&sni={sni}&obfs={obfs}&obfs-password={obfs_password}" + f"#hy2_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 107 | merged_proxies.append(hy2_meta) 108 | 109 | elif proxy['type'] == 'hysteria': 110 | server = proxy.get("server", "") 111 | port = int(proxy.get("port", 443)) 112 | ports = proxy.get("port", "") 113 | protocol = proxy.get("protocol", "udp") 114 | up_mbps = 50 115 | down_mbps = 80 116 | alpn = proxy.get("alpn", [])[0] if proxy.get("alpn") and len(proxy["alpn"]) > 0 else None 117 | obfs = proxy.get("obfs", "") 118 | insecure = int(proxy.get("skip-cert-verify", 0)) 119 | sni = proxy.get("sni", "") 120 | fast_open = int(proxy.get("fast_open", 1)) 121 | auth = proxy.get("auth-str", "") 122 | # 生成URL 123 | hy_meta = f"hysteria://{server}:{port}?peer={sni}&auth={auth}&insecure={insecure}&upmbps={up_mbps}&downmbps={down_mbps}&alpn={alpn}&mport={ports}&obfs={obfs}&protocol={protocol}&fastopen={fast_open}" + f"#hy_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 124 | merged_proxies.append(hy_meta) 125 | 126 | elif proxy['type'] == 'ssr': 127 | name = proxy.get("name", "") 128 | server = proxy.get("server", "") 129 | port = int(proxy.get("port", 443)) 130 | password = proxy.get("password", "") 131 | cipher = proxy.get("cipher", "") 132 | obfs = proxy.get("obfs", "") 133 | protocol = proxy.get("protocol", "") 134 | protocol_param = proxy.get("protocol-param", "") 135 | obfs_param = proxy.get("obfs-param", "") 136 | # 生成URL 137 | ssr_source=f"{server}:{port}:{protocol}:{cipher}:{obfs}:{password}/?obfsparam={obfs_param}&protoparam={protocol_param}&remarks=" + f"ssr_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 138 | 139 | #ssr_source=base64.b64encode(ssr_source.encode()).decode() 140 | ssr_meta = f"ssr://{ssr_source}" 141 | merged_proxies.append(ssr_meta) 142 | #目前仅支持最原始版本ss,无插件支持 143 | elif proxy['type'] == 'ss': 144 | name = proxy.get("name", "") 145 | server = proxy.get("server", "") 146 | port = int(proxy.get("port", 443)) 147 | password = proxy.get("password", "") 148 | cipher = proxy.get("cipher", "") 149 | # 生成URL 150 | ss_source=f"{cipher}:{password}@{server}:{port}&remarks=" + f"#ss_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 151 | 152 | #ss_source=base64.b64encode(ss_source.encode()).decode() 153 | ss_meta = f"ss://{ss_source}" 154 | merged_proxies.append(ss_meta) 155 | 156 | #naive 157 | def process_naive(data, index): 158 | try: 159 | json_data = json.loads(data) 160 | 161 | proxy_str = json_data["proxy"] 162 | naiveproxy = base64.b64encode(proxy_str.encode()).decode() 163 | merged_proxies.append(naiveproxy) 164 | except Exception as e: 165 | logging.error(f"Error processing naive data for index {index}: {e}") 166 | 167 | #sing-box 168 | def process_sb(data, index): 169 | try: 170 | json_data = json.loads(data) 171 | # 处理 shadowtls 数据 172 | server = json_data["outbounds"][1].get("server", "") 173 | server_port = json_data["outbounds"][1].get("server_port", "") 174 | method = json_data["outbounds"][0].get("method", "") 175 | password = json_data["outbounds"][0].get("password", "") 176 | version = int(json_data["outbounds"][1].get("version", 0)) 177 | host = json_data["outbounds"][1]["tls"].get("server_name", "") 178 | shadowtls_password = json_data["outbounds"][1].get("password", "") 179 | 180 | ss = f"{method}:{password}@{server}:{server_port}" 181 | shadowtls = f'{{"version": "{version}", "host": "{host}","password":{shadowtls_password}}}' 182 | shadowtls_proxy = "sb://"+base64.b64encode(ss.encode()).decode()+"?shadow-tls="+base64.b64encode(shadowtls.encode()).decode()+ f"#sb_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 183 | 184 | merged_proxies.append(shadowtls_proxy) 185 | 186 | except Exception as e: 187 | logging.error(f"Error processing shadowtls data for index {index}: {e}") 188 | 189 | #hysteria 190 | def process_hysteria(data, index): 191 | try: 192 | json_data = json.loads(data) 193 | # 处理 hysteria 数据 194 | # 提取字段值 195 | server = json_data.get("server", "") 196 | protocol = json_data.get("protocol", "") 197 | up_mbps = json_data.get("up_mbps", "") 198 | down_mbps = json_data.get("down_mbps", "") 199 | alpn = json_data.get("alpn", "") 200 | obfs = json_data.get("obfs", "") 201 | insecure = int(json_data.get("insecure", 0)) 202 | server_name = json_data.get("server_name", "") 203 | fast_open = int(json_data.get("fast_open", 0)) 204 | auth = json_data.get("auth_str", "") 205 | # 生成URL 206 | hysteria = f"hysteria://{server}?peer={server_name}&auth={auth}&insecure={insecure}&upmbps={up_mbps}&downmbps={down_mbps}&alpn={alpn}&obfs={obfs}&protocol={protocol}&fastopen={fast_open}" + f"#hy_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 207 | merged_proxies.append(hysteria) 208 | 209 | 210 | except Exception as e: 211 | logging.error(f"Error processing hysteria data for index {index}: {e}") 212 | 213 | #hysteria2 214 | def process_hysteria2(data, index): 215 | try: 216 | json_data = json.loads(data) 217 | # 处理 hysteria2 数据 218 | # 提取字段值 219 | server = json_data["server"] 220 | insecure = int(json_data["tls"]["insecure"]) 221 | sni = json_data["tls"]["sni"] 222 | auth = json_data["auth"] 223 | # 生成URL 224 | hysteria2 = f"hysteria2://{auth}@{server}?insecure={insecure}&sni={sni}" + f"#hy2_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 225 | 226 | merged_proxies.append(hysteria2) 227 | except Exception as e: 228 | logging.error(f"Error processing hysteria2 data for index {index}: {e}") 229 | 230 | #xray 231 | def process_xray(data, index): 232 | try: 233 | json_data = json.loads(data) 234 | # 处理 xray 数据 235 | protocol = json_data["outbounds"][0].get("protocol") 236 | 237 | if protocol == "vless": 238 | vnext = json_data["outbounds"][0]["settings"]["vnext"] 239 | 240 | if vnext: 241 | server = vnext[0].get("address", "") 242 | port = vnext[0].get("port", "") 243 | users = vnext[0]["users"] 244 | 245 | if users: 246 | user = users[0] 247 | uuid = user.get("id", "") 248 | flow = user.get("flow", "") 249 | 250 | stream_settings = json_data["outbounds"][0].get("streamSettings", {}) 251 | network = stream_settings.get("network", "") 252 | security = stream_settings.get("security", "") 253 | reality_settings = stream_settings.get("realitySettings", {}) 254 | 255 | publicKey = reality_settings.get("publicKey", "") 256 | short_id = reality_settings.get("shortId", "") 257 | sni = reality_settings.get("serverName", "") 258 | #tls 259 | tls_settings = stream_settings.get("tlsSettings", {}) 260 | sni = tls_settings.get("serverName", sni) 261 | insecure = int(tls_settings.get("allowInsecure", 0)) 262 | 263 | fp = reality_settings.get("fingerprint", "") 264 | fp = tls_settings.get("fingerprint", fp) 265 | spx = reality_settings.get("spiderX", "") 266 | 267 | grpc_settings = stream_settings.get("grpcSettings", {}) 268 | grpc_serviceName = grpc_settings.get("serviceName", "") 269 | 270 | ws_settings = stream_settings.get("wsSettings", {}) 271 | ws_path = ws_settings.get("path", "") 272 | ws_headers_host = ws_settings.get("headers", {}).get("Host", "") 273 | 274 | xray_proxy = f"vless://{uuid}@{server}:{port}?security={security}&allowInsecure={insecure}&flow={flow}&type={network}&fp={fp}&pbk={publicKey}&sid={short_id}&sni={sni}&serviceName={grpc_serviceName}&path={ws_path}&host={ws_headers_host}" + f"#vless_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 275 | 276 | # 将当前proxy字典添加到所有proxies列表中 277 | merged_proxies.append(xray_proxy) 278 | if protocol == "vmess": 279 | vnext = json_data["outbounds"][0]["settings"]["vnext"] 280 | 281 | if vnext: 282 | server = vnext[0].get("address", "") 283 | port = vnext[0].get("port", "") 284 | users = vnext[0]["users"] 285 | 286 | if users: 287 | user = users[0] 288 | uuid = user.get("id", "") 289 | alterId = user.get("alterId", "") 290 | flow = user.get("flow", "") 291 | 292 | stream_settings = json_data["outbounds"][0].get("streamSettings", {}) 293 | network = stream_settings.get("network", "") 294 | security = stream_settings.get("security", "") 295 | reality_settings = stream_settings.get("realitySettings", {}) 296 | 297 | publicKey = reality_settings.get("publicKey", "") 298 | short_id = reality_settings.get("shortId", "") 299 | sni = reality_settings.get("serverName", "") 300 | #tls 301 | tls_settings = stream_settings.get("tlsSettings", {}) 302 | sni = tls_settings.get("serverName", sni) 303 | insecure = int(tls_settings.get("allowInsecure", 0)) 304 | 305 | fp = reality_settings.get("fingerprint", "") 306 | fp = tls_settings.get("fingerprint", fp) 307 | spx = reality_settings.get("spiderX", "") 308 | 309 | grpc_settings = stream_settings.get("grpcSettings", {}) 310 | grpc_serviceName = grpc_settings.get("serviceName", "") 311 | 312 | ws_settings = stream_settings.get("wsSettings", {}) 313 | ws_path = ws_settings.get("path", "") 314 | ws_headers_host = ws_settings.get("headers", {}).get("Host", "") 315 | 316 | xray_vmess_format = {"add":server,"aid":alterId,"alpn":"","host":ws_headers_host,"id":uuid,"net":network,"path":ws_path,"port":port,"allowInsecure":insecure,"sni":sni,"tls":security,"ps": "xray_vmess_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}"} 317 | dataJson = json.dumps(xray_vmess_format) 318 | xray_vmess_meta = "vmess://" + base64.b64encode(dataJson.encode('utf-8')).decode('utf-8') 319 | 320 | merged_proxies.append(xray_vmess_meta) 321 | # 不支持插件 322 | if protocol == "shadowsocks": 323 | server = json_data["outbounds"][0]["settings"]["servers"]["address"] 324 | method = json_data["outbounds"][0]["settings"]["servers"]["method"] 325 | password = json_data["outbounds"][0]["settings"]["servers"]["password"] 326 | port = json_data["outbounds"][0]["settings"]["servers"]["port"] 327 | # 生成URL 328 | ss_source=f"{method}:{password}@{server}:{port}" 329 | ss_source=base64.b64encode(ss_source.encode()).decode() 330 | xray_proxy = f"ss://{ss_source}" 331 | 332 | # 将当前proxy字典添加到所有proxies列表中 333 | merged_proxies.append(xray_proxy) 334 | except Exception as e: 335 | logging.error(f"Error processing xray data for index {index}: {e}") 336 | 337 | #v2ray 338 | def process_v2(data, index): 339 | content = yaml.safe_load(data) 340 | v2ray_content.append(content) 341 | 342 | 343 | # 定义一个空列表用于存储合并后的代理配置 344 | merged_proxies = [] 345 | v2ray_content = [] 346 | 347 | # 处理 clash URLs 348 | process_urls('./urls/clash_urls.txt', process_clash) 349 | 350 | # 处理 shadowtls URLs 351 | process_urls('./urls/sb_urls.txt', process_sb) 352 | 353 | # 处理 naive URLs TODO 354 | # process_urls('./urls/naiverproxy_urls.txt', process_naive) 355 | 356 | # 处理 hysteria URLs 357 | process_urls('./urls/hysteria_urls.txt', process_hysteria) 358 | 359 | # 处理 hysteria2 URLs 360 | process_urls('./urls/hysteria2_urls.txt', process_hysteria2) 361 | 362 | # 处理 xray URLs 363 | process_urls('./urls/xray_urls.txt', process_xray) 364 | 365 | # 处理 v2ray base64 366 | process_urls('./urls/v2_urls.txt', process_v2) 367 | 368 | # 将结果写入文件 369 | merged_content = "\n".join(merged_proxies) 370 | decode_content = '' 371 | for item in v2ray_content: 372 | # base64 to url 373 | decode_content += base64.b64decode(item.encode("utf-8")).decode("utf-8") 374 | 375 | try: 376 | # 最终合并 377 | final_orignal = (merged_content + '\n' + decode_content) 378 | 379 | final_base64 = base64.b64encode((merged_content + '\n' + decode_content).encode("utf-8")).decode("utf-8") 380 | 381 | with open("./sub/ssr", "w") as file: 382 | file.write(final_orignal) 383 | print("Successfully written to ssr") 384 | 385 | with open("./sub/ssr64", "w") as file: 386 | file.write(final_base64) 387 | print("Successfully written to ssr64") 388 | except Exception as e: 389 | print(f"Error encoding and writing to file: {e}") 390 | 391 | -------------------------------------------------------------------------------- /meta_merge.py: -------------------------------------------------------------------------------- 1 | import yaml, json 2 | import urllib.request 3 | import logging, random, uuid 4 | myID = uuid 5 | # 提取节点 6 | def process_urls(url_file, processor): 7 | try: 8 | with open(url_file, 'r') as file: 9 | urls = file.read().splitlines() 10 | 11 | for index, url in enumerate(urls): 12 | try: 13 | response = urllib.request.urlopen(url) 14 | data = response.read().decode('utf-8') 15 | processor(data, index) 16 | except Exception as e: 17 | logging.error(f"Error processing URL {url}: {e}") 18 | except Exception as e: 19 | logging.error(f"Error reading file {url_file}: {e}") 20 | 21 | #clash 22 | def process_clash(data, index): 23 | content = yaml.safe_load(data) 24 | proxies = content.get('proxies', []) 25 | for i, proxy in enumerate(proxies): 26 | proxy['name'] = f"{proxy['type']}_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 27 | merged_proxies.extend(proxies) 28 | 29 | # sing-box 30 | def process_sb(data, index): 31 | try: 32 | json_data = json.loads(data) 33 | # 处理 shadowtls 数据 34 | 35 | # 提取所需字段 36 | method = json_data["outbounds"][0]["method"] 37 | password = json_data["outbounds"][0]["password"] 38 | server = json_data["outbounds"][1]["server"] 39 | server_port = json_data["outbounds"][1]["server_port"] 40 | server_name = json_data["outbounds"][1]["tls"]["server_name"] 41 | shadowtls_password = json_data["outbounds"][1]["password"] 42 | version = json_data["outbounds"][1]["version"] 43 | name = f"sb_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 44 | # 创建当前网址的proxy字典 45 | proxy = { 46 | "name": name, 47 | "type": "ss", 48 | "server": server, 49 | "port": server_port, 50 | "cipher": method, 51 | "password": password, 52 | "plugin": "shadow-tls", 53 | "client-fingerprint": "chrome", 54 | "plugin-opts": { 55 | "host": server_name, 56 | "password": shadowtls_password, 57 | "version": int(version) 58 | } 59 | } 60 | 61 | # 将当前proxy字典添加到所有proxies列表中 62 | merged_proxies.append(proxy) 63 | 64 | except Exception as e: 65 | logging.error(f"Error processing shadowtls data for index {index}: {e}") 66 | 67 | #hysteria 68 | def process_hysteria(data, index): 69 | try: 70 | json_data = json.loads(data) 71 | # 处理 hysteria 数据 72 | # 提取所需字段 73 | auth = json_data["auth_str"] 74 | server_ports = json_data["server"] 75 | server_ports_slt = server_ports.split(":") 76 | server = server_ports_slt[0] 77 | ports = server_ports_slt[1] 78 | ports_slt = ports.split(",") 79 | server_port = int(ports_slt[0]) 80 | if len(ports_slt) > 1: 81 | mport = ports_slt[1] 82 | else: 83 | mport = server_port 84 | fast_open = json_data["fast_open"] 85 | insecure = json_data["insecure"] 86 | server_name = json_data["server_name"] 87 | alpn = json_data["alpn"] 88 | protocol = json_data["protocol"] 89 | name = f"hy_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 90 | 91 | # 创建当前网址的proxy字典 92 | proxy = { 93 | "name": name, 94 | "type": "hysteria", 95 | "server": server, 96 | "port": server_port, 97 | "ports": mport, 98 | "auth_str": auth, 99 | "up": 80, 100 | "down": 100, 101 | "fast-open": fast_open, 102 | "protocol": protocol, 103 | "sni": server_name, 104 | "skip-cert-verify": insecure, 105 | "alpn": [alpn] 106 | } 107 | 108 | # 将当前proxy字典添加到所有proxies列表中 109 | merged_proxies.append(proxy) 110 | 111 | except Exception as e: 112 | logging.error(f"Error processing hysteria data for index {index}: {e}") 113 | 114 | #hysteria2 115 | def process_hysteria2(data, index): 116 | try: 117 | json_data = json.loads(data) 118 | # 处理 hysteria2 数据 119 | # 提取所需字段 120 | auth = json_data["auth"] 121 | server_ports = json_data["server"] 122 | server_ports_slt = server_ports.split(":") 123 | server = server_ports_slt[0] 124 | ports = server_ports_slt[1] 125 | ports_slt = ports.split(",") 126 | server_port = int(ports_slt[0]) 127 | fast_open = json_data["fastOpen"] 128 | insecure = json_data["tls"]["insecure"] 129 | sni = json_data["tls"]["sni"] 130 | name = f"hy2_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{index}" 131 | 132 | # 创建当前网址的proxy字典 133 | proxy = { 134 | "name": name, 135 | "type": "hysteria2", 136 | "server": server, 137 | "port": server_port, 138 | "password": auth, 139 | "fast-open": fast_open, 140 | "sni": sni, 141 | "skip-cert-verify": insecure 142 | } 143 | 144 | # 将当前proxy字典添加到所有proxies列表中 145 | merged_proxies.append(proxy) 146 | 147 | except Exception as e: 148 | logging.error(f"Error processing hysteria2 data for index {index}: {e}") 149 | 150 | #xray 151 | def process_xray(data, index): 152 | try: 153 | json_data = json.loads(data) 154 | # 处理 xray 数据 155 | protocol = json_data["outbounds"][0]["protocol"] 156 | #vless操作 157 | if protocol == "vless": 158 | # 提取所需字段 159 | server = json_data["outbounds"][0]["settings"]["vnext"][0]["address"] 160 | port = json_data["outbounds"][0]["settings"]["vnext"][0]["port"] 161 | uuid = json_data["outbounds"][0]["settings"]["vnext"][0]["users"][0]["id"] 162 | istls = True 163 | flow = json_data["outbounds"][0]["settings"]["vnext"][0]["users"][0]["flow"] 164 | # 传输方式 165 | network = json_data["outbounds"][0]["streamSettings"]["network"] 166 | publicKey = json_data["outbounds"][0]["streamSettings"]["realitySettings"]["publicKey"] 167 | shortId = json_data["outbounds"][0]["streamSettings"]["realitySettings"]["shortId"] 168 | serverName = json_data["outbounds"][0]["streamSettings"]["realitySettings"]["serverName"] 169 | fingerprint = json_data["outbounds"][0]["streamSettings"]["realitySettings"]["fingerprint"] 170 | # udp转发 171 | isudp = True 172 | name = f"reality_" + myID.uuid4().hex[27:] + str(random.randint(0,10)) + f"{i}" 173 | 174 | # 根据network判断tcp 175 | if network == "tcp": 176 | proxy = { 177 | "name": name, 178 | "type": protocol, 179 | "server": server, 180 | "port": port, 181 | "uuid": uuid, 182 | "network": network, 183 | "tls": istls, 184 | "udp": isudp, 185 | "flow": flow, 186 | "client-fingerprint": fingerprint, 187 | "servername": serverName, 188 | "reality-opts":{ 189 | "public-key": publicKey, 190 | "short-id": shortId} 191 | } 192 | 193 | # 根据network判断grpc 194 | elif network == "grpc": 195 | serviceName = json_data["outbounds"][0]["streamSettings"]["grpcSettings"]["serviceName"] 196 | 197 | # 创建当前网址的proxy字典 198 | proxy = { 199 | "name": name, 200 | "type": protocol, 201 | "server": server, 202 | "port": port, 203 | "uuid": uuid, 204 | "network": network, 205 | "tls": istls, 206 | "udp": isudp, 207 | "flow": flow, 208 | "client-fingerprint": fingerprint, 209 | "servername": serverName, 210 | "grpc-opts":{ 211 | "grpc-service-name": serviceName 212 | }, 213 | "reality-opts":{ 214 | "public-key": publicKey, 215 | "short-id": shortId} 216 | } 217 | 218 | # 将当前proxy字典添加到所有proxies列表中 219 | merged_proxies.append(proxy) 220 | except Exception as e: 221 | logging.error(f"Error processing xray data for index {index}: {e}") 222 | 223 | def update_proxy_groups(config_data, merged_proxies): 224 | for group in config_data['proxy-groups']: 225 | if group['name'] in ['自动选择', '节点选择']: 226 | group['proxies'].extend(proxy['name'] for proxy in merged_proxies) 227 | 228 | def update_warp_proxy_groups(config_warp_data, merged_proxies): 229 | for group in config_warp_data['proxy-groups']: 230 | if group['name'] in ['自动选择', '手动选择', '负载均衡']: 231 | group['proxies'].extend(proxy['name'] for proxy in merged_proxies) 232 | 233 | merged_proxies = [] 234 | 235 | #clash URLs 236 | process_urls('./urls/clash_urls.txt', process_clash) 237 | 238 | #shadowtls URLs 239 | process_urls('./urls/sb_urls.txt', process_sb) 240 | 241 | #hysteria URLs 242 | process_urls('./urls/hysteria_urls.txt', process_hysteria) 243 | 244 | #hysteria2 URLs 245 | process_urls('./urls/hysteria2_urls.txt', process_hysteria2) 246 | 247 | #xray URLs 248 | process_urls('./urls/xray_urls.txt', process_xray) 249 | 250 | # 读取普通的配置文件内容 251 | with open('./templates/clash_template.yaml', 'r', encoding='utf-8') as file: 252 | config_data = yaml.safe_load(file) 253 | 254 | # 读取warp配置文件内容 255 | with open('./templates/clash_warp_template.yaml', 'r', encoding='utf-8') as file: 256 | config_warp_data = yaml.safe_load(file) 257 | 258 | # 添加合并后的代理到proxies部分 259 | config_data['proxies'].extend(merged_proxies) 260 | config_warp_data['proxies'].extend(merged_proxies) 261 | 262 | # 更新自动选择和节点选择的proxies的name部分 263 | update_proxy_groups(config_data, merged_proxies) 264 | update_warp_proxy_groups(config_warp_data, merged_proxies) 265 | 266 | # 将更新后的数据写入到一个YAML文件中,并指定编码格式为UTF-8 267 | with open('./sub/merged_proxies_new.yaml', 'w', encoding='utf-8') as file: 268 | yaml.dump(config_data, file, sort_keys=False, allow_unicode=True) 269 | 270 | with open('./sub/merged_warp_proxies_new.yaml', 'w', encoding='utf-8') as file: 271 | yaml.dump(config_warp_data, file, sort_keys=False, allow_unicode=True) 272 | 273 | print("Successfully written to clash") -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==6.0.1 2 | -------------------------------------------------------------------------------- /templates/clash_template.yaml: -------------------------------------------------------------------------------- 1 | port: 7890 2 | allow-lan: true 3 | mode: rule 4 | log-level: info 5 | unified-delay: true 6 | global-client-fingerprint: chrome 7 | dns: 8 | enable: true 9 | listen: :53 10 | ipv6: true 11 | enhanced-mode: fake-ip 12 | fake-ip-range: 198.18.0.1/16 13 | default-nameserver: 14 | - 223.6.6.6 15 | - 8.8.4.4 16 | nameserver: 17 | - https://dns.alidns.com/dns-query 18 | - https://doh.pub/dns-query 19 | fallback: 20 | - https://1.0.0.1/dns-query 21 | - tls://dns.google 22 | fallback-filter: 23 | geoip: true 24 | geoip-code: CN 25 | ipcidr: 26 | - 240.0.0.0/4 27 | proxies: 28 | - { name: 'Template', type: trojan, server: 1, port: 1, password: 1} 29 | proxy-groups: 30 | - name: 节点选择 31 | type: select 32 | proxies: 33 | - 自动选择 34 | - DIRECT 35 | - name: 自动选择 36 | type: url-test 37 | url: https://www.gstatic.com/generate_204 38 | interval: 300 39 | tolerance: 50 40 | proxies: 41 | - Template 42 | rules: 43 | - GEOIP,LAN,DIRECT 44 | - GEOIP,CN,DIRECT 45 | - MATCH,节点选择 46 | -------------------------------------------------------------------------------- /templates/clash_warp_template.yaml: -------------------------------------------------------------------------------- 1 | port: 7890 2 | allow-lan: true 3 | mode: rule 4 | log-level: info 5 | unified-delay: true 6 | global-client-fingerprint: chrome 7 | dns: 8 | enable: true 9 | listen: :53 10 | ipv6: true 11 | enhanced-mode: fake-ip 12 | fake-ip-range: 198.18.0.1/16 13 | default-nameserver: 14 | - 223.5.5.5 15 | - 8.8.8.8 16 | nameserver: 17 | - https://dns.alidns.com/dns-query 18 | - https://doh.pub/dns-query 19 | fallback: 20 | - https://1.0.0.1/dns-query 21 | - tls://dns.google 22 | fallback-filter: 23 | geoip: true 24 | geoip-code: CN 25 | ipcidr: 26 | - 240.0.0.0/4 27 | proxies: 28 | - name: WARP 29 | type: wireguard 30 | server: engage.cloudflareclient.com 31 | port: 2408 32 | ip: 172.16.0.2 33 | ipv6: 2606:4700:110:87c0:ba32:773a:8d44:e353 34 | private-key: +HpHpY/KjSv5hJdGrN2ok1A6CKhCmTQv5Unwyul9S1g= 35 | public-key: bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= 36 | udp: true 37 | reserved: [0,0,0] 38 | remote-dns-resolve: true 39 | dns: [ 1.1.1.1, 8.8.8.8 ] 40 | dialer-proxy: "WARP前置节点" 41 | 42 | proxy-groups: 43 | - name: 节点选择 44 | type: select 45 | proxies: 46 | - WARP 47 | - 自动选择 48 | - 负载均衡 49 | - 手动选择 50 | - DIRECT 51 | - name: WARP前置节点 52 | type: select 53 | proxies: 54 | - 自动选择 55 | - 负载均衡 56 | - 手动选择 57 | 58 | - name: 自动选择 59 | type: url-test 60 | url: http://www.gstatic.com/generate_204 61 | interval: 300 62 | tolerance: 50 63 | proxies: 64 | - WARP 65 | - name: 手动选择 66 | type: select 67 | proxies: 68 | - WARP 69 | - name: 负载均衡 70 | type: load-balance #负载均衡 71 | proxies: 72 | - WARP 73 | url: 'http://www.gstatic.com/generate_204' 74 | interval: 300 75 | #lazy: true 76 | #disable-udp: true 77 | strategy: round-robin #作为前置节点⽤这个⽐较好 78 | 79 | 80 | rules: 81 | - GEOIP,LAN,DIRECT 82 | - GEOIP,CN,DIRECT 83 | - MATCH,节点选择 -------------------------------------------------------------------------------- /urls/clash_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/1/config.yaml 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/config.yaml 3 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/13/config.yaml 4 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/15/config.yaml 5 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/2/config.yaml 6 | https://raw.githubusercontent.com/Alvin9999/pac2/master/clash.meta2/3/config.yaml 7 | https://raw.githubusercontent.com/Alvin9999/pac2/master/quick/4/config.yaml 8 | https://raw.githubusercontent.com/Alvin9999/pac2/master/quick/3/config.yaml 9 | https://raw.githubusercontent.com/Alvin9999/pac2/master/quick/1/config.yaml 10 | https://raw.githubusercontent.com/Alvin9999/pac2/master/quick/config.yaml -------------------------------------------------------------------------------- /urls/hysteria2_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria2/config.json 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria2/1/config.json 3 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria2/2/config.json 4 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria2/13/config.json -------------------------------------------------------------------------------- /urls/hysteria_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria/1/config.json 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria/13/config.json 3 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria/2/config.json 4 | https://raw.githubusercontent.com/Alvin9999/pac2/master/hysteria/config.json -------------------------------------------------------------------------------- /urls/naiverproxy_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/PAC/master/naiveproxy/1/config.json 2 | https://raw.githubusercontent.com/Alvin9999/PAC/master/naiveproxy/config.json -------------------------------------------------------------------------------- /urls/sb_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/singbox/1/config.json 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/singbox/config.json -------------------------------------------------------------------------------- /urls/ss_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/ssr-wj/ssconfig.txt 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/ssr-wj/1/ssconfig.txt -------------------------------------------------------------------------------- /urls/v2_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/ripaojiedian/freenode/main/sub 2 | -------------------------------------------------------------------------------- /urls/xray_urls.txt: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/Alvin9999/pac2/master/xray/2/config.json 2 | https://raw.githubusercontent.com/Alvin9999/pac2/master/xray/3/config.json 3 | https://raw.githubusercontent.com/Alvin9999/pac2/master/xray/config.json --------------------------------------------------------------------------------