diff --git a/README.md b/README.md index 5a6d4d0f..82c8c93d 100644 --- a/README.md +++ b/README.md @@ -721,7 +721,12 @@ The Request body must have a notifications array. The following is a parameter t | mutable_content | bool | enable Notification Service app extension. | - | only iOS(10.0+). | | name | string | sets the name value on the aps sound dictionary. | - | only iOS | | volume | float32 | sets the volume value on the aps sound dictionary. | - | only iOS | -| interruption_level | string | defines the interruption level for the push notification. | - | only iOS(15.0+) | +| interruption_level | string | defines the interruption level for the push notification. | - | only iOS(15.0+) | +| content-state | string array | dynamic and custom content for live-activity notification. | - | only iOS(16.1+) | +| timestamp | int | the UNIX time when sending the remote notification that updates or ends a Live Activity | - | only iOS(16.1+) | +| event | string | describes whether you update or end an ongoing Live Activity | - | only iOS(16.1+) | +| stale-date | int | the date which a Live Activity becomes stale, or out of date | - | only iOS(16.1+) | +| dismissal-date | int | the UNIX time -timestamp- which a Live Activity will end and will be removed | - | only iOS(16.1+) | ### iOS alert payload diff --git a/go.mod b/go.mod index f4180cf4..d755e590 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/prometheus/client_golang v1.19.0 github.com/redis/go-redis/v9 v9.5.3 github.com/rs/zerolog v1.32.0 - github.com/sideshow/apns2 v0.23.0 + github.com/sideshow/apns2 v0.24.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index a01ae775..e3d39f10 100644 --- a/go.sum +++ b/go.sum @@ -291,8 +291,8 @@ github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgY github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sideshow/apns2 v0.23.0 h1:lpkikaZ995GIcKk6AFsYzHyezCrsrfEDvUWcWkEGErY= -github.com/sideshow/apns2 v0.23.0/go.mod h1:7Fceu+sL0XscxrfLSkAoH6UtvKefq3Kq1n4W3ayQZqE= +github.com/sideshow/apns2 v0.24.0 h1:syofL4rd8ZeqUVySgYyBSYr/oLMU/HzJi++r3FIp6Z4= +github.com/sideshow/apns2 v0.24.0/go.mod h1:7Fceu+sL0XscxrfLSkAoH6UtvKefq3Kq1n4W3ayQZqE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/notify/notification.go b/notify/notification.go index fcd9dfda..d7023f56 100644 --- a/notify/notification.go +++ b/notify/notification.go @@ -115,6 +115,14 @@ type PushNotification struct { // ref: https://github.com/sideshow/apns2/blob/54928d6193dfe300b6b88dad72b7e2ae138d4f0a/payload/builder.go#L7-L24 InterruptionLevel string `json:"interruption_level,omitempty"` + + // live-activity support + // ref: https://developer.apple.com/documentation/activitykit/updating-and-ending-your-live-activity-with-activitykit-push-notifications + ContentState D `json:"content-state,omitempty"` + StaleDate int64 `json:"stale-date,omitempty"` + DismissalDate int64 `json:"dismissal-date"` + Event string `json:"event,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` } // Bytes for queue message diff --git a/notify/notification_apns.go b/notify/notification_apns.go index 2e003709..1bae0455 100644 --- a/notify/notification_apns.go +++ b/notify/notification_apns.go @@ -295,6 +295,26 @@ func iosAlertDictionary(notificationPayload *payload.Payload, req *PushNotificat notificationPayload.AlertSummaryArgCount(req.Alert.SummaryArgCount) } + if len(req.ContentState) > 0 { + notificationPayload.SetContentState(req.ContentState) + } + + if req.StaleDate > 0 { + notificationPayload.SetStaleDate(req.StaleDate) + } + + if req.DismissalDate > 0 { + notificationPayload.SetDismissalDate(req.DismissalDate) + } + + if len(req.Event) > 0 { + notificationPayload.SetEvent(payload.ELiveActivityEvent(req.Event)) + } + + if req.Timestamp > 0 { + notificationPayload.SetTimestamp(req.Timestamp) + } + return notificationPayload } diff --git a/notify/notification_apns_test.go b/notify/notification_apns_test.go index 01339d8d..8716c20f 100644 --- a/notify/notification_apns_test.go +++ b/notify/notification_apns_test.go @@ -513,6 +513,11 @@ func TestMessageAndTitle(t *testing.T) { func TestIOSAlertNotificationStructure(t *testing.T) { var dat map[string]interface{} + unix := time.Now().Unix() + stale_date := time.Now().Unix() + dismissal_date := stale_date + 5 + timeStamp := time.Now().Unix() + itemId := float64(12345) req := &PushNotification{ Message: "Welcome", @@ -529,6 +534,14 @@ func TestIOSAlertNotificationStructure(t *testing.T) { TitleLocKey: testMessage, }, InterruptionLevel: testMessage, + StaleDate: stale_date, + DismissalDate: dismissal_date, + Event: testMessage, + Timestamp: timeStamp, + ContentState: D{ + "item_id": itemId, + "item_name": testMessage, + }, } notification := GetIOSNotification(req) @@ -550,10 +563,16 @@ func TestIOSAlertNotificationStructure(t *testing.T) { subtitle, _ := jsonparser.GetString(data, "aps", "alert", "subtitle") titleLocKey, _ := jsonparser.GetString(data, "aps", "alert", "title-loc-key") interruptionLevel, _ := jsonparser.GetString(data, "aps", "interruption-level") + staleDate, _ := jsonparser.GetInt(data, "aps", "stale-date") + event, _ := jsonparser.GetString(data, "aps", "event") + timestamp, _ := jsonparser.GetInt(data, "aps", "timestamp") aps := dat["aps"].(map[string]interface{}) alert := aps["alert"].(map[string]interface{}) titleLocArgs := alert["title-loc-args"].([]interface{}) locArgs := alert["loc-args"].([]interface{}) + contentState := aps["content-state"].(map[string]interface{}) + contentStateItemId := contentState["item_id"] + contentStateItemName := contentState["item_name"] assert.Equal(t, testMessage, action) assert.Equal(t, testMessage, actionLocKey) @@ -564,6 +583,16 @@ func TestIOSAlertNotificationStructure(t *testing.T) { assert.Equal(t, testMessage, subtitle) assert.Equal(t, testMessage, titleLocKey) assert.Equal(t, testMessage, interruptionLevel) + assert.Equal(t, testMessage, event) + assert.Equal(t, unix, staleDate) + assert.Equal(t, unix, timestamp) + + // dynamic contentState content + assert.Equal(t, contentStateItemId, itemId) + assert.Equal(t, contentStateItemName, testMessage) + assert.Contains(t, contentState, "item_id") + assert.Contains(t, contentState, "item_name") + assert.Contains(t, titleLocArgs, "a") assert.Contains(t, titleLocArgs, "b") assert.Contains(t, locArgs, "a")