build-sspanel-appprofile.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import fsp from 'fs/promises';
  2. import { getAppleCdnDomainsPromise } from './build-apple-cdn';
  3. import { getDomesticDomainsRulesetPromise } from './build-domestic-ruleset';
  4. import { surgeRulesetToClashClassicalTextRuleset } from './lib/clash';
  5. import { readFileByLine } from './lib/fetch-text-by-line';
  6. import { processLineFromReadline } from './lib/process-line';
  7. import { task } from './lib/trace-runner';
  8. import path from 'path';
  9. import { ALL as AllStreamServices } from '../Source/stream';
  10. import { getChnCidrPromise } from './build-chn-cidr';
  11. import { getTelegramCIDRPromise } from './build-telegram-cidr';
  12. import { compareAndWriteFile } from './lib/create-file';
  13. import { getMicrosoftCdnRulesetPromise } from './build-microsoft-cdn';
  14. const POLICY_GROUPS: Array<[name: string, insertProxy: boolean, insertDirect: boolean]> = [
  15. ['Default Proxy', true, false],
  16. ['Global', true, true],
  17. ['Microsoft & Apple', true, true],
  18. ['Stream', true, false],
  19. ['Domestic', false, true],
  20. ['Final Match', true, true]
  21. ];
  22. const removeNoResolved = (line: string) => line.replace(',no-resolve', '');
  23. /**
  24. * This only generates a simplified version, for under-used users only.
  25. */
  26. export const buildSSPanelUIMAppProfile = task(import.meta.path, async () => {
  27. const [
  28. domesticDomains,
  29. appleCdnDomains,
  30. microsoftCdnDomains,
  31. appleCnDomains,
  32. neteaseMusicDomains,
  33. microsoftDomains,
  34. appleDomains,
  35. streamDomains,
  36. globalDomains,
  37. globalPlusDomains,
  38. telegramDomains,
  39. domesticCidrs,
  40. streamCidrs,
  41. { results: rawTelegramCidrs }
  42. ] = await Promise.all([
  43. // domestic - domains
  44. getDomesticDomainsRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset),
  45. getAppleCdnDomainsPromise().then(domains => domains.map(domain => `DOMAIN-SUFFIX,${domain}`)),
  46. getMicrosoftCdnRulesetPromise().then(surgeRulesetToClashClassicalTextRuleset),
  47. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/apple_cn.conf'))),
  48. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/neteasemusic.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
  49. // microsoft & apple - domains
  50. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/microsoft.conf'))),
  51. (processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/apple_services.conf')))).then(surgeRulesetToClashClassicalTextRuleset),
  52. // stream - domains
  53. surgeRulesetToClashClassicalTextRuleset(AllStreamServices.flatMap((i) => i.rules)),
  54. // global - domains
  55. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/global.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
  56. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/global_plus.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
  57. processLineFromReadline(readFileByLine(path.resolve(import.meta.dir, '../Source/non_ip/telegram.conf'))).then(surgeRulesetToClashClassicalTextRuleset),
  58. // domestic - ip cidr
  59. getChnCidrPromise().then(cidrs => cidrs.map(cidr => `IP-CIDR,${cidr}`)),
  60. AllStreamServices.flatMap((i) => (
  61. i.ip
  62. ? [
  63. ...i.ip.v4.map((ip) => `IP-CIDR,${ip}`),
  64. ...i.ip.v6.map((ip) => `IP-CIDR6,${ip}`)
  65. ]
  66. : []
  67. )),
  68. // global - ip cidr
  69. getTelegramCIDRPromise()
  70. ] as const);
  71. const telegramCidrs = rawTelegramCidrs.map(removeNoResolved);
  72. const output = generateAppProfile(
  73. [
  74. ...domesticDomains,
  75. ...appleCdnDomains,
  76. ...microsoftCdnDomains,
  77. ...appleCnDomains,
  78. ...neteaseMusicDomains
  79. ],
  80. [
  81. ...microsoftDomains,
  82. ...appleDomains
  83. ],
  84. streamDomains,
  85. [
  86. ...globalDomains,
  87. ...globalPlusDomains,
  88. ...telegramDomains
  89. ],
  90. domesticCidrs,
  91. streamCidrs,
  92. [
  93. ...telegramCidrs
  94. ]
  95. );
  96. await fsp.mkdir(path.resolve(import.meta.dir, '../List/internal'), { recursive: true });
  97. await compareAndWriteFile(
  98. output,
  99. path.resolve(import.meta.dir, '../List/internal/appprofile.php')
  100. );
  101. });
  102. if (import.meta.main) {
  103. buildSSPanelUIMAppProfile();
  104. }
  105. const isTruthy = <T>(i: T | 0 | '' | false | null | undefined): i is T => !!i;
  106. function generateAppProfile(
  107. directDomains: string[],
  108. microsoftAppleDomains: string[],
  109. streamDomains: string[],
  110. globalDomains: string[],
  111. directCidrs: string[],
  112. streamCidrs: string[],
  113. globalCidrs: string[]
  114. ) {
  115. const result: string[] = [];
  116. result.push(
  117. '<?php',
  118. '',
  119. 'declare(strict_types=1);',
  120. '',
  121. '$_ENV[\'Clash_Config\'] = [',
  122. ' \'port\' => 7890,',
  123. ' \'socks-port\' => 7891,',
  124. ' \'allow-lan\' => false,',
  125. ' \'mode\' => \'Rule\',',
  126. ' \'ipv6\' => true,',
  127. ' \'log-level\' => \'error\',',
  128. ' \'external-controller\' => \'0.0.0.0:9090\',',
  129. '];',
  130. '',
  131. `$_ENV['Clash_Group_Indexes'] = [${JSON.stringify(POLICY_GROUPS.reduce<number[]>((acc, [, insertProxy], index) => {
  132. if (insertProxy) {
  133. acc.push(index);
  134. }
  135. return acc;
  136. }, [])).slice(1, -1)}];`,
  137. '$_ENV[\'Clash_Group_Config\'] = [',
  138. ' \'proxy-groups\' => [',
  139. ...POLICY_GROUPS.flatMap(([name, insertProxy, insertDirect]) => {
  140. return [
  141. ' [',
  142. ` 'name' => '${name}',`,
  143. ' \'type\' => \'select\',',
  144. ' \'proxies\' => [',
  145. insertProxy && name !== 'Default Proxy' && ' \'Default Proxy\',',
  146. insertDirect && ' \'DIRECT\',',
  147. ' ],',
  148. ' ],'
  149. ].filter(isTruthy);
  150. }),
  151. ' ],',
  152. ' \'rules\' => [',
  153. // domestic - domains
  154. ...directDomains.map(line => ` '${line},Domestic',`),
  155. // microsoft & apple - domains
  156. ...microsoftAppleDomains.map(line => ` '${line},Microsoft & Apple',`),
  157. // stream - domains
  158. ...streamDomains.map(line => ` '${line},Stream',`),
  159. // global - domains
  160. ...globalDomains.map(line => ` '${line},Global',`),
  161. // domestic - ip cidr
  162. ...directCidrs.map(line => ` '${line},Domestic,no-resolve',`),
  163. // microsoft & apple - ip cidr (nope)
  164. // stream - ip cidr
  165. ...streamCidrs.map(line => ` '${line},Stream,no-resolve',`),
  166. // global - ip cidr
  167. ...globalCidrs.map(line => ` '${line},Global,no-resolve',`),
  168. // match
  169. ' \'MATCH,Final Match\',',
  170. ' ],',
  171. '];'
  172. );
  173. return result;
  174. }