domainset.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { invariant } from 'foxact/invariant';
  2. import createKeywordFilter from '../aho-corasick';
  3. import { RuleOutput } from './base';
  4. import type { SingboxSourceFormat } from '../singbox';
  5. import * as tldts from 'tldts-experimental';
  6. import { looseTldtsOpt } from '../../constants/loose-tldts-opt';
  7. type Preprocessed = string[];
  8. export class DomainsetOutput extends RuleOutput<Preprocessed> {
  9. protected type = 'domainset' as const;
  10. preprocess() {
  11. const kwfilter = createKeywordFilter(this.domainKeywords);
  12. const results: string[] = [];
  13. this.domainTrie.dump((domain) => {
  14. if (kwfilter(domain)) {
  15. return;
  16. }
  17. results.push(domain);
  18. }, true);
  19. const sorted = results;
  20. sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
  21. return sorted;
  22. }
  23. surge(): string[] {
  24. return this.$preprocessed;
  25. }
  26. clash(): string[] {
  27. return this.$preprocessed.map(i => (i[0] === '.' ? `+${i}` : i));
  28. }
  29. singbox(): string[] {
  30. const domains: string[] = [];
  31. const domainSuffixes: string[] = [];
  32. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  33. const domain = this.$preprocessed[i];
  34. if (domain[0] === '.') {
  35. domainSuffixes.push(domain.slice(1));
  36. } else {
  37. domains.push(domain);
  38. }
  39. }
  40. return RuleOutput.jsonToLines({
  41. version: 2,
  42. rules: [{
  43. domain: domains,
  44. domain_suffix: domainSuffixes
  45. }]
  46. } satisfies SingboxSourceFormat);
  47. }
  48. protected apexDomainMap: Map<string, string> | null = null;
  49. getStatMap() {
  50. invariant(this.$preprocessed, 'Non dumped yet');
  51. if (!this.apexDomainMap) {
  52. const domainMap = new Map<string, string>();
  53. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  54. const cur = this.$preprocessed[i];
  55. if (!domainMap.has(cur)) {
  56. const domain = tldts.getDomain(cur, looseTldtsOpt);
  57. domainMap.set(cur, domain ?? cur);
  58. }
  59. }
  60. this.apexDomainMap = domainMap;
  61. }
  62. return Array.from(this.$preprocessed
  63. .reduce<Map<string, number>>(
  64. (acc, cur) => {
  65. const suffix = this.apexDomainMap!.get(cur);
  66. if (suffix) {
  67. acc.set(suffix, (acc.get(suffix) ?? 0) + 1);
  68. }
  69. return acc;
  70. },
  71. new Map()
  72. )
  73. .entries())
  74. .filter(a => a[1] > 9)
  75. .sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0]))
  76. .map(([domain, count]) => `${domain}${' '.repeat(100 - domain.length)}${count}`);
  77. }
  78. mitmSgmodule = undefined;
  79. adguardhome(): string[] {
  80. const results: string[] = [];
  81. // const whitelistArray = sortDomains(Array.from(whitelist));
  82. // for (let i = 0, len = whitelistArray.length; i < len; i++) {
  83. // const domain = whitelistArray[i];
  84. // if (domain[0] === '.') {
  85. // results.push(`@@||${domain.slice(1)}^`);
  86. // } else {
  87. // results.push(`@@|${domain}^`);
  88. // }
  89. // }
  90. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  91. const domain = this.$preprocessed[i];
  92. if (domain[0] === '.') {
  93. results.push(`||${domain.slice(1)}^`);
  94. } else {
  95. results.push(`|${domain}^`);
  96. }
  97. }
  98. for (const wildcard of this.domainWildcard) {
  99. const processed = wildcard.replaceAll('?', '*');
  100. if (processed.startsWith('*.')) {
  101. results.push(`||${processed.slice(2)}^`);
  102. } else {
  103. results.push(`|${processed}^`);
  104. }
  105. }
  106. for (const keyword of this.domainKeywords) {
  107. // Use regex to match keyword
  108. results.push(`/${keyword}/`);
  109. }
  110. for (const ipGroup of [this.ipcidr, this.ipcidrNoResolve]) {
  111. for (const ipcidr of ipGroup) {
  112. if (ipcidr.endsWith('/32')) {
  113. results.push(`||${ipcidr.slice(0, -3)}`);
  114. } else if (ipcidr.endsWith('.0/24')) {
  115. results.push(`||${ipcidr.slice(0, -6)}.*`);
  116. } else {
  117. results.push(`||${ipcidr}^`);
  118. }
  119. }
  120. }
  121. for (const ipGroup of [this.ipcidr6, this.ipcidr6NoResolve]) {
  122. for (const ipcidr of ipGroup) {
  123. if (ipcidr.endsWith('/128')) {
  124. results.push(`||${ipcidr.slice(0, -4)}`);
  125. } else {
  126. results.push(`||${ipcidr}`);
  127. }
  128. }
  129. }
  130. return results;
  131. }
  132. }