misc: copy all clashx files

This commit is contained in:
mrFq1
2023-05-25 23:20:44 +08:00
parent edc1529027
commit bbaf15f5b8
12 changed files with 1371 additions and 83 deletions
@@ -0,0 +1,110 @@
//
// ClashConfig.swift
// ClashX
//
// Created by CYC on 2018/7/30.
// Copyright © 2018 yichengchen. All rights reserved.
//
import Foundation
import CocoaLumberjackSwift
enum ClashProxyMode: String, Codable {
case rule
case global
case direct
}
extension ClashProxyMode {
var name: String {
switch self {
case .rule: return NSLocalizedString("Rule", comment: "")
case .global: return NSLocalizedString("Global", comment: "")
case .direct: return NSLocalizedString("Direct", comment: "")
}
}
}
enum ClashLogLevel: String, Codable {
case info
case warning
case error
case debug
case silent
case unknow = "unknown"
func toDDLogLevel() -> DDLogLevel {
switch self {
case .info:
return .info
case .warning:
return .warning
case .error:
return .error
case .debug:
return .debug
case .silent:
return .off
case .unknow:
return .error
}
}
}
class ClashConfig: Codable {
var port: Int
var socksPort: Int
var redirPort: Int
var allowLan: Bool
var mixedPort: Int
var mode: ClashProxyMode
var logLevel: ClashLogLevel
var sniffing: Bool
var ipv6: Bool
var tun: Tun
var interfaceName: String
struct Tun: Codable {
let enable: Bool
let device: String
let stack: String
// let dns-hijack: [String]
// let auto-route: Bool
// let auto-detect-interface: Bool
}
var usedHttpPort: Int {
if mixedPort > 0 {
return mixedPort
}
return port
}
var usedSocksPort: Int {
if mixedPort > 0 {
return mixedPort
}
return socksPort
}
private enum CodingKeys: String, CodingKey {
case port, socksPort = "socks-port", redirPort = "redir-port", mixedPort = "mixed-port", allowLan = "allow-lan", mode, logLevel = "log-level", sniffing, tun, interfaceName = "interface-name", ipv6
}
static func fromData(_ data: Data) -> ClashConfig? {
let decoder = JSONDecoder()
do {
return try decoder.decode(ClashConfig.self, from: data)
} catch let err {
Logger.log((err as NSError).description, level: .error)
return nil
}
}
func copy() -> ClashConfig? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
let copy = try? JSONDecoder().decode(ClashConfig.self, from: data)
return copy
}
}
@@ -0,0 +1,110 @@
//
// ClashConnection.swift
// ClashX
//
// Created by yicheng on 2019/10/28.
// Copyright © 2019 west2online. All rights reserved.
//
import Cocoa
import DifferenceKit
struct ClashConnectionSnapShot: Codable {
let downloadTotal: Int
let uploadTotal: Int
let connections: [ClashConnection]
}
struct ClashConnection: Codable, Hashable {
let id: String
let chains: [String]
let upload: Int64
let download: Int64
let start: Date
let rule: String
let rulePayload: String
let metadata: MetaConnectionData
}
struct MetaConnectionData: Codable, Hashable {
let uid: Int
let network: String
let type: String
let sourceIP: String
let destinationIP: String
let sourcePort: String
let destinationPort: String
let inboundIP: String
let inboundPort: String
let inboundName: String
let host: String
let dnsMode: String
let process: String
let processPath: String
let specialProxy: String
let specialRules: String
let remoteDestination: String
let sniffHost: String
}
class ClashConnectionObject: NSObject, Differentiable {
@objc let id: String
@objc let host: String
@objc let sniffHost: String
@objc let process: String
@objc let download: Int64
@objc let upload: Int64
let downloadString: String
let uploadString: String
let chains: [String]
@objc let chainString: String
@objc let ruleString: String
@objc let startDate: Date
let startString: String
@objc let source: String
@objc let destinationIP: String?
@objc let type: String
var differenceIdentifier: String {
return id
}
func isContentEqual(to source: ClashConnectionObject) -> Bool {
download == source.download &&
upload == source.upload &&
startString == source.startString
}
init(_ conn: ClashConnection) {
let byteCountFormatter = ByteCountFormatter()
let startFormatter = RelativeDateTimeFormatter()
startFormatter.unitsStyle = .short
let metadata = conn.metadata
id = conn.id
host = "\(metadata.host == "" ? metadata.destinationIP : metadata.host):\(metadata.destinationPort)"
sniffHost = metadata.sniffHost == "" ? "-" : metadata.sniffHost
process = metadata.process
download = conn.download
downloadString = byteCountFormatter.string(fromByteCount: conn.download)
upload = conn.upload
uploadString = byteCountFormatter.string(fromByteCount: conn.upload)
chains = conn.chains
chainString = conn.chains.reversed().joined(separator: "/")
ruleString = conn.rulePayload == "" ? conn.rule : "\(conn.rule) :: \(conn.rulePayload)"
startDate = conn.start
startString = startFormatter.localizedString(for: conn.start, relativeTo: Date())
source = "\(metadata.sourceIP):\(metadata.sourcePort)"
destinationIP = [metadata.remoteDestination,
metadata.destinationIP,
metadata.host].first(where: { $0 != "" })
type = "\(metadata.type)(\(metadata.network))"
}
}
@@ -0,0 +1,67 @@
//
// ClashProvider.swift
// ClashX
//
// Created by yichengchen on 2019/12/14.
// Copyright © 2019 west2online. All rights reserved.
//
import Cocoa
class ClashProviderResp: Codable {
let allProviders: [ClashProxyName: ClashProvider]
lazy var providers: [ClashProxyName: ClashProvider] = {
return allProviders.filter({ $0.value.vehicleType != .Compatible })
}()
init() {
allProviders = [:]
}
static var decoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.js)
return decoder
}
private enum CodingKeys: String, CodingKey {
case allProviders = "providers"
}
}
class ClashProvider: Codable {
let id = UUID().uuidString
enum ProviderType: String, Codable {
case Proxy
case String
}
enum ProviderVehicleType: String, Codable {
case HTTP
case File
case Compatible
case Unknown
}
let name: ClashProviderName
let proxies: [ClashProxy]
let type: ProviderType
let vehicleType: ProviderVehicleType
let updatedAt: Date?
let subscriptionInfo: ClashProviderSubInfo?
}
class ClashProviderSubInfo: Codable {
let upload: Int64
let download: Int64
let total: Int64
let expire: Int
private enum CodingKeys: String, CodingKey {
case upload = "Upload",
download = "Download",
total = "Total",
expire = "Expire"
}
}
@@ -0,0 +1,248 @@
//
// ClashProxy.swift
// ClashX
//
// Created by CYC on 2019/3/17.
// Copyright © 2019 west2online. All rights reserved.
//
import Cocoa
import SwiftyJSON
enum ClashProxyType: String, Codable {
case urltest = "URLTest"
case fallback = "Fallback"
case loadBalance = "LoadBalance"
case select = "Selector"
case direct = "Direct"
case reject = "Reject"
case shadowsocks = "Shadowsocks"
case shadowsocksR = "ShadowsocksR"
case socks5 = "Socks5"
case http = "Http"
case vmess = "Vmess"
case snell = "Snell"
case trojan = "Trojan"
case relay = "Relay"
case vless = "Vless"
case hysteria = "Hysteria"
case wireguardMeta = "WireGuard"
case wireguard = "Wireguard"
case tuic = "Tuic"
case pass = "Pass"
case unknown = "Unknown"
static let proxyGroups: [ClashProxyType] = [.select, .urltest, .fallback, .loadBalance]
var isAutoGroup: Bool {
switch self {
case .urltest, .fallback, .loadBalance:
return true
default:
return false
}
}
static func isProxyGroup(_ proxy: ClashProxy) -> Bool {
switch proxy.type {
case .select, .urltest, .fallback, .loadBalance, .relay: return true
default: return false
}
}
static func isBuiltInProxy(_ proxy: ClashProxy) -> Bool {
switch proxy.name {
case "DIRECT", "REJECT", "PASS": return true
default: return false
}
}
}
typealias ClashProxyName = String
typealias ClashProviderName = String
class ClashProxySpeedHistory: Codable {
let time: Date
let delay: Int
let meanDelay: Int?
class HisDateFormaterInstance {
static let shared = HisDateFormaterInstance()
lazy var formater: DateFormatter = {
var f = DateFormatter()
f.dateFormat = "HH:mm"
return f
}()
}
lazy var delayInt: Int = {
meanDelay ?? delay
}()
lazy var delayDisplay: String = {
switch delayInt {
case 0: return NSLocalizedString("fail", comment: "")
default: return "\(delayInt) ms"
}
}()
lazy var dateDisplay: String = {
return HisDateFormaterInstance.shared.formater.string(from: time)
}()
lazy var displayString: String = "\(dateDisplay) \(delayDisplay)"
}
class ClashProxy: Codable {
let id = UUID().uuidString
let name: ClashProxyName
let type: ClashProxyType
let all: [ClashProxyName]?
let history: [ClashProxySpeedHistory]
let now: ClashProxyName?
weak var enclosingResp: ClashProxyResp?
weak var enclosingProvider: ClashProvider?
let udp: Bool
let xudp: Bool
let tfo: Bool
enum SpeedtestAbleItem {
case proxy(name: ClashProxyName)
case provider(name: ClashProxyName, provider: ClashProviderName)
case group(name: ClashProxyName)
}
private static var nameLengthCachedMap = [ClashProxyName: CGFloat]()
static func cleanCache() {
nameLengthCachedMap.removeAll()
}
lazy var speedtestAble: [SpeedtestAbleItem] = {
guard let resp = enclosingResp, let allProxys = all else { return [] }
var proxys = [SpeedtestAbleItem]()
for proxy in allProxys {
if let p = resp.proxiesMap[proxy] {
if !ClashProxyType.isProxyGroup(p) {
if let provider = p.enclosingProvider {
proxys.append(.provider(name: p.name, provider: provider.name))
} else {
proxys.append(.proxy(name: p.name))
}
} else {
proxys.append(.group(name: p.name))
}
}
}
return proxys
}()
lazy var isSpeedTestable: Bool = {
return speedtestAble.count > 0
}()
private enum CodingKeys: String, CodingKey {
case type, all, history, now, name, udp, xudp, tfo
}
lazy var maxProxyNameLength: CGFloat = {
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
let lengths = all?.compactMap({ name -> CGFloat in
if let length = ClashProxy.nameLengthCachedMap[name] {
return length
}
let rects = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
let attr = [NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 14)]
let length = (name as NSString)
.boundingRect(with: rect,
options: .usesLineFragmentOrigin,
attributes: attr).width
ClashProxy.nameLengthCachedMap[name] = length
return length
})
return lengths?.max() ?? 0
}()
}
class ClashProxyResp {
var proxies: [ClashProxy]
var proxiesMap: [ClashProxyName: ClashProxy]
var enclosingProviderResp: ClashProviderResp?
init(_ data: Data?) {
guard let data
else {
self.proxiesMap = [:]
self.proxies = []
return
}
let proxies = JSON(data)["proxies"]
var proxiesModel = [ClashProxy]()
var proxiesMap = [ClashProxyName: ClashProxy]()
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.js)
for value in proxies.dictionaryValue.values {
guard let data = try? value.rawData() else {
continue
}
guard let proxy = try? decoder.decode(ClashProxy.self, from: data) else {
continue
}
proxiesModel.append(proxy)
proxiesMap[proxy.name] = proxy
}
self.proxiesMap = proxiesMap
self.proxies = proxiesModel
for proxy in self.proxies {
proxy.enclosingResp = self
}
}
func updateProvider(_ providerResp: ClashProviderResp) {
enclosingProviderResp = providerResp
for provider in providerResp.providers.values {
for proxy in provider.proxies {
proxy.enclosingProvider = provider
proxiesMap[proxy.name] = proxy
proxies.append(proxy)
}
}
}
lazy var proxiesSortMap: [ClashProxyName: Int] = {
var map = [ClashProxyName: Int]()
for (idx, proxy) in (self.proxiesMap["GLOBAL"]?.all ?? []).enumerated() {
map[proxy] = idx
}
return map
}()
lazy var proxyGroups: [ClashProxy] = {
return proxies.filter {
ClashProxyType.isProxyGroup($0)
}.sorted(by: { proxiesSortMap[$0.name] ?? -1 < proxiesSortMap[$1.name] ?? -1 })
}()
lazy var longestProxyGroupName = {
return proxyGroups.max { $1.name.count > $0.name.count }?.name ?? ""
}()
lazy var maxProxyNameLength: CGFloat = {
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
let attr = [NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 0)]
return (self.longestProxyGroupName as NSString)
.boundingRect(with: rect,
options: .usesLineFragmentOrigin,
attributes: attr).width
}()
}
@@ -0,0 +1,36 @@
//
// ClashRule.swift
// ClashX
//
// Created by CYC on 2018/10/27.
// Copyright © 2018 west2online. All rights reserved.
//
import Foundation
import Cocoa
class ClashRule: NSObject, Codable, Identifiable {
@objc let type: String
@objc let payload: String?
@objc let proxy: String?
init(type: String, payload: String?, proxy: String?) {
self.type = type
self.payload = payload
self.proxy = proxy
}
}
class ClashRuleResponse: Codable {
var rules: [ClashRule]?
static func empty() -> ClashRuleResponse {
return ClashRuleResponse()
}
static func fromData(_ data: Data) -> ClashRuleResponse {
let decoder = JSONDecoder()
let model = try? decoder.decode(ClashRuleResponse.self, from: data)
return model ?? ClashRuleResponse.empty()
}
}
@@ -0,0 +1,31 @@
//
// ClashRuleProvider.swift
// ClashX Meta
import Foundation
class ClashRuleProviderResp: Codable {
let allProviders: [ClashProxyName: ClashRuleProvider]
init() {
allProviders = [:]
}
static var decoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.js)
return decoder
}
private enum CodingKeys: String, CodingKey {
case allProviders = "providers"
}
}
class ClashRuleProvider: NSObject, Codable {
@objc let name: ClashProviderName
let ruleCount: Int
@objc let behavior: String
@objc let type: String
let updatedAt: Date?
}