// // MastodonController+Push.swift // Tusker // // Created by Shadowfacts on 4/9/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import Foundation import Pachyderm import PushNotifications import CryptoKit extension MastodonController { func createPushSubscription(subscription: PushNotifications.PushSubscription) async throws -> Pachyderm.PushSubscription { let req = Pachyderm.PushSubscription.create( endpoint: subscription.endpoint, // mastodon docs just say "Base64 encoded string of a public key from a ECDH keypair using the prime256v1 curve." // other apps use SecKeyCopyExternalRepresentation which is documented to use X9.63 for elliptic curve keys // and that seems to work publicKey: subscription.secretKey.publicKey.x963Representation, authSecret: subscription.authSecret, alerts: .init(subscription.alerts), policy: .init(subscription.policy) ) return try await run(req).0 } func updatePushSubscription(subscription: PushNotifications.PushSubscription) async throws -> Pachyderm.PushSubscription { // when updating anything other than the alerts/policy, we need to go through the create route return try await createPushSubscription(subscription: subscription) } func updatePushSubscription(alerts: PushNotifications.PushSubscription.Alerts, policy: PushNotifications.PushSubscription.Policy) async throws -> Pachyderm.PushSubscription { let req = Pachyderm.PushSubscription.update(alerts: .init(alerts), policy: .init(policy)) var result = try await run(req).0 if instanceFeatures.pushNotificationPolicyMissingFromResponse { // see https://github.com/mastodon/mastodon/issues/23145 // so just assume if the request was successful that it worked result.policy = .init(policy) } return result } func deletePushSubscription() async throws { let req = Pachyderm.PushSubscription.delete() _ = try await run(req) } } private extension Pachyderm.PushSubscription.Alerts { init(_ alerts: PushNotifications.PushSubscription.Alerts) { self.init( mention: alerts.contains(.mention), status: alerts.contains(.status), reblog: alerts.contains(.reblog), follow: alerts.contains(.follow), followRequest: alerts.contains(.followRequest), favourite: alerts.contains(.favorite), poll: alerts.contains(.poll), update: alerts.contains(.update), emojiReaction: alerts.contains(.emojiReaction) ) } } private extension Pachyderm.PushSubscription.Policy { init(_ policy: PushNotifications.PushSubscription.Policy) { switch policy { case .all: self = .all case .followers: self = .followers case .followed: self = .followed } } }