fix: ShadowRocket 策略组改用 policy-regex-filter 收节点
ShadowRocket 没有 Surge 的 use= 订阅引用语法,首版 conf 里 「订阅名 + use=true」在真机上解析失败,All Nodes 组只显示一个 无效的 Proxy Group 残留成员。改为原生的 policy-regex-filter 从 App 内全部节点筛选(收纳全部节点 = .*),订阅命名约束随之 取消,删除 SHADOWROCKET_SUB_NAME 常量并同步前端提示与文档。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,6 @@ import { convertSurgeList } from '../services/rulesets/convert.js';
|
||||
import { generateSurge } from '../services/generators/surge.js';
|
||||
import { generateShadowrocket } from '../services/generators/shadowrocket.js';
|
||||
import { generateShadowrocketConf } from '../services/generators/shadowrocket-conf.js';
|
||||
import { SHADOWROCKET_SUB_NAME } from '@proxy-station/shared';
|
||||
import { generateClash } from '../services/generators/clash.js';
|
||||
|
||||
function constantTimeEqual(a: string, b: string): boolean {
|
||||
@@ -65,9 +64,7 @@ export const subRoute = new Hono()
|
||||
const row = findToken(c.req.param('token'));
|
||||
if (!row) return c.text('not found', 404);
|
||||
touchToken(row.id, row.accessCount);
|
||||
const { content } = generateShadowrocketConf(irFor(row), {
|
||||
subscriptionName: SHADOWROCKET_SUB_NAME,
|
||||
});
|
||||
const { content } = generateShadowrocketConf(irFor(row));
|
||||
c.header('content-type', 'text/plain; charset=utf-8');
|
||||
c.header('content-disposition', 'attachment; filename="Proxy Station Rules.conf"; filename*=UTF-8\'\'Proxy%20Station%20Rules.conf');
|
||||
return c.body(content);
|
||||
|
||||
@@ -50,18 +50,15 @@ export interface ShadowrocketConfOutput {
|
||||
|
||||
/**
|
||||
* 生成 ShadowRocket 配置文件:策略组 + 规则,不含节点。
|
||||
* 节点由「节点订阅」单独导入,策略组通过 `use=true` 引用该订阅的名称,
|
||||
* 因此用户在 App 里给订阅起的名字必须与 subscriptionName 一致。
|
||||
* 节点由「节点订阅」单独导入。ShadowRocket 没有 Surge 的 `use=` 订阅引用语法,
|
||||
* 策略组收节点只能靠 `policy-regex-filter` 从 App 内全部节点里筛选,
|
||||
* 因此订阅在 App 里叫什么名字都可以。
|
||||
*/
|
||||
export function generateShadowrocketConf(
|
||||
ir: ProfileIR,
|
||||
opts: { subscriptionName: string }
|
||||
): ShadowrocketConfOutput {
|
||||
const sub = quoteName(opts.subscriptionName);
|
||||
export function generateShadowrocketConf(ir: ProfileIR): ShadowrocketConfOutput {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push('# 由 Proxy Station 生成的 ShadowRocket 配置(策略组 + 规则)');
|
||||
lines.push(`# 节点请另行导入「节点订阅」,并在 App 中将其命名为:${opts.subscriptionName}`);
|
||||
lines.push('# 节点请另行导入「节点订阅」');
|
||||
lines.push('');
|
||||
lines.push('[General]');
|
||||
lines.push(shadowrocketGeneral(ir.sections['General'] ?? ''));
|
||||
@@ -69,13 +66,7 @@ export function generateShadowrocketConf(
|
||||
lines.push('', '[Proxy Group]');
|
||||
for (const g of ir.groups) {
|
||||
const parts: string[] = [g.type];
|
||||
if (g.filterRegex) {
|
||||
// 地区组:引用订阅 + 正则筛选(ShadowRocket 原生支持 policy-regex-filter)
|
||||
parts.push(sub, 'use=true', `policy-regex-filter=${g.filterRegex}`);
|
||||
} else if (g.includeAllNodes) {
|
||||
// 收纳全部节点:引用订阅
|
||||
parts.push(sub, 'use=true');
|
||||
} else {
|
||||
if (!g.filterRegex && !g.includeAllNodes) {
|
||||
const members = g.members.map((m) => quoteName(m.name));
|
||||
parts.push(...(members.length ? members : ['DIRECT']));
|
||||
}
|
||||
@@ -84,6 +75,9 @@ export function generateShadowrocketConf(
|
||||
if (g.interval) parts.push(`interval=${g.interval}`);
|
||||
if (g.tolerance) parts.push(`tolerance=${g.tolerance}`);
|
||||
}
|
||||
// policy-regex-filter 从 App 内全部节点里筛选;「收纳全部节点」用全匹配正则
|
||||
if (g.filterRegex) parts.push(`policy-regex-filter=${g.filterRegex}`);
|
||||
else if (g.includeAllNodes) parts.push('policy-regex-filter=.*');
|
||||
lines.push(`${g.name} = ${parts.join(', ')}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user