build-speedtest-domainset.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import path from 'node:path';
  2. import { getHostname } from 'tldts-experimental';
  3. import { task } from './trace';
  4. import { $fetch } from './lib/make-fetch-happen';
  5. import { SHARED_DESCRIPTION } from './constants/description';
  6. import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
  7. import { DomainsetOutput } from './lib/create-file';
  8. import { OUTPUT_SURGE_DIR } from './constants/dir';
  9. import { newQueue } from '@henrygd/queue';
  10. const KEYWORDS = [
  11. 'Hong Kong',
  12. 'Taiwan',
  13. 'China Telecom',
  14. 'China Mobile',
  15. 'China Unicom',
  16. 'Japan',
  17. 'Tokyo',
  18. 'Singapore',
  19. 'Korea',
  20. 'Seoul',
  21. 'Canada',
  22. 'Toronto',
  23. 'Montreal',
  24. 'Los Ang',
  25. 'San Jos',
  26. 'Seattle',
  27. 'New York',
  28. 'Dallas',
  29. 'Miami',
  30. 'Berlin',
  31. 'Frankfurt',
  32. 'London',
  33. 'Paris',
  34. 'Amsterdam',
  35. 'Moscow',
  36. 'Australia',
  37. 'Sydney',
  38. 'Brazil',
  39. 'Turkey'
  40. ];
  41. const PREDEFINE_DOMAINS = [
  42. // speedtest.net
  43. '.speedtest.net',
  44. '.speedtestcustom.com',
  45. '.ooklaserver.net',
  46. '.speed.misaka.one',
  47. '.speedtest.rt.ru',
  48. '.speedtest.aptg.com.tw',
  49. '.speedtest.gslnetworks.com',
  50. '.speedtest.jsinfo.net',
  51. '.speedtest.i3d.net',
  52. '.speedtestkorea.com',
  53. '.speedtest.telus.com',
  54. '.speedtest.telstra.net',
  55. '.speedtest.clouvider.net',
  56. '.speedtest.idv.tw',
  57. '.speedtest.frontier.com',
  58. '.speedtest.orange.fr',
  59. '.speedtest.centurylink.net',
  60. '.srvr.bell.ca',
  61. '.speedtest.contabo.net',
  62. 'speedtest.hk.chinamobile.com',
  63. 'speedtestbb.hk.chinamobile.com',
  64. '.hizinitestet.com',
  65. '.linknetspeedtest.net.br',
  66. 'speedtest.rit.edu',
  67. 'speedtest.ropa.de',
  68. 'speedtest.sits.su',
  69. 'speedtest.tigo.cr',
  70. 'speedtest.upp.com',
  71. '.speedtest.pni.tw',
  72. '.speed.pfm.gg',
  73. '.speedtest.faelix.net',
  74. '.speedtest.labixe.net',
  75. '.speedtest.warian.net',
  76. '.speedtest.starhub.com',
  77. '.speedtest.gibir.net.tr',
  78. '.speedtest.ozarksgo.net',
  79. '.speedtest.exetel.com.au',
  80. '.speedtest.sbcglobal.net',
  81. '.speedtest.leaptel.com.au',
  82. '.speedtest.windstream.net',
  83. '.speedtest.vodafone.com.au',
  84. '.speedtest.rascom.ru',
  85. '.speedtest.dchost.com',
  86. '.speedtest.highnet.com',
  87. '.speedtest.seattle.wa.limewave.net',
  88. '.speedtest.optitel.com.au',
  89. '.speednet.net.tr',
  90. '.speedtest.angolacables.co.ao',
  91. '.ookla-speedtest.fsr.com',
  92. '.speedtest.comnet.com.tr',
  93. '.speedtest.gslnetworks.com.au',
  94. '.test.gslnetworks.com.au',
  95. '.speedtest.gslnetworks.com',
  96. '.speedtestunonet.com.br',
  97. '.speedtest.alagas.net',
  98. 'speedtest.surfshark.com',
  99. '.speedtest.aarnet.net.au',
  100. '.ookla.rcp.net',
  101. '.ookla-speedtests.e2ro.com',
  102. '.speedtest.com.sg',
  103. '.ookla.ddnsgeek.com',
  104. '.speedtest.pni.tw',
  105. '.speedtest.cmcnetworks.net',
  106. '.speedtestwnet.com.br',
  107. '.speedtest.moack.co.kr',
  108. 'speedtest.mtnetworks.mn',
  109. '.speedtest.waicore.com',
  110. // Cloudflare
  111. '.speed.cloudflare.com',
  112. // Wi-Fi Man
  113. '.wifiman.com',
  114. '.wifiman.me',
  115. '.wifiman.ubncloud.com',
  116. '.wifiman-stability-test.ubncloud.com',
  117. // Fast.com
  118. '.fast.com',
  119. // MacPaw
  120. 'speedtest.macpaw.com',
  121. // speedtestmaster
  122. '.netspeedtestmaster.com',
  123. // Google Search Result of "speedtest", powered by this
  124. '.measurement-lab.org',
  125. '.measurementlab.net',
  126. // Google Fiber legacy speedtest site (new fiber speedtest use speedtestcustom.com)
  127. '.speed.googlefiber.net',
  128. // librespeed
  129. '.backend.librespeed.org',
  130. // Apple,
  131. 'mensura.cdn-apple.com', // From netQuality command
  132. // OpenSpeedtest
  133. 'open.cachefly.net' // This is also used for openspeedtest server download
  134. ];
  135. const s = newQueue(2);
  136. const latestTopUserAgentsPromise = $fetch('https://raw.githubusercontent.com/microlinkhq/top-user-agents/master/src/desktop.json')
  137. .then(res => res.json())
  138. .then((userAgents: string[]) => userAgents.filter(ua => ua.startsWith('Mozilla/5.0 ')));
  139. async function querySpeedtestApi(keyword: string) {
  140. const topUserAgents = await latestTopUserAgentsPromise;
  141. const url = `https://www.speedtest.net/api/js/servers?engine=js&search=${keyword}&limit=100`;
  142. try {
  143. const randomUserAgent = topUserAgents[Math.floor(Math.random() * topUserAgents.length)];
  144. const data = await s.add<Array<{ url: string, host: string }>>(() => $fetch(url, {
  145. headers: {
  146. dnt: '1',
  147. Referer: 'https://www.speedtest.net/',
  148. accept: 'application/json, text/plain, */*',
  149. 'User-Agent': randomUserAgent,
  150. 'Accept-Language': 'en-US,en;q=0.9',
  151. ...(randomUserAgent.includes('Chrome')
  152. ? {
  153. 'Sec-Ch-Ua-Mobile': '?0',
  154. 'Sec-Fetch-Dest': 'empty',
  155. 'Sec-Fetch-Mode': 'cors',
  156. 'Sec-Fetch-Site': 'same-origin',
  157. 'Sec-Gpc': '1'
  158. }
  159. : {})
  160. },
  161. timeout: 1000 * 60
  162. }).then(res => res.json()));
  163. return data.reduce<string[]>(
  164. (prev, cur) => {
  165. const hn = getHostname(cur.host || cur.url, { detectIp: false, validateHostname: true });
  166. if (hn) {
  167. prev.push(hn);
  168. }
  169. return prev;
  170. },
  171. []
  172. );
  173. } catch (e) {
  174. console.error(e);
  175. return [];
  176. }
  177. }
  178. const getSpeedtestHostsGroupsPromise = Promise.all(KEYWORDS.flatMap(querySpeedtestApi));
  179. export const buildSpeedtestDomainSet = task(require.main === module, __filename)(async (span) => {
  180. const output = new DomainsetOutput(span, 'speedtest')
  181. .withTitle('Sukka\'s Ruleset - Speedtest Domains')
  182. .withDescription([
  183. ...SHARED_DESCRIPTION,
  184. '',
  185. 'This file contains common speedtest endpoints.'
  186. ])
  187. .addFromDomainset(PREDEFINE_DOMAINS)
  188. .addFromDomainset(await readFileIntoProcessedArray(path.resolve(OUTPUT_SURGE_DIR, 'domainset/speedtest.conf')));
  189. const hostnameGroup = await span.traceChildPromise('get speedtest hosts groups', getSpeedtestHostsGroupsPromise);
  190. hostnameGroup.forEach(hostname => output.bulkAddDomain(hostname));
  191. return output.write();
  192. });