Last active
July 3, 2024 02:08
-
-
Save wizos/3c0bb7dd4833498a4155e97dac792b1b to your computer and use it in GitHub Desktop.
将 ss, vmess, vless, trojan, hysteria, hysteria2 等链接格式的节点转化为 sing-box 格式的配置文件。可以放在 cloudflare worker 上
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Welcome to Cloudflare Workers! This is your first worker. | |
* | |
* - Run "npm run dev" in your terminal to start a development server | |
* - Open a browser tab at http://localhost:8787/ to see your worker in action | |
* - Run "npm run deploy" to publish your worker | |
* | |
* Learn more at https://developers.cloudflare.com/workers/ | |
*/ | |
export default { | |
/** | |
* @param {any} request | |
* @param {any} env | |
* @param {any} ctx | |
*/ | |
async fetch(request, env, ctx) { | |
return handler(request); | |
}, | |
}; | |
/** | |
* Many more examples available at: https://developers.cloudflare.com/workers/examples | |
* @param {Request} request | |
* @returns {Promise<Response>} | |
*/ | |
async function handler(request) { | |
const url = new URL(request.url); | |
const queryParams = url.searchParams; | |
let text = ""; | |
if (queryParams.get('sub')) { | |
text = await fetchRes(base64Decode(queryParams.get('sub'))); | |
if (isBase64(text)) { | |
text = base64Decode(text); | |
} | |
} else if (queryParams.get('node')) { | |
text = base64Decode(queryParams.get('node')); | |
} else { | |
return new Response('安能曲眉折腰事权贵,使我不得开心颜'); | |
} | |
const lines = text.split(/[(\r\n)\r\n]+/).map(uri => uri.trim()).filter(uri => uri); | |
const uniqueLines = [...new Set(lines)]; // 使用扩展运算符将 Set 转换为数组 | |
const results = deduplication( uniqueLines.map(parseLine).filter(config => config !== null) ); | |
if (results.length > 0) { | |
return new Response(JSON.stringify(echo(results), null, 2), { | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
} | |
return new Response('没有节点', { | |
status: 400, // 自定义状态码 | |
statusText: '没有节点' | |
}); | |
} | |
/** | |
* @param {string} uri | |
*/ | |
function parseLine(uri) { | |
try { | |
if (uri.startsWith('vmess://') || uri.startsWith('vless://') | |
|| uri.startsWith('trojan://') || uri.startsWith('trojango://') | |
|| uri.startsWith('hysteria://') || uri.startsWith('hy://') | |
|| uri.startsWith('hysteria2://') || uri.startsWith('hy2://') | |
|| uri.startsWith('ss://')) { | |
const protocol = uri.split('://')[0]; | |
let mainPart = uri.split('://')[1]; | |
// Decode Base64 if it is encoded | |
if (isBase64(mainPart)) { | |
// 此时 mainPart 可能是 uri,也有可能是 json | |
mainPart = base64Decode(mainPart); | |
} | |
// 参见 https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) | |
if (isJson(mainPart)) { | |
const jsonObject = JSON.parse(mainPart); | |
// console.log("对象转义:" + JSON.stringify(jsonObject)); | |
return parseV2RayNGJson(jsonObject); | |
} else { | |
let decodedURI = protocol + "://" + mainPart; | |
let url = new URL(decodedURI); | |
if (protocol === 'ss' && !url.password) { | |
const decodeUsernameAndPW = decodeURIComponent(url.username); | |
decodedURI = decodedURI.replace(decodeUsernameAndPW, base64Decode(decodeUsernameAndPW)); | |
// console.log("解码0:" + encodeURI(decodedURI) ); | |
// console.log("解码1:" + url.username); | |
// console.log("解码2:" + decodeURIComponent(url.username)); | |
url = new URL(decodedURI); | |
// url.username = base64Decode(decodeURIComponent(url.username)); | |
} | |
const result = {}; | |
// console.log("反编码 url B: " + url); | |
result.server = decodeURIComponent(url.hostname); | |
if (url.port) { | |
if ("string" === typeof url.port) { | |
result.server_port = parseInt(url.port); | |
} else { | |
result.server_port = url.port; | |
} | |
} else if (protocol === 'ss' || protocol === 'hy' || protocol === 'hy2' || protocol === 'hysteria' || protocol === 'hysteria2') { | |
result.server_port = 443; | |
} else { | |
result.server_port = 80; | |
} | |
if (protocol === 'ss') { | |
result.network = "tcp"; | |
} | |
if (protocol === 'vless') { | |
result.packet_encoding = "xudp"; | |
} | |
if (protocol === 'hy' || protocol === 'hysteria') { | |
parseHysteria1URI(url, protocol, result); | |
} else if (protocol === 'hy2' || protocol === 'hysteria2') { | |
parseHysteria2URI(url, protocol, result); | |
} else { | |
parseDuckSoftURI(url, protocol, result); | |
} | |
result.tag = decodeURIComponent(url.hash).replace('#', "") || (result.server + ":" + result.server_port); | |
return result; | |
} | |
} | |
} catch (error) { | |
} | |
return null; | |
} | |
/** | |
* @param {{ add: any; port: any; id: any; aid: number; net: string; type: string; scy: string; tls: string; sni: any; alpn: any; path: string; host: string; ps: string; }} jsonObject | |
*/ | |
function parseV2RayNGJson(jsonObject) { | |
const result = {}; | |
result.type = 'vmess'; | |
result.server = jsonObject.add; | |
result.server_port = jsonObject.port; | |
result.uuid = jsonObject.id; | |
result.alter_id = jsonObject.aid || 0; | |
// 加密方式 | |
result.security = jsonObject.scy || "auto"; | |
if (jsonObject.tls === 'tls' || jsonObject.tls === 'reality') { | |
let tls = {}; | |
tls.enabled = true; | |
tls.server_name = jsonObject.sni || jsonObject.add; | |
if (jsonObject.alpn) { | |
tls.alpn = jsonObject.alpn.split(/,|\n/).map((/** @type {string} */ item) => item.trim()).filter((/** @type {string} */ item) => item); | |
} | |
tls.insecure = false; | |
result.tls = tls; | |
} | |
let transport = {}; | |
if (jsonObject.net === 'ws') { | |
transport.type = 'ws'; | |
if (!jsonObject.path) { | |
transport.path = "/"; | |
} else if (jsonObject.path.includes("?ed=")) { | |
const path = jsonObject.path.split("?ed="); | |
transport.path = path[0]; | |
transport.max_early_data = parseInt(path[1]) || 2048; | |
transport.early_data_header_name = "Sec-WebSocket-Protocol"; | |
} else { | |
transport.path = jsonObject.path; | |
} | |
if (jsonObject.host) { | |
transport.headers = { Host: jsonObject.host }; | |
} | |
} else if (jsonObject.net === 'http' || jsonObject.net === 'tcp' && jsonObject.type === 'http') { | |
transport.type = 'http'; | |
if (result.security === 'tls') { | |
transport.method = "GET"; | |
} | |
if (jsonObject.host) { | |
transport.host = jsonObject.host.split(','); | |
} | |
transport.path = jsonObject.path || '/'; | |
} else if (jsonObject.net === 'quic') { | |
transport.type = 'quic'; | |
} else if (jsonObject.net === 'grpc') { | |
transport.type = "grpc"; | |
transport.service_name = jsonObject.path; | |
} else if (jsonObject.net === 'httpupgrade') { | |
transport.type = "httpupgrade"; | |
transport.host = jsonObject.host; | |
transport.path = jsonObject.path; | |
} | |
if (Object.keys(transport).length !== 0) { | |
result.transport = transport; | |
} | |
result.tag = jsonObject.ps || (result.server + ":" + result.server_port); | |
// console.log(result); | |
return result; | |
} | |
/** | |
* @param {URL} url | |
* @param {string} protocol | |
* @param {{ server?: string; tag?: string; type?: any; tls?: any; up_mbps?: any; down_mbps?: any; obfs?: any; }} result | |
*/ | |
function parseHysteria1URI(url, protocol, result) { | |
// const result = {}; | |
const params = new URLSearchParams(url.search); | |
result.type = 'hysteria'; | |
let tls = {}; | |
tls.enabled = true; | |
if (params.get("peer")) { | |
tls.server_name = params.get("peer"); | |
} | |
if (params.get("alpn")) { | |
tls.alpn = params.get("alpn").split(/,|\n/).map(item => item.trim()).filter(item => item); | |
} | |
if (params.get("insecure") || params.get("insecure") === "1") { | |
tls.insecure = true; | |
} else { | |
tls.insecure = false; | |
} | |
result.tls = tls; | |
if (params.get("upmbps")) { | |
result.up_mbps = parseInt(params.get("upmbps")); | |
} | |
if (params.get("downmbps")) { | |
result.down_mbps = parseInt(params.get("downmbps")); | |
} | |
if (params.get("obfsParam")) { | |
result.obfs = params.get("obfsParam"); | |
} | |
if (params.get("obfs-password")) { | |
result.obfs = params.get("obfsParam"); | |
} | |
return result; | |
} | |
/** | |
* @param {URL} url | |
* @param {string} protocol | |
* @param {{ server?: string; tag?: string; type?: any; tls?: any; up_mbps?: any; down_mbps?: any; obfs?: any; pinSHA256?: any; }} result | |
*/ | |
function parseHysteria2URI(url, protocol, result) { | |
// const result = {}; | |
const params = new URLSearchParams(url.search); | |
result.type = 'hysteria2'; | |
let tls = {}; | |
tls.enabled = true; | |
if (params.get("peer")) { | |
tls.server_name = params.get("peer"); | |
} | |
if (params.get("alpn")) { | |
tls.server_name = params.get("alpn").split(/,|\n/).map(item => item.trim()).filter(item => item); | |
} | |
if (params.get("insecure") || params.get("insecure") === "1") { | |
tls.insecure = true; | |
} else { | |
tls.insecure = false; | |
} | |
result.tls = tls; | |
if (params.get("upmbps")) { | |
result.up_mbps = parseInt(params.get("upmbps")); | |
} | |
if (params.get("downmbps")) { | |
result.down_mbps = parseInt(params.get("downmbps")); | |
} | |
if (params.get("obfs")) { | |
result.obfs = params.get("obfs"); | |
} | |
if (params.get("obfs-password")) { | |
result.obfs = params.get("obfs-password"); | |
} | |
if (params.get("pinSHA256")) { | |
result.pinSHA256 = params.get("pinSHA256"); | |
} | |
return result; | |
} | |
const allowableShadowsocksMethod = new Set(["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"]); | |
/** | |
* @param {URL} url | |
* @param {string} protocol | |
* @param {{ server: any; tag?: string; type?: any; method?: any; password?: any; plugin?: any; plugin_opts?: any; uuid?: any; security?: any; tls?: any; network?: any; transport?: any; }} result | |
*/ | |
function parseDuckSoftURI(url, protocol, result) { | |
// console.log("反编码 url B: " + url); | |
// const result = {}; | |
const params = new URLSearchParams(url.search); | |
if (protocol === 'ss') { | |
result.type = 'shadowsocks'; | |
result.method = decodeURIComponent(url.username); | |
if (!allowableShadowsocksMethod.has(result.method)) { | |
throw Error("不支持该加密方式"); | |
} | |
result.password = decodeURIComponent(url.password); | |
let pluginParam = params.get("plugin"); | |
if (pluginParam) { | |
const plugin = pluginParam.split(';'); | |
result.plugin = plugin[0]; | |
result.plugin_opts = plugin[1]; | |
} | |
} else if (protocol === 'vmess') { | |
result.type = 'vmess'; | |
result.uuid = decodeURIComponent(url.username); | |
} else if (protocol === 'vless') { | |
result.type = 'vless'; | |
result.uuid = decodeURIComponent(url.username); | |
} else if (protocol === 'trojan') { | |
result.type = 'trojan'; | |
result.password = decodeURIComponent(url.username); | |
} | |
let security = ''; | |
if (params.get("security")) { | |
security = params.get("security"); | |
} else if (protocol === 'trojan') { | |
security = "tls"; | |
} else { | |
security = "none"; | |
} | |
if (protocol === "vmess") { | |
result.security = security; | |
} | |
if (security === "tls" || security === "reality") { | |
let tls = {}; | |
tls.enabled = true; | |
tls.server_name = params.get("sni") || decodeURIComponent(url.hostname); | |
if (params.get("alpn")) { | |
tls.alpn = params.get("alpn").split(/,|\n/).map(item => item.trim()).filter(item => item); | |
} | |
if (params.get("allowInsecure") || params.get("allowInsecure") === "1") { | |
tls.insecure = true; | |
} else { | |
tls.insecure = false; | |
} | |
result.tls = tls; | |
} | |
if (params.get("type") === 'tcp') { | |
if (params.get("headerType") === "http") { | |
if (params.get("host")) { | |
params.set("type", "http"); | |
} | |
} | |
} | |
let transport = {}; | |
let network = params.get("type"); | |
if (network === 'ws') { | |
transport.type = 'ws'; | |
if (!params.get("path")) { | |
transport.path = "/"; | |
} else { | |
if (params.get("path") && params.get("path").indexOf("?ed=") !== -1) { | |
const path = params.get("path").split("?ed="); | |
transport.path = path[0]; | |
transport.max_early_data = parseInt(path[1]) || 2048; | |
transport.early_data_header_name = "Sec-WebSocket-Protocol"; | |
} else { | |
transport.path = params.get("path"); | |
} | |
} | |
transport.headers = { Host: result.server }; | |
if (params.get("ed")) { | |
const edInt = parseInt(params.get("ed")); | |
if (edInt > 0) { | |
transport.max_early_data = edInt; | |
} | |
} | |
if (params.get("eh")) { | |
transport.early_data_header_name = params.get("eh"); | |
} | |
} else if (network === "h2" || network === "http") { | |
transport.type = 'http'; | |
if (security === 'tls') { | |
transport.method = "GET"; | |
} | |
if (params.get("host")) { | |
transport.host = params.get("host").split(","); | |
} | |
transport.path = params.get("path") || '/'; | |
} else if (network === 'quic') { | |
transport.type = 'quic'; | |
} else if (network === 'grpc') { | |
transport.type = 'grpc'; | |
if (params.get("path")) { | |
transport.service_name = params.get("path"); | |
} | |
} else if (network === 'httpupgrade') { | |
transport.type = 'httpupgrade'; | |
if (params.get("host")) { | |
transport.host = params.get("host"); | |
} | |
if (params.get("path")) { | |
transport.service_name = params.get("path"); | |
} | |
} | |
if (Object.keys(transport).length !== 0) { | |
result.transport = transport; | |
} | |
return result; | |
} | |
/** | |
* @param {string} str | |
*/ | |
function base64Decode(str) { | |
// console.log("解密:" + str); | |
const bytes = new Uint8Array(atob(str).split('').map(c => c.charCodeAt(0))); | |
const decoder = new TextDecoder('utf-8'); | |
return decoder.decode(bytes); | |
} | |
/** | |
* @param {string} str | |
*/ | |
function isBase64(str) { | |
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; | |
return base64Regex.test(str); | |
} | |
/** | |
* @param {string} str | |
*/ | |
function isJson(str) { | |
try { | |
JSON.parse(str); | |
return true; | |
} catch (e) { | |
return false; | |
} | |
} | |
/** | |
* @param {RequestInfo<unknown, CfProperties<unknown>>} url | |
*/ | |
async function fetchRes(url) { | |
let response = await fetch(url, { | |
headers: new Headers({ | |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36', | |
}) | |
}); | |
try { | |
return await response.text(); | |
} catch (e) { | |
console.log("报错:" + e); | |
return ''; | |
} | |
} | |
/** | |
* @param {any[]} nodes | |
*/ | |
function deduplication(nodes) { | |
const grouped = nodes.reduce((acc, obj) => { | |
const key = obj.tag; | |
if (!acc[key]) { | |
acc[key] = []; | |
} | |
acc[key].push(obj); | |
return acc; | |
}, {}); | |
// 在每个组内进行比较 | |
for (const tag in grouped) { | |
const group = grouped[tag]; | |
for (let i = 0; i < group.length; i++) { | |
const obj1 = group[i]; | |
for (let j = i + 1; j < group.length; j++) { | |
const obj2 = group[j]; | |
if (obj1.server !== obj2.server || obj1.server_port !== obj2.server_port) { | |
obj1.tag = (obj1.server + ":" + obj1.server_port + "#" + Date.now()); | |
}else if(obj1.server === obj2.server && obj1.server_port === obj2.server_port){ | |
group[i] = null; | |
} | |
} | |
} | |
} | |
// 将分组后的数据拆分成原来的 list 列表 | |
return Object.values(grouped).flat().filter(config => config !== null); | |
} | |
/** | |
* @param {any[]} nodes | |
*/ | |
function echo(nodes) { | |
const names = nodes.map(obj => obj.tag); | |
const pre = ["fastly", "direct"]; | |
// console.log(names); | |
const config = { | |
"log": { | |
"level": "debug", | |
"timestamp": true | |
}, | |
"dns": { | |
"servers": [ | |
{ | |
"tag": "proxyDns", | |
"address": "https://8.8.8.8/dns-query", | |
"detour": "proxy" | |
}, | |
{ | |
"tag": "localDns", | |
"address": "https://223.5.5.5/dns-query", | |
"detour": "direct" | |
}, | |
{ | |
"tag": "block", | |
"address": "rcode://success" | |
} | |
], | |
"rules": [ | |
{ | |
"domain": [ | |
"icloudnative.io", | |
"fuckcloudnative.io", | |
"sealos.io", | |
"ghproxy.com", | |
"cdn.jsdelivr.net", | |
"testingcf.jsdelivr.net" | |
], | |
"server": "localDns" | |
}, | |
{ | |
"domain_suffix": [ | |
"wonderfulday27.live", | |
"sis001.com", | |
"dqzboy.com" | |
], | |
"server": "proxyDns" | |
}, | |
{ | |
"process_name": [ | |
"TencentMeeting", | |
"NemoDesktop", | |
"ToDesk", | |
"ToDesk_Service", | |
"WeChat", | |
"Tailscale", | |
"wireguard-go", | |
"Tunnelblick", | |
"softwareupdated", | |
"kubectl" | |
], | |
"server": "localDns" | |
}, | |
{ | |
"process_name": [ | |
"DropboxMacUpdate", | |
"Dropbox" | |
], | |
"server": "proxyDns" | |
}, | |
{ | |
"package_name": [ | |
"com.google.android.youtube", | |
"com.android.vending", | |
"org.telegram.messenger", | |
"org.telegram.plus" | |
], | |
"server": "proxyDns" | |
}, | |
{ | |
"rule_set": "geosite-category-ads-all", | |
"server": "block" | |
}, | |
{ | |
"outbound": "any", | |
"server": "localDns", | |
"disable_cache": true | |
}, | |
{ | |
"rule_set": "geosite-cn", | |
"server": "localDns" | |
}, | |
{ | |
"clash_mode": "direct", | |
"server": "localDns" | |
}, | |
{ | |
"clash_mode": "global", | |
"server": "proxyDns" | |
}, | |
{ | |
"rule_set": "geosite-geolocation-!cn", | |
"server": "proxyDns" | |
} | |
], | |
"final": "localDns", | |
"strategy": "ipv4_only" | |
}, | |
"inbounds": [ | |
{ | |
"type": "tun", | |
"mtu": 9000, | |
"inet4_address": "172.19.0.1/30", | |
"auto_route": true, | |
"strict_route": true, | |
"inet4_route_address": [ | |
"0.0.0.0/1", | |
"128.0.0.0/1" | |
], | |
"inet4_route_exclude_address": [ | |
"192.168.0.0/16", | |
"10.0.0.0/8", | |
"172.16.0.0/12", | |
"100.64.0.0/10" | |
], | |
"exclude_package": [ | |
"cmb.pb", | |
"cn.gov.pbc.dcep", | |
"com.MobileTicket", | |
"com.adguard.android", | |
"com.ainemo.dragoon", | |
"com.alibaba.android.rimet", | |
"com.alicloud.databox", | |
"com.amazing.cloudisk.tv", | |
"com.autonavi.minimap", | |
"com.bilibili.app.in", | |
"com.bishua666.luxxx1", | |
"com.cainiao.wireless", | |
"com.chebada", | |
"com.chinamworld.main", | |
"com.cmbchina.ccd.pluto.cmbActivity", | |
"com.coolapk.market", | |
"com.ctrip.ct", | |
"com.dianping.v1", | |
"com.douban.frodo", | |
"com.eg.android.AlipayGphone", | |
"com.farplace.qingzhuo", | |
"com.hanweb.android.zhejiang.activity", | |
"com.leoao.fitness", | |
"com.lucinhu.bili_you", | |
"com.mikrotik.android.tikapp", | |
"com.moji.mjweather", | |
"com.motorola.cn.calendar", | |
"com.motorola.cn.lrhealth", | |
"com.netease.cloudmusic", | |
"com.sankuai.meituan", | |
"com.sina.weibo", | |
"com.smartisan.notes", | |
"com.sohu.inputmethod.sogou.moto", | |
"com.sonelli.juicessh", | |
"com.ss.android.article.news", | |
"com.ss.android.lark", | |
"com.ss.android.ugc.aweme", | |
"com.tailscale.ipn", | |
"com.taobao.idlefish", | |
"com.taobao.taobao", | |
"com.tencent.mm", | |
"com.tencent.mp", | |
"com.tencent.soter.soterserver", | |
"com.tencent.wemeet.app", | |
"com.tencent.weread", | |
"com.tencent.wework", | |
"com.ttxapps.wifiadb", | |
"com.unionpay", | |
"com.unnoo.quan", | |
"com.wireguard.android", | |
"com.xingin.xhs", | |
"com.xunmeng.pinduoduo", | |
"com.zui.zhealthy", | |
"com.zhihu.android", | |
"ctrip.android.view", | |
"io.kubenav.kubenav", | |
"org.geekbang.geekTime", | |
"tv.danmaku.bili" | |
], | |
"udp_timeout": "5m0s", | |
"stack": "mixed", | |
"platform": { | |
"http_proxy": { | |
"enabled": true, | |
"server": "127.0.0.1", | |
"server_port": 2080 | |
} | |
}, | |
"sniff": true | |
}, | |
{ | |
"type": "mixed", | |
"listen": "127.0.0.1", | |
"listen_port": 2080, | |
"sniff": true | |
} | |
], | |
"outbounds": [ | |
{ | |
"type": "selector", | |
"tag": "proxy", | |
"outbounds": pre.concat(names) | |
}, | |
{ | |
"type": "urltest", | |
"tag": "fastly", | |
"outbounds": names, | |
"url": "http://www.gstatic.com/generate_204", | |
"interval": "10m0s", | |
"tolerance": 50 | |
}, | |
{ | |
"type": "selector", | |
"tag": "🤖 OpenAI", | |
"outbounds": [ | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🌌 Google", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "📟 Telegram", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🐦 Twitter", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "👤 Facebook", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🛍️ Amazon", | |
"outbounds": [ | |
"direct", | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🍎 Apple", | |
"outbounds": [ | |
"direct", | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🧩 Microsoft", | |
"outbounds": [ | |
"direct", | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🎮 Game", | |
"outbounds": [ | |
"direct", | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "📺 Bilibili", | |
"outbounds": [ | |
"direct", | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🎬 MediaVideo", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🌏 !cn", | |
"outbounds": [ | |
"🇭🇰 香港节点", | |
"🇹🇼 台湾节点", | |
"🇸🇬 狮城节点", | |
"🇯🇵 日本节点", | |
"🇺🇸 美国节点", | |
"✈️ 其他节点" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🇭🇰 香港节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🇹🇼 台湾节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🇸🇬 狮城节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🇯🇵 日本节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🇺🇸 美国节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "✈️ 其他节点", | |
"outbounds": [ | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🌏 cn", | |
"outbounds": [ | |
"direct", | |
"proxy" | |
] | |
}, | |
{ | |
"type": "selector", | |
"tag": "🛑 AdBlock", | |
"outbounds": [ | |
"block", | |
"direct" | |
] | |
}, | |
{ | |
"type": "direct", | |
"tag": "direct" | |
}, | |
{ | |
"type": "dns", | |
"tag": "dns-out" | |
}, | |
{ | |
"type": "block", | |
"tag": "block" | |
}, | |
], | |
"route": { | |
"rules": [ | |
{ | |
"protocol": "dns", | |
"outbound": "dns-out" | |
}, | |
{ | |
"network": "udp", | |
"port": 443, | |
"outbound": "block" | |
}, | |
{ | |
"rule_set": "geosite-category-ads-all", | |
"outbound": "🛑 AdBlock" | |
}, | |
{ | |
"clash_mode": "direct", | |
"outbound": "direct" | |
}, | |
{ | |
"clash_mode": "global", | |
"outbound": "proxy" | |
}, | |
{ | |
"domain_suffix": [ | |
"appcenter.ms", | |
"app-measurement.com", | |
"firebase.io", | |
"crashlytics.com", | |
"google-analytics.com" | |
], | |
"outbound": "block" | |
}, | |
{ | |
"rule_set": "geosite-openai", | |
"outbound": "🤖 OpenAI" | |
}, | |
{ | |
"rule_set": [ | |
"geosite-youtube", | |
"geoip-google", | |
"geosite-google", | |
"geosite-github" | |
], | |
"outbound": "🌌 Google" | |
}, | |
{ | |
"rule_set": "geoip-telegram", | |
"outbound": "📟 Telegram" | |
}, | |
{ | |
"rule_set": "geosite-telegram", | |
"outbound": "📟 Telegram" | |
}, | |
{ | |
"rule_set": "geoip-twitter", | |
"outbound": "🐦 Twitter" | |
}, | |
{ | |
"rule_set": "geosite-twitter", | |
"outbound": "🐦 Twitter" | |
}, | |
{ | |
"rule_set": "geoip-facebook", | |
"outbound": "👤 Facebook" | |
}, | |
{ | |
"rule_set": [ | |
"geosite-facebook", | |
"geosite-instagram" | |
], | |
"outbound": "👤 Facebook" | |
}, | |
{ | |
"rule_set": "geosite-amazon", | |
"outbound": "🛍️ Amazon" | |
}, | |
{ | |
"rule_set": "geosite-apple", | |
"outbound": "🍎 Apple" | |
}, | |
{ | |
"rule_set": "geosite-microsoft", | |
"outbound": "🧩 Microsoft" | |
}, | |
{ | |
"rule_set": "geosite-category-games", | |
"outbound": "🎮 Game" | |
}, | |
{ | |
"rule_set": "geosite-bilibili", | |
"outbound": "📺 Bilibili" | |
}, | |
{ | |
"rule_set": "geosite-wechat", | |
"outbound": "🌏 cn" | |
}, | |
{ | |
"rule_set": "geoip-netflix", | |
"outbound": "🎬 MediaVideo" | |
}, | |
{ | |
"rule_set": [ | |
"geosite-tiktok", | |
"geosite-netflix", | |
"geosite-hbo", | |
"geosite-disney", | |
"geosite-primevideo" | |
], | |
"outbound": "🎬 MediaVideo" | |
}, | |
{ | |
"rule_set": "geosite-geolocation-!cn", | |
"outbound": "🌏 !cn" | |
}, | |
{ | |
"rule_set": "gfw", | |
"outbound": "🌏 !cn" | |
}, | |
{ | |
"ip_is_private": true, | |
"outbound": "🌏 cn" | |
}, | |
{ | |
"rule_set": "geoip-cn", | |
"outbound": "🌏 cn" | |
}, | |
{ | |
"rule_set": "geosite-cn", | |
"outbound": "🌏 cn" | |
} | |
], | |
"rule_set": [ | |
{ | |
"type": "remote", | |
"tag": "geoip-google", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geoip/google.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geoip-telegram", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geoip/telegram.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geoip-twitter", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geoip/twitter.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geoip-facebook", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geoip/facebook.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geoip-netflix", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geoip/netflix.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geoip-cn", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-openai", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-openai.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-youtube", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-youtube.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-google", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-google.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-github", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-github.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-telegram", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-telegram.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-twitter", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-twitter.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-facebook", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-facebook.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-instagram", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-instagram.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-amazon", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-amazon.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-apple", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-apple.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-microsoft", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-microsoft.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-category-games", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-games.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-bilibili", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-bilibili.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-wechat", | |
"format": "source", | |
"url": "https://raw.githubusercontent.com/yangchuansheng/sing-box-geosite/main/rule/WeChat.json", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-tiktok", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-tiktok.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-netflix", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-netflix.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-hbo", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-hbo.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-disney", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-disney.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-primevideo", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-primevideo.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-cn", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "gfw", | |
"format": "source", | |
"url": "https://raw.githubusercontent.com/Toperlock/sing-box-geosite/main/rule/gfw.json", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-geolocation-!cn", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-!cn.srs", | |
"download_detour": "fastly" | |
}, | |
{ | |
"type": "remote", | |
"tag": "geosite-category-ads-all", | |
"format": "binary", | |
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ads-all.srs", | |
"download_detour": "fastly" | |
} | |
], | |
"final": "proxy", | |
"auto_detect_interface": true | |
}, | |
"experimental": { | |
"cache_file": { | |
"enabled": true | |
}, | |
"clash_api": { | |
"external_controller": "127.0.0.1:9090", | |
"external_ui": "ui", | |
"default_mode": "rule" | |
} | |
} | |
}; | |
config.outbounds.push(...nodes); | |
return config; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment