public struct SwiftDevRant { let request: Request let backend = DevRantBackend() public init(requestLogger: Logger) { self.request = Request(encoder: .devRant, decoder: .devRant, logger: requestLogger) } func makeConfig(_ method: Request.Method, path: String, urlParameters: [String: String] = [:], headers: [String: String] = [:], token: AuthToken? = nil) -> Request.Config { var urlParameters = urlParameters urlParameters["app"] = "3" if let token { urlParameters["token_id"] = String(token.id) urlParameters["token_key"] = token.key urlParameters["user_id"] = String(token.userId) } var headers = headers headers["Content-Type"] = "application/x-www-form-urlencoded" return .init(method: method, backend: backend, path: path, urlParameters: urlParameters, headers: headers) } public func logIn(username: String, password: String) async throws -> AuthToken { var parameters: [String: String] = [:] parameters["app"] = "3" parameters["username"] = username parameters["password"] = password let config = makeConfig(.post, path: "users/auth-token") // For the log in request the url encoded parameters are passed as a string in the http body instead of in the URL. let body = String(Request.urlEncodedQueryString(from: parameters).dropFirst()) // dropping the first character "?" let response: AuthToken.CodingData.Container = try await request.requestJson(config: config, string: body, apiError: DevRantApiError.CodingData.self) return response.auth_token.decoded } /// Gets a personalized feed of rants. /// /// - Parameters: /// - token: The token from the `logIn` call response. /// - limit: The number of rants to get for pagination. /// - skip: How many rants to skip for pagination. /// - sessionHash: Pass the session hash value from the last rant feed response or `nil` if calling for the first time. public func getRantFeed(token: AuthToken, sort: RantFeed.Sort = .algorithm, limit: Int = 20, skip: Int, sessionHash: String?) async throws -> RantFeed { var parameters: [String: String] = [:] parameters["sort"] = switch sort { case .algorithm: "algo" case .recent: "recent" case .top: "top" } switch sort { case .top(range: let range): parameters["range"] = switch range { case .day: "day" case .week: "week" case .month: "month" case .all: "all" } default: break } parameters["limit"] = String(limit) parameters["skip"] = String(skip) parameters["prev_set"] = sessionHash parameters["plat"] = "1" // I don't know wtf that is. parameters["nari"] = "1" // I don't know wtf that is. let config = makeConfig(.get, path: "devrant/rants", urlParameters: parameters) let response: RantFeed.CodingData = try await request.requestJson(config: config, apiError: DevRantApiError.CodingData.self) return response.decoded } /// Get all weeklies as a list. /// /// - Parameters: /// - token: The token from the `logIn` call response. public func getWeeklies(token: AuthToken) async throws -> [Weekly] { let config = makeConfig(.get, path: "devrant/weekly-list") let response: Weekly.CodingData.List = try await request.requestJson(config: config, apiError: DevRantApiError.CodingData.self) return response.weeks.map(\.decoded) } }