build-speedtest-domainset.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import { domainDeduper } from './lib/domain-deduper';
  2. import path from 'path';
  3. import { createRuleset } from './lib/create-file';
  4. import { sortDomains } from './lib/stable-sort-domain';
  5. import { Sema } from 'async-sema';
  6. import { getHostname } from 'tldts';
  7. import { task } from './trace';
  8. import { fetchWithRetry } from './lib/fetch-retry';
  9. import { SHARED_DESCRIPTION } from './lib/constants';
  10. import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
  11. import { TTL, deserializeArray, fsFetchCache, serializeArray, createCacheKey } from './lib/cache-filesystem';
  12. import { createTrie } from './lib/trie';
  13. const s = new Sema(2);
  14. const cacheKey = createCacheKey(__filename);
  15. const latestTopUserAgentsPromise = fsFetchCache.apply(
  16. cacheKey('https://cdn.jsdelivr.net/npm/top-user-agents@latest/src/desktop.json'),
  17. () => fetchWithRetry(
  18. 'https://cdn.jsdelivr.net/npm/top-user-agents@latest/src/desktop.json',
  19. { signal: AbortSignal.timeout(1000 * 60) }
  20. )
  21. .then(res => res.json() as Promise<string[]>)
  22. .then((userAgents) => userAgents.filter(ua => ua.startsWith('Mozilla/5.0 '))),
  23. {
  24. serializer: serializeArray,
  25. deserializer: deserializeArray,
  26. ttl: TTL.THREE_DAYS()
  27. }
  28. );
  29. const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>> => {
  30. const topUserAgents = await latestTopUserAgentsPromise;
  31. const url = `https://www.speedtest.net/api/js/servers?engine=js&search=${keyword}&limit=100`;
  32. try {
  33. const randomUserAgent = topUserAgents[Math.floor(Math.random() * topUserAgents.length)];
  34. return await fsFetchCache.apply(
  35. cacheKey(url),
  36. () => s.acquire().then(() => fetchWithRetry(url, {
  37. headers: {
  38. dnt: '1',
  39. Referer: 'https://www.speedtest.net/',
  40. accept: 'application/json, text/plain, */*',
  41. 'User-Agent': randomUserAgent,
  42. 'Accept-Language': 'en-US,en;q=0.9',
  43. ...(randomUserAgent.includes('Chrome')
  44. ? {
  45. 'Sec-Ch-Ua-Mobile': '?0',
  46. 'Sec-Fetch-Dest': 'empty',
  47. 'Sec-Fetch-Mode': 'cors',
  48. 'Sec-Fetch-Site': 'same-origin',
  49. 'Sec-Gpc': '1'
  50. }
  51. : {})
  52. },
  53. signal: AbortSignal.timeout(1000 * 60),
  54. retry: {
  55. retries: 2
  56. }
  57. })).then(r => r.json() as any).then((data: Array<{ url: string, host: string }>) => data.reduce<string[]>(
  58. (prev, cur) => {
  59. const line = cur.host || cur.url;
  60. const hn = getHostname(line, { detectIp: false, validateHostname: true });
  61. if (hn) {
  62. prev.push(hn);
  63. }
  64. return prev;
  65. }, []
  66. )).finally(() => s.release()),
  67. {
  68. ttl: TTL.ONE_WEEK(),
  69. serializer: serializeArray,
  70. deserializer: deserializeArray
  71. }
  72. );
  73. } catch (e) {
  74. console.error(e);
  75. return [];
  76. }
  77. };
  78. export const buildSpeedtestDomainSet = task(require.main === module, __filename)(async (span) => {
  79. const domainTrie = createTrie(
  80. [
  81. // speedtest.net
  82. '.speedtest.net',
  83. '.speedtestcustom.com',
  84. '.ooklaserver.net',
  85. '.speed.misaka.one',
  86. '.speedtest.rt.ru',
  87. '.speedtest.aptg.com.tw',
  88. '.speedtest.gslnetworks.com',
  89. '.speedtest.jsinfo.net',
  90. '.speedtest.i3d.net',
  91. '.speedtestkorea.com',
  92. '.speedtest.telus.com',
  93. '.speedtest.telstra.net',
  94. '.speedtest.clouvider.net',
  95. '.speedtest.idv.tw',
  96. '.speedtest.frontier.com',
  97. '.speedtest.orange.fr',
  98. '.speedtest.centurylink.net',
  99. '.srvr.bell.ca',
  100. '.speedtest.contabo.net',
  101. 'speedtest.hk.chinamobile.com',
  102. 'speedtestbb.hk.chinamobile.com',
  103. '.hizinitestet.com',
  104. '.linknetspeedtest.net.br',
  105. 'speedtest.rit.edu',
  106. 'speedtest.ropa.de',
  107. 'speedtest.sits.su',
  108. 'speedtest.tigo.cr',
  109. 'speedtest.upp.com',
  110. '.speedtest.pni.tw',
  111. '.speed.pfm.gg',
  112. '.speedtest.faelix.net',
  113. '.speedtest.labixe.net',
  114. '.speedtest.warian.net',
  115. '.speedtest.starhub.com',
  116. '.speedtest.gibir.net.tr',
  117. '.speedtest.ozarksgo.net',
  118. '.speedtest.exetel.com.au',
  119. '.speedtest.sbcglobal.net',
  120. '.speedtest.leaptel.com.au',
  121. '.speedtest.windstream.net',
  122. '.speedtest.vodafone.com.au',
  123. '.speedtest.rascom.ru',
  124. '.speedtest.dchost.com',
  125. '.speedtest.highnet.com',
  126. '.speedtest.seattle.wa.limewave.net',
  127. '.speedtest.optitel.com.au',
  128. '.speednet.net.tr',
  129. '.speedtest.angolacables.co.ao',
  130. '.ookla-speedtest.fsr.com',
  131. '.speedtest.comnet.com.tr',
  132. '.speedtest.gslnetworks.com.au',
  133. '.test.gslnetworks.com.au',
  134. '.speedtest.gslnetworks.com',
  135. '.speedtestunonet.com.br',
  136. '.speedtest.alagas.net',
  137. 'speedtest.surfshark.com',
  138. '.speedtest.aarnet.net.au',
  139. '.ookla.rcp.net',
  140. '.ookla-speedtests.e2ro.com',
  141. '.speedtest.com.sg',
  142. '.ookla.ddnsgeek.com',
  143. '.speedtest.pni.tw',
  144. '.speedtest.cmcnetworks.net',
  145. '.speedtestwnet.com.br',
  146. // Cloudflare
  147. '.speed.cloudflare.com',
  148. // Wi-Fi Man
  149. '.wifiman.com',
  150. '.wifiman.me',
  151. '.wifiman.ubncloud.com',
  152. // Fast.com
  153. '.fast.com',
  154. // MacPaw
  155. 'speedtest.macpaw.com',
  156. // speedtestmaster
  157. '.netspeedtestmaster.com',
  158. // Google Search Result of "speedtest", powered by this
  159. '.measurement-lab.org',
  160. '.measurementlab.net',
  161. // Google Fiber legacy speedtest site (new fiber speedtest use speedtestcustom.com)
  162. '.speed.googlefiber.net',
  163. // librespeed
  164. '.backend.librespeed.org',
  165. // Apple,
  166. 'mensura.cdn-apple.com', // From netQuality command
  167. // OpenSpeedtest
  168. 'open.cachefly.net'
  169. ],
  170. true,
  171. true
  172. );
  173. await span.traceChildAsync(
  174. 'fetch previous speedtest domainset',
  175. async () => {
  176. try {
  177. (
  178. await readFileIntoProcessedArray(path.resolve(__dirname, '../List/domainset/speedtest.conf'))
  179. ) .forEach(line => {
  180. const hn = getHostname(line, { detectIp: false, validateHostname: true });
  181. if (hn) {
  182. domainTrie.add(hn);
  183. }
  184. });
  185. } catch { }
  186. }
  187. );
  188. await Promise.all([
  189. 'Hong Kong',
  190. 'Taiwan',
  191. 'China Telecom',
  192. 'China Mobile',
  193. 'China Unicom',
  194. 'Japan',
  195. 'Tokyo',
  196. 'Singapore',
  197. 'Korea',
  198. 'Seoul',
  199. 'Canada',
  200. 'Toronto',
  201. 'Montreal',
  202. 'Los Ang',
  203. 'San Jos',
  204. 'Seattle',
  205. 'New York',
  206. 'Dallas',
  207. 'Miami',
  208. 'Berlin',
  209. 'Frankfurt',
  210. 'London',
  211. 'Paris',
  212. 'Amsterdam',
  213. 'Moscow',
  214. 'Australia',
  215. 'Sydney',
  216. 'Brazil',
  217. 'Turkey'
  218. ].map((keyword) => span.traceChildAsync(
  219. `fetch speedtest endpoints: ${keyword}`,
  220. () => querySpeedtestApi(keyword)
  221. ).then(hostnameGroup => hostnameGroup.forEach(hostname => {
  222. if (hostname) {
  223. domainTrie.add(hostname);
  224. }
  225. }))));
  226. const deduped = span.traceChildSync('sort result', () => sortDomains(domainDeduper(domainTrie)));
  227. const description = [
  228. ...SHARED_DESCRIPTION,
  229. '',
  230. 'This file contains common speedtest endpoints.'
  231. ];
  232. return createRuleset(
  233. span,
  234. 'Sukka\'s Ruleset - Speedtest Domains',
  235. description,
  236. new Date(),
  237. deduped,
  238. 'domainset',
  239. path.resolve(__dirname, '../List/domainset/speedtest.conf'),
  240. path.resolve(__dirname, '../Clash/domainset/speedtest.txt'),
  241. path.resolve(__dirname, '../Clash/clash_mrs_domain/speedtest.mrs')
  242. );
  243. });