domainset.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { invariant } from 'foxact/invariant';
  2. import createKeywordFilter from '../aho-corasick';
  3. import { buildParseDomainMap, sortDomains } from '../stable-sort-domain';
  4. import { RuleOutput } from './base';
  5. import type { SingboxSourceFormat } from '../singbox';
  6. type Preprocessed = string[];
  7. export class DomainsetOutput extends RuleOutput<Preprocessed> {
  8. protected type = 'domainset' as const;
  9. preprocess() {
  10. const kwfilter = createKeywordFilter(this.domainKeywords);
  11. const results: string[] = [];
  12. this.domainTrie.dump((domain) => {
  13. if (kwfilter(domain)) {
  14. return;
  15. }
  16. results.push(domain);
  17. }, true);
  18. const sorted = results;
  19. sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
  20. return sorted;
  21. }
  22. surge(): string[] {
  23. return this.$preprocessed;
  24. }
  25. clash(): string[] {
  26. return this.$preprocessed.map(i => (i[0] === '.' ? `+${i}` : i));
  27. }
  28. singbox(): string[] {
  29. const domains: string[] = [];
  30. const domainSuffixes: string[] = [];
  31. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  32. const domain = this.$preprocessed[i];
  33. if (domain[0] === '.') {
  34. domainSuffixes.push(domain.slice(1));
  35. } else {
  36. domains.push(domain);
  37. }
  38. }
  39. return RuleOutput.jsonToLines({
  40. version: 2,
  41. rules: [{
  42. domain: domains,
  43. domain_suffix: domainSuffixes
  44. }]
  45. } satisfies SingboxSourceFormat);
  46. }
  47. protected apexDomainMap: Map<string, string> | null = null;
  48. protected subDomainMap: Map<string, string> | null = null;
  49. withDomainMap(apexDomainMap: Map<string, string>, subDomainMap: Map<string, string>) {
  50. this.apexDomainMap = apexDomainMap;
  51. this.subDomainMap = subDomainMap;
  52. return this;
  53. }
  54. getStatMap() {
  55. invariant(this.$preprocessed, 'Non dumped yet');
  56. if (!this.apexDomainMap || !this.subDomainMap) {
  57. const { domainMap } = buildParseDomainMap(this.$preprocessed);
  58. this.apexDomainMap = domainMap;
  59. }
  60. return Array.from(this.$preprocessed
  61. .reduce<Map<string, number>>(
  62. (acc, cur) => {
  63. const suffix = this.apexDomainMap!.get(cur);
  64. if (suffix) {
  65. acc.set(suffix, (acc.get(suffix) ?? 0) + 1);
  66. }
  67. return acc;
  68. },
  69. new Map()
  70. )
  71. .entries())
  72. .filter(a => a[1] > 9)
  73. .sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0]))
  74. .map(([domain, count]) => `${domain}${' '.repeat(100 - domain.length)}${count}`);
  75. }
  76. mitmSgmodule = undefined;
  77. adguardhome(whitelist: Set<string | `.${string}`>): string[] {
  78. const results: string[] = [];
  79. const whitelistArray = sortDomains(Array.from(whitelist));
  80. for (let i = 0, len = whitelistArray.length; i < len; i++) {
  81. const domain = whitelistArray[i];
  82. if (domain[0] === '.') {
  83. results.push(`@@||${domain.slice(1)}^`);
  84. } else {
  85. results.push(`@@|${domain}^`);
  86. }
  87. }
  88. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  89. const domain = this.$preprocessed[i];
  90. if (domain[0] === '.') {
  91. results.push(`||${domain.slice(1)}^`);
  92. } else {
  93. results.push(`|${domain}^`);
  94. }
  95. }
  96. return results;
  97. }
  98. }