parse-filter.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. const lineStartsWithDoubleVerticalBar = line.startsWith('||');
  76. if (
  77. line === ''
  78. || line.includes('#')
  79. || line.includes('!')
  80. || line.includes('*')
  81. || line.includes('/')
  82. || line.includes('$') && !lineStartsWithDoubleVerticalBar
  83. || line === ''
  84. || isIP(line) !== 0
  85. ) {
  86. return;
  87. }
  88. const lineEndsWithCaret = line.endsWith('^');
  89. const lineEndsWithCaretVerticalBar = line.endsWith('^|');
  90. if (lineStartsWithDoubleVerticalBar && line.endsWith('^$badfilter')) {
  91. const domain = line.replace('||', '').replace('^$badfilter', '').trim();
  92. if (rDomain.test(domain)) {
  93. whitelistDomainSets.add(domain);
  94. }
  95. } else if (line.startsWith('@@||')
  96. && (
  97. lineEndsWithCaret
  98. || lineEndsWithCaretVerticalBar
  99. || line.endsWith('^$badfilter')
  100. || line.endsWith('^$1p')
  101. )
  102. ) {
  103. const domain = line
  104. .replaceAll('@@||', '')
  105. .replaceAll('^$badfilter', '')
  106. .replaceAll('^$1p', '')
  107. .replaceAll('^|', '')
  108. .replaceAll('^', '')
  109. .trim();
  110. if (rDomain.test(domain)) {
  111. whitelistDomainSets.add(domain);
  112. }
  113. } else if (
  114. lineStartsWithDoubleVerticalBar
  115. && (
  116. lineEndsWithCaret
  117. || lineEndsWithCaretVerticalBar
  118. || line.endsWith('^$all')
  119. )
  120. ) {
  121. const domain = line
  122. .replaceAll('||', '')
  123. .replaceAll('^|', '')
  124. .replaceAll('^$all', '')
  125. .replaceAll('^', '')
  126. .trim();
  127. if (rDomain.test(domain)) {
  128. blacklistDomainSets.add(`.${domain}`);
  129. }
  130. } else if (line.startsWith('://')
  131. && (
  132. lineEndsWithCaret
  133. || lineEndsWithCaretVerticalBar
  134. )
  135. ) {
  136. const domain = `${line.replaceAll('://', '').replaceAll('^|', '').replaceAll('^', '')}`.trim();
  137. if (rDomain.test(domain)) {
  138. blacklistDomainSets.add(domain);
  139. }
  140. }
  141. });
  142. return {
  143. white: whitelistDomainSets,
  144. black: blacklistDomainSets
  145. };
  146. }
  147. module.exports.processDomainLists = processDomainLists;
  148. module.exports.processHosts = processHosts;
  149. module.exports.processFilterRules = processFilterRules;