// // KMAIRequestServerManager.swift // PDF Reader Pro Edition // // Created by wanjun on 2024/1/17. // import Cocoa import AFNetworking @objcMembers class ResultWrapper: NSObject { var success: Bool = false var result: NSDictionary = [:] // var message: String = "" // var code : Int = 200 // var error: NSError? // var state: KMAIRewritingState = .rewrite var content: String = "" // init(success: Bool, result: NSDictionary, message: String, code: Int, error: NSError? = nil, state: KMAIRewritingState) { // self.success = success // self.result = result // self.message = message // self.code = code // self.error = error // self.state = state // } init(success: Bool, content: String) { self.success = success self.content = content self.result = NSDictionary.init() } init(success: Bool, resultData: NSDictionary) { self.success = success self.content = "" self.result = resultData } } @objc enum KMAIRewritingState : Int { case rewrite = 0 // 重写 case correctTypos // 纠错 case uploadTranslate // 翻译上传文件 case fileTranslate // 翻译文件 case textTranslate // 翻译文本 case extractSummaryFile //摘要 } @objc enum KMAIFileUploadState : Int { case Translation = 0 case ExtractAbstract } //typealias KMRequestServerComplete = (_ success: Bool, _ result: Result?) -> Void @objcMembers class KMAIRequestServerManager: NSObject { static let defaultManager = KMAIRequestServerManager() typealias KMRequestServerComplete = (_ wrapper: ResultWrapper) -> Void private var downFileUrl: String = "" // 文件翻译 var downFileName: String = "" // 文件翻译 var fromLanguages: [String] = ["Automatic", "English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"] var toLanguages: [String] = ["English", "Simplified Chinese", "Traditional Chinese", "Japanese", "Korean", "French", "Spanish", "Italian", "German", "Portuguese", "Russian", "Vietnamese", "Thai", "Arabic", "Greek", "Bulgarian", "Finnish", "Slovene", "Dutch", "Czech", "Swedish", "Polish", "Danish", "Romanian", "Hungarian"] // MARK: AI Action (public) func aiAction(content: String, state: KMAIRewritingState, from: String = "auto", to: String = "en", complete: @escaping KMRequestServerComplete) { if state == .rewrite || state == .correctTypos { //重写 && 纠错 aiRewriting(content: content, state: state, complete: complete) } else if state == .uploadTranslate || state == .fileTranslate { // 上传文件 || 文件翻译 aiTranslationFileUpload(file: content, complete: { [unowned self] wrapper in let success: Bool = wrapper.success let fileKey = wrapper.content if success { let fileData = wrapper.result complete(wrapper) // aiTranslationFileTranslateHandle(fileKey: fileKey, from: languageAbbreviation(from), to: languageAbbreviation(to), complete: complete) } else { complete(wrapper) } }) } else if state == .textTranslate { // 文本翻译 aiTranslationTextTrans(q: content, from: languageAbbreviation(from), to: languageAbbreviation(to), complete: complete) } else if state == .extractSummaryFile { // 提取摘要 extractSummaryFile(file: content, complete: complete) } } // MARK: Private private func uuid() -> String { return GetHardwareUUID()! } private func platform() -> String { var platform = "DMG" #if VERSION_DMG platform = "DMG" #else platform = "AiStore" #endif return platform } private func app_name() -> String { var bundleID: String! bundleID = Bundle.main.object(forInfoDictionaryKey: "CFBundleIdentifier") as! String return bundleID; } func downloadFile(filePath: String, downFileName: String, toPath: String, complete: @escaping KMRequestServerComplete) { self.downFileUrl = "" guard let fileURL = URL(string: filePath) else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Invalid file link", comment: "") alert.runModal() return } var destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent(downFileName) if URL(fileURLWithPath: toPath) != nil { destinationURL = URL(fileURLWithPath: toPath) } if FileManager.default.fileExists(atPath: destinationURL.path) { do { try FileManager.default.removeItem(at: destinationURL) print("删除旧文件成功") } catch { print("删除旧文件失败:\(error)") } } let sessionConfiguration = URLSessionConfiguration.default let session = URLSession(configuration: sessionConfiguration) let downloadTask = session.downloadTask(with: fileURL) { (tempLocalURL, response, error) in if let error = error { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = String(format: "%@:\(error)", NSLocalizedString("Download failed", comment: "")) alert.runModal() return } guard let tempLocalURL = tempLocalURL else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Invalid temporary directory", comment: "") alert.runModal() return } do { try FileManager.default.moveItem(at: tempLocalURL, to: destinationURL) let wrapper = ResultWrapper(success: true, content: "") wrapper.content = destinationURL.path complete(wrapper) } catch { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = String(format: "%@:\(error)", NSLocalizedString("Failed to save file", comment: "")) alert.runModal() } } self.downFileUrl = destinationURL.path downloadTask.resume() } private func languageAbbreviation(_ language: String) -> String { if language == "Automatic" || language == "auto" { return "auto" } else if language == "English" || language == "en" { return "en" } else if language == "Simplified Chinese" || language == "zh" { return "zh" } else if language == "Traditional Chinese" || language == "cht" { return "cht" } else if language == "Japanese" || language == "jp" { return "jp" } else if language == "Korean" || language == "kor" { return "kor" } else if language == "French" || language == "fra" { return "fra" } else if language == "Spanish" || language == "spa" { return "spa" } else if language == "Italian" || language == "it" { return "it" } else if language == "German" || language == "de" { return "de" } else if language == "Portuguese" || language == "pt" { return "pt" } else if language == "Russian" || language == "ru" { return "ru" } else if language == "Vietnamese" || language == "vie" { return "vie" } else if language == "Thai" || language == "th" { return "th" } else if language == "Arabic" || language == "ara" { return "ara" } else if language == "Greek" || language == "el" { return "el" } else if language == "Bulgarian" || language == "bul" { return "bul" } else if language == "Finnish" || language == "fin" { return "fin" } else if language == "Slovene" || language == "slo" { return "slo" } else if language == "Dutch" || language == "nl" { return "nl" } else if language == "Czech" || language == "cs" { return "cs" } else if language == "Swedish" || language == "swe" { return "swe" } else if language == "Polish" || language == "pl" { return "pl" } else if language == "Danish" || language == "dan" { return "dan" } else if language == "Romanian" || language == "rom" { return "rom" } else if language == "Hungarian" || language == "hu" { return "hu" } return "auto" } // MARK: AI Action (private) /** @abstract 翻译上传 @param file 文件路径 @param complete 上传完成回调 */ private func aiTranslationFileUpload(file: String, complete: @escaping KMRequestServerComplete) { let infoDictionary = Bundle .main.infoDictionary! let urlString = AIInfoConfig().aiActionURL + "/api/AI/uploadTranslate" let fileData = FileManager.default.contents(atPath: file) let params: [String: Any] = ["file": fileData, "uuid": uuid(), "platform": platform(), "app_name": app_name(), "calculate":"1"] KMAIRequestServer.requestServer.uploadFile(urlString: urlString, params: params) { formData in let fileURL = URL(fileURLWithPath: file) try? formData.appendPart(withFileURL: fileURL, name: "file", fileName: fileURL.lastPathComponent, mimeType: "application/octet-stream") } requestSerializer: { requestSerializer in requestSerializer.setValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent") } completion: { task, responseObject, error in if responseObject != nil { let data: NSDictionary = responseObject!["data"] as? NSDictionary ?? [:] var code: String = responseObject!["code"] as? String ?? "06005" if code == "06005" { let tempCode: Int = responseObject!["code"] as? Int ?? 0 if tempCode == 501 { code = "501" } } let message: String = responseObject!["message"] as? String ?? "unknown error" let error = NSError(domain: message, code: Int(code)!) // let result = ResultWrapper(success: true, result: data, message: message, code: Int(code)!, error: error, state: .uploadTranslate) // if result.code == 200 { // result.success = true // complete(result) // } else { // result.success = false // complete(result) // } if Int(code)! == 200 { let wrapper = ResultWrapper(success: true, resultData: data) wrapper.content = data["fileKey"] as! String complete(wrapper) } else { let wrapper = ResultWrapper(success: false, resultData: data) if Int(code)! == 501 { wrapper.content = "501" } else { wrapper.content = message } complete(wrapper) } } else { // let error = NSError(domain: "unknown error", code: 404) // let result = ResultWrapper(success: false, result: [:], message: "unknown error", code: 404, error: error, state: .uploadTranslate) // complete(result) complete(ResultWrapper(success: false, content: "unknown error")) } } } /** @abstract 文件翻译 @param fileKey 文件Key @param from 初始语言 @param to 结束语言 */ func aiTranslationFileTranslateHandle(fileKey: String, from: String, to: String, complete: @escaping KMRequestServerComplete) { let infoDictionary = Bundle .main.infoDictionary! let urlString = AIInfoConfig().aiActionURL + "/api/AI/fileTranslate" let paraDict = ["fileKey": fileKey, "from": languageAbbreviation(from), "to": languageAbbreviation(to), "uuid": uuid(), "platform": platform(), "app_name": app_name()] var postData = try! JSONSerialization.data(withJSONObject: paraDict) var request = URLRequest(url: URL(string: urlString)!,timeoutInterval: Double.infinity) request.addValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent") request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" request.httpBody = postData let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data else { print(String(describing: error)) return } let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary ?? [:] if jsonObject != nil { let data1: NSDictionary = jsonObject!["data"] as? NSDictionary ?? [:] let code: String = jsonObject!["code"] as? String ?? "06005" let message: String = jsonObject!["message"] as? String ?? "unknown error" let error = NSError(domain: message, code: Int(code)!) // let result = ResultWrapper(success: true, result: data1, message: message, code: Int(code)!, error: error, state: .fileTranslate) // if result.code == 200 { // result.success = true // complete(result) // } else { // result.success = false // complete(result) // } if Int(code)! == 200 { let fileUrl = data1["fileUrl"] let downFileUrl = data1["downFileUrl"] let ossDownUrl = data1["ossDownUrl"] let fileName = data1["fileName"] let downFileName = data1["downFileName"] let fromStr = data1["from"] let toStr = data1["to"] self.downFileName = downFileName as! String // self.downloadFile(filePath: ossDownUrl as! String, downFileName: downFileName as! String) complete(ResultWrapper(success: true, content: ossDownUrl as! String)) } else { complete(ResultWrapper(success: false, content: message)) } } else { // let error = NSError(domain: "unknown error", code: 404) // let result = ResultWrapper(success: false, result: [:], message: "unknown error", code: 404, error: error, state: .fileTranslate) // complete(result) complete(ResultWrapper(success: false, content: "unknown error")) } } task.resume() } /** @abstract 内容翻译 @param q 选中 @param from 初始语言 @param to 结束语言 */ private func aiTranslationTextTrans(q: String, from: String, to: String, complete: @escaping KMRequestServerComplete) { let infoDictionary = Bundle .main.infoDictionary! let urlString = AIInfoConfig().aiActionURL + "/api/AI/textTranslate" var escapedString = q escapedString = String(data: escapedString.data(using: .utf8)!, encoding: .utf8)! let paraDict = ["text":escapedString, "from":from, "to":to, "uuid":uuid(), "platform": platform(), "app_name": app_name()] var postData = try! JSONSerialization.data(withJSONObject: paraDict) var request = URLRequest(url: URL(string: urlString)!,timeoutInterval: Double.infinity) request.addValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent") request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" request.httpBody = postData let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data else { print(String(describing: error)) return } let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary ?? [:] if jsonObject != nil { let data1: NSDictionary = jsonObject!["data"] as? NSDictionary ?? [:] let code: String = jsonObject!["code"] as? String ?? "06005" let message: String = jsonObject!["message"] as? String ?? "unknown error" let error = NSError(domain: message, code: Int(code)!) if Int(code)! == 200 { var resultStr = data1["dst"] as! String complete(ResultWrapper(success: true, content: resultStr)) } else { complete(ResultWrapper(success: false, content: message)) } } else { complete(ResultWrapper(success: false, content: "unknown error")) } } task.resume() } /** @abstract重写 & 纠错 @param content 内容 @param state 类型 */ private func aiRewriting(content: String, state: KMAIRewritingState, complete: @escaping KMRequestServerComplete) { var urlString = AIInfoConfig().aiActionURL + "/api/AI/rewrite" if state == .correctTypos { urlString = AIInfoConfig().aiActionURL + "/api/AI/correctTypos" } let params: [String: Any] = ["content": content, "uuid": uuid(), "platform": platform(), "app_name": app_name()] KMAIRequestServer.requestServer.aiRewriting(urlString: urlString, params: params) { formData in } requestSerializer: { requestSerializer in requestSerializer.setValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent") } completion: { task, responseObject, error in if responseObject != nil { let data: NSDictionary = responseObject!["data"] as? NSDictionary ?? [:] let code: String = responseObject!["code"] as? String ?? "06005" let message: String = responseObject!["message"] as? String ?? "unknown error" let error = NSError(domain: message, code: Int(code)!) // let result = ResultWrapper(success: true, result: data, message: message, code: Int(code)!, error: error, state: state) // if result.code == 200 { // result.success = true // complete(result) // } else { // result.success = false // complete(result) // } if Int(code)! == 200 { complete(ResultWrapper(success: true, content: data["content"] as! String)) } else { complete(ResultWrapper(success: false, content: message)) } } else { // let error = NSError(domain: "unknown error", code: 404) // let result = ResultWrapper(success: false, result: [:], message: "unknown error", code: 404, error: error, state: state) // complete(result) complete(ResultWrapper(success: false, content: "unknown error")) } } } /** @abstract 提取摘要 @param file 文件路径 */ private func extractSummaryFile(file: String, complete: @escaping KMRequestServerComplete) { let infoDictionary = Bundle .main.infoDictionary! let urlString = AIInfoConfig().aiActionURL + "/api/AI/extractSummaryFile" let fileData = FileManager.default.contents(atPath: file) let params: [String: Any] = ["file": fileData, "uuid": uuid(), "platform": platform(), "app_name": app_name()] KMAIRequestServer.requestServer.uploadFile(urlString: urlString, params: params) { formData in let fileURL = URL(fileURLWithPath: file) try? formData.appendPart(withFileURL: fileURL, name: "file", fileName: fileURL.lastPathComponent, mimeType: "application/octet-stream") } requestSerializer: { requestSerializer in requestSerializer.setValue("Apifox/1.0.0 (https://www.apifox.cn)", forHTTPHeaderField: "User-Agent") } completion: { task, responseObject, error in if responseObject != nil { let data: NSDictionary = responseObject!["data"] as? NSDictionary ?? [:] let code: Int = responseObject!["code"] as? Int ?? 06005 let message: String = responseObject!["message"] as? String ?? "unknown error" let error = NSError(domain: message, code: code) // let result = ResultWrapper(success: true, result: data, message: message, code: Int(code)!, error: error, state: .extractSummaryFile) // if result.code == 200 { // result.success = true // complete(result) // } else { // result.success = false // complete(result) // } if code == 200 { complete(ResultWrapper(success: true, content: data["summary"] as! String)) } else { complete(ResultWrapper(success: false, content: message)) } } else { let error = NSError(domain: "unknown error", code: 404) // let result = ResultWrapper(success: false, result: [:], message: "unknown error", code: 404, error: error, state: .extractSummaryFile) // complete(result) complete(ResultWrapper(success: false, content: "unknown error")) } } } } //typealias KMHttpRequestServerComplete = (_ task: URLSessionDataTask?, _ responseObject: AnyObject?, _ error: NSError?) -> Void class KMAIRequestServer { var sessionManager: AFHTTPSessionManager! static let requestServer = KMAIRequestServer() var task: URLSessionTask? init() { let configuration: URLSessionConfiguration = URLSessionConfiguration.default sessionManager = AFHTTPSessionManager.init(sessionConfiguration: configuration) sessionManager.securityPolicy = AFSecurityPolicy.default() } /** 上传文件 - 翻译 - 摘要提取 */ func uploadFile(urlString: String, params: Dictionary?, body: ((_ formData:AFMultipartFormData) -> Void)?, requestSerializer: ((_ requestSerializer:AFHTTPRequestSerializer) -> Void)?, completion: KMHttpRequestServerComplete?) -> Void { sessionManager.requestSerializer.setValue("multipart/form-data", forHTTPHeaderField: "Content-Type") sessionManager.requestSerializer.timeoutInterval = 60 sessionManager.responseSerializer = AFJSONResponseSerializer() sessionManager.responseSerializer.acceptableContentTypes = ["application/json","text/html","text/json","text/javascript","text/plain","image/gif"] if (requestSerializer != nil) { requestSerializer!(sessionManager.requestSerializer); } if (body != nil) { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { formData in body!(formData) } progress: { progress in } success: { task, responseObject in let responseObject = responseObject as AnyObject if (completion != nil) { completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } else { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { progress in } success: { task, responseObject in if (completion != nil) { let responseObject = responseObject as AnyObject completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } } /** 文件翻译 */ func aiFileTranslateHandle(urlString: String, params: Dictionary?, body: ((_ formData:AFMultipartFormData) -> Void)?, requestSerializer: ((_ requestSerializer:AFHTTPRequestSerializer) -> Void)?, completion: KMHttpRequestServerComplete?) -> Void { sessionManager.requestSerializer = AFJSONRequestSerializer() sessionManager.responseSerializer = AFJSONResponseSerializer() sessionManager.responseSerializer.acceptableContentTypes = ["application/json","text/html","text/json","text/javascript","text/plain","image/gif"] if (requestSerializer != nil) { requestSerializer!(sessionManager.requestSerializer); } if (body != nil) { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { formData in body!(formData) } progress: { progress in } success: { task, responseObject in let responseObject = responseObject as AnyObject if (completion != nil) { completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } else { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { progress in } success: { task, responseObject in if (completion != nil) { let responseObject = responseObject as AnyObject completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } } /** 重写 */ func aiRewriting(urlString: String, params: Dictionary?, body: ((_ formData:AFMultipartFormData) -> Void)?, requestSerializer: ((_ requestSerializer:AFHTTPRequestSerializer) -> Void)?, completion: KMHttpRequestServerComplete?) -> Void { sessionManager.requestSerializer.setValue("multipart/form-data", forHTTPHeaderField: "Content-Type") sessionManager.requestSerializer.timeoutInterval = 60 sessionManager.responseSerializer = AFJSONResponseSerializer() sessionManager.responseSerializer.acceptableContentTypes = ["application/json","text/html","text/json","text/javascript","text/plain","image/gif"] if (requestSerializer != nil) { requestSerializer!(sessionManager.requestSerializer); } if (body != nil) { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { formData in body!(formData) } progress: { progress in } success: { task, responseObject in let responseObject = responseObject as AnyObject if (completion != nil) { completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } else { self.task = sessionManager.post(urlString, parameters: params, headers: nil) { progress in } success: { task, responseObject in if (completion != nil) { let responseObject = responseObject as AnyObject completion!(task,responseObject,nil); } } failure: { task, error in if (completion != nil) { completion!(task, nil, error as NSError); } } } } }