feat: 脚本内部 produceArtifact 支持指定 produceType: 'internal', produceOpts: { 'include-unsupported-proxy': true } 来获得内部的数据结构; 订阅链接参数支持 type=internal&includeUnsupportedProxy=true; 文件支持 nunjucks 模板, 为 sing-box 增加的 Filter 用法 sub/col 为订阅/组合订阅中的节点名 {{ '订阅的name' | sub('美国|🇺🇸|us', 'i') }}, subNode/colNode 为订阅/组合订阅中的节点 {{ '订阅的name' | subNode('美国|🇺🇸|us', 'i') }}, 底层 produceArtifact('subscription', 'sing-box', 'internal', '美国|🇺🇸|us', 'i')

This commit is contained in:
xream
2024-01-14 12:13:29 +08:00
parent 12903d77f7
commit 5ecce27f4e
12 changed files with 586 additions and 286 deletions

View File

@@ -20,7 +20,7 @@ export function getPlatformFromHeaders(headers) {
} else if (UA.indexOf('Decar') !== -1 || UA.indexOf('Loon') !== -1) {
return 'Loon';
} else if (UA.indexOf('Shadowrocket') !== -1) {
return 'ShadowRocket';
return 'Shadowrocket';
} else if (UA.indexOf('Stash') !== -1) {
return 'Stash';
} else if (

210
backend/src/utils/tpl.js Normal file
View File

@@ -0,0 +1,210 @@
import nunjucks from 'nunjucks';
import { ProxyUtils } from '@/core/proxy-utils';
import { produceArtifact } from '@/restful/sync';
import lodash from 'lodash';
import $ from '@/core/app';
import scriptResourceCache from '@/utils/script-resource-cache';
import { getFlowHeaders, parseFlowHeaders, flowTransfer } from '@/utils/flow';
const flowUtils = { getFlowHeaders, parseFlowHeaders, flowTransfer };
const n = nunjucks.configure({ autoescape: false });
n.addFilter(
'produceArtifact',
(...args) => {
const callback = args.pop();
const name = args[0];
const type = args[1];
const platform = args[2];
const produceType = args[3];
const nameRegex = args[4];
const nameRegexFlags = args[5];
produceArtifact({
type,
name,
platform,
produceType,
})
.then((artifact) => {
callback(
null,
artifact.filter(({ tag }) =>
nameRegex
? new RegExp(nameRegex, nameRegexFlags).test(tag)
: true,
),
);
})
.catch((e) => {
$.error(`produceArtifact filter error: ${e.message ?? e}`);
callback(e);
});
},
true,
);
n.addFilter(
'subNode',
(...args) => {
const callback = args.pop();
const name = args[0];
const nameRegex = args[1];
const nameRegexFlags = args[2];
produceArtifact({
type: 'subscription',
name,
platform: 'sing-box',
produceType: 'internal',
})
.then((artifact) => {
callback(
null,
JSON.stringify(
artifact.filter(({ tag }) =>
nameRegex
? new RegExp(nameRegex, nameRegexFlags).test(
tag,
)
: true,
),
).replace(/(^\[|\]$)/g, ''),
);
})
.catch((e) => {
$.error(`subNode filter error: ${e.message ?? e}`);
callback(e);
});
},
true,
);
n.addFilter(
'colNode',
(...args) => {
const callback = args.pop();
const name = args[0];
const nameRegex = args[1];
const nameRegexFlags = args[2];
produceArtifact({
type: 'collection',
name,
platform: 'sing-box',
produceType: 'internal',
})
.then((artifact) => {
callback(
null,
JSON.stringify(
artifact.filter(({ tag }) =>
nameRegex
? new RegExp(nameRegex, nameRegexFlags).test(
tag,
)
: true,
),
).replace(/(^\[|\]$)/g, ''),
);
})
.catch((e) => {
$.error(`colNode filter error: ${e.message ?? e}`);
callback(e);
});
},
true,
);
n.addFilter(
'sub',
(...args) => {
const callback = args.pop();
const name = args[0];
const nameRegex = args[1];
const nameRegexFlags = args[2];
produceArtifact({
type: 'subscription',
name,
platform: 'sing-box',
produceType: 'internal',
})
.then((artifact) => {
callback(
null,
JSON.stringify(
artifact
.filter(({ tag }) =>
nameRegex
? new RegExp(
nameRegex,
nameRegexFlags,
).test(tag)
: true,
)
.map((p) => p.tag),
).replace(/(^\[|\]$)/g, ''),
);
})
.catch((e) => {
$.error(`sub filter error: ${e.message ?? e}`);
callback(e);
});
},
true,
);
n.addFilter(
'col',
(...args) => {
const callback = args.pop();
const name = args[0];
const nameRegex = args[1];
const nameRegexFlags = args[2];
produceArtifact({
type: 'collection',
name,
platform: 'sing-box',
produceType: 'internal',
})
.then((artifact) => {
callback(
null,
JSON.stringify(
artifact
.filter(({ tag }) =>
nameRegex
? new RegExp(
nameRegex,
nameRegexFlags,
).test(tag)
: true,
)
.map((p) => p.tag),
).replace(/(^\[|\]$)/g, ''),
);
})
.catch((e) => {
$.error(`col filter error: ${e.message ?? e}`);
callback(e);
});
},
true,
);
export const render = async (tpl = '', data = {}) => {
return new Promise((resolve) => {
n.renderString(
tpl,
{
$substore: $,
lodash: lodash,
ProxyUtils: ProxyUtils,
scriptResourceCache: scriptResourceCache,
flowUtils: flowUtils,
// produceArtifact: produceArtifact,
...data,
},
(e, result) => {
if (e) {
$.error(`rendering error: ${e.message ?? e}`);
resolve('');
} else {
resolve(result);
}
},
);
});
};