|
|
|
@ -18,12 +18,21 @@ public class Instance: Decodable {
|
|
|
|
|
public let thumbnail: URL?
|
|
|
|
|
public let languages: [String]?
|
|
|
|
|
public let stats: Stats?
|
|
|
|
|
public let configuration: Configuration?
|
|
|
|
|
|
|
|
|
|
// pleroma doesn't currently implement these
|
|
|
|
|
public let contactAccount: Account?
|
|
|
|
|
|
|
|
|
|
// MARK: Unofficial additions to the Mastodon API.
|
|
|
|
|
public let maxStatusCharacters: Int?
|
|
|
|
|
// superseded by mastodon's configuration.statuses.max_characters, still used by older instances & pleroma
|
|
|
|
|
let maxTootCharacters: Int?
|
|
|
|
|
let pollLimits: PollsConfiguration?
|
|
|
|
|
|
|
|
|
|
public var maxStatusCharacters: Int? {
|
|
|
|
|
configuration?.statuses.maxCharacters ?? maxTootCharacters
|
|
|
|
|
}
|
|
|
|
|
public var pollsConfiguration: PollsConfiguration? {
|
|
|
|
|
configuration?.polls ?? pollLimits
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we need a custom decoder, because all API-compatible implementations don't return some data in the same format
|
|
|
|
|
public required init(from decoder: Decoder) throws {
|
|
|
|
@ -44,14 +53,18 @@ public class Instance: Decodable {
|
|
|
|
|
|
|
|
|
|
self.stats = try? container.decodeIfPresent(Stats.self, forKey: .stats)
|
|
|
|
|
self.thumbnail = try? container.decodeIfPresent(URL.self, forKey: .thumbnail)
|
|
|
|
|
if let maxStatusCharacters = try? container.decodeIfPresent(Int.self, forKey: .maxStatusCharacters) {
|
|
|
|
|
self.maxStatusCharacters = maxStatusCharacters
|
|
|
|
|
} else if let str = try? container.decodeIfPresent(String.self, forKey: .maxStatusCharacters),
|
|
|
|
|
let maxStatusCharacters = Int(str, radix: 10) {
|
|
|
|
|
self.maxStatusCharacters = maxStatusCharacters
|
|
|
|
|
|
|
|
|
|
self.configuration = try? container.decodeIfPresent(Configuration.self, forKey: .configuration)
|
|
|
|
|
|
|
|
|
|
if let maxTootCharacters = try? container.decodeIfPresent(Int.self, forKey: .maxTootCharacters) {
|
|
|
|
|
self.maxTootCharacters = maxTootCharacters
|
|
|
|
|
} else if let str = try? container.decodeIfPresent(String.self, forKey: .maxTootCharacters),
|
|
|
|
|
let maxTootCharacters = Int(str, radix: 10) {
|
|
|
|
|
self.maxTootCharacters = maxTootCharacters
|
|
|
|
|
} else {
|
|
|
|
|
self.maxStatusCharacters = nil
|
|
|
|
|
self.maxTootCharacters = nil
|
|
|
|
|
}
|
|
|
|
|
self.pollLimits = try? container.decodeIfPresent(PollsConfiguration.self, forKey: .pollLimits)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -65,15 +78,16 @@ public class Instance: Decodable {
|
|
|
|
|
case thumbnail
|
|
|
|
|
case languages
|
|
|
|
|
case stats
|
|
|
|
|
|
|
|
|
|
case configuration
|
|
|
|
|
case contactAccount = "contact_account"
|
|
|
|
|
|
|
|
|
|
case maxStatusCharacters = "max_toot_chars"
|
|
|
|
|
case maxTootCharacters = "max_toot_chars"
|
|
|
|
|
case pollLimits = "poll_limits"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Instance {
|
|
|
|
|
public class Stats: Decodable {
|
|
|
|
|
public struct Stats: Decodable {
|
|
|
|
|
public let domainCount: Int?
|
|
|
|
|
public let statusCount: Int?
|
|
|
|
|
public let userCount: Int?
|
|
|
|
@ -85,3 +99,68 @@ extension Instance {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Instance {
|
|
|
|
|
public struct Configuration: Decodable {
|
|
|
|
|
public let statuses: StatusesConfiguration
|
|
|
|
|
public let mediaAttachments: MediaAttachmentsConfiguration
|
|
|
|
|
/// Use Instance.pollsConfiguration to support older instance that don't have this nested
|
|
|
|
|
let polls: PollsConfiguration
|
|
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
|
|
|
case statuses
|
|
|
|
|
case mediaAttachments = "media_attachments"
|
|
|
|
|
case polls
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Instance {
|
|
|
|
|
public struct StatusesConfiguration: Decodable {
|
|
|
|
|
public let maxCharacters: Int
|
|
|
|
|
public let maxMediaAttachments: Int
|
|
|
|
|
public let charactersReservedPerURL: Int
|
|
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
|
|
|
case maxCharacters = "max_characters"
|
|
|
|
|
case maxMediaAttachments = "max_media_attachments"
|
|
|
|
|
case charactersReservedPerURL = "characters_reserved_per_url"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Instance {
|
|
|
|
|
public struct MediaAttachmentsConfiguration: Decodable {
|
|
|
|
|
public let supportedMIMETypes: [String]
|
|
|
|
|
public let imageSizeLimit: Int
|
|
|
|
|
public let imageMatrixLimit: Int
|
|
|
|
|
public let videoSizeLimit: Int
|
|
|
|
|
public let videoFrameRateLimit: Int
|
|
|
|
|
public let videoMatrixLimit: Int
|
|
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
|
|
|
case supportedMIMETypes = "supported_mime_types"
|
|
|
|
|
case imageSizeLimit = "image_size_limit"
|
|
|
|
|
case imageMatrixLimit = "image_matrix_limit"
|
|
|
|
|
case videoSizeLimit = "video_size_limit"
|
|
|
|
|
case videoFrameRateLimit = "video_frame_rate_limit"
|
|
|
|
|
case videoMatrixLimit = "video_matrix_limit"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Instance {
|
|
|
|
|
public struct PollsConfiguration: Decodable {
|
|
|
|
|
public let maxOptions: Int
|
|
|
|
|
public let maxCharactersPerOption: Int
|
|
|
|
|
public let minExpiration: TimeInterval
|
|
|
|
|
public let maxExpiration: TimeInterval
|
|
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
|
|
|
case maxOptions = "max_options"
|
|
|
|
|
case maxCharactersPerOption = "max_characters_per_option"
|
|
|
|
|
case minExpiration = "min_expiration"
|
|
|
|
|
case maxExpiration = "max_expiration"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|