1→import Foundation 2→ 3→/// Service para automatizar posts em redes sociais via scripts externos 4→actor SocialPostingService { 5→ static let shared = SocialPostingService() 6→ 7→ /// Mapeamento de plataformas para scripts de automação 8→ private let scripts: [Platform: String] = [ 9→ .twitter: "~/bin/tweet", 10→ .linkedin: "~/bin/linkedin" 11→ ] 12→ 13→ /// Posta conteúdo em uma rede social (suporta threads para Twitter) 14→ /// - Parameters: 15→ /// - content: O texto a ser postado 16→ /// - platform: A plataforma de destino 17→ func post(content: String, to platform: Platform) async throws { 18→ // Para Twitter, usa o parser de threads 19→ if platform == .twitter { 20→ try await postTwitterThread(content: content) 21→ } else { 22→ try await postSingle(content: content, to: platform) 23→ } 24→ } 25→ 26→ /// Posta uma thread no Twitter usando o modo --thread com JSON 27→ private func postTwitterThread(content: String) async throws { 28→ let tweets = TwitterThreadParser.parseThread(content) 29→ 30→ guard !tweets.isEmpty else { 31→ throw PostingError.emptyContent 32→ } 33→ 34→ let scriptPath = NSString(string: scripts[.twitter]!).expandingTildeInPath 35→ 36→ guard FileManager.default.fileExists(atPath: scriptPath) else { 37→ throw PostingError.scriptNotFound(scriptPath) 38→ } 39→ 40→ if tweets.count == 1 { 41→ // Tweet único - usa modo normal 42→ try await executeTweetScript(scriptPath, content: tweets[0]) 43→ } else { 44→ // Thread - usa modo --thread com JSON (salva URLs entre posts) 45→ try await executeThreadScript(scriptPath, tweets: tweets) 46→ } 47→ } 48→ 49→ /// Executa o script de thread com JSON array de tweets 50→ private func executeThreadScript(_ scriptPath: String, tweets: [String]) async throws { 51→ let task = Process() 52→ task.executableURL = URL(fileURLWithPath: "/usr/bin/python3") 53→ 54→ // Converte tweets para JSON 55→ guard let jsonData = try? JSONSerialization.data(withJSONObject: tweets), 56→ let jsonString = String(data: jsonData, encoding: .utf8) else { 57→ throw PostingError.executionFailed("Erro ao converter tweets para JSON") 58→ } 59→ 60→ // Usa o script Python diretamente 61→ let pythonScript = NSString(string: "~/Scripts/automation/twitter-post.py").expandingTildeInPath 62→ 63→ print("🧵 Postando thread com \(tweets.count) tweets...") 64→ task.arguments = [pythonScript, "--thread", jsonString] 65→ 66→ // Configura ambiente para herdar permissões 67→ task.environment = ProcessInfo.processInfo.environment 68→ 69→ let outputPipe = Pipe() 70→ let errorPipe = Pipe() 71→ task.standardOutput = outputPipe 72→ task.standardError = errorPipe 73→ 74→ try task.run() 75→ task.waitUntilExit() 76→ 77→ let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() 78→ if let output = String(data: outputData, encoding: .utf8), !output.isEmpty { 79→ print(output) 80→ } 81→ 82→ if task.terminationStatus != 0 { 83→ let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() 84→ let errorMsg = String(data: errorData, encoding: .utf8) ?? "Erro desconhecido" 85→ throw PostingError.scriptFailed(errorMsg) 86→ } 87→ 88→ print("✅ Thread completa postada! (\(tweets.count) tweets)") 89→ } 90→ 91→ /// Executa o script de tweet único 92→ private func executeTweetScript(_ scriptPath: String, content: String) async throws { 93→ let task = Process() 94→ task.executableURL = URL(fileURLWithPath: "/usr/bin/python3") 95→ 96→ // Usa o script Python diretamente 97→ let pythonScript = NSString(string: "~/Scripts/automation/twitter-post.py").expandingTildeInPath 98→ task.arguments = [pythonScript, content] 99→ 100→ // Configura ambiente para herdar permissões 101→ task.environment = ProcessInfo.processInfo.environment 102→ 103→ let outputPipe = Pipe() 104→ let errorPipe = Pipe() 105→ task.standardOutput = outputPipe 106→ task.standardError = errorPipe 107→ 108→ try task.run() 109→ task.waitUntilExit() 110→ 111→ if task.terminationStatus != 0 { 112→ let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() 113→ let errorMsg = String(data: errorData, encoding: .utf8) ?? "Erro desconhecido" 114→ throw PostingError.scriptFailed(errorMsg) 115→ } 116→ } 117→ 118→ /// Posta conteúdo único (não-thread) 119→ private func postSingle(content: String, to platform: Platform) async throws { 120→ guard let scriptPath = scripts[platform] else { 121→ throw PostingError.unsupportedPlatform(platform.rawValue) 122→ } 123→ 124→ let expandedPath = NSString(string: scriptPath).expandingTildeInPath 125→ 126→ guard FileManager.default.fileExists(atPath: expandedPath) else { 127→ throw PostingError.scriptNotFound(expandedPath) 128→ } 129→ 130→ // Limpa o conteúdo antes de postar 131→ let cleanedContent = TwitterThreadParser.cleanContent(content) 132→ 133→ let task = Process() 134→ task.executableURL = URL(fileURLWithPath: "/bin/bash") 135→ task.arguments = [expandedPath, cleanedContent] 136→ 137→ let outputPipe = Pipe() 138→ let errorPipe = Pipe() 139→ task.standardOutput = outputPipe 140→ task.standardError = errorPipe 141→ 142→ do { 143→ try task.run() 144→ task.waitUntilExit() 145→ 146→ if task.terminationStatus != 0 { 147→ let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile() 148→ let errorMsg = String(data: errorData, encoding: .utf8) ?? "Erro desconhecido" 149→ throw PostingError.scriptFailed(errorMsg) 150→ } 151→ 152→ let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() 153→ if let output = String(data: outputData, encoding: .utf8), !output.isEmpty { 154→ print("✅ Post publicado em \(platform.rawValue)") 155→ print(output) 156→ } 157→ } catch let error as PostingError { 158→ throw error 159→ } catch { 160→ throw PostingError.executionFailed(error.localizedDescription) 161→ } 162→ } 163→ 164→ /// Verifica se uma plataforma tem suporte para posting automático 165→ func isSupported(_ platform: Platform) -> Bool { 166→ guard let scriptPath = scripts[platform] else { return false } 167→ let expandedPath = NSString(string: scriptPath).expandingTildeInPath 168→ return FileManager.default.fileExists(atPath: expandedPath) 169→ } 170→ 171→ /// Retorna as plataformas suportadas 172→ var supportedPlatforms: [Platform] { 173→ scripts.keys.filter { isSupported($0) } 174→ } 175→} 176→ 177→// MARK: - Posting Errors 178→ 179→enum PostingError: LocalizedError { 180→ case unsupportedPlatform(String) 181→ case scriptNotFound(String) 182→ case scriptFailed(String) 183→ case executionFailed(String) 184→ case emptyContent 185→ 186→ var errorDescription: String? { 187→ switch self { 188→ case .unsupportedPlatform(let platform): 189→ return "Plataforma '\(platform)' não suporta posting automático" 190→ case .scriptNotFound(let path): 191→ return "Script de automação não encontrado: \(path)" 192→ case .scriptFailed(let message): 193→ return "Falha ao postar: \(message)" 194→ case .executionFailed(let message): 195→ return "Erro de execução: \(message)" 196→ case .emptyContent: 197→ return "Conteúdo vazio - nada para postar" 198→ } 199→ } 200→} 201→ Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.