singbox.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { BaseWriteStrategy } from './base';
  2. import { appendArrayInPlace } from '../append-array-in-place';
  3. import { noop } from 'foxts/noop';
  4. import { fastIpVersion, withIdentityContent } from '../misc';
  5. import stringify from 'json-stringify-pretty-compact';
  6. import { OUTPUT_SINGBOX_DIR } from '../../constants/dir';
  7. interface SingboxHeadlessRule {
  8. domain: string[], // this_ruleset_is_made_by_sukkaw.ruleset.skk.moe
  9. domain_suffix: string[], // this_ruleset_is_made_by_sukkaw.ruleset.skk.moe
  10. domain_keyword?: string[],
  11. domain_regex?: string[],
  12. source_ip_cidr?: string[],
  13. ip_cidr?: string[],
  14. source_port?: number[],
  15. source_port_range?: string[],
  16. port?: number[],
  17. port_range?: string[],
  18. process_name?: string[],
  19. process_path?: string[]
  20. }
  21. export interface SingboxSourceFormat {
  22. version: 2 | number & {},
  23. rules: SingboxHeadlessRule[]
  24. }
  25. export class SingboxSource extends BaseWriteStrategy {
  26. readonly fileExtension = 'json';
  27. static readonly jsonToLines = (json: unknown): string[] => stringify(json).split('\n');
  28. private singbox: SingboxHeadlessRule = {
  29. domain: ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe'],
  30. domain_suffix: ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe']
  31. };
  32. protected get result() {
  33. return SingboxSource.jsonToLines({
  34. version: 2,
  35. rules: [this.singbox]
  36. });
  37. }
  38. constructor(
  39. /** Since sing-box only have one format that does not reflect type, we need to specify it */
  40. public type: 'domainset' | 'non_ip' | 'ip' /* | (string & {}) */,
  41. public readonly outputDir = OUTPUT_SINGBOX_DIR
  42. ) {
  43. super(outputDir);
  44. }
  45. withPadding = withIdentityContent;
  46. writeDomain(domain: string): void {
  47. this.singbox.domain.push(domain);
  48. }
  49. writeDomainSuffix(domain: string): void {
  50. this.singbox.domain_suffix.push(domain);
  51. }
  52. writeDomainKeywords(keyword: Set<string>): void {
  53. appendArrayInPlace(
  54. this.singbox.domain_keyword ??= [],
  55. Array.from(keyword)
  56. );
  57. }
  58. writeDomainWildcards(wildcard: Set<string>): void {
  59. appendArrayInPlace(
  60. this.singbox.domain_regex ??= [],
  61. Array.from(wildcard, SingboxSource.domainWildCardToRegex)
  62. );
  63. }
  64. writeUserAgents = noop;
  65. writeProcessNames(processName: Set<string>): void {
  66. appendArrayInPlace(
  67. this.singbox.process_name ??= [],
  68. Array.from(processName)
  69. );
  70. }
  71. writeProcessPaths(processPath: Set<string>): void {
  72. appendArrayInPlace(
  73. this.singbox.process_path ??= [],
  74. Array.from(processPath)
  75. );
  76. }
  77. writeUrlRegexes = noop;
  78. writeIpCidrs(ipCidr: string[]): void {
  79. appendArrayInPlace(
  80. this.singbox.ip_cidr ??= [],
  81. ipCidr
  82. );
  83. }
  84. writeIpCidr6s(ipCidr6: string[]): void {
  85. appendArrayInPlace(
  86. this.singbox.ip_cidr ??= [],
  87. ipCidr6
  88. );
  89. }
  90. writeGeoip = noop;
  91. writeIpAsns = noop;
  92. writeSourceIpCidrs(sourceIpCidr: string[]): void {
  93. this.singbox.source_ip_cidr ??= [];
  94. for (let i = 0, len = sourceIpCidr.length; i < len; i++) {
  95. const value = sourceIpCidr[i];
  96. if (value.includes('/')) {
  97. this.singbox.source_ip_cidr.push(value);
  98. continue;
  99. }
  100. const v = fastIpVersion(value);
  101. if (v === 4) {
  102. this.singbox.source_ip_cidr.push(`${value}/32`);
  103. continue;
  104. }
  105. if (v === 6) {
  106. this.singbox.source_ip_cidr.push(`${value}/128`);
  107. continue;
  108. }
  109. }
  110. }
  111. writeSourcePorts(port: Set<string>): void {
  112. this.singbox.source_port ??= [];
  113. for (const i of port) {
  114. const tmp = Number(i);
  115. if (!Number.isNaN(tmp)) {
  116. this.singbox.source_port.push(tmp);
  117. }
  118. }
  119. }
  120. writeDestinationPorts(port: Set<string>): void {
  121. this.singbox.port ??= [];
  122. for (const i of port) {
  123. const tmp = Number(i);
  124. if (!Number.isNaN(tmp)) {
  125. this.singbox.port.push(tmp);
  126. }
  127. }
  128. }
  129. writeOtherRules = noop;
  130. }