domainset.ts 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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. });
  18. if (!this.apexDomainMap || !this.subDomainMap) {
  19. const { domainMap, subdomainMap } = buildParseDomainMap(results);
  20. this.apexDomainMap = domainMap;
  21. this.subDomainMap = subdomainMap;
  22. }
  23. const sorted = sortDomains(results, this.apexDomainMap, this.subDomainMap);
  24. sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
  25. return sorted;
  26. }
  27. surge(): string[] {
  28. return this.$preprocessed;
  29. }
  30. clash(): string[] {
  31. return this.$preprocessed.map(i => (i[0] === '.' ? `+${i}` : i));
  32. }
  33. singbox(): string[] {
  34. const domains: string[] = [];
  35. const domainSuffixes: string[] = [];
  36. for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
  37. const domain = this.$preprocessed[i];
  38. if (domain[0] === '.') {
  39. domainSuffixes.push(domain.slice(1));
  40. } else {
  41. domains.push(domain);
  42. }
  43. }
  44. return RuleOutput.jsonToLines({
  45. version: 2,
  46. rules: [{
  47. domain: domains,
  48. domain_suffix: domainSuffixes
  49. }]
  50. } satisfies SingboxSourceFormat);
  51. }
  52. getStatMap() {
  53. invariant(this.$preprocessed, 'Non dumped yet');
  54. invariant(this.apexDomainMap, 'Missing apex domain map');
  55. return Array.from(this.$preprocessed
  56. .reduce<Map<string, number>>(
  57. (acc, cur) => {
  58. const suffix = this.apexDomainMap!.get(cur);
  59. if (suffix) {
  60. acc.set(suffix, (acc.get(suffix) ?? 0) + 1);
  61. }
  62. return acc;
  63. },
  64. new Map()
  65. )
  66. .entries())
  67. .filter(a => a[1] > 9)
  68. .sort(
  69. (a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0])
  70. )
  71. .map(([domain, count]) => `${domain}${' '.repeat(100 - domain.length)}${count}`);
  72. }
  73. mitmSgmodule = undefined;
  74. }