= []
210 | var newConfigs: any
211 | const configPerList = Math.floor(maxConfigs / configProviders.length)
212 |
213 | for (const sub of configProviders) {
214 | try {
215 | if (selectedProviders.length > 0 && !selectedProviders.includes(sub.name)) {
216 | continue
217 | }
218 | newConfigs = []
219 | for (const link of sub.urls) {
220 | var content: string = await fetch(link).then(r => r.text())
221 | if (sub.type === "b64") {
222 | content = Buffer.from(content, "base64").toString("utf-8")
223 | }
224 | newConfigs.push(content)
225 | }
226 | newConfigs = newConfigs.join("\n").split("\n")
227 | if (!onlyOriginalConfigs) {
228 | acceptableConfigList.push({
229 | name: sub.name,
230 | random: sub.random,
231 | count: configPerList,
232 | configs: newConfigs.filter((cnf: any) => cnf.match(/^(vmess|vless|trojan):\/\//i)),
233 | mergedConfigs: null,
234 | })
235 | }
236 | if (includeOriginalConfigs) {
237 | configList.push({
238 | name: sub.name,
239 | random: sub.random,
240 | count: configPerList,
241 | configs: newConfigs.filter((cnf: any) => cnf.match(new RegExp(`(${selectedTypes.join("|")})`, "i"))),
242 | renamesConfigs: null,
243 | })
244 | }
245 | } catch (e) { }
246 | }
247 |
248 | var ipList = []
249 | if (!cleanIPs.length) {
250 | operators = ["General"]
251 | cleanIPs = [{ip: "", operator: "General"}]
252 | }
253 |
254 | for (const operator of operators) {
255 | var ipList = cleanIPs.filter(el => el.operator == operator).slice(0, 5)
256 | var ip = ipList[Math.floor(Math.random() * ipList.length)].ip
257 | for (const i in acceptableConfigList) {
258 | const el = acceptableConfigList[i]
259 | acceptableConfigList[i].mergedConfigs = el.configs
260 | .map(decodeConfig)
261 | .map((cnf: any) => mixConfig(cnf, url, ip, operator, el.name))
262 | .filter((cnf: any) => (!!cnf && cnf.id))
263 | .map(encodeConfig)
264 | .filter((cnf: any) => !!cnf)
265 | }
266 | var remaining = 0
267 | for (var i = 0; i < 5; i++) {
268 | for (const el of acceptableConfigList) {
269 | if (el.count > el.mergedConfigs.length) {
270 | remaining = remaining + el.count - el.mergedConfigs.length
271 | el.count = el.mergedConfigs.length
272 | } else if (el.count < el.mergedConfigs.length && remaining > 0) {
273 | el.count = el.count + Math.ceil(remaining / 3)
274 | remaining = remaining - Math.ceil(remaining / 3)
275 | }
276 | }
277 | }
278 | for (const el of acceptableConfigList) {
279 | finalConfigList = finalConfigList.concat(
280 | el.random ? getMultipleRandomElements(el.mergedConfigs, el.count) : el.mergedConfigs.slice(0, el.count)
281 | )
282 | }
283 | }
284 | if (includeOriginalConfigs) {
285 | for (const i in configList) {
286 | const el = configList[i]
287 | configList[i].renamedConfigs = el.configs
288 | .map(decodeConfig)
289 | .map((cnf: any) => renameConfig(cnf, el.name))
290 | .filter((cnf: any) => (!!cnf && cnf.id))
291 | .map(encodeConfig)
292 | .filter((cnf: any) => !!cnf)
293 | }
294 | var remaining = 0
295 | for (var i = 0; i < 5; i++) {
296 | for (const el of configList) {
297 | if (el.count > el.renamedConfigs.length) {
298 | remaining = remaining + el.count - el.renamedConfigs.length
299 | el.count = el.renamedConfigs.length
300 | } else if (el.count < el.renamedConfigs.length && remaining > 0) {
301 | el.count = el.count + Math.ceil(remaining / 3)
302 | remaining = remaining - Math.ceil(remaining / 3)
303 | }
304 | }
305 | }
306 | for (const el of configList) {
307 | finalConfigList = finalConfigList.concat(
308 | el.random ? getMultipleRandomElements(el.renamedConfigs, el.count) : el.renamedConfigs.slice(0, el.count)
309 | )
310 | }
311 | }
312 | // return new Response(finalConfigList.join("\n"))
313 | return new Response(Buffer.from(finalConfigList.join("\n"), "utf-8").toString("base64"))
314 | } else if (path) {
315 | const addrPath = url.pathname.replace(/^\/|\/$/g, "")
316 | const newUrl = new URL("https://" + addrPath)
317 | return fetch(new Request(newUrl, request))
318 | } else {
319 | return new Response(`\
320 |
321 |
322 | همه چی درسته
323 |
324 |
325 | این لینک sub را در اپ v2ray خود به شکل زیر کپی کنید. در این صورت یک دامین اتفاقی از خود ورکر به عنوان آیپی تمیز انتخاب شده و روی بیشتر اوپراتورها با کیفیت خوب پاسخ خواهد داد:
326 |
327 |
328 | https://${url.hostname}/sub
329 |
330 |
331 | این لینک sub را همراه با کد اپراتور در اپ v2ray خود کپی کنید. برای مثال در همراه اول به شکل زیر خواهد بود:
332 |
333 |
334 | https://${url.hostname}/sub/mci
335 |
336 |
337 | و یا همین لینک را همراه آیپی تمیز در اپ خود اضافه کنید:
338 |
339 |
340 | https://${url.hostname}/sub/1.2.3.4
341 |
342 |
343 | میتوانید چند آیپی تمیز را با کاما جدا کنید. در این صورت برای هر آیپی تمیز به تعداد قدید شده، کانفیک ترکیب شده با ورکر تحویل می دهد:
344 |
345 |
346 | https://${url.hostname}/sub/1.2.3.4,9.8.7.6
347 |
348 |
349 | دقیقا با همین مدل میتوانید دامین آیپی تمیز نیز استفاده کنید:
350 |
351 |
352 | https://${url.hostname}/sub/mci.ircf.space
353 |
354 |
355 | میتوانید از چند سابدامنین آیءی تمیز نیز استفاده کنید:
356 |
357 |
358 | https://${url.hostname}/sub/mci.ircf.space,my.domain.me
359 |
360 |
361 | میتوانید با متغیر max تعداد کانفیگ را مشخص کنید:
362 |
363 |
364 | https://${url.hostname}/sub?max=200
365 |
366 |
367 | همچنین میتوانید با متغیر original با عدد 0 یا 1 و یا با yes/no مشخص کنید که کانفیگهای اصلی (ترکیب نشده با ورکر) هم در خروجی آورده شوند یا نه:
368 |
369 |
370 | https://${url.hostname}/sub/1.2.3.4?max=200&original=yes
371 |
372 |
373 | https://${url.hostname}/sub?max=200&original=0
374 |
375 |
376 | در صورت لزوم می توانید با متغیر merge مشخص کنید که کانفیگهای ترکیبی حذف شوند:
377 |
378 |
379 | https://${url.hostname}/sub?max=200&original=yes&merge=no
380 |
381 |
382 | همچنین میتوانید fp و alpn را نیز مشخص کنید:
383 |
384 |
385 | https://${url.hostname}/sub?max=200&fp=chrome&alpn=h2,http/1.1
386 |
387 |
388 | در صورت نیاز میتوانید برای کانفیگهای اصلی، تعیین کنید که کدام نوع از کانفیگها را برای شما لیست کند:
389 |
390 |
391 | https://${url.hostname}/sub?max=200&type=vmess,ss,ssr,vless
392 |
393 |
394 | در صورت نیاز میتوانید لیست پرووایدرها را محدود کنید:
395 |
396 |
397 | https://${url.hostname}/sub?provider=mahdibland,vpei
398 |
399 | `, {
400 | headers: {
401 | "content-type": "text/html;charset=UTF-8",
402 | },
403 | })
404 | }
405 | }
406 | }
407 |
408 | function encodeConfig(conf: any): string|null {
409 | var configStr: string|null = null
410 |
411 | try {
412 | if (conf.protocol === "vmess") {
413 | delete conf.protocol
414 | configStr = "vmess://" + Buffer.from(JSON.stringify(conf), "utf-8").toString("base64")
415 | } else if (["vless", "trojan"].includes(conf?.protocol)) {
416 | configStr = `${conf.protocol}://${conf.id}@${conf.add}:${conf.port}?security=${conf.tls}&type=${conf.net}&path=${encodeURIComponent(conf.path)}&host=${encodeURIComponent(conf.host)}&tls=${conf.tls}&sni=${conf.sni}#${encodeURIComponent(conf.ps)}`;
417 | }
418 | } catch (e) {
419 | // console.log(`Failed to encode ${JSON.stringify(conf)}`, e)
420 | }
421 |
422 | return configStr
423 | }
424 |
425 | function decodeConfig(configStr: string): any {
426 | var match: any = null
427 | var conf: any = null
428 | if (configStr.startsWith("vmess://")) {
429 | try {
430 | conf = JSON.parse(Buffer.from(configStr.substring(8), "base64").toString("utf-8"))
431 | conf.protocol = "vmess"
432 | } catch (e) { }
433 | } else if (match = configStr.match(/^(?trojan|vless):\/\/(?.*)@(?.*):(?\d+)\??(?.*)#(?.*)$/)) {
434 | try {
435 | const optionsArr = match.groups.options.split('&') ?? []
436 | const optionsObj = optionsArr.reduce((obj: Record, option: string) => {
437 | const [key, value] = option.split('=')
438 | obj[key] = decodeURIComponent(value)
439 | return obj
440 | }, {} as Record)
441 |
442 | conf = {
443 | protocol: match.groups.protocol,
444 | id: match.groups.id,
445 | add: match.groups?.add,
446 | port: match.groups.port ?? 443,
447 | ps: match.groups?.ps,
448 | net: optionsObj.type ?? (optionsObj.net ?? "tcp"),
449 | host: optionsObj?.host,
450 | path: optionsObj?.path,
451 | tls: optionsObj.security ?? "none",
452 | sni: optionsObj?.sni,
453 | alpn: optionsObj?.alpn,
454 | }
455 | } catch (e) {
456 | // console.log(`Failed to decode ${configStr}`, e)
457 | }
458 | }
459 | return conf
460 | }
461 |
462 | function mixConfig(conf: any, url: URL, ip: string, operator: string, provider: string) {
463 | try {
464 | if (conf.tls != "tls" || conf.net == "tcp") {
465 | // console.log(`notls ${JSON.stringify(conf)}`)
466 | return {}
467 | }
468 |
469 | var addr = conf.sni
470 | if (!addr) {
471 | if (conf.host && !isIp(conf.host)) {
472 | addr = conf.host
473 | } else if (conf.add && !isIp(conf.add)) {
474 | addr = conf.add
475 | }
476 | }
477 | if (!addr) {
478 | // console.log(`noaddress ${JSON.stringify(conf)}`)
479 | return {}
480 | }
481 |
482 | if (addr.endsWith('.workers.dev')) {
483 | // Already merged with worker
484 | const part1 = conf.path.split("/").pop()
485 | const part2 = conf.path.substring(0, conf.path.length - part1.length - 1)
486 | var path
487 | if (part1.includes(":")) {
488 | addr = part1.replace(/^\//g, "").split(":")
489 | conf.port = parseInt(addr[1])
490 | addr = addr[0]
491 | path = "/" + part2.replace(/^\//g, "")
492 | } else if (part2.includes(":")) {
493 | addr = part2.replace(/^\//g, "").split(":")
494 | conf.port = parseInt(addr[1])
495 | addr = addr[0]
496 | path = "/" + part1.replace(/^\//g, "")
497 | } else if (part1.includes(".")) {
498 | addr = part1.replace(/^\//g, "")
499 | conf.port = 443
500 | path = "/" + part2.replace(/^\//g, "")
501 | } else {
502 | addr = part2.replace(/^\//g, "")
503 | conf.port = 443
504 | path = "/" + part1.replace(/^\//g, "")
505 | }
506 | conf.path = path
507 | }
508 |
509 | conf.ps = conf?.ps ? conf.ps : conf.name
510 | if (provider) {
511 | conf.ps = provider + "-" + conf.ps
512 | }
513 |
514 | conf.ps = conf.ps + "-worker-" + operator.toLocaleLowerCase()
515 | conf.name = conf.ps
516 | conf.host = url.hostname
517 | conf.sni = url.hostname
518 | if (ip) {
519 | conf.add = ip
520 | } else {
521 | conf.add = domainList[Math.floor(Math.random() * domainList.length)]
522 | }
523 |
524 | if (conf?.port != 443) {
525 | return {}
526 | }
527 |
528 | // conf.path = "/" + addr + ":" + conf.port + (conf?.path ? "/" + conf.path.replace(/^\//g, "") : "")
529 | conf.path = "/" + addr + (conf?.path ? "/" + conf.path.replace(/^\//g, "") : "")
530 | conf.alpn = alpnList[Math.floor(Math.random() * alpnList.length)]
531 | conf.fp = fpList[Math.floor(Math.random() * fpList.length)]
532 | conf.utls = conf.fp
533 | // conf.port = 443
534 | return conf
535 | } catch (e) {
536 | // console.log(`Failed to merge config ${JSON.stringify(conf)}`, e)
537 | return {}
538 | }
539 | }
540 |
541 | function renameConfig(conf: any, provider: string) {
542 | try {
543 | conf.ps = conf?.ps ? conf.ps : conf.name
544 | conf.ps = provider + "-" + conf.ps
545 | return conf
546 | } catch (e) {
547 | // console.log(`Failed to rename config ${JSON.stringify(conf)}`, e)
548 | return {}
549 | }
550 | }
551 |
552 | function getMultipleRandomElements(arr: Array, num: number) {
553 | var shuffled = arr.slice(0, num * 2).sort(() => 0.5 - Math.random())
554 | return shuffled.slice(0, num)
555 | }
556 |
557 | function isIp(str: string) {
558 | try {
559 | if (str == "" || str == undefined) return false
560 | if (!/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-4])$/.test(str)) {
561 | return false
562 | }
563 | var ls = str.split('.')
564 | if (ls == null || ls.length != 4 || ls[3] == "0" || parseInt(ls[3]) === 0) {
565 | return false
566 | }
567 | return true
568 | } catch (e) { }
569 | return false
570 | }
571 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Enable incremental compilation */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | "lib": [
16 | "es2021"
17 | ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
18 | "jsx": "react" /* Specify what JSX code is generated. */,
19 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
20 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
21 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
22 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
23 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
24 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
25 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
26 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
27 |
28 | /* Modules */
29 | "module": "es2022" /* Specify what module code is generated. */,
30 | // "rootDir": "./", /* Specify the root folder within your source files. */
31 | "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
32 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
36 | "types": [
37 | "@cloudflare/workers-types",
38 | "node"
39 | ] /* Specify type package names to be included without being referenced in a source file. */,
40 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
41 | "resolveJsonModule": true /* Enable importing .json files */,
42 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */
43 |
44 | /* JavaScript Support */
45 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
46 | "checkJs": false /* Enable error reporting in type-checked JavaScript files. */,
47 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
48 |
49 | /* Emit */
50 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
51 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
52 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
53 | "sourceMap": false /* Create source map files for emitted JavaScript files. */,
54 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
55 | // "outDir": "./", /* Specify an output folder for all emitted files. */
56 | "removeComments": false /* Disable emitting comments. */,
57 | "noEmit": true /* Disable emitting files from a compilation. */,
58 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
59 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
60 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
61 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
62 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
63 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
64 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
65 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
66 | // "newLine": "crlf", /* Set the newline character for emitting files. */
67 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
68 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
69 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
70 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
71 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
72 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
73 |
74 | /* Interop Constraints */
75 | "isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
76 | "allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
77 | // "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
78 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
79 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
80 |
81 | /* Type Checking */
82 | "strict": true /* Enable all strict type-checking options. */,
83 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
84 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
85 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
86 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
87 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
88 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
89 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
90 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
91 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
92 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
93 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
94 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
95 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
96 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
97 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
98 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
99 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
100 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
101 |
102 | /* Completeness */
103 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
104 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/wrangler.toml:
--------------------------------------------------------------------------------
1 | name = "sub"
2 | main = "src/index.ts"
3 | compatibility_date = "2023-04-11"
4 |
--------------------------------------------------------------------------------