Created
August 15, 2025 14:33
-
-
Save kidonng/f56cb090d8b9b07d6b24982f2c1d40d0 to your computer and use it in GitHub Desktop.
Usage: deno run -A sing-box-sub.ts --address <address> --secret $(uuidgen) out </etc/sing-box/config.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { readAllSync } from "jsr:@std/[email protected]"; | |
import { ensureDirSync } from "jsr:@std/[email protected]"; | |
import { join } from "jsr:@std/[email protected]"; | |
import { v5 } from "jsr:@std/[email protected]"; | |
import { parseArgs } from "jsr:@std/[email protected]"; | |
// https://sing-box.sagernet.org/manual/proxy-protocol/shadowsocks/#server-example | |
interface SingBoxConfig { | |
inbounds: SingBoxInbound[]; | |
} | |
interface SingBoxInbound { | |
type: "shadowsocks"; | |
listen_port: number; | |
method: string; | |
password: string; | |
users: { name: string; password: string }[]; | |
} | |
// https://github.com/Shadowsocks-NET/OpenOnlineConfig/blob/master/docs/0001-open-online-config-v1.md#3-api-specification | |
interface OOC1Config { | |
username: string; | |
protocols: string[]; | |
shadowsocks: OOC1ShadowsocksServer[]; | |
} | |
// https://github.com/Shadowsocks-NET/OpenOnlineConfig/blob/master/docs/0002-open-online-config-v1-shadowsocks.md#2-api-specification | |
interface OOC1ShadowsocksServer { | |
id: string; | |
name: string; | |
address: string; | |
port: number; | |
method: string; | |
password: string; | |
} | |
interface OOC1APIToken { | |
version: number; | |
baseUrl: string; | |
secret: string; | |
userId: string; | |
} | |
const args: { | |
address: string; | |
secret: string; | |
_: [string]; | |
} = parseArgs(Deno.args, { | |
alias: { | |
a: "address", | |
s: "secret", | |
}, | |
}); | |
const config: SingBoxConfig = JSON.parse( | |
new TextDecoder().decode(readAllSync(Deno.stdin)), | |
); | |
const users = new Map<string, OOC1ShadowsocksServer[]>(); | |
for (const inbound of config.inbounds) { | |
if (inbound.type !== "shadowsocks") { | |
continue; | |
} | |
for (const user of inbound.users) { | |
const password = [inbound.password, user.password].join(":"); | |
users.set(user.name, [ | |
...(users.get(user.name) || []), | |
{ | |
id: await v5.generate(args.secret, new TextEncoder().encode(password)), | |
name: `[${args.address}] ${inbound.type}`, | |
address: args.address, | |
port: inbound.listen_port, | |
method: inbound.method, | |
password, | |
}, | |
]); | |
} | |
} | |
const out = args._[0]; | |
const dir = join(out, args.secret, "ooc", "v1"); | |
ensureDirSync(dir); | |
for (const [name, servers] of users) { | |
const id = await v5.generate(args.secret, new TextEncoder().encode(name)); | |
Deno.writeTextFileSync( | |
join(dir, id), | |
JSON.stringify({ | |
username: name, | |
protocols: ["shadowsocks"], | |
shadowsocks: servers, | |
} as OOC1Config), | |
); | |
// https://github.com/Shadowsocks-NET/OpenOnlineConfig/blob/master/docs/0001-open-online-config-v1.md#22-delivery | |
console.log(name, JSON.stringify({ | |
version: 1, | |
baseUrl: `https://${args.address}`, | |
secret: args.secret, | |
userId: id, | |
} as OOC1APIToken)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment