singbox.ts 4.4 KB

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