mirror of
https://github.com/SukkaW/Surge.git
synced 2026-01-29 01:51:52 +08:00
New build infra: Build for Clash (#11)
This commit is contained in:
@@ -4,7 +4,7 @@ const { isIPv4, isIPv6 } = require('net');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
const { fetchRemoteTextAndCreateReadlineInterface, readFileByLine } = require('./lib/fetch-remote-text-by-line');
|
||||
const { minifyRules } = require('./lib/minify-rules');
|
||||
const { surgeRulesetToClashClassicalTextRuleset } = require('./lib/clash');
|
||||
|
||||
(async () => {
|
||||
console.time('Total Time - build-anti-bogus-domain');
|
||||
@@ -21,7 +21,6 @@ const { minifyRules } = require('./lib/minify-rules');
|
||||
console.timeEnd('* Download bogus-nxdomain-list');
|
||||
|
||||
const filePath = path.resolve(__dirname, '../Source/ip/reject.conf');
|
||||
const resultPath = path.resolve(__dirname, '../List/ip/reject.conf');
|
||||
|
||||
/** @type {string[]} */
|
||||
const result = [];
|
||||
@@ -39,24 +38,37 @@ const { minifyRules } = require('./lib/minify-rules');
|
||||
}
|
||||
}
|
||||
|
||||
await compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Anti Bogus Domain',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains known addresses that are hijacking NXDOMAIN results returned by DNS servers.',
|
||||
'',
|
||||
'Data from:',
|
||||
' - https://github.com/felixonmars/dnsmasq-china-list'
|
||||
],
|
||||
new Date(),
|
||||
minifyRules(result)
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains known addresses that are hijacking NXDOMAIN results returned by DNS servers.',
|
||||
'',
|
||||
'Data from:',
|
||||
' - https://github.com/felixonmars/dnsmasq-china-list'
|
||||
];
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Anti Bogus Domain',
|
||||
description,
|
||||
new Date(),
|
||||
result
|
||||
),
|
||||
path.resolve(__dirname, '../List/ip/reject.conf')
|
||||
),
|
||||
resultPath
|
||||
);
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Anti Bogus Domain',
|
||||
description,
|
||||
new Date(),
|
||||
surgeRulesetToClashClassicalTextRuleset(result)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/ip/reject.txt')
|
||||
)
|
||||
]);
|
||||
|
||||
console.timeEnd('Total Time - build-anti-bogus-domain');
|
||||
})();
|
||||
|
||||
@@ -4,48 +4,63 @@ const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
|
||||
const { parseFelixDnsmasq } = require('./lib/parse-dnsmasq');
|
||||
const { surgeRulesetToClashClassicalTextRuleset, surgeDomainsetToClashDomainset } = require('./lib/clash');
|
||||
|
||||
(async () => {
|
||||
console.time('Total Time - build-apple-cdn-conf');
|
||||
|
||||
const res = await parseFelixDnsmasq('https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/apple.china.conf');
|
||||
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains Apple\'s domains using their China mainland CDN servers.',
|
||||
'',
|
||||
'Data from:',
|
||||
' - https://github.com/felixonmars/dnsmasq-china-list'
|
||||
];
|
||||
|
||||
const ruleset = res.map(domain => `DOMAIN-SUFFIX,${domain}`);
|
||||
const domainset = res.map(i => `.${i}`);
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Apple CDN',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains Apple\'s domains using their China mainland CDN servers.',
|
||||
'',
|
||||
'Data from:',
|
||||
' - https://github.com/felixonmars/dnsmasq-china-list'
|
||||
],
|
||||
'Sukka\'s Ruleset - Apple CDN',
|
||||
description,
|
||||
new Date(),
|
||||
res.map(domain => `DOMAIN-SUFFIX,${domain}`)
|
||||
ruleset
|
||||
),
|
||||
path.resolve(__dirname, '../List/non_ip/apple_cdn.conf')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Apple CDN',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains Apple\'s domains using their China mainland CDN servers.',
|
||||
'',
|
||||
'Data from:',
|
||||
' - https://github.com/felixonmars/dnsmasq-china-list'
|
||||
],
|
||||
'Sukka\'s Ruleset - Apple CDN',
|
||||
description,
|
||||
new Date(),
|
||||
res.map(i => `.${i}`)
|
||||
surgeRulesetToClashClassicalTextRuleset(ruleset)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/non_ip/apple_cdn.txt')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Apple CDN',
|
||||
description,
|
||||
new Date(),
|
||||
domainset
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/apple_cdn.conf')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Apple CDN',
|
||||
description,
|
||||
new Date(),
|
||||
surgeDomainsetToClashDomainset(domainset)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/domainset/apple_cdn.txt')
|
||||
)
|
||||
]);
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@ const path = require('path');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
const { minifyRules } = require('./lib/minify-rules');
|
||||
const { domainDeduper } = require('./lib/domain-deduper');
|
||||
const { processLine } = require('./lib/process-line');
|
||||
const { fetchRemoteTextAndCreateReadlineInterface, readFileByLine } = require('./lib/fetch-remote-text-by-line');
|
||||
const Trie = require('./lib/trie');
|
||||
const { surgeRulesetToClashClassicalTextRuleset } = require('./lib/clash');
|
||||
|
||||
(async () => {
|
||||
console.time('Total Time - build-cdn-conf');
|
||||
@@ -40,51 +39,33 @@ const Trie = require('./lib/trie');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedupe cdn.conf
|
||||
*/
|
||||
/** @type {Set<string>} */
|
||||
const cdnDomains = new Set();
|
||||
|
||||
for await (const line of readFileByLine(
|
||||
path.resolve(__dirname, '../Source/domainset/cdn.conf')
|
||||
)) {
|
||||
const l = processLine(line);
|
||||
if (l) {
|
||||
cdnDomains.add(l);
|
||||
}
|
||||
}
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains object storage and static assets CDN domains.'
|
||||
];
|
||||
const ruleset = minifyRules(cdnDomainsList);
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - CDN Domains',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains object storage and static assets CDN domains.'
|
||||
],
|
||||
'Sukka\'s Ruleset - CDN Domains',
|
||||
description,
|
||||
new Date(),
|
||||
minifyRules(cdnDomainsList)
|
||||
ruleset
|
||||
),
|
||||
path.resolve(__dirname, '../List/non_ip/cdn.conf')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - CDN Domains',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains object storage and static assets CDN domains.'
|
||||
],
|
||||
'Sukka\'s Ruleset - CDN Domains',
|
||||
description,
|
||||
new Date(),
|
||||
minifyRules(domainDeduper(Array.from(cdnDomains)))
|
||||
surgeRulesetToClashClassicalTextRuleset(ruleset)
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/cdn.conf')
|
||||
path.resolve(__dirname, '../Clash/non_ip/cdn.txt')
|
||||
)
|
||||
]);
|
||||
|
||||
|
||||
@@ -28,21 +28,38 @@ const EXCLUDE_CIDRS = [
|
||||
const filteredCidr = excludeCidrs(Array.from(cidr), EXCLUDE_CIDRS, true);
|
||||
console.log('After Merge:', filteredCidr.length);
|
||||
|
||||
await compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Mainland China IPv4 CIDR',
|
||||
[
|
||||
'License: CC BY-SA 2.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'Data from https://misaka.io (misakaio @ GitHub)'
|
||||
],
|
||||
new Date(),
|
||||
filteredCidr.map(i => `IP-CIDR,${i}`)
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Mainland China IPv4 CIDR',
|
||||
[
|
||||
'License: CC BY-SA 2.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'Data from https://misaka.io (misakaio @ GitHub)'
|
||||
],
|
||||
new Date(),
|
||||
filteredCidr.map(i => `IP-CIDR,${i}`)
|
||||
),
|
||||
pathResolve(__dirname, '../List/ip/china_ip.conf')
|
||||
),
|
||||
pathResolve(__dirname, '../List/ip/china_ip.conf')
|
||||
);
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Mainland China IPv4 CIDR',
|
||||
[
|
||||
'License: CC BY-SA 2.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'Data from https://misaka.io (misakaio @ GitHub)'
|
||||
],
|
||||
new Date(),
|
||||
filteredCidr
|
||||
),
|
||||
pathResolve(__dirname, '../Clash/ip/china_ip.txt')
|
||||
)
|
||||
]);
|
||||
|
||||
console.timeEnd('Total Time - build-chnroutes-cidr');
|
||||
})();
|
||||
|
||||
@@ -6,6 +6,7 @@ const { processLine } = require('./lib/process-line');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const domainSorter = require('./lib/stable-sort-domain');
|
||||
const { surgeRulesetToClashClassicalTextRuleset } = require('./lib/clash');
|
||||
|
||||
(async () => {
|
||||
const rl = readFileByLine(path.resolve(__dirname, '../Source/non_ip/domestic.conf'));
|
||||
@@ -25,22 +26,33 @@ const domainSorter = require('./lib/stable-sort-domain');
|
||||
.map((domain) => `DOMAIN-SUFFIX,${domain}`)
|
||||
);
|
||||
|
||||
const rulesetDescription = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains known addresses that are avaliable in the Mainland China.'
|
||||
];
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Domestic Domain',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'This file contains known addresses that are avaliable in the Mainland China.'
|
||||
],
|
||||
'Sukka\'s Ruleset - Domestic Domains',
|
||||
rulesetDescription,
|
||||
new Date(),
|
||||
results
|
||||
),
|
||||
path.resolve(__dirname, '../List/non_ip/domestic.conf')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Domestic Domains',
|
||||
rulesetDescription,
|
||||
new Date(),
|
||||
surgeRulesetToClashClassicalTextRuleset(results)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/non_ip/domestic.txt')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
[
|
||||
'#!name=[Sukka] Local DNS Mapping',
|
||||
|
||||
@@ -5,6 +5,7 @@ const { withBannerArray } = require('./lib/with-banner.js');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { processLine } = require('./lib/process-line.js');
|
||||
const domainSorter = require('./lib/stable-sort-domain');
|
||||
const { surgeDomainsetToClashDomainset } = require('./lib/clash.js');
|
||||
|
||||
const WHITELIST_DOMAIN = new Set([
|
||||
'w3s.link',
|
||||
@@ -141,21 +142,34 @@ const BLACK_TLD = new Set([
|
||||
|
||||
results.sort(domainSorter);
|
||||
|
||||
await compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Reject Phishing',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'The domainset supports enhanced phishing protection',
|
||||
'Build from:',
|
||||
' - https://gitlab.com/malware-filter/phishing-filter'
|
||||
],
|
||||
new Date(),
|
||||
results
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'The domainset supports enhanced phishing protection',
|
||||
'Build from:',
|
||||
' - https://gitlab.com/malware-filter/phishing-filter'
|
||||
];
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Reject Phishing',
|
||||
description,
|
||||
new Date(),
|
||||
results
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/reject_phishing.conf')
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/reject_phishing.conf')
|
||||
);
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Reject Phishing',
|
||||
description,
|
||||
new Date(),
|
||||
surgeDomainsetToClashDomainset(results)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/domainset/reject_phishing.txt')
|
||||
)
|
||||
]);
|
||||
})();
|
||||
|
||||
@@ -16,6 +16,7 @@ const { domainDeduper } = require('./lib/domain-deduper');
|
||||
const createKeywordFilter = require('./lib/aho-corasick');
|
||||
const { readFileByLine } = require('./lib/fetch-remote-text-by-line');
|
||||
const domainSorter = require('./lib/stable-sort-domain');
|
||||
const { surgeDomainsetToClashDomainset } = require('./lib/clash');
|
||||
|
||||
/** Whitelists */
|
||||
const filterRuleWhitelistDomainSets = new Set(PREDEFINED_WHITELIST);
|
||||
@@ -196,26 +197,38 @@ const domainSuffixSet = new Set();
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'The domainset supports AD blocking, tracking protection, privacy protection, anti-phishing, anti-mining',
|
||||
'',
|
||||
'Build from:',
|
||||
...HOSTS.map(host => ` - ${host[0]}`),
|
||||
...ADGUARD_FILTERS.map(filter => ` - ${Array.isArray(filter) ? filter[0] : filter}`)
|
||||
];
|
||||
const domainset = dudupedDominArray.sort(domainSorter);
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Reject Base',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'',
|
||||
'The domainset supports AD blocking, tracking protection, privacy protection, anti-phishing, anti-mining',
|
||||
'',
|
||||
'Build from:',
|
||||
...HOSTS.map(host => ` - ${host[0]}`),
|
||||
...ADGUARD_FILTERS.map(filter => ` - ${Array.isArray(filter) ? filter[0] : filter}`)
|
||||
],
|
||||
'Sukka\'s Ruleset - Reject Base',
|
||||
description,
|
||||
new Date(),
|
||||
dudupedDominArray.sort(domainSorter)
|
||||
domainset
|
||||
),
|
||||
pathResolve(__dirname, '../List/domainset/reject.conf')
|
||||
),
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Reject Base',
|
||||
description,
|
||||
new Date(),
|
||||
surgeDomainsetToClashDomainset(domainset)
|
||||
),
|
||||
pathResolve(__dirname, '../Clash/domainset/reject.txt')
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
pathResolve(__dirname, '../List/internal/reject-stats.txt'),
|
||||
Object.entries(rejectDomainsStats)
|
||||
|
||||
@@ -6,6 +6,7 @@ const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const domainSorter = require('./lib/stable-sort-domain');
|
||||
|
||||
const { Sema } = require('async-sema');
|
||||
const { surgeDomainsetToClashDomainset } = require('./lib/clash');
|
||||
const s = new Sema(2);
|
||||
|
||||
/**
|
||||
@@ -107,19 +108,31 @@ const querySpeedtestApi = async (keyword) => {
|
||||
}
|
||||
}
|
||||
|
||||
const reduped = domainDeduper(Array.from(domains)).sort(domainSorter);
|
||||
const deduped = domainDeduper(Array.from(domains)).sort(domainSorter);
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge'
|
||||
];
|
||||
|
||||
await compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Speedtest Domains',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge'
|
||||
],
|
||||
new Date(),
|
||||
reduped
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Speedtest Domains',
|
||||
description,
|
||||
new Date(),
|
||||
deduped
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/speedtest.conf')
|
||||
),
|
||||
path.resolve(__dirname, '../List/domainset/speedtest.conf')
|
||||
);
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Speedtest Domains',
|
||||
description,
|
||||
new Date(),
|
||||
surgeDomainsetToClashDomainset(deduped)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/domainset/speedtest.txt')
|
||||
)
|
||||
]);
|
||||
})();
|
||||
|
||||
@@ -5,6 +5,7 @@ const { isIPv4, isIPv6 } = require('net');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
const { processLine } = require('./lib/process-line');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { surgeRulesetToClashClassicalTextRuleset } = require('./lib/clash');
|
||||
|
||||
(async () => {
|
||||
console.time('Total Time - build-telegram-cidr');
|
||||
@@ -34,21 +35,34 @@ const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
throw new Error('Failed to fetch data!');
|
||||
}
|
||||
|
||||
await compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Surge Rules - Telegram IP CIDR',
|
||||
[
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'Data from:',
|
||||
' - https://core.telegram.org/resources/cidr.txt'
|
||||
],
|
||||
date,
|
||||
results
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
'Data from:',
|
||||
' - https://core.telegram.org/resources/cidr.txt'
|
||||
];
|
||||
|
||||
await Promise.all([
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Telegram IP CIDR',
|
||||
description,
|
||||
date,
|
||||
results
|
||||
),
|
||||
path.resolve(__dirname, '../List/ip/telegram.conf')
|
||||
),
|
||||
path.resolve(__dirname, '../List/ip/telegram.conf')
|
||||
);
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
'Sukka\'s Ruleset - Telegram IP CIDR',
|
||||
description,
|
||||
date,
|
||||
surgeRulesetToClashClassicalTextRuleset(results)
|
||||
),
|
||||
path.resolve(__dirname, '../Clash/ip/telegram.txt')
|
||||
)
|
||||
]);
|
||||
|
||||
console.timeEnd('Total Time - build-telegram-cidr');
|
||||
})();
|
||||
|
||||
179
Build/build.js
Normal file
179
Build/build.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// @ts-check
|
||||
|
||||
const path = require('path');
|
||||
const { PathScurry } = require('path-scurry');
|
||||
const { readFileByLine } = require('./lib/fetch-remote-text-by-line');
|
||||
const { processLine } = require('./lib/process-line');
|
||||
const { compareAndWriteFile } = require('./lib/string-array-compare');
|
||||
const { withBannerArray } = require('./lib/with-banner');
|
||||
const { domainDeduper } = require('./lib/domain-deduper');
|
||||
const { surgeRulesetToClashClassicalTextRuleset, surgeDomainsetToClashDomainset } = require('./lib/clash');
|
||||
|
||||
const MAGIC_COMMAND_SKIP = '# $ custom_build_script';
|
||||
const MAGIC_COMMAND_TITLE = '# $ meta_title ';
|
||||
const MAGIC_COMMAND_DESCRIPTION = '# $ meta_description ';
|
||||
|
||||
const sourceDir = path.resolve(__dirname, '../Source');
|
||||
const outputSurgeDir = path.resolve(__dirname, '../List');
|
||||
const outputClashDir = path.resolve(__dirname, '../Clash');
|
||||
|
||||
(async () => {
|
||||
/** @type {Promise<void>[]} */
|
||||
const promises = [];
|
||||
|
||||
const pw = new PathScurry(sourceDir);
|
||||
for await (const entry of pw) {
|
||||
if (entry.isFile()) {
|
||||
if (path.extname(entry.name) === '.js') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const relativePath = entry.relative();
|
||||
if (relativePath.startsWith('domainset/')) {
|
||||
promises.push(transformDomainset(entry.fullpath(), relativePath));
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
relativePath.startsWith('ip/')
|
||||
|| relativePath.startsWith('non_ip/')
|
||||
) {
|
||||
promises.push(transformRuleset(entry.fullpath(), relativePath));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
})();
|
||||
|
||||
/**
|
||||
* @param {string} sourcePath
|
||||
*/
|
||||
const processFile = async (sourcePath) => {
|
||||
/** @type {Set<string>} */
|
||||
const lines = new Set();
|
||||
|
||||
let title = '';
|
||||
/** @type {string[]} */
|
||||
const descriptions = [];
|
||||
|
||||
for await (const line of readFileByLine(sourcePath)) {
|
||||
if (line === MAGIC_COMMAND_SKIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.startsWith(MAGIC_COMMAND_TITLE)) {
|
||||
title = line.slice(MAGIC_COMMAND_TITLE.length).trim();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith(MAGIC_COMMAND_DESCRIPTION)) {
|
||||
descriptions.push(line.slice(MAGIC_COMMAND_DESCRIPTION.length).trim());
|
||||
continue;
|
||||
}
|
||||
|
||||
const l = processLine(line);
|
||||
if (l) {
|
||||
lines.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
return /** @type {const} */ ([title, descriptions, lines]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} sourcePath
|
||||
* @param {string} relativePath
|
||||
*/
|
||||
async function transformDomainset(sourcePath, relativePath) {
|
||||
const res = await processFile(sourcePath);
|
||||
if (!res) return;
|
||||
const [title, descriptions, lines] = res;
|
||||
|
||||
const deduped = domainDeduper(Array.from(lines));
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
...(
|
||||
descriptions.length
|
||||
? ['', ...descriptions]
|
||||
: []
|
||||
)
|
||||
];
|
||||
|
||||
await Promise.all([
|
||||
// Surge DOMAIN-SET
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
title,
|
||||
description,
|
||||
new Date(),
|
||||
deduped
|
||||
),
|
||||
path.resolve(outputSurgeDir, relativePath)
|
||||
),
|
||||
// Clash domain text
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
title,
|
||||
description,
|
||||
new Date(),
|
||||
surgeDomainsetToClashDomainset(deduped)
|
||||
),
|
||||
// change path extname to .txt
|
||||
path.resolve(outputClashDir, `${relativePath.slice(0, -path.extname(relativePath).length)}.txt`)
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output Surge RULE-SET and Clash classical text format
|
||||
*
|
||||
* @param {string} sourcePath
|
||||
* @param {string} relativePath
|
||||
*/
|
||||
async function transformRuleset(sourcePath, relativePath) {
|
||||
const res = await processFile(sourcePath);
|
||||
if (!res) return;
|
||||
const [title, descriptions, set] = res;
|
||||
|
||||
const description = [
|
||||
'License: AGPL 3.0',
|
||||
'Homepage: https://ruleset.skk.moe',
|
||||
'GitHub: https://github.com/SukkaW/Surge',
|
||||
...(
|
||||
descriptions.length
|
||||
? ['', ...descriptions]
|
||||
: []
|
||||
)
|
||||
];
|
||||
|
||||
const lines = Array.from(set);
|
||||
|
||||
const clashSupported = surgeRulesetToClashClassicalTextRuleset(set);
|
||||
|
||||
await Promise.all([
|
||||
// Surge RULE-SET
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
title,
|
||||
description,
|
||||
new Date(),
|
||||
lines
|
||||
),
|
||||
path.resolve(outputSurgeDir, relativePath)
|
||||
),
|
||||
// Clash domainset
|
||||
compareAndWriteFile(
|
||||
withBannerArray(
|
||||
title,
|
||||
description,
|
||||
new Date(),
|
||||
clashSupported
|
||||
),
|
||||
// change path extname to .txt
|
||||
path.resolve(outputClashDir, `${relativePath.slice(0, -path.extname(relativePath).length)}.txt`)
|
||||
)
|
||||
]);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ const { tmpdir } = require('os');
|
||||
const { Readable } = require('stream');
|
||||
const { pipeline } = require('stream/promises');
|
||||
const { readFileByLine } = require('./lib/fetch-remote-text-by-line');
|
||||
const { isCI } = require('ci-info');
|
||||
|
||||
const fileExists = (path) => {
|
||||
return fs.promises.access(path, fs.constants.F_OK)
|
||||
@@ -19,18 +20,22 @@ const fileExists = (path) => {
|
||||
|
||||
let allFileExists = true;
|
||||
|
||||
for await (const line of readFileByLine(resolve(__dirname, '../.gitignore'))) {
|
||||
if (
|
||||
(
|
||||
line.startsWith('List/')
|
||||
|| line.startsWith('Modules/')
|
||||
) && !line.endsWith('/')
|
||||
) {
|
||||
allFileExists = await fileExists(join(__dirname, '..', line));
|
||||
filesList.push(line);
|
||||
if (isCI) {
|
||||
allFileExists = false;
|
||||
} else {
|
||||
for await (const line of readFileByLine(resolve(__dirname, '../.gitignore'))) {
|
||||
if (
|
||||
(
|
||||
// line.startsWith('List/')
|
||||
line.startsWith('Modules/')
|
||||
) && !line.endsWith('/')
|
||||
) {
|
||||
allFileExists = await fileExists(join(__dirname, '..', line));
|
||||
filesList.push(line);
|
||||
|
||||
if (!allFileExists) {
|
||||
console.log(`File not exists: ${line}`);
|
||||
if (!allFileExists) {
|
||||
console.log(`File not exists: ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
Build/lib/clash.js
Normal file
34
Build/lib/clash.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// @ts-check
|
||||
const _Trie = require('mnemonist/trie');
|
||||
const Trie = _Trie.default || _Trie;
|
||||
|
||||
const CLASH_SUPPORTED_RULE_TYPE = [
|
||||
'DOMAIN-SUFFIX',
|
||||
'DOMAIN-KEYWORD',
|
||||
'DOMAIN',
|
||||
'SRC-IP-CIDR',
|
||||
'GEOIP',
|
||||
'IP-CIDR',
|
||||
'IP-CIDR6',
|
||||
'DST-PORT',
|
||||
'SRC-PORT'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string[] | Set<string>} rules
|
||||
*/
|
||||
const surgeRulesetToClashClassicalTextRuleset = (rules) => {
|
||||
const trie = Trie.from(rules);
|
||||
return CLASH_SUPPORTED_RULE_TYPE.map(
|
||||
type => trie.find(`${type},`)
|
||||
).flat();
|
||||
};
|
||||
module.exports.surgeRulesetToClashClassicalTextRuleset = surgeRulesetToClashClassicalTextRuleset;
|
||||
|
||||
/**
|
||||
* @param {string[]} domainset
|
||||
*/
|
||||
const surgeDomainsetToClashDomainset = (domainset) => {
|
||||
return domainset.map(i => (i[0] === '.' ? `+${i}` : i));
|
||||
};
|
||||
module.exports.surgeDomainsetToClashDomainset = surgeDomainsetToClashDomainset;
|
||||
Reference in New Issue
Block a user