Chore: create rule set / Add China IP sing-box

This commit is contained in:
SukkaW
2024-08-14 02:29:03 +08:00
parent da50896ae0
commit 640bac160c
14 changed files with 102 additions and 170 deletions

View File

@@ -1,11 +1,11 @@
// @ts-check // @ts-check
import path from 'path';
import { createRuleset } from './lib/create-file'; import { createRuleset } from './lib/create-file';
import { parseFelixDnsmasq } from './lib/parse-dnsmasq'; import { parseFelixDnsmasq } from './lib/parse-dnsmasq';
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './lib/constants'; import { SHARED_DESCRIPTION } from './lib/constants';
import { createMemoizedPromise } from './lib/memo-promise'; import { createMemoizedPromise } from './lib/memo-promise';
import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem'; import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem';
import { output } from './lib/misc';
const cacheKey = createCacheKey(__filename); const cacheKey = createCacheKey(__filename);
@@ -42,9 +42,7 @@ export const buildAppleCdn = task(require.main === module, __filename)(async (sp
new Date(), new Date(),
ruleset, ruleset,
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/non_ip/apple_cdn.conf'), ...output('apple_cdn', 'non_ip')
path.resolve(__dirname, '../Clash/non_ip/apple_cdn.txt'),
path.resolve(__dirname, '../sing-box/non_ip/apple_cdn.json')
), ),
createRuleset( createRuleset(
span, span,
@@ -53,10 +51,7 @@ export const buildAppleCdn = task(require.main === module, __filename)(async (sp
new Date(), new Date(),
domainset, domainset,
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/apple_cdn.conf'), ...output('apple_cdn', 'domainset')
path.resolve(__dirname, '../Clash/domainset/apple_cdn.txt'),
path.resolve(__dirname, '../sing-box/domainset/apple_cdn.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/apple_cdn.mrs')
) )
]); ]);
}); });

View File

@@ -8,6 +8,7 @@ import { getPublicSuffixListTextPromise } from './lib/download-publicsuffixlist'
import { domainDeduper } from './lib/domain-deduper'; import { domainDeduper } from './lib/domain-deduper';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { sortDomains } from './lib/stable-sort-domain'; import { sortDomains } from './lib/stable-sort-domain';
import { output } from './lib/misc';
const getS3OSSDomainsPromise = (async (): Promise<string[]> => { const getS3OSSDomainsPromise = (async (): Promise<string[]> => {
const trie = createTrie( const trie = createTrie(
@@ -77,10 +78,7 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
new Date(), new Date(),
sortDomains(domainDeduper(cdnDomainsList)), sortDomains(domainDeduper(cdnDomainsList)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/cdn.conf'), ...output('cdn', 'domainset')
path.resolve(__dirname, '../Clash/domainset/cdn.txt'),
path.resolve(__dirname, '../sing-box/domainset/cdn.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/cdn.mrs')
), ),
createRuleset( createRuleset(
span, span,
@@ -93,10 +91,7 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
new Date(), new Date(),
sortDomains(domainDeduper(downloadDomainSet)), sortDomains(domainDeduper(downloadDomainSet)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/download.conf'), ...output('download', 'domainset')
path.resolve(__dirname, '../Clash/domainset/download.txt'),
path.resolve(__dirname, '../sing-box/domainset/download.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/download.mrs')
) )
]); ]);
}); });

View File

@@ -1,6 +1,5 @@
import { fetchRemoteTextByLine } from './lib/fetch-text-by-line'; import { fetchRemoteTextByLine } from './lib/fetch-text-by-line';
import { resolve as pathResolve } from 'path'; import { createRuleset } from './lib/create-file';
import { compareAndWriteFile, withBannerArray } from './lib/create-file';
import { processLineFromReadline } from './lib/process-line'; import { processLineFromReadline } from './lib/process-line';
import { task } from './trace'; import { task } from './trace';
@@ -8,6 +7,7 @@ import { exclude } from 'fast-cidr-tools';
import { createMemoizedPromise } from './lib/memo-promise'; import { createMemoizedPromise } from './lib/memo-promise';
import { CN_CIDR_NOT_INCLUDED_IN_CHNROUTE, NON_CN_CIDR_INCLUDED_IN_CHNROUTE } from './constants/cidr'; import { CN_CIDR_NOT_INCLUDED_IN_CHNROUTE, NON_CN_CIDR_INCLUDED_IN_CHNROUTE } from './constants/cidr';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { output } from './lib/misc';
export const getChnCidrPromise = createMemoizedPromise(async () => { export const getChnCidrPromise = createMemoizedPromise(async () => {
const cidr4 = await processLineFromReadline(await fetchRemoteTextByLine('https://raw.githubusercontent.com/misakaio/chnroutes2/master/chnroutes.txt')); const cidr4 = await processLineFromReadline(await fetchRemoteTextByLine('https://raw.githubusercontent.com/misakaio/chnroutes2/master/chnroutes.txt'));
@@ -30,57 +30,29 @@ export const buildChnCidr = task(require.main === module, __filename)(async (spa
// Can not use createRuleset here, as Clash support advanced ipset syntax // Can not use createRuleset here, as Clash support advanced ipset syntax
return Promise.all([ return Promise.all([
compareAndWriteFile( createRuleset(
span, span,
withBannerArray( 'Sukka\'s Ruleset - Mainland China IPv4 CIDR',
'Sukka\'s Ruleset - Mainland China IPv4 CIDR', [
[ ...description,
...description, 'Data from https://misaka.io (misakaio @ GitHub)'
'Data from https://misaka.io (misakaio @ GitHub)' ],
], new Date(),
new Date(), filteredCidr4,
filteredCidr4.map(i => `IP-CIDR,${i}`) 'ipcidr',
), ...output('china_ip', 'ip')
pathResolve(__dirname, '../List/ip/china_ip.conf')
), ),
compareAndWriteFile( createRuleset(
span, span,
withBannerArray( 'Sukka\'s Ruleset - Mainland China IPv6 CIDR',
'Sukka\'s Ruleset - Mainland China IPv6 CIDR', [
[ ...description,
...description, 'Data from https://github.com/gaoyifan/china-operator-ip'
'Data from https://github.com/gaoyifan/china-operator-ip' ],
], new Date(),
new Date(), cidr6,
cidr6.map(i => `IP-CIDR6,${i}`) 'ipcidr6',
), ...output('china_ip_ipv6', 'ip')
pathResolve(__dirname, '../List/ip/china_ip_ipv6.conf')
),
compareAndWriteFile(
span,
withBannerArray(
'Sukka\'s Ruleset - Mainland China IPv4 CIDR',
[
...description,
'Data from https://misaka.io (misakaio @ GitHub)'
],
new Date(),
filteredCidr4
),
pathResolve(__dirname, '../Clash/ip/china_ip.txt')
),
compareAndWriteFile(
span,
withBannerArray(
'Sukka\'s Ruleset - Mainland China IPv6 CIDR',
[
...description,
'Data from https://github.com/gaoyifan/china-operator-ip'
],
new Date(),
cidr6
),
pathResolve(__dirname, '../Clash/ip/china_ip_ipv6.txt')
) )
]); ]);
}); });

View File

@@ -9,7 +9,7 @@ import { SHARED_DESCRIPTION } from './lib/constants';
import { createMemoizedPromise } from './lib/memo-promise'; import { createMemoizedPromise } from './lib/memo-promise';
import * as yaml from 'yaml'; import * as yaml from 'yaml';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { writeFile } from './lib/misc'; import { output, writeFile } from './lib/misc';
export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => { export const getDomesticAndDirectDomainsRulesetPromise = createMemoizedPromise(async () => {
const domestics = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/domestic.conf')); const domestics = await readFileIntoProcessedArray(path.resolve(__dirname, '../Source/non_ip/domestic.conf'));
@@ -48,9 +48,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
new Date(), new Date(),
res[0], res[0],
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/non_ip/domestic.conf'), ...output('domestic', 'non_ip')
path.resolve(__dirname, '../Clash/non_ip/domestic.txt'),
path.resolve(__dirname, '../sing-box/non_ip/domestic.json')
), ),
createRuleset( createRuleset(
span, span,
@@ -63,9 +61,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
new Date(), new Date(),
res[1], res[1],
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/non_ip/direct.conf'), ...output('direct', 'non_ip')
path.resolve(__dirname, '../Clash/non_ip/direct.txt'),
path.resolve(__dirname, '../sing-box/non_ip/direct.json')
), ),
createRuleset( createRuleset(
span, span,
@@ -78,9 +74,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
new Date(), new Date(),
res[2], res[2],
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/non_ip/lan.conf'), ...output('lan', 'non_ip')
path.resolve(__dirname, '../Clash/non_ip/lan.txt'),
path.resolve(__dirname, '../sing-box/non_ip/lan.json')
), ),
compareAndWriteFile( compareAndWriteFile(
span, span,

View File

@@ -1,4 +1,3 @@
import path from 'path';
import { task } from './trace'; import { task } from './trace';
import { createRuleset } from './lib/create-file'; import { createRuleset } from './lib/create-file';
import { fetchRemoteTextByLine } from './lib/fetch-text-by-line'; import { fetchRemoteTextByLine } from './lib/fetch-text-by-line';
@@ -7,6 +6,7 @@ import { SHARED_DESCRIPTION } from './lib/constants';
import { createMemoizedPromise } from './lib/memo-promise'; import { createMemoizedPromise } from './lib/memo-promise';
import { extractDomainsFromFelixDnsmasq } from './lib/parse-dnsmasq'; import { extractDomainsFromFelixDnsmasq } from './lib/parse-dnsmasq';
import { sortDomains } from './lib/stable-sort-domain'; import { sortDomains } from './lib/stable-sort-domain';
import { output } from './lib/misc';
const PROBE_DOMAINS = ['.microsoft.com', '.windows.net', '.windows.com', '.windowsupdate.com', '.windowssearch.com', '.office.net']; const PROBE_DOMAINS = ['.microsoft.com', '.windows.net', '.windows.com', '.windowsupdate.com', '.windowssearch.com', '.office.net'];
@@ -63,8 +63,6 @@ export const buildMicrosoftCdn = task(require.main === module, __filename)(async
new Date(), new Date(),
res, res,
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/non_ip/microsoft_cdn.conf'), ...output('microsoft_cdn', 'non_ip')
path.resolve(__dirname, '../Clash/non_ip/microsoft_cdn.txt'),
path.resolve(__dirname, '../sing-box/non_ip/microsoft_cdn.json')
); );
}); });

