parse-filter.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. const { isIP } = require('net');
  2. const { fetch } = require('undici');
  3. const rDomain = /^(((?!\-))(xn\-\-)?[a-z0-9\-_]{0,61}[a-z0-9]{1,1}\.)*(xn\-\-)?([a-z0-9\-]{1,61}|[a-z0-9\-]{1,30})\.[a-z]{2,}$/m
  4. /**
  5. * @param {string | URL} domainListsUrl
  6. */
  7. async function processDomainLists(domainListsUrl) {
  8. if (typeof domainListsUrl === 'string') {
  9. domainListsUrl = new URL(domainListsUrl);
  10. }
  11. /** @type Set<string> */
  12. const domainSets = new Set();
  13. /** @type string[] */
  14. const domains = (await (await fetch(domainListsUrl)).text()).split('\n');
  15. domains.forEach(line => {
  16. if (
  17. line.startsWith('#')
  18. || line.startsWith('!')
  19. || line.startsWith(' ')
  20. || line === ''
  21. || line.startsWith('\r')
  22. || line.startsWith('\n')
  23. ) {
  24. return;
  25. }
  26. domainSets.add(line.trim());
  27. });
  28. return [...domainSets];
  29. }
  30. /**
  31. * @param {string | URL} hostsUrl
  32. */
  33. async function processHosts(hostsUrl, includeAllSubDomain = false) {
  34. if (typeof hostsUrl === 'string') {
  35. hostsUrl = new URL(hostsUrl);
  36. }
  37. /** @type Set<string> */
  38. const domainSets = new Set();
  39. /** @type string[] */
  40. const hosts = (await(await fetch(hostsUrl)).text()).split('\n');
  41. hosts.forEach(line => {
  42. if (line.includes('#')) {
  43. return;
  44. }
  45. if (line.startsWith(' ') || line.startsWith('\r') || line.startsWith('\n') || line.trim() === '') {
  46. return;
  47. }
  48. const [, ...domains] = line.split(' ');
  49. const domain = domains.join(' ').trim();
  50. if (rDomain.test(domain)) {
  51. if (includeAllSubDomain) {
  52. domainSets.add(`.${domain}`);
  53. } else {
  54. domainSets.add(domain);
  55. }
  56. }
  57. });
  58. return [...domainSets];
  59. }
  60. /**
  61. * @param {string | URL} filterRulesUrl
  62. * @returns {Promise<{ white: Set<string>, black: Set<string> }>}
  63. */
  64. async function processFilterRules(filterRulesUrl) {
  65. if (typeof filterRulesUrl === 'string') {
  66. filterRulesUrl = new URL(filterRulesUrl);
  67. }
  68. /** @type Set<string> */
  69. const whitelistDomainSets = new Set();
  70. /** @type Set<string> */
  71. const blacklistDomainSets = new Set();
  72. /** @type string[] */
  73. const filterRules = (await (await fetch(filterRulesUrl)).text()).split('\n').map(line => line.trim());
  74. filterRules.forEach(line => {
  75. if (
  76. line === ''
  77. || line.includes('#')
  78. || line.includes('!')
  79. || line.includes('*')
  80. || line.includes('/')
  81. || line.includes('$') && !line.startsWith('@@')
  82. || line.trim() === ''
  83. || isIP(line) !== 0
  84. ) {
  85. return;
  86. }
  87. if (line.startsWith('||') && line.endsWith('^$badfilter')) {
  88. const domain = line.replaceAll('||', '').replaceAll('^$badfilter', '').trim();
  89. if (rDomain.test(domain)) {
  90. whitelistDomainSets.add(domain);
  91. }
  92. } else if (line.startsWith('@@||')
  93. && (
  94. line.endsWith('^')
  95. || line.endsWith('^|')
  96. || line.endsWith('^$badfilter')
  97. || line.endsWith('^$1p')
  98. )
  99. ) {
  100. const domain = line
  101. .replaceAll('@@||', '')
  102. .replaceAll('^$badfilter', '')
  103. .replaceAll('^$1p', '')
  104. .replaceAll('^|', '')
  105. .replaceAll('^', '')
  106. .trim();
  107. if (rDomain.test(domain)) {
  108. whitelistDomainSets.add(domain);
  109. }
  110. } else if (
  111. line.startsWith('||')
  112. && (
  113. line.endsWith('^')
  114. || line.endsWith('^|')
  115. || line.endsWith('^$all')
  116. )
  117. ) {
  118. const domain = line
  119. .replaceAll('||', '')
  120. .replaceAll('^|', '')
  121. .replaceAll('^$all', '')
  122. .replaceAll('^', '')
  123. .trim();
  124. if (rDomain.test(domain)) {
  125. blacklistDomainSets.add(`.${domain}`);
  126. }
  127. } else if (line.startsWith('://')
  128. && (
  129. line.endsWith('^')
  130. || line.endsWith('^|')
  131. )
  132. ) {
  133. const domain = `${line.replaceAll('://', '').replaceAll('^|', '').replaceAll('^', '')}`.trim();
  134. if (rDomain.test(domain)) {
  135. blacklistDomainSets.add(domain);
  136. }
  137. }
  138. });
  139. return {
  140. white: whitelistDomainSets,
  141. black: blacklistDomainSets
  142. };
  143. }
  144. module.exports.processDomainLists = processDomainLists;
  145. module.exports.processHosts = processHosts;
  146. module.exports.processFilterRules = processFilterRules;