mirror of
https://github.com/SukkaW/Surge.git
synced 2026-01-29 01:51:52 +08:00
Chore: create rule set / Add China IP sing-box
This commit is contained in:
@@ -7,7 +7,7 @@ import fs from 'fs';
|
||||
import { fastStringArrayJoin, writeFile } from './misc';
|
||||
import { readFileByLine } from './fetch-text-by-line';
|
||||
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) {
|
||||
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 [
|
||||
'#########################################',
|
||||
`# ${title}`,
|
||||
@@ -154,15 +154,32 @@ const MARK = 'this_ruleset_is_made_by_sukkaw.ruleset.skk.moe';
|
||||
export const createRuleset = (
|
||||
parentSpan: Span,
|
||||
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
|
||||
) => parentSpan.traceChild(`create ruleset: ${path.basename(surgePath, path.extname(surgePath))}`).traceAsyncFn(async (childSpan) => {
|
||||
const surgeContent = withBannerArray(
|
||||
title, description, date,
|
||||
type === 'domainset'
|
||||
? [MARK, ...content]
|
||||
: sortRuleSet([`DOMAIN,${MARK}`, ...content])
|
||||
);
|
||||
content = sortRuleSet(content);
|
||||
const surgeContent = childSpan.traceChildSync('process surge ruleset', () => {
|
||||
let _surgeContent;
|
||||
switch (type) {
|
||||
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', () => {
|
||||
let _clashContent;
|
||||
switch (type) {
|
||||
@@ -172,6 +189,10 @@ export const createRuleset = (
|
||||
case 'ruleset':
|
||||
_clashContent = [`DOMAIN,${MARK}`, ...surgeRulesetToClashClassicalTextRuleset(content)];
|
||||
break;
|
||||
case 'ipcidr':
|
||||
case 'ipcidr6':
|
||||
_clashContent = content;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError(`Unknown type: ${type}`);
|
||||
}
|
||||
@@ -186,6 +207,10 @@ export const createRuleset = (
|
||||
case 'ruleset':
|
||||
_singBoxContent = surgeRulesetToSingbox([`DOMAIN,${MARK}`, ...content]);
|
||||
break;
|
||||
case 'ipcidr':
|
||||
case 'ipcidr6':
|
||||
_singBoxContent = ipCidrListToSingbox(content);
|
||||
break;
|
||||
default:
|
||||
throw new TypeError(`Unknown type: ${type}`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { dirname } from 'path';
|
||||
import path, { dirname } from 'path';
|
||||
import fs from 'fs';
|
||||
import fsp from 'fs/promises';
|
||||
import { makeRe } from 'picomatch';
|
||||
@@ -33,3 +33,15 @@ export const writeFile: Write = async (destination: string, input, dir = dirname
|
||||
export const domainWildCardToRegex = (domain: string) => {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -110,3 +110,12 @@ export const surgeDomainsetToSingbox = (domainset: string[]) => {
|
||||
rules: [rule]
|
||||
};
|
||||
};
|
||||
|
||||
export const ipCidrListToSingbox = (ipCidrList: string[]): SingboxSourceFormat => {
|
||||
return {
|
||||
version: 2,
|
||||
rules: [{
|
||||
ip_cidr: ipCidrList
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user