View File

@@ -18,6 +18,7 @@ import { SHARED_DESCRIPTION } from './lib/constants';
import { getPhishingDomains } from './lib/get-phishing-domains'; import { getPhishingDomains } from './lib/get-phishing-domains';
import { setAddFromArray, setAddFromArrayCurried } from './lib/set-add-from-array'; import { setAddFromArray, setAddFromArrayCurried } from './lib/set-add-from-array';
import { output } from './lib/misc';
const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/reject_sukka.conf')); const getRejectSukkaConfPromise = readFileIntoProcessedArray(path.resolve(__dirname, '../Source/domainset/reject_sukka.conf'));
@@ -190,10 +191,7 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
new Date(), new Date(),
span.traceChildSync('sort reject domainset (base)', () => sortDomains(dudupedDominArray, domainArrayMainDomainMap, domainArraySubdomainMap)), span.traceChildSync('sort reject domainset (base)', () => sortDomains(dudupedDominArray, domainArrayMainDomainMap, domainArraySubdomainMap)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/reject.conf'), ...output('reject', 'domainset')
path.resolve(__dirname, '../Clash/domainset/reject.txt'),
path.resolve(__dirname, '../sing-box/domainset/reject.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/reject.mrs')
), ),
createRuleset( createRuleset(
span, span,
@@ -212,10 +210,7 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
new Date(), new Date(),
span.traceChildSync('sort reject domainset (extra)', () => sortDomains(dudupedDominArrayExtra, domainArrayMainDomainMap, domainArraySubdomainMap)), span.traceChildSync('sort reject domainset (extra)', () => sortDomains(dudupedDominArrayExtra, domainArrayMainDomainMap, domainArraySubdomainMap)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/reject_extra.conf'), ...output('reject_extra', 'domainset')
path.resolve(__dirname, '../Clash/domainset/reject_extra.txt'),
path.resolve(__dirname, '../sing-box/domainset/reject_extra.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/reject_extra.mrs')
), ),
compareAndWriteFile( compareAndWriteFile(
span, span,

View File

@@ -9,6 +9,7 @@ import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } f
import { fetchAssets } from './lib/fetch-assets'; import { fetchAssets } from './lib/fetch-assets';
import { processLine } from './lib/process-line'; import { processLine } from './lib/process-line';
import { appendArrayInPlace } from './lib/append-array-in-place'; import { appendArrayInPlace } from './lib/append-array-in-place';
import { output } from './lib/misc';
const cacheKey = createCacheKey(__filename); const cacheKey = createCacheKey(__filename);
@@ -100,8 +101,6 @@ export const buildRejectIPList = task(require.main === module, __filename)(async
new Date(), new Date(),
result, result,
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/ip/reject.conf'), ...output('reject', 'ip')
path.resolve(__dirname, '../Clash/ip/reject.txt'),
path.resolve(__dirname, '../sing-box/ip/reject.json')
); );
}); });

