// // VaporTGClient.swift // Vapor-telegram-bot-example // // Created by Oleh Hudeichuk on 01.07.2024. // import Foundation import Vapor import SwiftTelegramSdk public enum TGHTTPMediaType: String, Equatable { case formData case json } private struct TGEmptyParams: Encodable {} public final class VaporTGClient: TGClientPrtcl { public typealias HTTPMediaType = SwiftTelegramSdk.HTTPMediaType public var log: Logging.Logger = .init(label: "VaporTGClient") private let client: Vapor.Client public init(client: Vapor.Client) { self.client = client } @discardableResult public func post ( _ url: URL, params: Params? = nil, as mediaType: HTTPMediaType? = nil ) async throws -> Response { let clientResponse: ClientResponse = try await client.post(URI(string: url.absoluteString), headers: HTTPHeaders()) { clientRequest in if mediaType == .formData || mediaType == nil { #warning("THIS CODE FOR FAST FIX, BECAUSE https://github.com/vapor/multipart-kit/issues/63 not accepted yet") var rawMultipart: (body: NSMutableData, boundary: String)! do { /// Content-Disposition: form-data; name="nested_object" /// /// { json string } if let currentParams: Params = params { rawMultipart = try currentParams.toMultiPartFormData(log: log) } else { rawMultipart = try TGEmptyParams().toMultiPartFormData(log: log) } } catch { log.critical("Post request error: \(error.logMessage)") } clientRequest.headers.add(name: "Content-Type", value: "multipart/form-data; boundary=\(rawMultipart.boundary)") let buffer = ByteBuffer.init(data: rawMultipart.body as Data) clientRequest.body = buffer /// Debug // TGBot.log.critical("url: \(url)\n\(String(decoding: rawMultipart.body, as: UTF8.self))") } else { let mediaType: Vapor.HTTPMediaType = if let mediaType { .init(type: mediaType.type, subType: mediaType.subType, parameters: mediaType.parameters) } else { .json } try clientRequest.content.encode(params ?? (TGEmptyParams() as! Params), as: mediaType) } } let telegramContainer: TGTelegramContainer = try clientResponse.content.decode(TGTelegramContainer.self) return try processContainer(telegramContainer) } @discardableResult public func post(_ url: URL) async throws -> Response { try await post(url, params: TGEmptyParams(), as: nil) } private func processContainer(_ container: TGTelegramContainer) throws -> T { guard container.ok else { let desc = """ Response marked as `not Ok`, it seems something wrong with request Code: \(container.errorCode ?? -1) \(container.description ?? "Empty") """ let error = BotError( type: .server, description: desc ) log.error(error.logMessage) throw error } guard let result = container.result else { let error = BotError( type: .server, reason: "Response marked as `Ok`, but doesn't contain `result` field." ) log.error(error.logMessage) throw error } let logString = """ Response: Code: \(container.errorCode ?? 0) Status OK: \(container.ok) Description: \(container.description ?? "Empty") """ log.trace(logString.logMessage) return result } }