commit 6ac68b5a737cdabb4e2c6c15dfabc620cadaa532
parent 2048e68d67d6efa219ece0c20fa6fb37d8ac02f7
Author: symbsrcool <nostr9779@pm.me>
Date:   Sun, 16 Apr 2023 22:29:40 -0400
Add nokyctranslate translation option
Changelog-Added: Add nokyctranslate translation option
Closes: #946
Diffstat:
4 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/damus/Models/TranslationService.swift b/damus/Models/TranslationService.swift
@@ -31,6 +31,7 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable {
     case none
     case libretranslate
     case deepl
+    case nokyctranslate
 
     var model: Model {
         switch self {
@@ -40,6 +41,8 @@ enum TranslationService: String, CaseIterable, Identifiable, StringCodable {
             return .init(tag: self.rawValue, displayName: NSLocalizedString("LibreTranslate (Open Source)", comment: "Dropdown option for selecting LibreTranslate as the translation service."))
         case .deepl:
             return .init(tag: self.rawValue, displayName: NSLocalizedString("DeepL (Proprietary, Higher Accuracy)", comment: "Dropdown option for selecting DeepL as the translation service."))
+        case .nokyctranslate:
+            return .init(tag: self.rawValue, displayName: NSLocalizedString("NoKYCTranslate.com (Prepay with BTC)", comment: "Dropdown option for selecting NoKYCTranslate.com as the translation service."))
         }
     }
 
diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift
@@ -196,6 +196,20 @@ class UserSettingsStore: ObservableObject {
             }
         }
     }
+    
+    @Published var nokyctranslate_api_key: String {
+        didSet {
+            do {
+                if nokyctranslate_api_key == "" {
+                    try clearNoKYCTranslateApiKey()
+                } else {
+                    try saveNoKYCTranslateApiKey(nokyctranslate_api_key)
+                }
+            } catch {
+                // No-op.
+            }
+        }
+    }
 
     init() {
         do {
@@ -203,6 +217,13 @@ class UserSettingsStore: ObservableObject {
         } catch {
             deepl_api_key = ""
         }
+        
+        do {
+            nokyctranslate_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
+        } catch {
+            nokyctranslate_api_key = ""
+        }
+        
     }
 
     private func saveLibreTranslateApiKey(_ apiKey: String) throws {
@@ -213,6 +234,14 @@ class UserSettingsStore: ObservableObject {
         try Vault.deletePrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
     }
 
+    private func saveNoKYCTranslateApiKey(_ apiKey: String) throws {
+        try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
+    }
+    
+    private func clearNoKYCTranslateApiKey() throws {
+        try Vault.deletePrivateKey(keychainConfiguration: DamusNoKYCTranslateKeychainConfiguration())
+    }
+    
     private func saveDeepLApiKey(_ apiKey: String) throws {
         try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusDeepLKeychainConfiguration())
     }
@@ -229,6 +258,8 @@ class UserSettingsStore: ObservableObject {
             return URLComponents(string: libretranslate_url) != nil
         case .deepl:
             return deepl_api_key != ""
+        case .nokyctranslate:
+            return nokyctranslate_api_key != ""
         }
     }
 }
@@ -245,7 +276,12 @@ struct DamusDeepLKeychainConfiguration: KeychainConfiguration {
     var accountName = "deepl_apikey"
 }
 
+struct DamusNoKYCTranslateKeychainConfiguration: KeychainConfiguration {
+    var serviceName = "damus"
+    var accessGroup: String? = nil
+    var accountName = "nokyctranslate_apikey"
+}
+
 func pk_setting_key(_ pubkey: String, key: String) -> String {
     return "\(pubkey)_\(key)"
 }
-
diff --git a/damus/Util/Translator.swift b/damus/Util/Translator.swift
@@ -24,6 +24,8 @@ public struct Translator {
         switch userSettingsStore.translation_service {
         case .libretranslate:
             return try await translateWithLibreTranslate(text, from: sourceLanguage, to: targetLanguage)
+        case .nokyctranslate:
+            return try await translateWithNoKYCTranslate(text, from: sourceLanguage, to: targetLanguage)
         case .deepl:
             return try await translateWithDeepL(text, from: sourceLanguage, to: targetLanguage)
         case .none:
@@ -85,6 +87,29 @@ public struct Translator {
         let response: Response = try await decodedData(for: request)
         return response.translations.map { $0.text }.joined(separator: " ")
     }
+    
+    private func translateWithNoKYCTranslate(_ text: String, from sourceLanguage: String, to targetLanguage: String) async throws -> String? {
+        let url = try makeURL("https://translate.nokyctranslate.com", path: "/translate")
+
+        var request = URLRequest(url: url)
+        request.httpMethod = "POST"
+        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+
+        struct RequestBody: Encodable {
+            let q: String
+            let source: String
+            let target: String
+            let api_key: String?
+        }
+        let body = RequestBody(q: text, source: sourceLanguage, target: targetLanguage, api_key: userSettingsStore.nokyctranslate_api_key)
+        request.httpBody = try encoder.encode(body)
+
+        struct Response: Decodable {
+            let translatedText: String
+        }
+        let response: Response = try await decodedData(for: request)
+        return response.translatedText
+    }
 
     private func makeURL(_ baseUrl: String, path: String) throws -> URL {
         guard var components = URLComponents(string: baseUrl) else {
diff --git a/damus/Views/Settings/TranslationSettingsView.swift b/damus/Views/Settings/TranslationSettingsView.swift
@@ -65,6 +65,17 @@ struct TranslationSettingsView: View {
                     }
                 }
 
+                if settings.translation_service == .nokyctranslate {
+                    SecureField(NSLocalizedString("API Key (required)", comment: "Prompt for optional entry of API Key to use translation server."), text: $settings.nokyctranslate_api_key)
+                        .disableAutocorrection(true)
+                        .disabled(settings.translation_service != .nokyctranslate)
+                        .autocapitalization(UITextAutocapitalizationType.none)
+                    
+                    if settings.nokyctranslate_api_key == "" {
+                        Link(NSLocalizedString("Get API Key with BTC/Lightning", comment: "Button to navigate to nokyctranslate website to get a translation API key."), destination: URL(string: "https://nokyctranslate.com")!)
+                    }
+                }
+                
                 if settings.translation_service != .none {
                     Toggle(NSLocalizedString("Automatically translate notes", comment: "Toggle to automatically translate notes."), isOn: $settings.auto_translate)
                         .toggleStyle(.switch)