View File

@@ -12,6 +12,7 @@ import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem'; import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem';
import { createTrie } from './lib/trie'; import { createTrie } from './lib/trie';
import { output } from './lib/misc';
const s = new Sema(2); const s = new Sema(2);
const cacheKey = createCacheKey(__filename); const cacheKey = createCacheKey(__filename);
@@ -250,9 +251,6 @@ export const buildSpeedtestDomainSet = task(require.main === module, __filename)
new Date(), new Date(),
deduped, deduped,
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/speedtest.conf'), ...output('speedtest', 'domainset')
path.resolve(__dirname, '../Clash/domainset/speedtest.txt'),
path.resolve(__dirname, '../sing-box/domainset/speedtest.json'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/speedtest.mrs')
); );
}); });

View File

@@ -2,11 +2,11 @@
import type { Span } from './trace'; import type { Span } from './trace';
import { task } from './trace'; import { task } from './trace';
import path from 'path';
import { createRuleset } from './lib/create-file'; import { createRuleset } from './lib/create-file';
import { ALL, NORTH_AMERICA, EU, HK, TW, JP, KR } from '../Source/stream'; import { ALL, NORTH_AMERICA, EU, HK, TW, JP, KR } from '../Source/stream';
import { SHARED_DESCRIPTION } from './lib/constants'; import { SHARED_DESCRIPTION } from './lib/constants';
import { output } from './lib/misc';
export const createRulesetForStreamService = (span: Span, fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => { export const createRulesetForStreamService = (span: Span, fileId: string, title: string, streamServices: Array<import('../Source/stream').StreamService>) => {
return span.traceChildAsync(fileId, async (childSpan) => Promise.all([ return span.traceChildAsync(fileId, async (childSpan) => Promise.all([
@@ -22,9 +22,7 @@ export const createRulesetForStreamService = (span: Span, fileId: string, title:
new Date(), new Date(),
streamServices.flatMap((i) => i.rules), streamServices.flatMap((i) => i.rules),
'ruleset', 'ruleset',
path.resolve(__dirname, `../List/non_ip/${fileId}.conf`), ...output(fileId, 'non_ip')
path.resolve(__dirname, `../Clash/non_ip/${fileId}.txt`),
path.resolve(__dirname, `../sing-box/non_ip/${fileId}.json`)
), ),
// IP // IP
createRuleset( createRuleset(
@@ -45,9 +43,7 @@ export const createRulesetForStreamService = (span: Span, fileId: string, title:
: [] : []
)), )),
'ruleset', 'ruleset',
path.resolve(__dirname, `../List/ip/${fileId}.conf`), ...output(fileId, 'ip')
path.resolve(__dirname, `../Clash/ip/${fileId}.txt`),
path.resolve(__dirname, `../sing-box/ip/${fileId}.json`)
) )
])); ]));
}; };

View File

