diff --git a/docs/guide/custom-provider.md b/docs/guide/custom-provider.md
index 2cb9b72a3..b82630865 100644
--- a/docs/guide/custom-provider.md
+++ b/docs/guide/custom-provider.md
@@ -117,6 +117,13 @@ module.exports = {
- 如果你正在使用 [DlerCloud](https://dlercloud.com/auth/register?affid=45071),可以使用「通用」类型的订阅地址
:::
+### compatibleMode
+
+- 类型: `boolean`
+- 默认值: `false`
+
+部分机场提供的订阅地址不符合标准,提供一个兼容模式进行解析。
+
## clash
:::warning 注意
diff --git a/lib/provider/V2rayNSubscribeProvider.ts b/lib/provider/V2rayNSubscribeProvider.ts
index 938d461f8..13208c872 100644
--- a/lib/provider/V2rayNSubscribeProvider.ts
+++ b/lib/provider/V2rayNSubscribeProvider.ts
@@ -11,10 +11,10 @@ import Provider from './Provider';
export default class V2rayNSubscribeProvider extends Provider {
private readonly _url: string;
-
+ private readonly _compatibleMode?: boolean;
constructor(name: string, config: V2rayNSubscribeProviderConfig) {
super(name, config);
-
+ this._compatibleMode = config.compatibleMode;
const schema = Joi.object({
url: Joi
.string()
@@ -46,15 +46,16 @@ export default class V2rayNSubscribeProvider extends Provider {
}
public getNodeList(): ReturnType {
- return getV2rayNSubscription(this.url);
+ return getV2rayNSubscription(this.url, this._compatibleMode);
}
}
export const getV2rayNSubscription = async (
url: string,
+ isCompatibleMode: boolean
): Promise> => {
assert(url, '未指定订阅地址 url');
-
+ if (isCompatibleMode) { logger.warn('运行在兼容模式,请注意生成的节点是否正确。'); }
async function requestConfigFromRemote(): Promise> {
const response = ConfigCache.has(url) ? ConfigCache.get(url) : await (async () => {
const res = await got.get(url, {
@@ -74,10 +75,9 @@ export const getV2rayNSubscription = async (
const json = JSON.parse(fromBase64(item.replace('vmess://', '')));
// istanbul ignore next
- if (!json.v || Number(json.v) !== 2) {
- throw new Error(`该订阅 ${url} 可能不是一个有效的 V2rayN 订阅。请参考 http://bit.ly/2N4lZ8X 进行排查`);
+ if (!isCompatibleMode && (!json.v || Number(json.v) !== 2)) {
+ throw new Error(`该订阅 ${url} 可能不是一个有效的 V2rayN 订阅。请参考 http://bit.ly/2N4lZ8X 进行排查, 或者将解析模式改为兼容模式`);
}
-
// istanbul ignore next
if (['kcp', 'http'].indexOf(json.net) > -1) {
logger.warn(`不支持读取 network 类型为 ${json.net} 的 Vmess 节点,节点 ${json.ps} 会被省略`);
diff --git a/lib/provider/__tests__/V2rayNSubscribeProvider.test.ts b/lib/provider/__tests__/V2rayNSubscribeProvider.test.ts
index 85b1c35ba..00a5d37bb 100644
--- a/lib/provider/__tests__/V2rayNSubscribeProvider.test.ts
+++ b/lib/provider/__tests__/V2rayNSubscribeProvider.test.ts
@@ -15,7 +15,7 @@ test('V2rayNSubscribeProvider', async t => {
test('getV2rayNSubscription', async t => {
const url = 'http://example.com/test-v2rayn-sub.txt';
- const configList = await getV2rayNSubscription(url);
+ const configList = await getV2rayNSubscription(url, false);
t.deepEqual(configList[0], {
alterId: '64',
@@ -44,3 +44,22 @@ test('getV2rayNSubscription', async t => {
uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
});
});
+
+test('getV2rayNSubscription compatible mode', async t => {
+ const url = 'http://example.com/test-v2rayn-sub-compatible.txt';
+ const configList = await getV2rayNSubscription(url, true);
+
+ t.deepEqual(configList[0], {
+ alterId: '64',
+ host: 'example.com',
+ hostname: '1.1.1.1',
+ method: 'auto',
+ network: 'ws',
+ nodeName: '测试 1',
+ path: '/',
+ port: 8080,
+ tls: false,
+ type: NodeTypeEnum.Vmess,
+ uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
+ });
+});
diff --git a/lib/types.ts b/lib/types.ts
index 97050a9d5..3940fc4b8 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -123,6 +123,7 @@ export interface ShadowsocksrSubscribeProviderConfig extends ProviderConfig {
export interface V2rayNSubscribeProviderConfig extends ProviderConfig {
readonly url: string;
+ readonly compatibleMode?: boolean;
}
export interface ClashProviderConfig extends ProviderConfig {
diff --git a/test/asset/test-v2rayn-sub-compatible.txt b/test/asset/test-v2rayn-sub-compatible.txt
new file mode 100644
index 000000000..4dd00beff
--- /dev/null
+++ b/test/asset/test-v2rayn-sub-compatible.txt
@@ -0,0 +1 @@
+dm1lc3M6Ly9leUp3Y3lJNkl1YTFpK2l2bFNBeElpd2lZV1JrSWpvaU1TNHhMakV1TVNJc0luQnZjblFpT2pnd09EQXNJbWxrSWpvaU1UTTRObVk0TldVdE5qVTNZaTAwWkRabExUbGtOVFl0TnpoaVlXUmlOelZsTVdaa0lpd2lZV2xrSWpvaU5qUWlMQ0p1WlhRaU9pSjNjeUlzSW5SNWNHVWlPaUp1YjI1bElpd2lhRzl6ZENJNkltVjRZVzF3YkdVdVkyOXRJaXdpY0dGMGFDSTZJbHd2SWl3aWJYVjRJanA3SW1WdVlXSnNaV1FpT2lKbVlXeHpaU0o5ZlE9PQ==
\ No newline at end of file
diff --git a/test/stub-axios.js b/test/stub-axios.js
index 3757c55eb..bb1ffe4e6 100644
--- a/test/stub-axios.js
+++ b/test/stub-axios.js
@@ -33,6 +33,13 @@ const scope = nock('http://example.com')
encoding: 'utf8',
})
)
+ .get(/\/test-v2rayn-sub-compatible\.txt/)
+ .reply(
+ 200,
+ fs.readFileSync(path.join(__dirname, 'asset/test-v2rayn-sub-compatible.txt'), {
+ encoding: 'utf8',
+ })
+ )
.get(/\/netflix\.list/)
.reply(
200,