// // NotificationService.swift // SolianNotifyExt // // Created by LittleSheep on 2024/7/19. // import UserNotifications import Intents enum ParseNotificationPayloadError: Error { case noMetadata(String) case noAvatarUrl(String) } class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { do { switch bestAttemptContent.categoryIdentifier { case "messaging.message", "messaging.callStart": guard let metadata = bestAttemptContent.userInfo["metadata"] as? [AnyHashable : Any] else { throw ParseNotificationPayloadError.noMetadata("The notification has no metadata.") } guard let avatarUrl = bestAttemptContent.userInfo["avatar"] as? String else { throw ParseNotificationPayloadError.noMetadata("The notification has no avatar url.") } let handle = INPersonHandle(value: String(metadata["user_id"] as! Int), type: .unknown) let avatar = INImage( url: URL(string: avatarUrl)! )! let sender = INPerson(personHandle: handle, nameComponents: nil, displayName: bestAttemptContent.title, image: avatar, contactIdentifier: nil, customIdentifier: nil) let intent = INSendMessageIntent(recipients: nil, outgoingMessageType: .outgoingMessageText, content: bestAttemptContent.body, speakableGroupName: nil, conversationIdentifier: String(metadata["channel_id"] as! Int), serviceName: nil, sender: sender, attachments: nil) let interaction = INInteraction(intent: intent, response: nil) interaction.direction = .incoming interaction.donate(completion: nil) let updatedContent = try request.content.updating(from: intent) contentHandler(updatedContent) break default: contentHandler(bestAttemptContent) break } } catch { contentHandler(bestAttemptContent) } } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }