Refactor: support RULE-SET as Mihomo's nameserver-policy

This commit is contained in:
SukkaW 2025-10-16 23:01:38 +08:00
parent 7aa2597641
commit f96612053e
3 changed files with 90 additions and 23 deletions

View File

@ -120,30 +120,56 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
return; return;
} }
const output = new SurgeOnlyRulesetOutput(span, name.toLowerCase(), 'sukka_local_dns_mapping', OUTPUT_MODULES_RULES_DIR) const surgeOutput = new SurgeOnlyRulesetOutput(
span,
name.toLowerCase(),
'sukka_local_dns_mapping',
OUTPUT_MODULES_RULES_DIR
)
.withTitle(`Sukka's Ruleset - Local DNS Mapping (${name})`) .withTitle(`Sukka's Ruleset - Local DNS Mapping (${name})`)
.appendDescription( .appendDescription(
SHARED_DESCRIPTION, SHARED_DESCRIPTION,
'', '',
'This is an internal rule that is only referenced by sukka_local_dns_mapping.sgmodule', '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.' 'Do not use this file in your Rule section, all entries are included in non_ip/domestic.conf already.'
);
const mihomoOutput = new SurgeOnlyRulesetOutput(
span,
name.toLowerCase(),
'mihomo_nameserver_policy',
OUTPUT_INTERNAL_DIR
)
.withTitle(`Sukka's Ruleset - Local DNS Mapping for Mihomo NameServer Policy (${name})`)
.appendDescription(
SHARED_DESCRIPTION,
'',
'This ruleset is only used for mihomo\'s nameserver-policy feature, which',
'is similar to the RULE-SET referenced by sukka_local_dns_mapping.sgmodule.',
'Do not use this file in your Rule section, all entries are included in non_ip/domestic.conf already.'
); );
domains.forEach((domain) => { domains.forEach((domain) => {
switch (domain[0]) { switch (domain[0]) {
case '$': case '$':
output.addDomain(domain.slice(1)); surgeOutput.addDomain(domain.slice(1));
mihomoOutput.addDomain(domain.slice(1));
break; break;
case '+': case '+':
output.addDomainSuffix(domain.slice(1)); surgeOutput.addDomainSuffix(domain.slice(1));
mihomoOutput.addDomainSuffix(domain.slice(1));
break; break;
default: default:
output.addDomainSuffix(domain); surgeOutput.addDomainSuffix(domain);
mihomoOutput.addDomainSuffix(domain);
break; break;
} }
}); });
return output.write(); return Promise.all([
surgeOutput.write(),
mihomoOutput.write()
]);
}), }),
compareAndWriteFile( compareAndWriteFile(
@ -194,24 +220,49 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
yaml.stringify( yaml.stringify(
dataset.reduce<{ dataset.reduce<{
dns: { 'nameserver-policy': Record<string, string | string[]> }, dns: { 'nameserver-policy': Record<string, string | string[]> },
hosts: Record<string, string | string[]> hosts: Record<string, string | string[]>,
'rule-providers': Record<string, {
type: 'http',
path: `./sukkaw_ruleset/${string}`,
url: string,
behavior: 'classical',
format: 'text',
interval: number
}>
}>((acc, cur) => { }>((acc, cur) => {
const { domains, dns, ...rest } = cur[1]; const { domains, dns, ruleset, ...rest } = cur[1];
domains.forEach((domain) => {
switch (domain[0]) {
case '$':
domain = domain.slice(1);
break;
case '+':
domain = `*.${domain.slice(1)}`;
break;
default:
domain = `+.${domain}`;
break;
}
acc.dns['nameserver-policy'][domain] = dns; if (ruleset) {
}); const ruleset_name = cur[0].toLowerCase();
const mihomo_ruleset_id = `mihomo_nameserver_policy_${ruleset_name}`;
acc.dns['nameserver-policy'][`rule-set:${mihomo_ruleset_id}`] = dns;
acc['rule-providers'][mihomo_ruleset_id] = {
type: 'http',
path: `./sukkaw_ruleset/${mihomo_ruleset_id}.txt`,
url: `https://ruleset.skk.moe/Internal/mihomo_nameserver_policy/${ruleset_name}.txt`,
behavior: 'classical',
format: 'text',
interval: 43200
};
} else {
domains.forEach((domain) => {
switch (domain[0]) {
case '$':
domain = domain.slice(1);
break;
case '+':
domain = `*.${domain.slice(1)}`;
break;
default:
domain = `+.${domain}`;
break;
}
acc.dns['nameserver-policy'][domain] = dns;
});
}
if ('hosts' in rest) { if ('hosts' in rest) {
// eslint-disable-next-line guard-for-in -- known plain object // eslint-disable-next-line guard-for-in -- known plain object
@ -234,6 +285,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
return acc; return acc;
}, { }, {
dns: { 'nameserver-policy': {} }, dns: { 'nameserver-policy': {} },
'rule-providers': {},
hosts: {} hosts: {}
}), }),
{ version: '1.1' } { version: '1.1' }

View File

@ -35,6 +35,21 @@ export class SurgeOnlyRulesetOutput extends FileOutput {
} }
} }
export class MihomoNameserverPolicyOutput extends FileOutput {
constructor(
span: Span,
id: string,
type: 'non_ip' | (string & {}),
overrideOutputDir?: string
) {
super(span, id);
this.strategies = [
new ClashClassicRuleSet(type, overrideOutputDir)
];
}
}
export class ClashOnlyRulesetOutput extends FileOutput { export class ClashOnlyRulesetOutput extends FileOutput {
constructor( constructor(
span: Span, span: Span,

View File

@ -94,7 +94,7 @@ export class ClashClassicRuleSet extends BaseWriteStrategy {
protected result: string[] = [`DOMAIN,${MARKER_DOMAIN}`]; protected result: string[] = [`DOMAIN,${MARKER_DOMAIN}`];
constructor(public readonly type: 'ip' | 'non_ip' /* | (string & {}) */, public readonly outputDir = OUTPUT_CLASH_DIR) { constructor(public readonly type: 'ip' | 'non_ip' | (string & {}), public readonly outputDir = OUTPUT_CLASH_DIR) {
super(outputDir); super(outputDir);
} }