@@ -1,13 +1,13 @@
// @ts-check // @ts-check
import { defaultRequestInit, fetchWithRetry } from './lib/fetch-retry'; import { defaultRequestInit, fetchWithRetry } from './lib/fetch-retry';
import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line'; import { createReadlineInterfaceFromResponse } from './lib/fetch-text-by-line';
import path from 'path';
import { isProbablyIpv4, isProbablyIpv6 } from './lib/is-fast-ip'; import { isProbablyIpv4, isProbablyIpv6 } from './lib/is-fast-ip';
import { processLine } from './lib/process-line'; import { processLine } from './lib/process-line';
import { createRuleset } from './lib/create-file'; import { createRuleset } from './lib/create-file';
import { task } from './trace'; import { task } from './trace';
import { SHARED_DESCRIPTION } from './lib/constants'; import { SHARED_DESCRIPTION } from './lib/constants';
import { createMemoizedPromise } from './lib/memo-promise'; import { createMemoizedPromise } from './lib/memo-promise';
import { output } from './lib/misc';
export const getTelegramCIDRPromise = createMemoizedPromise(async () => { export const getTelegramCIDRPromise = createMemoizedPromise(async () => {
const resp = await fetchWithRetry('https://core.telegram.org/resources/cidr.txt', defaultRequestInit); const resp = await fetchWithRetry('https://core.telegram.org/resources/cidr.txt', defaultRequestInit);
@@ -52,8 +52,6 @@ export const buildTelegramCIDR = task(require.main === module, __filename)(async
date, date,
results, results,
'ruleset', 'ruleset',
path.resolve(__dirname, '../List/ip/telegram.conf'), ...output('telegram', 'ip')
path.resolve(__dirname, '../Clash/ip/telegram.txt'),
path.resolve(__dirname, '../sing-box/ip/telegram.json')
); );
}); });

View File

@@ -7,7 +7,7 @@ import fs from 'fs';
import { fastStringArrayJoin, writeFile } from './misc'; import { fastStringArrayJoin, writeFile } from './misc';
import { readFileByLine } from './fetch-text-by-line'; import { readFileByLine } from './fetch-text-by-line';
import stringify from 'json-stringify-pretty-compact'; import stringify from 'json-stringify-pretty-compact';
import { surgeDomainsetToSingbox, surgeRulesetToSingbox } from './singbox'; import { ipCidrListToSingbox, surgeDomainsetToSingbox, surgeRulesetToSingbox } from './singbox';
export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) { export async function compareAndWriteFile(span: Span, linesA: string[], filePath: string) {
let isEqual = true; let isEqual = true;
@@ -79,7 +79,7 @@ export async function compareAndWriteFile(span: Span, linesA: string[], filePath
}); });
} }
export const withBannerArray = (title: string, description: string[] | readonly string[], date: Date, content: string[]) => { const withBannerArray = (title: string, description: string[] | readonly string[], date: Date, content: string[]) => {
return [ return [
'#########################################', '#########################################',
`# ${title}`, `# ${title}`,
@@ -154,15 +154,32 @@ const MARK = 'this_ruleset_is_made_by_sukkaw.ruleset.skk.moe';
export const createRuleset = ( export const createRuleset = (
parentSpan: Span, parentSpan: Span,
title: string, description: string[] | readonly string[], date: Date, content: string[], title: string, description: string[] | readonly string[], date: Date, content: string[],
type: ('ruleset' | 'domainset' | string & {}), type: 'ruleset' | 'domainset' | 'ipcidr' | 'ipcidr6',
surgePath: string, clashPath: string, singBoxPath: string, _clashMrsPath?: string surgePath: string, clashPath: string, singBoxPath: string, _clashMrsPath?: string
) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn(async (childSpan) => { ) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn(async (childSpan) => {
const surgeContent = withBannerArray( content = sortRuleSet(content);
title, description, date, const surgeContent = childSpan.traceChildSync('process surge ruleset', () => {
type === 'domainset' let _surgeContent;
? [MARK, ...content] switch (type) {
: sortRuleSet([`DOMAIN,${MARK}`, ...content]) case 'domainset':
); _surgeContent = [MARK, ...content];
break;
case 'ruleset':
_surgeContent = [`DOMAIN,${MARK}`, ...content];
break;
case 'ipcidr':
_surgeContent = [`DOMAIN,${MARK}`, ...content.map(i => `IP-CIDR,${i}`)];
break;
case 'ipcidr6':
_surgeContent = [`DOMAIN,${MARK}`, ...content.map(i => `IP-CIDR6,${i}`)];
break;
default:
throw new TypeError(`Unknown type: ${type}`);
}
return withBannerArray(title, description, date, _surgeContent);
});
const clashContent = childSpan.traceChildSync('convert incoming ruleset to clash', () => { const clashContent = childSpan.traceChildSync('convert incoming ruleset to clash', () => {
let _clashContent; let _clashContent;
switch (type) { switch (type) {
@@ -172,6 +189,10 @@ export const createRuleset = (
case 'ruleset': case 'ruleset':
_clashContent = [`DOMAIN,${MARK}`, ...surgeRulesetToClashClassicalTextRuleset(content)]; _clashContent = [`DOMAIN,${MARK}`, ...surgeRulesetToClashClassicalTextRuleset(content)];
break; break;
case 'ipcidr':
case 'ipcidr6':
_clashContent = content;
break;
default: default:
throw new TypeError(`Unknown type: ${type}`); throw new TypeError(`Unknown type: ${type}`);
} }
@@ -186,6 +207,10 @@ export const createRuleset = (
case 'ruleset': case 'ruleset':
_singBoxContent = surgeRulesetToSingbox([`DOMAIN,${MARK}`, ...content]); _singBoxContent = surgeRulesetToSingbox([`DOMAIN,${MARK}`, ...content]);
break; break;
case 'ipcidr':
case 'ipcidr6':
_singBoxContent = ipCidrListToSingbox(content);
break;
default: default:
throw new TypeError(`Unknown type: ${type}`); throw new TypeError(`Unknown type: ${type}`);
} }

View File

@@ -1,4 +1,4 @@
import { dirname } from 'path'; import path, { dirname } from 'path';
import fs from 'fs'; import fs from 'fs';
import fsp from 'fs/promises'; import fsp from 'fs/promises';
import { makeRe } from 'picomatch'; import { makeRe } from 'picomatch';
@@ -33,3 +33,15 @@ export const writeFile: Write = async (destination: string, input, dir = dirname
export const domainWildCardToRegex = (domain: string) => { export const domainWildCardToRegex = (domain: string) => {
return makeRe(domain, { contains: false, strictSlashes: true }).source; return makeRe(domain, { contains: false, strictSlashes: true }).source;
}; };
const OUTPUT_SURGE_DIR = path.resolve(__dirname, '../../List');
const OUTPUT_CLASH_DIR = path.resolve(__dirname, '../../Clash');
const OUTPUT_SINGBOX_DIR = path.resolve(__dirname, '../../sing-box');
export const output = (id: string, type: 'non_ip' | 'ip' | 'domainset') => {
return [
path.join(OUTPUT_SURGE_DIR, type, id + '.conf'),
path.join(OUTPUT_CLASH_DIR, type, id + '.txt'),
path.join(OUTPUT_SINGBOX_DIR, type, id + '.json')
] as const;
};

View File

@@ -110,3 +110,12 @@ export const surgeDomainsetToSingbox = (domainset: string[]) => {
rules: [rule] rules: [rule]
}; };
}; };
export const ipCidrListToSingbox = (ipCidrList: string[]): SingboxSourceFormat => {
return {
version: 2,
rules: [{
ip_cidr: ipCidrList
}]
};
};

View File

@@ -1,54 +0,0 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Polyfill for TextEncoderStream and TextDecoderStream
// Modified by Sukka (https://skk.moe) to increase compatibility and performance with Bun.
export class PolyfillTextDecoderStream extends TransformStream<Uint8Array, string> {
readonly fatal: boolean;
readonly ignoreBOM: boolean;
constructor(
public readonly encoding: BufferEncoding = 'utf-8',
{
fatal = false,
ignoreBOM = false
}: ConstructorParameters<typeof TextDecoder>[1] = {}
) {
const decoder = new TextDecoder(encoding, { fatal, ignoreBOM });
const nonLastChunkDecoderOpt = { stream: true };
super({
transform(chunk: Uint8Array, controller: TransformStreamDefaultController<string>) {
const decoded = decoder.decode(chunk, nonLastChunkDecoderOpt);
controller.enqueue(decoded);
},
flush(controller: TransformStreamDefaultController<string>) {
// If {fatal: false} is in options (the default), then the final call to
// decode() can produce extra output (usually the unicode replacement
// character 0xFFFD). When fatal is true, this call is just used for its
// side-effect of throwing a TypeError exception if the input is
// incomplete.
const output = decoder.decode();
if (output.length > 0) {
controller.enqueue(output);
}
}
});
this.fatal = fatal;
this.ignoreBOM = ignoreBOM;
}
}