Feat: implement Clash Meta mrs format

This commit is contained in:
SukkaW 2024-08-06 19:10:29 +08:00
parent 03f1a0058e
commit 50ca0c5e9e
9 changed files with 114 additions and 10 deletions

View File

@ -53,7 +53,8 @@ export const buildAppleCdn = task(require.main === module, __filename)(async (sp
domainset, domainset,
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/apple_cdn.conf'), path.resolve(__dirname, '../List/domainset/apple_cdn.conf'),
path.resolve(__dirname, '../Clash/domainset/apple_cdn.txt') path.resolve(__dirname, '../Clash/domainset/apple_cdn.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/apple_cdn.mrs')
) )
]); ]);
}); });

View File

@ -78,7 +78,8 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
sortDomains(domainDeduper(cdnDomainsList)), sortDomains(domainDeduper(cdnDomainsList)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/cdn.conf'), path.resolve(__dirname, '../List/domainset/cdn.conf'),
path.resolve(__dirname, '../Clash/domainset/cdn.txt') path.resolve(__dirname, '../Clash/domainset/cdn.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/cdn.mrs')
), ),
createRuleset( createRuleset(
span, span,
@ -92,7 +93,8 @@ export const buildCdnDownloadConf = task(require.main === module, __filename)(as
sortDomains(domainDeduper(downloadDomainSet)), sortDomains(domainDeduper(downloadDomainSet)),
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/download.conf'), path.resolve(__dirname, '../List/domainset/download.conf'),
path.resolve(__dirname, '../Clash/domainset/download.txt') path.resolve(__dirname, '../Clash/domainset/download.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/download.mrs')
) )
]); ]);
}); });

View File

@ -127,6 +127,8 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
description = SHARED_DESCRIPTION; description = SHARED_DESCRIPTION;
} }
const clashFileBasename = relativePath.slice(0, -path.extname(relativePath).length);
return createRuleset( return createRuleset(
span, span,
title, title,
@ -135,7 +137,7 @@ function transformDomainset(parentSpan: Span, sourcePath: string, relativePath:
deduped, deduped,
'domainset', 'domainset',
path.resolve(outputSurgeDir, relativePath), path.resolve(outputSurgeDir, relativePath),
path.resolve(outputClashDir, `${relativePath.slice(0, -path.extname(relativePath).length)}.txt`) path.resolve(outputClashDir, `${clashFileBasename}.txt`)
); );
} }
); );

View File

@ -191,7 +191,8 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
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'), path.resolve(__dirname, '../List/domainset/reject.conf'),
path.resolve(__dirname, '../Clash/domainset/reject.txt') path.resolve(__dirname, '../Clash/domainset/reject.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/reject.mrs')
), ),
createRuleset( createRuleset(
span, span,
@ -211,7 +212,8 @@ export const buildRejectDomainSet = task(require.main === module, __filename)(as
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'), path.resolve(__dirname, '../List/domainset/reject_extra.conf'),
path.resolve(__dirname, '../Clash/domainset/reject_extra.txt') path.resolve(__dirname, '../Clash/domainset/reject_extra.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/reject_extra.mrs')
), ),
compareAndWriteFile( compareAndWriteFile(
span, span,

View File

@ -251,6 +251,7 @@ export const buildSpeedtestDomainSet = task(require.main === module, __filename)
deduped, deduped,
'domainset', 'domainset',
path.resolve(__dirname, '../List/domainset/speedtest.conf'), path.resolve(__dirname, '../List/domainset/speedtest.conf'),
path.resolve(__dirname, '../Clash/domainset/speedtest.txt') path.resolve(__dirname, '../Clash/domainset/speedtest.txt'),
path.resolve(__dirname, '../Clash/clash_mrs_domain/speedtest.mrs')
); );
}); });

View File

