Skip to content

Commit

Permalink
feat: 生成 quan 订阅 scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
geekdada committed Aug 26, 2019
1 parent 981e6d7 commit afe0a21
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 2 deletions.
3 changes: 3 additions & 0 deletions lib/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
getClashNodes,
getDownloadUrl,
getNodeNames,
getQuantumultNodes,
getShadowsocksJSONConfig,
getShadowsocksNodes,
getShadowsocksNodesJSON,
Expand Down Expand Up @@ -163,6 +164,7 @@ export async function generate(
}),
nodeList,
provider,
providerName: provider,
artifactName,
getDownloadUrl: (name: string) => getDownloadUrl(config.urlBase, name),
getNodeNames,
Expand All @@ -172,6 +174,7 @@ export async function generate(
getShadowsocksNodes,
getShadowsocksNodesJSON,
getShadowsocksrNodes,
getQuantumultNodes,
usFilter,
hkFilter,
toUrlSafeBase64,
Expand Down
49 changes: 49 additions & 0 deletions lib/utils/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,52 @@ test('getV2rayNNodes', t => {
t.is(schemeList[1], 'vmess://eyJ2IjoiMiIsInBzIjoi5rWL6K+VIDIiLCJhZGQiOiIxLjEuMS4xIiwicG9ydCI6IjgwODAiLCJpZCI6IjEzODZmODVlLTY1N2ItNGQ2ZS05ZDU2LTc4YmFkYjc1ZTFmZCIsImFpZCI6IjY0IiwibmV0IjoidGNwIiwidHlwZSI6Im5vbmUiLCJob3N0IjoiIiwicGF0aCI6Ii8iLCJ0bHMiOiIifQ==');
t.is(schemeList[2], 'vmess://eyJ2IjoiMiIsInBzIjoi5rWL6K+VIDMiLCJhZGQiOiIxLjEuMS4xIiwicG9ydCI6IjgwODAiLCJpZCI6IjEzODZmODVlLTY1N2ItNGQ2ZS05ZDU2LTc4YmFkYjc1ZTFmZCIsImFpZCI6IjY0IiwibmV0Ijoid3MiLCJ0eXBlIjoibm9uZSIsImhvc3QiOiIiLCJwYXRoIjoiLyIsInRscyI6IiJ9');
});

test('getQuantumultNodes', t => {
const schemeList = utils.getQuantumultNodes([
{
type: NodeTypeEnum.Vmess,
alterId: '64',
hostname: '1.1.1.1',
method: 'auto',
network: 'ws',
nodeName: '测试 1',
path: '/',
port: 8080,
tls: false,
host: 'example.com',
uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
},
{
type: NodeTypeEnum.Vmess,
alterId: '64',
hostname: '1.1.1.1',
method: 'auto',
network: 'tcp',
nodeName: '测试 2',
path: '/',
port: 8080,
tls: false,
host: '',
uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
},
{
type: NodeTypeEnum.Vmess,
alterId: '64',
hostname: '1.1.1.1',
method: 'auto',
network: 'ws',
nodeName: '测试 3',
path: '/',
port: 8080,
tls: false,
host: '',
uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
},
])
.split('\n');

t.is(schemeList[0], 'vmess://5rWL6K+VIDEgPSB2bWVzcywxLjEuMS4xLDgwODAsY2hhY2hhMjAtaWV0Zi1wb2x5MTMwNSwiMTM4NmY4NWUtNjU3Yi00ZDZlLTlkNTYtNzhiYWRiNzVlMWZkIiw2NCxncm91cD1TdXJnaW8sb3Zlci10bHM9ZmFsc2UsY2VydGlmaWNhdGU9MSxvYmZzPXdzLG9iZnMtcGF0aD0iLyIsb2Jmcy1oZWFkZXI9Ikhvc3Q6ZXhhbXBsZS5jb21bUnJdW05uXVVzZXItQWdlbnQ6TW96aWxsYS81LjAgKGlQaG9uZTsgQ1BVIGlQaG9uZSBPUyAxMl8zXzEgbGlrZSBNYWMgT1MgWCkgQXBwbGVXZWJLaXQvNjA1LjEuMTUgKEtIVE1MLCBsaWtlIEdlY2tvKSBNb2JpbGUvMTVFMTQ4Ig==');
t.is(schemeList[1], 'vmess://5rWL6K+VIDIgPSB2bWVzcywxLjEuMS4xLDgwODAsY2hhY2hhMjAtaWV0Zi1wb2x5MTMwNSwiMTM4NmY4NWUtNjU3Yi00ZDZlLTlkNTYtNzhiYWRiNzVlMWZkIiw2NCxncm91cD1TdXJnaW8sb3Zlci10bHM9ZmFsc2UsY2VydGlmaWNhdGU9MSxvYmZzPXRjcCxvYmZzLXBhdGg9Ii8iLG9iZnMtaGVhZGVyPSJIb3N0OjEuMS4xLjFbUnJdW05uXVVzZXItQWdlbnQ6TW96aWxsYS81LjAgKGlQaG9uZTsgQ1BVIGlQaG9uZSBPUyAxMl8zXzEgbGlrZSBNYWMgT1MgWCkgQXBwbGVXZWJLaXQvNjA1LjEuMTUgKEtIVE1MLCBsaWtlIEdlY2tvKSBNb2JpbGUvMTVFMTQ4Ig==');
t.is(schemeList[2], 'vmess://5rWL6K+VIDMgPSB2bWVzcywxLjEuMS4xLDgwODAsY2hhY2hhMjAtaWV0Zi1wb2x5MTMwNSwiMTM4NmY4NWUtNjU3Yi00ZDZlLTlkNTYtNzhiYWRiNzVlMWZkIiw2NCxncm91cD1TdXJnaW8sb3Zlci10bHM9ZmFsc2UsY2VydGlmaWNhdGU9MSxvYmZzPXdzLG9iZnMtcGF0aD0iLyIsb2Jmcy1oZWFkZXI9Ikhvc3Q6MS4xLjEuMVtScl1bTm5dVXNlci1BZ2VudDpNb3ppbGxhLzUuMCAoaVBob25lOyBDUFUgaVBob25lIE9TIDEyXzNfMSBsaWtlIE1hYyBPUyBYKSBBcHBsZVdlYktpdC82MDUuMS4xNSAoS0hUTUwsIGxpa2UgR2Vja28pIE1vYmlsZS8xNUUxNDgi');
});
55 changes: 53 additions & 2 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export const fromBase64 = (str: string): string => Buffer.from(str, 'base64').to
*/
export const getShadowsocksNodes = (
list: ReadonlyArray<ShadowsocksNodeConfig>,
groupName: string
groupName: string = 'Surgio'
): string => {
const result: ReadonlyArray<any> = list
.map(nodeConfig => {
Expand Down Expand Up @@ -386,7 +386,7 @@ export const getShadowsocksrNodes = (list: ReadonlyArray<ShadowsocksrNodeConfig>

export const getV2rayNNodes = (list: ReadonlyArray<VmessNodeConfig>): string => {
const result: ReadonlyArray<string> = list
.map(nodeConfig => {
.map<string>(nodeConfig => {
if (nodeConfig.enable === false) { return null; }

switch (nodeConfig.type) {
Expand Down Expand Up @@ -417,6 +417,57 @@ export const getV2rayNNodes = (list: ReadonlyArray<VmessNodeConfig>): string =>
return result.join('\n');
};

export const getQuantumultNodes = (
list: ReadonlyArray<ShadowsocksNodeConfig | VmessNodeConfig>,
groupName: string = 'Surgio'
): string => {
function getHeader(
host,
ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'
): string {
return [
`Host:${host}`,
`User-Agent:${ua}`,
].join('[Rr][Nn]');
}

const result: ReadonlyArray<string> = list
.map<string>(nodeConfig => {
if (nodeConfig.enable === false) { return null; }

switch (nodeConfig.type) {
case NodeTypeEnum.Vmess: {
const config = [
'vmess', nodeConfig.hostname, nodeConfig.port,
(nodeConfig.method === 'auto' ? 'chacha20-ietf-poly1305' : nodeConfig.method),
JSON.stringify(nodeConfig.uuid), nodeConfig.alterId,
`group=${groupName}`,
`over-tls=${nodeConfig.tls === true ? 'true' : 'false'}`,
`certificate=1`,
`obfs=${nodeConfig.network}`,
`obfs-path=${JSON.stringify(nodeConfig.path || '/')}`,
`obfs-header=${JSON.stringify(getHeader(nodeConfig.host || nodeConfig.hostname ))}`,
].filter(value => !!value).join(',');

return 'vmess://' + toBase64([
nodeConfig.nodeName,
config,
].join(' = '));
}

case NodeTypeEnum.Shadowsocks: {
return getShadowsocksNodes([nodeConfig], groupName);
}

default:
return null;
}
})
.filter(item => !!item);

return result.join('\n');
};

export const getShadowsocksNodesJSON = (list: ReadonlyArray<ShadowsocksNodeConfig>): string => {
const nodes: ReadonlyArray<object> = list
.map(nodeConfig => {
Expand Down

0 comments on commit afe0a21

Please sign in to comment.