// // Copyright 2026 Element Creations Ltd. // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. // Please see LICENSE files in the repository root for full details. // import Foundation /// Reads profiles straight from the homeserver's profile endpoint, which — unlike the /// Rust SDK's `getProfile` — includes the extended profile fields (MSC4133). final class ExtendedProfileService: ExtendedProfileServiceProtocol { private let clientProxy: ClientProxyProtocol private let urlSession: URLSession init(clientProxy: ClientProxyProtocol, urlSession: URLSession = .shared) { self.clientProxy = clientProxy self.urlSession = urlSession } func profile(for userID: String) async -> Result { guard case let .success(credentials) = clientProxy.homeserverCredentials() else { return .failure(.missingCredentials) } let url = credentials.homeserverURL.appending(path: "_matrix/client/v3/profile/\(userID)") var request = URLRequest(url: url) request.setValue("Bearer \(credentials.accessToken)", forHTTPHeaderField: "Authorization") let data: Data do { let (responseData, response) = try await urlSession.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { return .failure(.networkFailure) } guard httpResponse.statusCode == 200 else { MXLog.error("Failed fetching the extended profile for \(userID) with status: \(httpResponse.statusCode)") return .failure(.httpStatus(httpResponse.statusCode)) } data = responseData } catch { MXLog.error("Failed fetching the extended profile for \(userID) with error: \(error)") return .failure(.networkFailure) } do { return try .success(JSONDecoder().decode(ExtendedProfile.self, from: data)) } catch { MXLog.error("Failed decoding the extended profile for \(userID) with error: \(error)") return .failure(.decodingFailure) } } }