@ -0,0 +1,57 @@
import path from 'path';
import fs from 'fs';
import fsp from 'fs/promises';
import { Readable } from 'stream';
import { pipeline } from 'stream/promises';
import zlib from 'zlib';
import { async as ezspawn } from '@jsdevtools/ez-spawn';
const mihomoBinaryDir = path.join(__dirname, '../../node_modules/.cache/mihomo');
const mihomoBinaryPath = path.join(mihomoBinaryDir, 'mihomo');
const mihomoBinaryUrl: Partial<Record<NodeJS.Platform, Partial<Record<NodeJS.Architecture, string>>>> = {
linux: {
x64: 'https://github.com/MetaCubeX/mihomo/releases/download/v1.18.7/mihomo-linux-amd64-compatible-v1.18.7.gz'
},
darwin: {
x64: 'https://github.com/MetaCubeX/mihomo/releases/download/v1.18.7/mihomo-darwin-amd64-v1.18.7.gz',
arm64: 'https://github.com/MetaCubeX/mihomo/releases/download/v1.18.7/mihomo-darwin-arm64-v1.18.7.gz'
}
};
const ensureMihomoBinary = async () => {
await fsp.mkdir(mihomoBinaryDir, { recursive: true });
if (!fs.existsSync(mihomoBinaryPath)) {
const writeStream = fs.createWriteStream(mihomoBinaryPath);
const downloadUrl = mihomoBinaryUrl[process.platform]?.[process.arch];
if (!downloadUrl) {
throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`);
}
const res = await fetch(downloadUrl);
if (!res.ok || !res.body) {
throw new Error(`Failed to download mihomo binary: ${res.statusText}`);
}
const gunzip = zlib.createGunzip();
await pipeline(
Readable.fromWeb(res.body),
gunzip,
writeStream
);
}
await fsp.chmod(mihomoBinaryPath, 0o755);
};
export const convertClashMetaMrs = async (type: 'domain', format: 'text', input: string, output: string) => {
await ensureMihomoBinary();
const { stderr } = await ezspawn(mihomoBinaryPath, ['convert-ruleset', type, format, input, output]);
if (stderr) {
throw new Error(stderr);
}
};

View File

@ -151,8 +151,10 @@ 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 & {}), surgePath: string, clashPath: string type: ('ruleset' | 'domainset' | string & {}),
) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn((childSpan) => { surgePath: string, clashPath: string,
clashMrsPath?: string
) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn(async (childSpan) => {
const surgeContent = withBannerArray( const surgeContent = withBannerArray(
title, description, date, title, description, date,
sortRuleSet(type === 'domainset' sortRuleSet(type === 'domainset'
@ -174,8 +176,19 @@ export const createRuleset = (
return withBannerArray(title, description, date, _clashContent); return withBannerArray(title, description, date, _clashContent);
}); });
return Promise.all([ await Promise.all([
compareAndWriteFile(childSpan, surgeContent, surgePath), compareAndWriteFile(childSpan, surgeContent, surgePath),
compareAndWriteFile(childSpan, clashContent, clashPath) compareAndWriteFile(childSpan, clashContent, clashPath)
]); ]);
// if (clashMrsPath) {
// if (type === 'domainset') {
// await childSpan.traceChildAsync('clash meta mrs domain ' + clashMrsPath, async () => {
// await fs.promises.mkdir(path.dirname(clashMrsPath), { recursive: true });
// await convertClashMetaMrs(
// 'domain', 'text', clashPath, clashMrsPath
// );
// });
// }
// }
}); });

View File

@ -21,6 +21,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@cliqz/adblocker": "^1.31.1", "@cliqz/adblocker": "^1.31.1",
"@jsdevtools/ez-spawn": "^3.0.4",
"async-retry": "^1.3.3", "async-retry": "^1.3.3",
"async-sema": "^3.1.1", "async-sema": "^3.1.1",
"better-sqlite3": "^11.1.2", "better-sqlite3": "^11.1.2",

25
pnpm-lock.yaml generated
View File

@ -14,6 +14,9 @@ importers:
'@cliqz/adblocker': '@cliqz/adblocker':
specifier: ^1.31.1 specifier: ^1.31.1
version: 1.31.1 version: 1.31.1
'@jsdevtools/ez-spawn':
specifier: ^3.0.4
version: 3.0.4
async-retry: async-retry:
specifier: ^1.3.3 specifier: ^1.3.3
version: 1.3.3 version: 1.3.3
@ -188,6 +191,10 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@jsdevtools/ez-spawn@3.0.4':
resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==}
engines: {node: '>=10'}
'@napi-rs/wasm-runtime@0.2.4': '@napi-rs/wasm-runtime@0.2.4':
resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
@ -615,6 +622,9 @@ packages:
buffer@5.7.1: buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
call-me-maybe@1.0.2:
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
callsites@3.1.0: callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -1363,6 +1373,10 @@ packages:
streamx@2.18.0: streamx@2.18.0:
resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==}
string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
engines: {node: '>=0.6.19'}
string-similarity@4.0.4: string-similarity@4.0.4:
resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
@ -1634,6 +1648,13 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {} '@humanwhocodes/retry@0.3.0': {}
'@jsdevtools/ez-spawn@3.0.4':
dependencies:
call-me-maybe: 1.0.2
cross-spawn: 7.0.3
string-argv: 0.3.2
type-detect: 4.0.8
'@napi-rs/wasm-runtime@0.2.4': '@napi-rs/wasm-runtime@0.2.4':
dependencies: dependencies:
'@emnapi/core': 1.2.0 '@emnapi/core': 1.2.0
@ -2076,6 +2097,8 @@ snapshots:
base64-js: 1.5.1 base64-js: 1.5.1
ieee754: 1.2.1 ieee754: 1.2.1
call-me-maybe@1.0.2: {}
callsites@3.1.0: {} callsites@3.1.0: {}
camelcase@6.3.0: {} camelcase@6.3.0: {}
@ -2884,6 +2907,8 @@ snapshots:
optionalDependencies: optionalDependencies:
bare-events: 2.4.2 bare-events: 2.4.2
string-argv@0.3.2: {}
string-similarity@4.0.4: {} string-similarity@4.0.4: {}
string-width@4.2.3: string-width@4.2.3: