// // PushSubscription.swift // Pachyderm // // Created by Shadowfacts on 9/9/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import Foundation public struct PushSubscription: Decodable, Sendable { public var id: String public var endpoint: URL public var serverKey: String public var alerts: Alerts public var policy: Policy public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // id is documented as being a string, but mastodon returns a json number if let s = try? container.decode(String.self, forKey: .id) { self.id = s } else { let i = try container.decode(Int.self, forKey: .id) self.id = i.description } self.endpoint = try container.decode(URL.self, forKey: .endpoint) self.serverKey = try container.decode(String.self, forKey: .serverKey) self.alerts = try container.decode(PushSubscription.Alerts.self, forKey: .alerts) // added in mastodon 4.1.0 self.policy = try container.decodeIfPresent(PushSubscription.Policy.self, forKey: .policy) ?? .all } public static func create(endpoint: URL, publicKey: Data, authSecret: Data, alerts: Alerts, policy: Policy) -> Request { return Request(method: .post, path: "/api/v1/push/subscription", body: ParametersBody([ "subscription[endpoint]" => endpoint.absoluteString, "subscription[keys][p256dh]" => publicKey.base64EncodedString(), "subscription[keys][auth]" => authSecret.base64EncodedString(), "data[alerts][mention]" => alerts.mention, "data[alerts][status]" => alerts.status, "data[alerts][reblog]" => alerts.reblog, "data[alerts][follow]" => alerts.follow, "data[alerts][follow_request]" => alerts.followRequest, "data[alerts][favourite]" => alerts.favourite, "data[alerts][poll]" => alerts.poll, "data[alerts][update]" => alerts.update, "data[alerts][pleroma:emoji_reaction]" => alerts.emojiReaction, "data[policy]" => policy.rawValue, ])) } public static func update(alerts: Alerts, policy: Policy) -> Request { return Request(method: .put, path: "/api/v1/push/subscription", body: ParametersBody([ "data[alerts][mention]" => alerts.mention, "data[alerts][status]" => alerts.status, "data[alerts][reblog]" => alerts.reblog, "data[alerts][follow]" => alerts.follow, "data[alerts][follow_request]" => alerts.followRequest, "data[alerts][favourite]" => alerts.favourite, "data[alerts][poll]" => alerts.poll, "data[alerts][update]" => alerts.update, "data[alerts][pleroma:emoji_reaction]" => alerts.emojiReaction, "data[policy]" => policy.rawValue, ])) } public static func delete() -> Request { return Request(method: .delete, path: "/api/v1/push/subscription") } private enum CodingKeys: String, CodingKey { case id case endpoint case serverKey = "server_key" case alerts case policy } } extension PushSubscription { public struct Alerts: Decodable, Sendable { public let mention: Bool public let status: Bool public let reblog: Bool public let follow: Bool public let followRequest: Bool public let favourite: Bool public let poll: Bool public let update: Bool public let emojiReaction: Bool public init( mention: Bool, status: Bool, reblog: Bool, follow: Bool, followRequest: Bool, favourite: Bool, poll: Bool, update: Bool, emojiReaction: Bool ) { self.mention = mention self.status = status self.reblog = reblog self.follow = follow self.followRequest = followRequest self.favourite = favourite self.poll = poll self.update = update self.emojiReaction = emojiReaction } public init(from decoder: any Decoder) throws { let container: KeyedDecodingContainer = try decoder.container(keyedBy: PushSubscription.Alerts.CodingKeys.self) self.mention = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.mention) // status added in mastodon 3.3.0 self.status = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.status) ?? false self.reblog = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.reblog) self.follow = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.follow) // follow_request added in 3.1.0 self.followRequest = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.followRequest) ?? false self.favourite = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.favourite) self.poll = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.poll) // update added in mastodon 3.5.0 self.update = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.update) ?? false // pleroma/akkoma only self.emojiReaction = try container.decodeIfPresent(Bool.self, forKey: .emojiReaction) ?? false } private enum CodingKeys: String, CodingKey { case mention case status case reblog case follow case followRequest = "follow_request" case favourite case poll case update case emojiReaction = "pleroma:emoji_reaction" } } } extension PushSubscription { public enum Policy: String, Decodable, Sendable { case all case followed case followers case none } }