diff --git a/config/clash.json b/config/clash.json index 04819b9..3457748 100644 --- a/config/clash.json +++ b/config/clash.json @@ -273,9 +273,9 @@ "name": "📺 哔哩哔哩", "type": "select", "proxies": [ + "🌍 全球直连", "🌐 节点选择", - "💡 自动选择", - "🌍 全球直连" + "💡 自动选择" ] }, { diff --git a/config/result_v6.csv b/config/result_v6.csv new file mode 100644 index 0000000..70ff1b0 --- /dev/null +++ b/config/result_v6.csv @@ -0,0 +1,201 @@ +IP:PORT, LOSS, DELAY +[2606:4700:d1::7cb3:2eac:791b:a339]:4198,0.00%,180 ms +[2606:4700:d1::2704:1d16:ebc6:b93e]:1180,0.00%,181 ms +[2606:4700:d1::b0ad:73b5:2649:160a]:1180,0.00%,181 ms +[2606:4700:d0::e7be:4de8:8761:2f26]:1180,0.00%,181 ms +[2606:4700:d0::4323:7e9a:782:f0d4]:1180,0.00%,181 ms +[2606:4700:d0::1dd5:ebc9:5fe0:14e3]:4198,0.00%,181 ms +[2606:4700:d0::7b63:af68:f114:38b5]:1180,0.00%,181 ms +[2606:4700:d0::89af:8176:90c3:a0a9]:1180,0.00%,181 ms +[2606:4700:d0::7cb3:2eac:791b:a339]:4198,0.00%,181 ms +[2606:4700:d0::7971:dd97:8037:e6de]:4198,0.00%,181 ms +[2606:4700:d1::da16:9261:a602:41c7]:4198,0.00%,182 ms +[2606:4700:d1::3c64:f06d:afc:8c99]:4198,0.00%,183 ms +[2606:4700:d1::2704:1d16:ebc6:b93e]:4198,0.00%,183 ms +[2606:4700:d1::d2b5:cea:dc66:20b8]:4198,0.00%,183 ms +[2606:4700:d1::d2b5:cea:dc66:20b8]:1180,0.00%,183 ms +[2606:4700:d1::5bfe:e354:4b9f:d692]:1180,0.00%,183 ms +[2606:4700:d0::537b:ddeb:486a:33df]:1180,0.00%,183 ms +[2606:4700:d0::b980:6563:e874:154a]:1180,0.00%,183 ms +[2606:4700:d0::7877:c11d:4da0:69c4]:4198,0.00%,183 ms +[2606:4700:d0::e7be:4de8:8761:2f26]:4198,0.00%,183 ms +[2606:4700:d0::e880:821:5e5e:627c]:1180,0.00%,183 ms +[2606:4700:d0::b980:6563:e874:154a]:4198,0.00%,183 ms +[2606:4700:d0::b0ad:73b5:2649:160a]:1180,0.00%,183 ms +[2606:4700:d0::7877:c11d:4da0:69c4]:1180,0.00%,183 ms +[2606:4700:d0::6f99:4eb2:7232:4006]:4198,0.00%,183 ms +[2606:4700:d0::ddf8:f0eb:585b:3cfc]:1180,0.00%,183 ms +[2606:4700:d0::d2b5:cea:dc66:20b8]:4198,0.00%,183 ms +[2606:4700:d0::3c64:f06d:afc:8c99]:1180,0.00%,183 ms +[2606:4700:d0::54f4:4a11:5bbe:1ad2]:4198,0.00%,183 ms +[2606:4700:d0::388c:402f:c066:1dcf]:4198,0.00%,183 ms +[2606:4700:d0::2704:1d16:ebc6:b93e]:4198,0.00%,183 ms +[2606:4700:d0::388c:402f:c066:1dcf]:1180,0.00%,183 ms +[2606:4700:d0::8222:16ed:cecc:e59d]:1180,0.00%,183 ms +[2606:4700:d1::2853:cac6:21b8:25a6]:4198,0.00%,188 ms +[2606:4700:d1::ed12:eff2:7979:cd85]:1180,0.00%,188 ms +[2606:4700:d1::388c:402f:c066:1dcf]:1180,0.00%,188 ms +[2606:4700:d1::6f99:4eb2:7232:4006]:4198,0.00%,188 ms +[2606:4700:d1::e880:821:5e5e:627c]:1180,0.00%,188 ms +[2606:4700:d1::c63c:8f7b:f537:2001]:4198,0.00%,188 ms +[2606:4700:d1::b1ba:329:9d82:b433]:1180,0.00%,188 ms +[2606:4700:d1::e880:821:5e5e:627c]:4198,0.00%,188 ms +[2606:4700:d1::da16:9261:a602:41c7]:1180,0.00%,188 ms +[2606:4700:d1::7b63:af68:f114:38b5]:1180,0.00%,192 ms +[2606:4700:d1::bbaf:edc7:2149:4b99]:4198,0.00%,192 ms +[2606:4700:d1::b1ba:329:9d82:b433]:4198,0.00%,192 ms +[2606:4700:d1::54f4:4a11:5bbe:1ad2]:4198,0.00%,192 ms +[2606:4700:d1::fab7:d891:3d77:f64f]:4198,0.00%,192 ms +[2606:4700:d0::b1ba:329:9d82:b433]:1180,0.00%,192 ms +[2606:4700:d1::bbaf:edc7:2149:4b99]:1180,0.00%,193 ms +[2606:4700:d1::a67a:49d9:b78b:8d95]:4198,0.00%,193 ms +[2606:4700:d1::537b:ddeb:486a:33df]:1180,0.00%,193 ms +[2606:4700:d1::dd6:148d:4706:1b11]:4198,0.00%,193 ms +[2606:4700:d0::b1ba:329:9d82:b433]:4198,0.00%,193 ms +[2606:4700:d1::d4f0:1c5d:e094:9e1f]:1180,0.00%,193 ms +[2606:4700:d1::fab7:d891:3d77:f64f]:1180,0.00%,193 ms +[2606:4700:d0::ddf8:f0eb:585b:3cfc]:4198,0.00%,193 ms +[2606:4700:d1::1dd5:ebc9:5fe0:14e3]:1180,0.00%,193 ms +[2606:4700:d1::3c53:f98c:e6ce:79e3]:1180,0.00%,194 ms +[2606:4700:d1::3c53:f98c:e6ce:79e3]:4198,0.00%,194 ms +[2606:4700:d1::b980:6563:e874:154a]:1180,0.00%,194 ms +[2606:4700:d1::1e25:105f:f5c3:5473]:4198,0.00%,195 ms +[2606:4700:d1::89af:8176:90c3:a0a9]:1180,0.00%,195 ms +[2606:4700:d1::1854:1ad:a5b3:934d]:4198,0.00%,195 ms +[2606:4700:d0::4323:7e9a:782:f0d4]:4198,0.00%,195 ms +[2606:4700:d0::5bfe:e354:4b9f:d692]:1180,0.00%,195 ms +[2606:4700:d0::3c53:f98c:e6ce:79e3]:4198,0.00%,195 ms +[2606:4700:d1::1854:1ad:a5b3:934d]:1180,0.00%,196 ms +[2606:4700:d1::89af:8176:90c3:a0a9]:4198,0.00%,196 ms +[2606:4700:d0::7b63:af68:f114:38b5]:4198,0.00%,196 ms +[2606:4700:d1::2eff:d4b:8b32:3a03]:1180,0.00%,197 ms +[2606:4700:d1::8222:16ed:cecc:e59d]:1180,0.00%,201 ms +[2606:4700:d1::f6d8:c775:2571:4e65]:4198,0.00%,201 ms +[2606:4700:d1::8222:16ed:cecc:e59d]:4198,0.00%,201 ms +[2606:4700:d1::7cb3:2eac:791b:a339]:1180,0.00%,201 ms +[2606:4700:d1::2853:cac6:21b8:25a6]:1180,0.00%,201 ms +[2606:4700:d1::f6d8:c775:2571:4e65]:1180,0.00%,201 ms +[2606:4700:d1::c989:92d5:994c:9d83]:4198,0.00%,201 ms +[2606:4700:d1::ed12:eff2:7979:cd85]:4198,0.00%,201 ms +[2606:4700:d1::c989:92d5:994c:9d83]:1180,0.00%,201 ms +[2606:4700:d1::ddf8:f0eb:585b:3cfc]:1180,0.00%,201 ms +[2606:4700:d1::b0ad:73b5:2649:160a]:4198,0.00%,201 ms +[2606:4700:d1::b980:6563:e874:154a]:4198,0.00%,201 ms +[2606:4700:d0::c14:8e4b:5911:1b4a]:4198,0.00%,201 ms +[2606:4700:d1::a67a:49d9:b78b:8d95]:1180,0.00%,201 ms +[2606:4700:d0::c14:8e4b:5911:1b4a]:1180,0.00%,201 ms +[2606:4700:d1::1dd5:ebc9:5fe0:14e3]:4198,0.00%,201 ms +[2606:4700:d0::54f4:4a11:5bbe:1ad2]:1180,0.00%,201 ms +[2606:4700:d1::9e4b:c08f:d46f:3116]:4198,0.00%,201 ms +[2606:4700:d0::bbaf:edc7:2149:4b99]:4198,0.00%,201 ms +[2606:4700:d1::9e4b:c08f:d46f:3116]:1180,0.00%,201 ms +[2606:4700:d0::a09a:45a7:6c6f:f31e]:1180,0.00%,201 ms +[2606:4700:d0::7971:dd97:8037:e6de]:1180,0.00%,201 ms +[2606:4700:d0::b0ad:73b5:2649:160a]:4198,0.00%,201 ms +[2606:4700:d0::ed12:eff2:7979:cd85]:1180,0.00%,201 ms +[2606:4700:d0::ed12:eff2:7979:cd85]:4198,0.00%,201 ms +[2606:4700:d0::929a:36f8:bace:cc9c]:4198,0.00%,201 ms +[2606:4700:d0::e880:821:5e5e:627c]:4198,0.00%,201 ms +[2606:4700:d0::89af:8176:90c3:a0a9]:4198,0.00%,201 ms +[2606:4700:d0::929a:36f8:bace:cc9c]:1180,0.00%,201 ms +[2606:4700:d0::dd6:148d:4706:1b11]:1180,0.00%,201 ms +[2606:4700:d0::da16:9261:a602:41c7]:4198,0.00%,202 ms +[2606:4700:d0::dd6:148d:4706:1b11]:4198,0.00%,202 ms +[2606:4700:d0::da16:9261:a602:41c7]:1180,0.00%,202 ms +[2606:4700:d0::3c64:f06d:afc:8c99]:4198,0.00%,202 ms +[2606:4700:d0::9e4b:c08f:d46f:3116]:4198,0.00%,202 ms +[2606:4700:d0::8222:16ed:cecc:e59d]:4198,0.00%,202 ms +[2606:4700:d0::5bfe:e354:4b9f:d692]:4198,0.00%,202 ms +[2606:4700:d0::1dd5:ebc9:5fe0:14e3]:1180,0.00%,202 ms +[2606:4700:d0::7cb3:2eac:791b:a339]:1180,0.00%,202 ms +[2606:4700:d0::3c53:f98c:e6ce:79e3]:1180,0.00%,202 ms +[2606:4700:d0::2704:1d16:ebc6:b93e]:1180,0.00%,202 ms +[2606:4700:d0::2eff:d4b:8b32:3a03]:4198,0.00%,202 ms +[2606:4700:d0::2eff:d4b:8b32:3a03]:1180,0.00%,202 ms +[2606:4700:d1::4bdc:3d40:be9:ee68]:4198,0.00%,206 ms +[2606:4700:d1::4323:7e9a:782:f0d4]:1180,0.00%,206 ms +[2606:4700:d1::4bdc:3d40:be9:ee68]:1180,0.00%,206 ms +[2606:4700:d1::388c:402f:c066:1dcf]:4198,0.00%,206 ms +[2606:4700:d1::2eff:d4b:8b32:3a03]:4198,0.00%,206 ms +[2606:4700:d1::7971:dd97:8037:e6de]:1180,0.00%,206 ms +[2606:4700:d1::7b63:af68:f114:38b5]:4198,0.00%,206 ms +[2606:4700:d1::2293:bf58:c8bc:198d]:4198,0.00%,206 ms +[2606:4700:d1::c14:8e4b:5911:1b4a]:1180,0.00%,206 ms +[2606:4700:d1::7877:c11d:4da0:69c4]:1180,0.00%,206 ms +[2606:4700:d1::2293:bf58:c8bc:198d]:1180,0.00%,206 ms +[2606:4700:d1::c14:8e4b:5911:1b4a]:4198,0.00%,206 ms +[2606:4700:d1::d4f0:1c5d:e094:9e1f]:4198,0.00%,206 ms +[2606:4700:d0::537b:ddeb:486a:33df]:4198,0.00%,207 ms +[2606:4700:d1::a09a:45a7:6c6f:f31e]:4198,0.00%,207 ms +[2606:4700:d1::1e25:105f:f5c3:5473]:1180,0.00%,207 ms +[2606:4700:d1::54f4:4a11:5bbe:1ad2]:1180,0.00%,207 ms +[2606:4700:d1::537b:ddeb:486a:33df]:4198,0.00%,207 ms +[2606:4700:d1::a09a:45a7:6c6f:f31e]:1180,0.00%,207 ms +[2606:4700:d0::bbaf:edc7:2149:4b99]:1180,0.00%,207 ms +[2606:4700:d0::f6d8:c775:2571:4e65]:4198,0.00%,207 ms +[2606:4700:d0::af95:d26c:c568:d508]:4198,0.00%,207 ms +[2606:4700:d0::f6d8:c775:2571:4e65]:1180,0.00%,207 ms +[2606:4700:d0::a67a:49d9:b78b:8d95]:4198,0.00%,207 ms +[2606:4700:d0::af95:d26c:c568:d508]:1180,0.00%,207 ms +[2606:4700:d0::a67a:49d9:b78b:8d95]:1180,0.00%,207 ms +[2606:4700:d0::9e4b:c08f:d46f:3116]:1180,0.00%,207 ms +[2606:4700:d0::d2b5:cea:dc66:20b8]:1180,0.00%,207 ms +[2606:4700:d0::1e25:105f:f5c3:5473]:1180,0.00%,207 ms +[2606:4700:d1::3c64:f06d:afc:8c99]:1180,0.00%,213 ms +[2606:4700:d1::7971:dd97:8037:e6de]:4198,0.00%,213 ms +[2606:4700:d1::c63c:8f7b:f537:2001]:1180,0.00%,214 ms +[2606:4700:d1::5bfe:e354:4b9f:d692]:4198,0.00%,214 ms +[2606:4700:d1::dd6:148d:4706:1b11]:1180,0.00%,214 ms +[2606:4700:d0::b9fc:4a86:a512:b1da]:4198,0.00%,214 ms +[2606:4700:d0::a09a:45a7:6c6f:f31e]:4198,0.00%,214 ms +[2606:4700:d0::698a:62be:fdaf:da9b]:1180,0.00%,214 ms +[2606:4700:d0::c989:92d5:994c:9d83]:4198,0.00%,214 ms +[2606:4700:d0::1e25:105f:f5c3:5473]:4198,0.00%,214 ms +[2606:4700:d1::4323:7e9a:782:f0d4]:4198,0.00%,221 ms +[2606:4700:d1::7877:c11d:4da0:69c4]:4198,0.00%,221 ms +[2606:4700:d1::6f99:4eb2:7232:4006]:1180,0.00%,221 ms +[2606:4700:d1::e7be:4de8:8761:2f26]:4198,0.00%,221 ms +[2606:4700:d1::929a:36f8:bace:cc9c]:4198,0.00%,221 ms +[2606:4700:d0::fab7:d891:3d77:f64f]:1180,0.00%,221 ms +[2606:4700:d1::8669:4ba7:363e:77dc]:1180,0.00%,222 ms +[2606:4700:d0::4bdc:3d40:be9:ee68]:4198,0.00%,222 ms +[2606:4700:d0::d4f0:1c5d:e094:9e1f]:4198,0.00%,222 ms +[2606:4700:d0::6f99:4eb2:7232:4006]:1180,0.00%,222 ms +[2606:4700:d0::6256:5e12:f8b0:612]:1180,0.00%,222 ms +[2606:4700:d0::d4f0:1c5d:e094:9e1f]:1180,0.00%,222 ms +[2606:4700:d0::d1ab:1f4f:4ba6:cf6f]:4198,0.00%,222 ms +[2606:4700:d0::8669:4ba7:363e:77dc]:4198,0.00%,222 ms +[2606:4700:d0::1854:1ad:a5b3:934d]:4198,0.00%,222 ms +[2606:4700:d1::45fe:77c0:b2d3:146b]:4198,0.00%,233 ms +[2606:4700:d1::45fe:77c0:b2d3:146b]:1180,0.00%,233 ms +[2606:4700:d1::d1ab:1f4f:4ba6:cf6f]:1180,0.00%,233 ms +[2606:4700:d1::e7be:4de8:8761:2f26]:1180,0.00%,233 ms +[2606:4700:d1::698a:62be:fdaf:da9b]:1180,0.00%,233 ms +[2606:4700:d1::b9fc:4a86:a512:b1da]:4198,0.00%,233 ms +[2606:4700:d1::b9fc:4a86:a512:b1da]:1180,0.00%,233 ms +[2606:4700:d1::af95:d26c:c568:d508]:4198,0.00%,233 ms +[2606:4700:d1::d1ab:1f4f:4ba6:cf6f]:4198,0.00%,233 ms +[2606:4700:d0::fab7:d891:3d77:f64f]:4198,0.00%,233 ms +[2606:4700:d0::4bdc:3d40:be9:ee68]:1180,0.00%,233 ms +[2606:4700:d0::45fe:77c0:b2d3:146b]:1180,0.00%,233 ms +[2606:4700:d0::8669:4ba7:363e:77dc]:1180,0.00%,234 ms +[2606:4700:d0::c989:92d5:994c:9d83]:1180,0.00%,234 ms +[2606:4700:d0::c63c:8f7b:f537:2001]:4198,0.00%,234 ms +[2606:4700:d0::2853:cac6:21b8:25a6]:1180,0.00%,234 ms +[2606:4700:d0::c63c:8f7b:f537:2001]:1180,0.00%,234 ms +[2606:4700:d0::2853:cac6:21b8:25a6]:4198,0.00%,234 ms +[2606:4700:d0::2293:bf58:c8bc:198d]:4198,0.00%,234 ms +[2606:4700:d0::1854:1ad:a5b3:934d]:1180,0.00%,234 ms +[2606:4700:d1::6256:5e12:f8b0:612]:1180,0.00%,237 ms +[2606:4700:d1::6256:5e12:f8b0:612]:4198,0.00%,237 ms +[2606:4700:d1::af95:d26c:c568:d508]:1180,0.00%,237 ms +[2606:4700:d1::8669:4ba7:363e:77dc]:4198,0.00%,237 ms +[2606:4700:d0::6256:5e12:f8b0:612]:4198,0.00%,237 ms +[2606:4700:d0::2293:bf58:c8bc:198d]:1180,0.00%,237 ms +[2606:4700:d1::929a:36f8:bace:cc9c]:1180,0.00%,247 ms +[2606:4700:d0::d1ab:1f4f:4ba6:cf6f]:1180,0.00%,248 ms +[2606:4700:d0::45fe:77c0:b2d3:146b]:4198,10.00%,282 ms +[2606:4700:d0::b9fc:4a86:a512:b1da]:1180,10.00%,291 ms +[2606:4700:d0::698a:62be:fdaf:da9b]:4198,10.00%,294 ms +[2606:4700:d1::ddf8:f0eb:585b:3cfc]:4198,10.00%,301 ms +[2606:4700:d1::698a:62be:fdaf:da9b]:4198,10.00%,304 ms diff --git a/config/sing-box.json b/config/sing-box.json new file mode 100644 index 0000000..5d73066 --- /dev/null +++ b/config/sing-box.json @@ -0,0 +1,118 @@ +{ + "log": { + "level": "info" + }, + "dns": { + "servers": [ + { + "tag": "google", + "address": "tls://8.8.8.8" + }, + { + "tag": "local", + "address": "223.5.5.5", + "detour": "direct" + } + ], + "rules": [ + { + "outbound": "any", + "server": "local" + }, + { + "clash_mode": "Direct", + "server": "local" + }, + { + "clash_mode": "Global", + "server": "google" + }, + { + "rule_set": "geosite-geolocation-cn", + "server": "local" + } + ], + "disable_cache": false + }, + "inbounds": [ + { + "type": "tun", + "inet4_address": "198.18.0.1/30", + "inet6_address": "fdfe:dcba:9876::1/126", + "mtu": 9000, + "auto_route": true, + "strict_route": false, + "stack": "gvisor", + "sniff": true, + "sniff_override_destination": true + } + ], + "outbounds": [ + { + "tag": "select", + "type": "selector", + "default": "urltest", + "outbounds": [ + "urltest" + ] + }, + { + "tag": "urltest", + "type": "urltest", + "outbounds": [] + }, + { + "tag": "direct", + "type": "direct" + }, + { + "tag": "block", + "type": "block" + }, + { + "tag": "dns-out", + "type": "dns" + } + ], + "route": { + "rules": [ + { + "type": "logical", + "mode": "or", + "rules": [{ "protocol": "dns" }, { "port": 53 }], + "outbound": "dns-out" + }, + { "ip_is_private": true, "outbound": "direct" }, + { "clash_mode": "Direct", "outbound": "direct" }, + { "clash_mode": "Global", "outbound": "select" }, + { + "type": "logical", + "mode": "or", + "rules": [ + { "port": 853 }, + { "network": "udp", "port": 443 }, + { "protocol": "stun" } + ], + "outbound": "dns-out" + }, + { + "rule_set": ["geoip-cn", "geosite-geolocation-cn"], + "outbound": "direct" + } + ], + "rule_set": [ + { + "type": "remote", + "tag": "geoip-cn", + "format": "binary", + "url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs" + }, + { + "type": "remote", + "tag": "geosite-geolocation-cn", + "format": "binary", + "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs" + } + ] + } +} diff --git a/scripts/get_entrypoints.sh b/scripts/get_entrypoints.sh index 942578d..9709189 100644 --- a/scripts/get_entrypoints.sh +++ b/scripts/get_entrypoints.sh @@ -1,8 +1,7 @@ #!/bin/bash # This script is modified from https://gitlab.com/Misaka-blog/warp-script/-/raw/main/files/get_entrypoints.sh - -archAffix(){ +archAffix() { case "$(uname -m)" in i386 | i686 ) echo '386' ;; x86_64 | amd64 ) echo 'amd64' ;; @@ -12,9 +11,12 @@ archAffix(){ esac } -endpointyx(){ - # 下载优选工具软件,感谢某匿名网友的分享的优选工具 - wget https://gitlab.com/Misaka-blog/warp-script/-/raw/main/files/warp-yxip/warp-linux-"$(archAffix)" -O warp +endpointyx() { + # 判断 wget 是否存在 + if ! command -v wget >/dev/null; then + # 下载优选工具软件,感谢某匿名网友的分享的优选工具 + wget https://gitlab.com/Misaka-blog/warp-script/-/raw/main/files/warp-yxip/warp-linux-"$(archAffix)" -O warp || { echo "下载优选工具失败"; exit 1; } + fi # 取消 Linux 自带的线程限制,以便生成优选 Endpoint IP ulimit -n 102400 @@ -24,9 +26,9 @@ endpointyx(){ # 读取 WARP Endpoint IP 优选工具生成的 Endpoint IP 段列表 process_result_csv() { - awk -F, '$3!="timeout ms" {print}' | - sort -t, -nk2 -nk3 | - uniq | + awk -F, '$3!="timeout ms" {print}' | + sort -t, -nk2 -nk3 | + uniq | head -11 } @@ -35,117 +37,82 @@ endpointyx(){ # 将优选结果移动到指定目录 if [ -n "$RUN_IN_DOCKER" ]; then - mv -f result.csv /app/config/result.csv + mv -f result.csv "/app/config/result${suffix}.csv" else - mv -f result.csv ./config/result.csv + mv -f result.csv "./config/result${suffix}.csv" fi - # 删除 WARP Endpoint IP 优选工具及其附属文件 - rm -f warp ip.txt + # 删除附属文件 + rm -f ip.txt } -endpoint4(){ - # 生成优选 WARP IPv4 Endpoint IP 段列表 - n=0 - iplist=100 - while true; do - temp[$n]=$(echo 162.159.192.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 162.159.193.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 162.159.195.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 162.159.204.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 188.114.96.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 188.114.97.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 188.114.98.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi - temp[$n]=$(echo 188.114.99.$(($RANDOM % 256))) - n=$(($n + 1)) - if [ $n -ge $iplist ]; then - break - fi +generate_random_ips() { + local iplist=100 + local count=0 + local ip_base=("162.159.192." "162.159.193." "162.159.195." "162.159.204." "188.114.96." "188.114.97." "188.114.98." "188.114.99.") + + while [ $count -lt $iplist ]; do + for base in "${ip_base[@]}"; do + temp[$count]="${base}$(($RANDOM % 256))" + ((count++)) + [ $count -ge $iplist ] && break + done done - while true; do - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 162.159.192.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 162.159.193.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 162.159.195.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 162.159.204.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 188.114.96.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 188.114.97.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 188.114.98.$(($RANDOM % 256))) - n=$(($n + 1)) - fi - if [ "$(echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u | wc -l)" -ge $iplist ]; then - break - else - temp[$n]=$(echo 188.114.99.$(($RANDOM % 256))) - n=$(($n + 1)) - fi + + # 确保列表中的 IP 地址是唯一的 + printf '%s\n' "${temp[@]}" | sort -u > ip.txt +} + +endpoint4() { + generate_random_ips + endpointyx +} + +generate_random_ipv6s() { + local iplist=100 + local n=0 + local temp + + while [ $n -lt $iplist ]; do + local hex1 hex2 hex3 hex4 + hex1=$(printf '%x\n' $((RANDOM * 2 + RANDOM % 2))) + hex2=$(printf '%x\n' $((RANDOM * 2 + RANDOM % 2))) + hex3=$(printf '%x\n' $((RANDOM * 2 + RANDOM % 2))) + hex4=$(printf '%x\n' $((RANDOM * 2 + RANDOM % 2))) + temp[$n]="[2606:4700:d0::$hex1:$hex2:$hex3:$hex4]" + ((n++)) + [ $n -ge $iplist ] && break + + temp[$n]="[2606:4700:d1::$hex1:$hex2:$hex3:$hex4]" + ((n++)) done - # 将生成的 IP 段列表放到 ip.txt 里,待程序优选 - echo "${temp[@]}" | sed -e 's/ /\n/g' | sort -u > ip.txt + # 确保列表中的 IP 地址是唯一的 + printf '%s\n' "${temp[@]}" | sort -u > ip.txt +} - # 启动优选程序 +endpoint6() { + generate_random_ipv6s endpointyx } -endpoint4 +suffix= + +# 检查传入参数,若含有-4或-6则优选对应的IP段 +while getopts "46" OPT; do + case $OPT in + 6) + suffix=_v6 + endpoint6 + ;; + 4) + endpoint4 + ;; # 默认优选 IPv4 + *) + echo "Usage: $0 [-4|-6]" && exit 1 + ;; + esac +done + +# 若未传入参数则优选 IPv4 +[ -z "$1" ] && endpoint4 diff --git a/services/subscription.py b/services/subscription.py index 08ead89..ad4a643 100644 --- a/services/subscription.py +++ b/services/subscription.py @@ -40,18 +40,22 @@ SURGE_RULE = open("./config/surge-rule.txt", "r", encoding="utf8").read() SURGE_SUB = open("./config/surge-sub.txt", "r", encoding="utf8").read() +SING_BOX = json.load(open("./config/sing-box.json", "r", encoding="utf8")) + GEOIP = GeoIP('./config/geolite/GeoLite2-Country.mmdb') def getRandomEntryPoints(best=False, - logger=logging.getLogger(__name__)): + logger=logging.getLogger(__name__), + ipv6=False): """ Get random entry points :param best: Whether to use the best entrypoints :param logger: + :param ipv6: Whether to use ipv6 entrypoints :return: list of entrypoints """ - entrypoints = getEntrypoints() + entrypoints = getEntrypoints(ipv6=ipv6) # If there is no entrypoints, return a message if entrypoints is None or len(entrypoints) == 0: @@ -62,7 +66,8 @@ def getRandomEntryPoints(best=False, logger.warning(f"Entrypoints is less than {RANDOM_COUNT}, only {len(entrypoints)} available.") random_points = entrypoints else: - random_points = random.sample(entrypoints, RANDOM_COUNT) if not best else getBestEntrypoints(RANDOM_COUNT) + random_points = random.sample(entrypoints, RANDOM_COUNT) if not best else getBestEntrypoints(RANDOM_COUNT, + ipv6=ipv6) return random_points, "" @@ -72,7 +77,8 @@ def generateClashSubFile(account: Account = None, best=False, proxy_format='full', random_name=False, - is_android=False): + is_android=False, + ipv6=False): """ Generate Clash subscription file :param random_name: Whether to use random name @@ -81,11 +87,12 @@ def generateClashSubFile(account: Account = None, :param logger: :param best: Whether to use the best entrypoints :param is_android: Whether the client is Android + :param ipv6: Whether to use ipv6 entrypoints :return: """ account = getCurrentAccount(logger) if account is None else account - random_points, msg = getRandomEntryPoints(best, logger) + random_points, msg = getRandomEntryPoints(best, logger, ipv6=ipv6) if random_points is None: return msg @@ -139,22 +146,28 @@ def generateClashSubFile(account: Account = None, def generateWireguardSubFile(account: Account = None, logger=logging.getLogger(__name__), - best=False): + best=False, + ipv6=False): """ Generate Wireguard subscription file :param account: :param logger: :param best: Whether to use the best entrypoints + :param ipv6: Whether to use ipv6 entrypoints :return: """ account = getCurrentAccount(logger) if account is None else account - entrypoints = getEntrypoints() + entrypoints = getEntrypoints(ipv6=ipv6) # If there is no entrypoints, return a message if entrypoints is None or len(entrypoints) == 0: return "No entrypoints available. Please try again later." - random_point = random.choice(entrypoints) if not best else getBestEntrypoints(1)[0] + random_point = random.choice(entrypoints) if not best else getBestEntrypoints(1, ipv6=ipv6)[0] + + ip = random_point.ip + if ipv6: + ip = f"[{ip}]" # ipv6 address should be wrapped in square brackets # Generate user configuration file text = f"""[Interface] @@ -166,7 +179,7 @@ def generateWireguardSubFile(account: Account = None, [Peer] PublicKey = {CF_CONFIG.get("publicKey")} AllowedIPs = 0.0.0.0/0, ::/0 -Endpoint = {random_point.ip}:{random_point.port} +Endpoint = {ip}:{random_point.port} PersistentKeepalive = 25 """ return text @@ -176,7 +189,8 @@ def generateSurgeSubFile(account: Account = None, logger=logging.getLogger(__name__), best=False, proxy_format='full', - random_name=False): + random_name=False, + ipv6=False): """ Generate Surge subscription file :param random_name: Whether to use random name @@ -184,11 +198,12 @@ def generateSurgeSubFile(account: Account = None, :param account: :param logger: :param best: Whether to use the best entrypoints + :param ipv6: Whether to use ipv6 entrypoints :return: """ account = getCurrentAccount(logger) if account is None else account - random_points, msg = getRandomEntryPoints(best, logger) + random_points, msg = getRandomEntryPoints(best, logger, ipv6) if random_points is None: return msg @@ -198,14 +213,18 @@ def generateSurgeSubFile(account: Account = None, # Use len() instead of RANDOM_COUNT because the entrypoints may be less than RANDOM_COUNT for i in range(len(random_points)): point = random_points[i] + ip = point.ip + if ipv6: + ip = f"[{ip}]" # ipv6 address should be wrapped in square brackets user_config.append( { - "self-ip": point.ip, + "self-ip": "172.16.0.2", "private-key": account.private_key, "dns-server": "1.1.1.1", "mtu": 1420, "peer": f'(public-key = {CF_CONFIG.get("publicKey")}, allowed-ips = "0.0.0.0/0, ::/0", ' - f'endpoint = {point.ip}:{point.port})' + f'endpoint = {ip}:{point.port})', + "endpoint": f"{ip}|{point.port}" }) surge_config = copy.deepcopy(SURGE) @@ -216,8 +235,12 @@ def generateSurgeSubFile(account: Account = None, for config in user_config: # random a name like 2FDEC93F, num and upper letter name = ''.join(random.sample(string.ascii_uppercase + string.digits, 8)) - country = GEOIP.lookup(config['self-ip']) - country_emoji = GEOIP.lookup_emoji(config['self-ip']) + + ip, port = config['endpoint'].split("|") + country = GEOIP.lookup(ip) + country_emoji = GEOIP.lookup_emoji(ip) + del config['endpoint'] + proxy_name = node_name_generator.next(country_emoji, country) surge_config[f'WireGuard {name}'] = config @@ -259,18 +282,20 @@ def generateSurgeSubFile(account: Account = None, def generateShadowRocketSubFile(account: Account = None, logger=logging.getLogger(__name__), best=False, - random_name=False): + random_name=False, + ipv6=False): """ Generate ShadowRocket subscription file :param account: :param logger: :param best: Whether to use the best entrypoints :param random_name: Whether to use random name + :param ipv6: Whether to use ipv6 entrypoints :return: """ account = getCurrentAccount(logger) if account is None else account - random_points, msg = getRandomEntryPoints(best, logger) + random_points, msg = getRandomEntryPoints(best, logger, ipv6) if random_points is None: return msg @@ -284,7 +309,10 @@ def generateShadowRocketSubFile(account: Account = None, country = GEOIP.lookup(point.ip) country_emoji = GEOIP.lookup_emoji(point.ip) name = node_name_generator.next(country_emoji, country) - url = f"wg://{point.ip}:{point.port}?publicKey={CF_CONFIG.get('publicKey')}&privateKey={account.private_key}" \ + ip = point.ip + if ipv6: + ip = f"[{ip}]" # ipv6 address should be wrapped in square brackets + url = f"wg://{ip}:{point.port}?publicKey={CF_CONFIG.get('publicKey')}&privateKey={account.private_key}" \ f"&dns=1.1.1.1,1.0.0.1" \ f"&ip=172.16.0.2&udp=1&flag={country}#{urllib.parse.quote(name)}" url_list.append(url) @@ -293,3 +321,73 @@ def generateShadowRocketSubFile(account: Account = None, sub_data = base64.b64encode(sub_data.encode("utf-8")).decode("utf-8") return sub_data + + +def generateSingBoxSubFile(account: Account = None, + logger=logging.getLogger(__name__), + random_name=False, + best=False, + ipv6=False): + """ + Generate SingBox subscription file + :param account: + :param logger: + :param random_name: Whether to use random name + :param best: Whether to use the best entrypoints + :param ipv6: Whether to use ipv6 entrypoints + :return: + """ + account = getCurrentAccount(logger) if account is None else account + + random_points, msg = getRandomEntryPoints(best, logger, ipv6) + if random_points is None: + return msg + + # Initialize NodeNameGenerator + node_name_generator = NodeNameGenerator(random_name) + + sing_box_json = copy.deepcopy(SING_BOX) + + # Generate outbounds + outbounds = [] + name_list = [] + + # Use len() instead of RANDOM_COUNT because the entrypoints may be less than RANDOM_COUNT + for i in range(len(random_points)): + point = random_points[i] + country = GEOIP.lookup(point.ip) + country_emoji = GEOIP.lookup_emoji(point.ip) + name = node_name_generator.next(country_emoji, country) + + outbounds.append( + { + "server": point.ip, + "server_port": point.port, + "peers": [{ + "server": point.ip, + "server_port": point.port, + "public_key": CF_CONFIG.get("publicKey"), + "pre_shared_key": "", + "allowed_ips": ['0.0.0.0/0', '::/0'] + }], + "tag": name, + "type": "wireguard", + "local_address": ["172.16.0.2/32"], + "private_key": account.private_key, + "peer_public_key": CF_CONFIG.get("publicKey"), + "system_interface": False, + "mtu": 1280 + } + ) + + name_list.append(name) + + sing_box_json["outbounds"].extend(outbounds) + + # Section Select + sing_box_json["outbounds"][0]["outbounds"].extend(name_list) + + # Section UrlTest + sing_box_json["outbounds"][1]["outbounds"].extend(name_list) + + return json.dumps(sing_box_json, ensure_ascii=False) diff --git a/services/web_service.py b/services/web_service.py index 020b028..14333ab 100644 --- a/services/web_service.py +++ b/services/web_service.py @@ -24,7 +24,7 @@ from services.account import resetAccountKey, doUpdateLicenseKey from services.common import * from services.subscription import generateClashSubFile, generateWireguardSubFile, generateSurgeSubFile, \ - generateShadowRocketSubFile + generateShadowRocketSubFile, generateSingBoxSubFile RATE_LIMIT_MAP = {} @@ -121,6 +121,8 @@ def httpAutoSub(): return httpSubscription("clash") elif "surge" in user_agent: # Surge return httpSubscription("surge") + elif "sing-box" in user_agent: # Sing Box + return httpSubscription("sing-box") # By default, return Clash return httpSubscription("clash") @@ -183,6 +185,13 @@ def httpSubscription(sub_type: str): best = request.args.get('best', 'false').lower() == "true" or False random_name = request.args.get('randomName', 'false').lower() == "true" or False proxy_format = request.args.get('proxyFormat', 'full').lower() + ipv6 = request.args.get('ipv6', 'false').lower() == "true" or False + + headers = { + 'Content-Type': 'application/x-yaml; charset=utf-8', + "Subscription-Userinfo": f"upload=0; download={account.usage}; total={account.quota}; " + f"expire=253388144714" + } if sub_type == "clash": # Clash @@ -191,61 +200,65 @@ def httpSubscription(sub_type: str): # https://github.com/vvbbnn00/WARP-Clash-API/issues/74 is_android = "android" in user_agent - fileData = generateClashSubFile(account, - logger, - best=best, - proxy_format=proxy_format, - random_name=random_name, - is_android=is_android) - headers = { - 'Content-Type': 'application/x-yaml; charset=utf-8', - 'Content-Disposition': f'attachment; filename=Clash-{fake.color_name()}.yaml', - "Subscription-Userinfo": f"upload=0; download={account.usage}; total={account.quota}; " - f"expire=253388144714" - } + file_data = generateClashSubFile(account, + logger, + best=best, + proxy_format=proxy_format, + random_name=random_name, + is_android=is_android, + ipv6=ipv6) + file_name = f'Clash-{fake.color_name()}.yaml' + elif sub_type == "wireguard": # Wireguard - fileData = generateWireguardSubFile(account, logger, best=best) - headers = { - 'Content-Type': 'application/x-conf; charset=utf-8', - 'Content-Disposition': f'attachment; filename={fake.lexify("????????????").lower()}.conf' - } + file_data = generateWireguardSubFile(account, + logger, + best=best, + ipv6=ipv6) + file_name = f'WireGuard-{fake.lexify("????????????").lower()}.conf' + elif sub_type == "surge": # Surge - fileData = generateSurgeSubFile(account, - logger, - best=best, - random_name=random_name, - proxy_format=proxy_format) - headers = { - 'Content-Type': 'text/plain; charset=utf-8', - 'Content-Disposition': 'attachment; filename=surge.conf', - "Subscription-Userinfo": f"upload=0; download={account.usage}; total={account.quota}; " - f"expire=253388144714" - } + file_data = generateSurgeSubFile(account, + logger, + best=best, + random_name=random_name, + proxy_format=proxy_format, + ipv6=ipv6) + file_name = f'Surge-{fake.color_name()}.conf' + elif sub_type == 'shadowrocket': # Shadowrocket - fileData = generateShadowRocketSubFile(account, logger, best=best, random_name=random_name) - headers = { - 'Content-Type': 'text/plain; charset=utf-8', - 'Content-Disposition': 'attachment; filename=Shadowrocket.txt', - "Subscription-Userinfo": f"upload=0; download={account.usage}; total={account.quota}; " - f"expire=253388144714" - } + file_data = generateShadowRocketSubFile(account, + logger, + best=best, + random_name=random_name, + ipv6=ipv6) + file_name = f'Shadowrocket-{fake.color_name()}.conf' + + elif sub_type == 'sing-box': # Sing Box + file_data = generateSingBoxSubFile(account, + logger, + best=best, + random_name=random_name, + ipv6=ipv6) + file_name = f'SingBox-{fake.color_name()}.json' + # This might be deprecated in the future. elif sub_type == "only_proxies": # Only proxies - fileData = generateClashSubFile(account, logger, best=best, proxy_format='with_groups', - random_name=random_name) - headers = { - 'Content-Type': 'application/x-yaml; charset=utf-8', - 'Content-Disposition': f'attachment; filename=Clash-{fake.color_name()}.yaml', - "Subscription-Userinfo": f"upload=0; download={account.usage}; total={account.quota}; " - f"expire=253388144714" - } + file_data = generateClashSubFile(account, + logger, + best=best, + proxy_format='with_groups', + random_name=random_name, + ipv6=ipv6) + file_name = f'Clash-{fake.color_name()}.yaml' + else: return { 'code': 400, 'message': 'Unsupported sub type.' }, 400 - response = make_response(fileData) + headers['Content-Disposition'] = f'attachment; filename="{file_name}"' + response = make_response(file_data) response.headers = headers return response diff --git a/templates/index.html b/templates/index.html index 571f560..b880420 100644 --- a/templates/index.html +++ b/templates/index.html @@ -115,6 +115,9 @@

WARP Clash 订阅地址生成器

+

@@ -160,11 +163,13 @@

账户信息

const best = document.getElementById('alwaysBest').checked; const randomName = document.getElementById('randomName').checked; const proxyFormat = document.getElementById('proxyFormat').value; + const ipv6 = document.getElementById('ipv6').checked; const baseUrl = location.protocol + '//' + location.host + '/'; const queryParams = new URLSearchParams({ best, randomName, - proxyFormat + proxyFormat, + ipv6 }); if (password.length) { diff --git a/utils/entrypoints.py b/utils/entrypoints.py index bc5a7ba..6acb9fc 100644 --- a/utils/entrypoints.py +++ b/utils/entrypoints.py @@ -14,16 +14,24 @@ along with this program; if not, see . """ +import copy import csv -from models import Entrypoint -from config import * from flask import current_app -ENTRYPOINTS = [] +from config import * +from models import Entrypoint + ENTRYPOINT_SCRIPT_PATH = './scripts/get_entrypoints.sh' -RESULT_LAST_MODIFIED = 0 + RESULT_PATH = './config/result.csv' +RESULT_LAST_MODIFIED = 0 + +RESULT_PATH_V6 = './config/result_v6.csv' +RESULT_LAST_MODIFIED_V6 = 0 + +ENTRYPOINTS = [] +ENTRYPOINTS_V6 = [] def readCsv(file_path): @@ -38,20 +46,33 @@ def readCsv(file_path): yield row -def reloadEntrypoints(): +def reloadEntrypoints(ipv6=False): """ Reload entrypoints from csv file + + :param ipv6: if load ipv6 entrypoints :return: list of entrypoints """ - current_app.logger.info(f"Reload entrypoints from {RESULT_PATH}") - global ENTRYPOINTS, RESULT_LAST_MODIFIED - RESULT_LAST_MODIFIED = os.path.getmtime(RESULT_PATH) - ENTRYPOINTS = [] - for row in readCsv(RESULT_PATH): + global ENTRYPOINTS, ENTRYPOINTS_V6, RESULT_LAST_MODIFIED, RESULT_LAST_MODIFIED_V6 + + result_file = RESULT_PATH_V6 if ipv6 else RESULT_PATH + current_app.logger.info(f"Reload entrypoints from {result_file}") + + if ipv6: + RESULT_LAST_MODIFIED_V6 = os.path.getmtime(result_file) + ENTRYPOINTS_V6 = [] + else: + RESULT_LAST_MODIFIED = os.path.getmtime(result_file) + ENTRYPOINTS = [] + + entrypoints = copy.copy(ENTRYPOINTS_V6 if ipv6 else ENTRYPOINTS) + + for row in readCsv(result_file): try: if row[0].lower() == 'ip:port': continue - ip, port = row[0].split(':') + ip, port = row[0].split(':') if not ipv6 else (row[0].split("]:")) + ip = ip.replace('[', '') if ipv6 else ip loss = float(row[1].replace('%', '')) delay = int(row[2].replace('ms', '')) @@ -60,41 +81,49 @@ def reloadEntrypoints(): entrypoint = Entrypoint() entrypoint.ip = ip - entrypoint.port = port + entrypoint.port = int(port) entrypoint.loss = loss entrypoint.delay = delay - ENTRYPOINTS.append(entrypoint) + entrypoints.append(entrypoint) except Exception as e: current_app.logger.error(f"Error when reading row: {row}, error: {e}") - return ENTRYPOINTS + return entrypoints -def getEntrypoints(): +def getEntrypoints(ipv6=False): """ Get entrypoints + + :param ipv6: if get ipv6 entrypoints :return: list of entrypoints """ - if not ENTRYPOINTS: - reloadEntrypoints() + entrypoints = copy.copy(ENTRYPOINTS_V6 if ipv6 else ENTRYPOINTS) + + if not entrypoints or len(entrypoints) == 0: + return reloadEntrypoints(ipv6) + + last_modified = RESULT_LAST_MODIFIED_V6 if ipv6 else RESULT_LAST_MODIFIED + result_file = RESULT_PATH_V6 if ipv6 else RESULT_PATH # Check if file has been modified - if RESULT_LAST_MODIFIED != os.path.getmtime(RESULT_PATH): - current_app.logger.info(f"File {RESULT_PATH} has been modified, will reload entrypoints.") - reloadEntrypoints() + if last_modified != os.path.getmtime(result_file): + current_app.logger.info(f"File {last_modified} has been modified, will reload entrypoints.") + return reloadEntrypoints(ipv6) - return ENTRYPOINTS + return entrypoints -def getBestEntrypoints(num=1): +def getBestEntrypoints(num=1, ipv6=False): """ Get best entrypoints :param num: number of entrypoints + :param ipv6: if get ipv6 entrypoints :return: list of entrypoints """ # sort by loss and delay - returnEntryPoints = sorted(getEntrypoints(), key=lambda x: (x.loss, x.delay))[:num] + returnEntryPoints = sorted(getEntrypoints(ipv6), key=lambda x: (x.loss, x.delay))[:num] return returnEntryPoints @@ -116,8 +145,13 @@ def optimizeEntrypoints(): file.write(data) file.close() - # Run ./scripts/get_entrypoint.sh - os.system("bash ./scripts/get_entrypoints.sh") + # Get ipv4 entrypoints + print("Getting IPv4 entrypoints") + os.system(f"bash {ENTRYPOINT_SCRIPT_PATH} -4") + + # Get ipv6 entrypoints + print("Getting IPv6 entrypoints") + os.system(f"bash {ENTRYPOINT_SCRIPT_PATH} -6") # if __name__ == '__main__': # reloadEntrypoints()