Skip to content

Instantly share code, notes, and snippets.

@wizos
Last active July 3, 2024 02:08
Show Gist options
  • Save wizos/3c0bb7dd4833498a4155e97dac792b1b to your computer and use it in GitHub Desktop.
Save wizos/3c0bb7dd4833498a4155e97dac792b1b to your computer and use it in GitHub Desktop.
将 ss, vmess, vless, trojan, hysteria, hysteria2 等链接格式的节点转化为 sing-box 格式的配置文件。可以放在 cloudflare worker 上
/**
* 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