diff --git a/Build/build-apple-cdn.ts b/Build/build-apple-cdn.ts index d0642b79..fa1f6bbb 100644 --- a/Build/build-apple-cdn.ts +++ b/Build/build-apple-cdn.ts @@ -12,14 +12,14 @@ export const buildAppleCdn = task(require.main === module, __filename)(async (sp return new DomainsetOutput(span, 'apple_cdn') .withTitle('Sukka\'s Ruleset - Apple CDN') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription(SHARED_DESCRIPTION) + .appendDescription( '', 'This file contains Apple\'s domains using their China mainland CDN servers.', '', 'Data from:', ' - https://github.com/felixonmars/dnsmasq-china-list' - ]) + ) .bulkAddDomainSuffix(res) .write(); }); diff --git a/Build/build-cdn-download-conf.ts b/Build/build-cdn-download-conf.ts index 0d1da1d1..92a87874 100644 --- a/Build/build-cdn-download-conf.ts +++ b/Build/build-cdn-download-conf.ts @@ -75,21 +75,21 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as return Promise.all([ new DomainsetOutput(span, 'cdn') .withTitle('Sukka\'s Ruleset - CDN Domains') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription(SHARED_DESCRIPTION) + .appendDescription( '', 'This file contains object storage and static assets CDN domains.' - ]) + ) .addFromDomainset(cdnDomainsList) .write(), new DomainsetOutput(span, 'download') .withTitle('Sukka\'s Ruleset - Large Files Hosting Domains') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription(SHARED_DESCRIPTION) + .appendDescription( '', 'This file contains domains for software updating & large file hosting.' - ]) + ) .addFromDomainset(downloadDomainSet) .write() ]); diff --git a/Build/build-chn-cidr.ts b/Build/build-chn-cidr.ts index 777bc102..59534701 100644 --- a/Build/build-chn-cidr.ts +++ b/Build/build-chn-cidr.ts @@ -21,18 +21,16 @@ export const buildChnCidr = task(require.main === module, __filename)(async (spa return Promise.all([ new IPListOutput(span, 'china_ip', false) .withTitle('Sukka\'s Ruleset - Mainland China IPv4 CIDR') - .withDescription([ - ...description, - 'Data from https://misaka.io (misakaio @ GitHub)' - ]) + .withDescription(description) + .appendDataSource('https://chnroutes2.cdn.skk.moe/chnroutes.txt') .bulkAddCIDR4(filteredCidr4) .write(), new IPListOutput(span, 'china_ip_ipv6', false) .withTitle('Sukka\'s Ruleset - Mainland China IPv6 CIDR') - .withDescription([ - ...description, - 'Data from https://github.com/gaoyifan/china-operator-ip' - ]) + .withDescription(description) + .appendDataSource( + 'https://github.com/gaoyifan/china-operator-ip' + ) .bulkAddCIDR6(cidr6) .write() ]); diff --git a/Build/build-cloudmounter-rules.ts b/Build/build-cloudmounter-rules.ts index 5650473c..c92c54d4 100644 --- a/Build/build-cloudmounter-rules.ts +++ b/Build/build-cloudmounter-rules.ts @@ -16,11 +16,9 @@ export const buildCloudMounterRules = task(require.main === module, __filename)( ].map(cidr => `AND,((${domain}),(SRC-IP,${cidr}))`) ])); - const description = SHARED_DESCRIPTION; - return new RulesetOutput(span, 'cloudmounter', 'non_ip') .withTitle('Sukka\'s Ruleset - CloudMounter / RaiDrive') - .withDescription(description) + .withDescription(SHARED_DESCRIPTION) .addFromRuleset(results) .write(); }); diff --git a/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts b/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts index 133c74be..6e63098d 100644 --- a/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts +++ b/Build/build-domestic-direct-lan-ruleset-dns-mapping-module.ts @@ -89,29 +89,29 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as return Promise.all([ new RulesetOutput(span, 'domestic', 'non_ip') .withTitle('Sukka\'s Ruleset - Domestic Domains') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This file contains known addresses that are avaliable in the Mainland China.' - ]) + ) .addFromRuleset(domestics) .write(), new RulesetOutput(span, 'direct', 'non_ip') .withTitle('Sukka\'s Ruleset - Direct Rules') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This file contains domains and process that should not be proxied.' - ]) + ) .addFromRuleset(directs) .write(), new RulesetOutput(span, 'lan', 'non_ip') .withTitle('Sukka\'s Ruleset - LAN') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This file includes rules for LAN DOMAIN and reserved TLDs.' - ]) + ) .addFromRuleset(lans) .write(), @@ -122,12 +122,12 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as const output = new SurgeOnlyRulesetOutput(span, name.toLowerCase(), 'sukka_local_dns_mapping', OUTPUT_MODULES_RULES_DIR) .withTitle(`Sukka's Ruleset - Local DNS Mapping (${name})`) - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This is an internal rule that is only referenced by sukka_local_dns_mapping.sgmodule', 'Do not use this file in your Rule section, all rules are included in non_ip/domestic.conf already.' - ]); + ); domains.forEach((domain) => { switch (domain[0]) { diff --git a/Build/build-microsoft-cdn.ts b/Build/build-microsoft-cdn.ts index d4e30f2f..45532d93 100644 --- a/Build/build-microsoft-cdn.ts +++ b/Build/build-microsoft-cdn.ts @@ -55,20 +55,16 @@ export const getMicrosoftCdnRulesetPromise = once { - const description = [ - ...SHARED_DESCRIPTION, - '', - 'This file contains Microsoft\'s domains using their China mainland CDN servers.', - '', - 'Data from:', - ' - https://github.com/felixonmars/dnsmasq-china-list' - ]; - const [domains, domainSuffixes] = await span.traceChildPromise('get microsoft cdn domains', getMicrosoftCdnRulesetPromise()); return new RulesetOutput(span, 'microsoft_cdn', 'non_ip') .withTitle('Sukka\'s Ruleset - Microsoft CDN') - .withDescription(description) + .appendDescription(SHARED_DESCRIPTION) + .appendDescription( + '', + 'This file contains Microsoft\'s domains using their China mainland CDN servers.' + ) + .appendDataSource('https://github.com/felixonmars/dnsmasq-china-list') .bulkAddDomain(domains) .bulkAddDomainSuffix(domainSuffixes) .write(); diff --git a/Build/build-reject-domainset.ts b/Build/build-reject-domainset.ts index c10583d3..778f0a59 100644 --- a/Build/build-reject-domainset.ts +++ b/Build/build-reject-domainset.ts @@ -41,51 +41,50 @@ const adguardFiltersWhitelistsDownloads = ADGUARD_FILTERS_WHITELIST.map(entry => export const buildRejectDomainSet = task(require.main === module, __filename)(async (span) => { const rejectDomainsetOutput = new DomainsetOutput(span, 'reject') .withTitle('Sukka\'s Ruleset - Reject Base') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'The domainset supports AD blocking, tracking protection, privacy protection, anti-mining' - ]) + ) .appendDataSource(HOSTS.map(host => host[0])) .appendDataSource(DOMAIN_LISTS.map(domainList => domainList[0])); const rejectExtraDomainsetOutput = new DomainsetOutput(span, 'reject_extra') .withTitle('Sukka\'s Ruleset - Reject Extra') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'The domainset supports AD blocking, tracking protection, privacy protection, anti-mining' - ]) + ) .appendDataSource(HOSTS_EXTRA.map(host => host[0])) .appendDataSource(DOMAIN_LISTS_EXTRA.map(domainList => domainList[0])); const rejectPhisingDomainsetOutput = new DomainsetOutput(span, 'reject_phishing') .withTitle('Sukka\'s Ruleset - Reject Phishing') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'The domainset is specifically designed for anti-phishing' - ]) + ) .appendDataSource(PHISHING_HOSTS_EXTRA.map(host => host[0])) .appendDataSource(PHISHING_DOMAIN_LISTS_EXTRA.map(domainList => domainList[0])); const rejectNonIpRulesetOutput = new RulesetOutput(span, 'reject', 'non_ip') .withTitle('Sukka\'s Ruleset - Reject Non-IP') - .withDescription([ - ...SHARED_DESCRIPTION, - '', + .appendDescription(SHARED_DESCRIPTION, '') + .appendDescription( 'The ruleset supports AD blocking, tracking protection, privacy protection, anti-phishing, anti-mining', '', 'The file contains wildcard domains from data source mentioned in /domainset/reject file' - ]); + ); const rejectIPOutput = new RulesetOutput(span, 'reject', 'ip') .withTitle('Sukka\'s Ruleset - Anti Bogus Domain') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This file contains known addresses that are hijacking NXDOMAIN results returned by DNS servers, and botnet controller IPs.' - ]) + ) .appendDataSource('https://github.com/felixonmars/dnsmasq-china-list') .appendDataSource('https://github.com/curbengh/botnet-filter') .bulkAddIPASN(AUGUST_ASN) diff --git a/Build/build-speedtest-domainset.ts b/Build/build-speedtest-domainset.ts index 8563a70e..32cb10bc 100644 --- a/Build/build-speedtest-domainset.ts +++ b/Build/build-speedtest-domainset.ts @@ -48,11 +48,11 @@ const getSpeedtestHostsGroupsPromise = $$fetch('https://speedtest-net-servers.cd export const buildSpeedtestDomainSet = task(require.main === module, __filename)( async (span) => new DomainsetOutput(span, 'speedtest') .withTitle('Sukka\'s Ruleset - Speedtest Domains') - .withDescription([ - ...SHARED_DESCRIPTION, + .appendDescription( + SHARED_DESCRIPTION, '', 'This file contains common speedtest endpoints.' - ]) + ) .addFromDomainset(readFileIntoProcessedArray(path.resolve(SOURCE_DIR, 'domainset/speedtest.conf'))) .addFromDomainset(readFileIntoProcessedArray(path.resolve(OUTPUT_SURGE_DIR, 'domainset/speedtest.conf'))) .bulkAddDomain(await span.traceChildPromise('get speedtest hosts groups', getSpeedtestHostsGroupsPromise)) diff --git a/Build/build-stream-service.ts b/Build/build-stream-service.ts index f8b7fab2..f2981929 100644 --- a/Build/build-stream-service.ts +++ b/Build/build-stream-service.ts @@ -15,21 +15,17 @@ function createRulesetForStreamService( // Domains new RulesetOutput(span, fileId, 'non_ip') .withTitle(`Sukka's Ruleset - Stream Services: ${title}`) - .withDescription([ - ...SHARED_DESCRIPTION, - '', - ...streamServices.map((i) => `- ${i.name}`) - ]) + .appendDescription(SHARED_DESCRIPTION) + .appendDescription('') + .appendDescription(streamServices.map((i) => `- ${i.name}`)) .addFromRuleset(streamServices.flatMap((i) => i.rules)) .write(), // IP new RulesetOutput(span, fileId, 'ip') .withTitle(`Sukka's Ruleset - Stream Services IPs: ${title}`) - .withDescription([ - ...SHARED_DESCRIPTION, - '', - ...streamServices.map((i) => `- ${i.name}`) - ]) + .appendDescription(SHARED_DESCRIPTION) + .appendDescription('') + .appendDescription(streamServices.map((i) => `- ${i.name}`)) .bulkAddCIDR4NoResolve(streamServices.flatMap(i => i.ip?.v4 ?? [])) .bulkAddCIDR6NoResolve(streamServices.flatMap(i => i.ip?.v6 ?? [])) .write() diff --git a/Build/lib/rules/base.ts b/Build/lib/rules/base.ts index 516c0461..9b48c69e 100644 --- a/Build/lib/rules/base.ts +++ b/Build/lib/rules/base.ts @@ -9,6 +9,7 @@ import { merge as mergeCidr } from 'fast-cidr-tools'; import { createRetrieKeywordFilter as createKeywordFilter } from 'foxts/retrie'; import path from 'node:path'; import { SurgeMitmSgmodule } from '../writing-strategy/surge'; +import { appendArrayInPlace } from 'foxts/append-array-in-place'; /** * Holds the universal rule data (domain, ip, url-regex, etc. etc.) @@ -86,6 +87,21 @@ export class FileOutput { return this; } + appendDescription(description: string | string[], ...rest: string[]) { + this.description ??= []; + if (typeof description === 'string') { + this.description.push(description); + } else { + appendArrayInPlace(this.description, description); + } + + if (rest.length) { + appendArrayInPlace(this.description, rest); + } + + return this; + } + protected date = new Date(); withDate(date: Date) { this.date = date;