diff --git a/gorush/const.go b/gorush/const.go deleted file mode 100644 index cf8e5dae2..000000000 --- a/gorush/const.go +++ /dev/null @@ -1,24 +0,0 @@ -package gorush - -const ( - // PlatFormIos constant is 1 for iOS - PlatFormIos = iota + 1 - // PlatFormAndroid constant is 2 for Android - PlatFormAndroid -) - -const ( - // SucceededPush is log block - SucceededPush = "succeeded-push" - // FailedPush is log block - FailedPush = "failed-push" -) - -// Stat variable for redis -const ( - TotalCountKey = "gorush-total-count" - IosSuccessKey = "gorush-ios-success-count" - IosErrorKey = "gorush-ios-error-count" - AndroidSuccessKey = "gorush-android-success-count" - AndroidErrorKey = "gorush-android-error-count" -) diff --git a/gorush/consts/const.go b/gorush/consts/const.go new file mode 100644 index 000000000..41f8de066 --- /dev/null +++ b/gorush/consts/const.go @@ -0,0 +1,96 @@ +package consts + +import ( + "github.com/appleboy/gorush/gorush/structs" + "github.com/sideshow/apns2" +) + +const ( + // PlatFormIos constant is 1 for iOS + PlatFormIos structs.Platform = iota + 1 + // PlatFormAndroid constant is 2 for Android + PlatFormAndroid +) + +const ( + // SucceededPush is log block + SucceededPush = "succeeded-push" + // FailedPush is log block + FailedPush = "failed-push" +) + +// Stat variable for redis +const ( + TotalCountKey = "gorush-total-count" + IosSuccessKey = "gorush-ios-success-count" + IosErrorKey = "gorush-ios-error-count" + AndroidSuccessKey = "gorush-android-success-count" + AndroidErrorKey = "gorush-android-error-count" +) + +const ( + // ApnsPriorityLow will tell APNs to send the push message at a time that takes + // into account power considerations for the device. Notifications with this + // priority might be grouped and delivered in bursts. They are throttled, and + // in some cases are not delivered. + ApnsPriorityLow = apns2.PriorityLow + + // ApnsPriorityHigh will tell APNs to send the push message immediately. + // Notifications with this priority must trigger an alert, sound, or badge on + // the target device. It is an error to use this priority for a push + // notification that contains only the content-available key. + ApnsPriorityHigh = apns2.PriorityHigh +) + +const ( + // PriorityNormal sets push notification with normal priority + PriorityNormal structs.Priority = "normal" + // PriorityHigh sets push notification with high priority + PriorityHigh structs.Priority = "high" +) + +const ( + // PushTypeAlert is used for notifications that trigger a user interaction — + // for example, an alert, badge, or sound. If you set this push type, the + // apns-topic header field must use your app’s bundle ID as the topic. The + // alert push type is required on watchOS 6 and later. It is recommended on + // macOS, iOS, tvOS, and iPadOS. + PushTypeAlert = apns2.PushTypeAlert + + // PushTypeBackground is used for notifications that deliver content in the + // background, and don’t trigger any user interactions. If you set this push + // type, the apns-topic header field must use your app’s bundle ID as the + // topic. The background push type is required on watchOS 6 and later. It is + // recommended on macOS, iOS, tvOS, and iPadOS. + PushTypeBackground = apns2.PushTypeBackground + + // PushTypeVOIP is used for notifications that provide information about an + // incoming Voice-over-IP (VoIP) call. If you set this push type, the + // apns-topic header field must use your app’s bundle ID with .voip appended + // to the end. If you’re using certificate-based authentication, you must + // also register the certificate for VoIP services. The voip push type is + // not available on watchOS. It is recommended on macOS, iOS, tvOS, and + // iPadOS. + PushTypeVOIP = apns2.PushTypeVOIP + + // PushTypeComplication is used for notifications that contain update + // information for a watchOS app’s complications. If you set this push type, + // the apns-topic header field must use your app’s bundle ID with + // .complication appended to the end. If you’re using certificate-based + // authentication, you must also register the certificate for WatchKit + // services. The complication push type is recommended for watchOS and iOS. + // It is not available on macOS, tvOS, and iPadOS. + PushTypeComplication = apns2.PushTypeComplication + + // PushTypeFileProvider is used to signal changes to a File Provider + // extension. If you set this push type, the apns-topic header field must + // use your app’s bundle ID with .pushkit.fileprovider appended to the end. + // The fileprovider push type is not available on watchOS. It is recommended + // on macOS, iOS, tvOS, and iPadOS. + PushTypeFileProvider = apns2.PushTypeFileProvider + + // PushTypeMDM is used for notifications that tell managed devices to + // contact the MDM server. If you set this push type, you must use the topic + // from the UID attribute in the subject of your MDM push certificate. + PushTypeMDM = apns2.PushTypeMDM +) diff --git a/gorush/log.go b/gorush/log.go index d33307199..ca6c738db 100644 --- a/gorush/log.go +++ b/gorush/log.go @@ -7,6 +7,9 @@ import ( "os" "strings" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" + "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" ) @@ -116,22 +119,22 @@ func SetLogLevel(log *logrus.Logger, levelString string) error { return nil } -func colorForPlatForm(platform int) string { +func colorForPlatForm(platform structs.Platform) string { switch platform { - case PlatFormIos: + case consts.PlatFormIos: return blue - case PlatFormAndroid: + case consts.PlatFormAndroid: return yellow default: return reset } } -func typeForPlatForm(platform int) string { +func typeForPlatForm(platform structs.Platform) string { switch platform { - case PlatFormIos: + case consts.PlatFormIos: return "ios" - case PlatFormAndroid: + case consts.PlatFormAndroid: return "android" default: return "" @@ -197,7 +200,7 @@ func LogPush(status, token string, req PushNotification, errPush error) { } else { var typeColor string switch status { - case SucceededPush: + case consts.SucceededPush: if isTerm { typeColor = green } @@ -208,7 +211,7 @@ func LogPush(status, token string, req PushNotification, errPush error) { log.Token, log.Message, ) - case FailedPush: + case consts.FailedPush: if isTerm { typeColor = red } @@ -224,9 +227,9 @@ func LogPush(status, token string, req PushNotification, errPush error) { } switch status { - case SucceededPush: + case consts.SucceededPush: LogAccess.Info(output) - case FailedPush: + case consts.FailedPush: LogError.Error(output) } } diff --git a/gorush/log_test.go b/gorush/log_test.go index be2867af6..e84e30d5b 100644 --- a/gorush/log_test.go +++ b/gorush/log_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/appleboy/gorush/config" + "github.com/appleboy/gorush/gorush/consts" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) @@ -79,14 +81,14 @@ func TestErrorLogPath(t *testing.T) { } func TestPlatFormType(t *testing.T) { - assert.Equal(t, "ios", typeForPlatForm(PlatFormIos)) - assert.Equal(t, "android", typeForPlatForm(PlatFormAndroid)) + assert.Equal(t, "ios", typeForPlatForm(consts.PlatFormIos)) + assert.Equal(t, "android", typeForPlatForm(consts.PlatFormAndroid)) assert.Equal(t, "", typeForPlatForm(10000)) } func TestPlatFormColor(t *testing.T) { - assert.Equal(t, blue, colorForPlatForm(PlatFormIos)) - assert.Equal(t, yellow, colorForPlatForm(PlatFormAndroid)) + assert.Equal(t, blue, colorForPlatForm(consts.PlatFormIos)) + assert.Equal(t, yellow, colorForPlatForm(consts.PlatFormAndroid)) assert.Equal(t, reset, colorForPlatForm(1000000)) } diff --git a/gorush/notification.go b/gorush/notification.go index f38e66d19..1dc5f9fcb 100644 --- a/gorush/notification.go +++ b/gorush/notification.go @@ -8,42 +8,10 @@ import ( "strings" "sync" - "github.com/appleboy/go-fcm" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" ) -// D provide string array -type D map[string]interface{} - -const ( - // ApnsPriorityLow will tell APNs to send the push message at a time that takes - // into account power considerations for the device. Notifications with this - // priority might be grouped and delivered in bursts. They are throttled, and - // in some cases are not delivered. - ApnsPriorityLow = 5 - - // ApnsPriorityHigh will tell APNs to send the push message immediately. - // Notifications with this priority must trigger an alert, sound, or badge on - // the target device. It is an error to use this priority for a push - // notification that contains only the content-available key. - ApnsPriorityHigh = 10 -) - -// Alert is APNs payload -type Alert struct { - Action string `json:"action,omitempty"` - ActionLocKey string `json:"action-loc-key,omitempty"` - Body string `json:"body,omitempty"` - LaunchImage string `json:"launch-image,omitempty"` - LocArgs []string `json:"loc-args,omitempty"` - LocKey string `json:"loc-key,omitempty"` - Title string `json:"title,omitempty"` - Subtitle string `json:"subtitle,omitempty"` - TitleLocArgs []string `json:"title-loc-args,omitempty"` - TitleLocKey string `json:"title-loc-key,omitempty"` - SummaryArg string `json:"summary-arg,omitempty"` - SummaryArgCount int `json:"summary-arg-count,omitempty"` -} - // RequestPush support multiple notification request. type RequestPush struct { Notifications []PushNotification `json:"notifications" binding:"required"` @@ -54,47 +22,7 @@ type PushNotification struct { wg *sync.WaitGroup log *[]LogPushEntry - // Common - ID string `json:"notif_id,omitempty"` - Tokens []string `json:"tokens" binding:"required"` - Platform int `json:"platform" binding:"required"` - Message string `json:"message,omitempty"` - Title string `json:"title,omitempty"` - Image string `json:"image,omitempty"` - Priority string `json:"priority,omitempty"` - ContentAvailable bool `json:"content_available,omitempty"` - MutableContent bool `json:"mutable_content,omitempty"` - Sound interface{} `json:"sound,omitempty"` - Data D `json:"data,omitempty"` - Retry int `json:"retry,omitempty"` - - // Android - APIKey string `json:"api_key,omitempty"` - To string `json:"to,omitempty"` - CollapseKey string `json:"collapse_key,omitempty"` - DelayWhileIdle bool `json:"delay_while_idle,omitempty"` - TimeToLive *uint `json:"time_to_live,omitempty"` - RestrictedPackageName string `json:"restricted_package_name,omitempty"` - DryRun bool `json:"dry_run,omitempty"` - Condition string `json:"condition,omitempty"` - Notification *fcm.Notification `json:"notification,omitempty"` - - // iOS - Expiration *int64 `json:"expiration,omitempty"` - ApnsID string `json:"apns_id,omitempty"` - CollapseID string `json:"collapse_id,omitempty"` - Topic string `json:"topic,omitempty"` - PushType string `json:"push_type,omitempty"` - Badge *int `json:"badge,omitempty"` - Category string `json:"category,omitempty"` - ThreadID string `json:"thread-id,omitempty"` - URLArgs []string `json:"url-args,omitempty"` - Alert Alert `json:"alert,omitempty"` - Production bool `json:"production,omitempty"` - Development bool `json:"development,omitempty"` - SoundName string `json:"name,omitempty"` - SoundVolume float32 `json:"volume,omitempty"` - Apns D `json:"apns,omitempty"` + structs.PushNotification } // WaitDone decrements the WaitGroup counter. @@ -121,7 +49,7 @@ func (p *PushNotification) AddLog(log LogPushEntry) { // IsTopic check if message format is topic for FCM // ref: https://firebase.google.com/docs/cloud-messaging/send-message#topic-http-post-request func (p *PushNotification) IsTopic() bool { - return (p.Platform == PlatFormAndroid && p.To != "" && strings.HasPrefix(p.To, "/topics/")) || + return (p.Platform == consts.PlatFormAndroid && p.To != "" && strings.HasPrefix(p.To, "/topics/")) || p.Condition != "" } @@ -136,20 +64,20 @@ func CheckMessage(req PushNotification) error { return errors.New(msg) } - if len(req.Tokens) == PlatFormIos && len(req.Tokens[0]) == 0 { + if len(req.Tokens) == 1 && len(req.Tokens[0]) == 0 { msg = "the token must not be empty" LogAccess.Debug(msg) return errors.New(msg) } - if req.Platform == PlatFormAndroid && len(req.Tokens) > 1000 { + if req.Platform == consts.PlatFormAndroid && len(req.Tokens) > 1000 { msg = "the message may specify at most 1000 registration IDs" LogAccess.Debug(msg) return errors.New(msg) } // ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref - if req.Platform == PlatFormAndroid && req.TimeToLive != nil && (*req.TimeToLive < uint(0) || uint(2419200) < *req.TimeToLive) { + if req.Platform == consts.PlatFormAndroid && req.TimeToLive != nil && (*req.TimeToLive < uint(0) || uint(2419200) < *req.TimeToLive) { msg = "the message's TimeToLive field must be an integer " + "between 0 and 2419200 (4 weeks)" LogAccess.Debug(msg) diff --git a/gorush/notification_apns.go b/gorush/notification_apns.go index 5815c3451..1e56464b4 100644 --- a/gorush/notification_apns.go +++ b/gorush/notification_apns.go @@ -11,6 +11,8 @@ import ( "sync" "time" + "github.com/appleboy/gorush/gorush/consts" + "github.com/mitchellh/mapstructure" "github.com/sideshow/apns2" "github.com/sideshow/apns2/certificate" @@ -273,9 +275,9 @@ func GetIOSNotification(req PushNotification) *apns2.Notification { } if len(req.Priority) > 0 { - if req.Priority == "normal" { + if req.Priority == consts.PriorityNormal { notification.Priority = apns2.PriorityLow - } else if req.Priority == "high" { + } else if req.Priority == consts.PriorityHigh { notification.Priority = apns2.PriorityHigh } } @@ -399,17 +401,17 @@ Retry: err = errors.New(res.Reason) } // apns server error - LogPush(FailedPush, token, req, err) + LogPush(consts.FailedPush, token, req, err) if PushConf.Core.Sync { - req.AddLog(getLogPushEntry(FailedPush, token, req, err)) + req.AddLog(getLogPushEntry(consts.FailedPush, token, req, err)) } else if PushConf.Core.FeedbackURL != "" { go func(logger *logrus.Logger, log LogPushEntry, url string, timeout int64) { err := DispatchFeedback(log, url, timeout) if err != nil { logger.Error(err) } - }(LogError, getLogPushEntry(FailedPush, token, req, err), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout) + }(LogError, getLogPushEntry(consts.FailedPush, token, req, err), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout) } StatStorage.AddIosError(1) @@ -422,7 +424,7 @@ Retry: } if res.Sent() && !isError { - LogPush(SucceededPush, token, req, nil) + LogPush(consts.SucceededPush, token, req, nil) StatStorage.AddIosSuccess(1) } // free push slot diff --git a/gorush/notification_apns_test.go b/gorush/notification_apns_test.go index 35a0325ce..24826be8c 100644 --- a/gorush/notification_apns_test.go +++ b/gorush/notification_apns_test.go @@ -11,6 +11,9 @@ import ( "time" "github.com/appleboy/gorush/config" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" + "github.com/buger/jsonparser" "github.com/sideshow/apns2" "github.com/stretchr/testify/assert" @@ -60,24 +63,26 @@ func TestIOSNotificationStructure(t *testing.T) { message := "Welcome notification Server" expiration := int64(time.Now().Unix()) req := PushNotification{ - ApnsID: test, - Topic: test, - Expiration: &expiration, - Priority: "normal", - Message: message, - Badge: &expectBadge, - Sound: Sound{ - Critical: 1, - Name: test, - Volume: 1.0, - }, - ContentAvailable: true, - Data: D{ - "key1": "test", - "key2": 2, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Expiration: &expiration, + Priority: consts.PriorityNormal, + Message: message, + Badge: &expectBadge, + Sound: Sound{ + Critical: 1, + Name: test, + Volume: 1.0, + }, + ContentAvailable: true, + Data: structs.D{ + "key1": "test", + "key2": 2, + }, + Category: test, + URLArgs: []string{"a", "b"}, }, - Category: test, - URLArgs: []string{"a", "b"}, } notification := GetIOSNotification(req) @@ -104,7 +109,7 @@ func TestIOSNotificationStructure(t *testing.T) { assert.Equal(t, test, notification.ApnsID) assert.Equal(t, test, notification.Topic) assert.Equal(t, unix, notification.Expiration.Unix()) - assert.Equal(t, ApnsPriorityLow, notification.Priority) + assert.Equal(t, consts.ApnsPriorityLow, notification.Priority) assert.Equal(t, message, alert) assert.Equal(t, expectBadge, int(badge)) assert.Equal(t, expectBadge, *req.Badge) @@ -125,14 +130,16 @@ func TestIOSSoundAndVolume(t *testing.T) { test := "test" message := "Welcome notification Server" req := PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Message: message, - Sound: Sound{ - Critical: 3, - Name: test, - Volume: 4.5, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Message: message, + Sound: Sound{ + Critical: 3, + Name: test, + Volume: 4.5, + }, }, } @@ -152,7 +159,7 @@ func TestIOSSoundAndVolume(t *testing.T) { assert.Equal(t, test, notification.ApnsID) assert.Equal(t, test, notification.Topic) - assert.Equal(t, ApnsPriorityLow, notification.Priority) + assert.Equal(t, consts.ApnsPriorityLow, notification.Priority) assert.Equal(t, message, alert) assert.Equal(t, test, soundName) assert.Equal(t, 4.5, soundVolume) @@ -176,14 +183,16 @@ func TestIOSSoundAndVolume(t *testing.T) { assert.Equal(t, "foobar", soundName) req = PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Message: message, - Sound: map[string]interface{}{ - "critical": 3, - "name": "test", - "volume": 4.5, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Message: message, + Sound: map[string]interface{}{ + "critical": 3, + "name": "test", + "volume": 4.5, + }, }, } @@ -203,11 +212,13 @@ func TestIOSSoundAndVolume(t *testing.T) { assert.Equal(t, "test", soundName) req = PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Message: message, - Sound: "default", + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Message: message, + Sound: "default", + }, } notification = GetIOSNotification(req) @@ -228,13 +239,15 @@ func TestIOSSummaryArg(t *testing.T) { test := "test" message := "Welcome notification Server" req := PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Message: message, - Alert: Alert{ - SummaryArg: "test", - SummaryArgCount: 3, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Message: message, + Alert: structs.Alert{ + SummaryArg: "test", + SummaryArgCount: 3, + }, }, } @@ -249,7 +262,7 @@ func TestIOSSummaryArg(t *testing.T) { assert.Equal(t, test, notification.ApnsID) assert.Equal(t, test, notification.Topic) - assert.Equal(t, ApnsPriorityLow, notification.Priority) + assert.Equal(t, consts.ApnsPriorityLow, notification.Priority) assert.Equal(t, "test", dat["aps"].(map[string]interface{})["alert"].(map[string]interface{})["summary-arg"]) assert.Equal(t, float64(3), dat["aps"].(map[string]interface{})["alert"].(map[string]interface{})["summary-arg-count"]) } @@ -262,14 +275,16 @@ func TestSendZeroValueForBadgeKey(t *testing.T) { test := "test" message := "Welcome notification Server" req := PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Message: message, - Sound: test, - ContentAvailable: true, - MutableContent: true, - ThreadID: test, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Message: message, + Sound: test, + ContentAvailable: true, + MutableContent: true, + ThreadID: test, + }, } notification := GetIOSNotification(req) @@ -295,7 +310,7 @@ func TestSendZeroValueForBadgeKey(t *testing.T) { assert.Equal(t, test, notification.ApnsID) assert.Equal(t, test, notification.Topic) - assert.Equal(t, ApnsPriorityLow, notification.Priority) + assert.Equal(t, consts.ApnsPriorityLow, notification.Priority) assert.Equal(t, message, alert) assert.Equal(t, 0, int(badge)) assert.Equal(t, test, sound) @@ -335,11 +350,13 @@ func TestCheckSilentNotification(t *testing.T) { test := "test" req := PushNotification{ - ApnsID: test, - Topic: test, - CollapseID: test, - Priority: "normal", - ContentAvailable: true, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + CollapseID: test, + Priority: consts.PriorityNormal, + ContentAvailable: true, + }, } notification := GetIOSNotification(req) @@ -382,13 +399,15 @@ func TestAlertStringExample2ForIos(t *testing.T) { body := "Bob wants to play poker" actionLocKey := "PLAY" req := PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - Alert: Alert{ - Title: title, - Body: body, - ActionLocKey: actionLocKey, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + Alert: structs.Alert{ + Title: title, + Body: body, + ActionLocKey: actionLocKey, + }, }, } @@ -425,13 +444,15 @@ func TestAlertStringExample3ForIos(t *testing.T) { badge := 9 sound := "bingbong.aiff" req := PushNotification{ - ApnsID: test, - Topic: test, - Priority: "normal", - ContentAvailable: true, - Message: test, - Badge: &badge, - Sound: sound, + PushNotification: structs.PushNotification{ + ApnsID: test, + Topic: test, + Priority: consts.PriorityNormal, + ContentAvailable: true, + Message: test, + Badge: &badge, + Sound: sound, + }, } notification := GetIOSNotification(req) @@ -454,18 +475,20 @@ func TestIOSAlertNotificationStructure(t *testing.T) { test := "test" req := PushNotification{ - Message: "Welcome", - Title: test, - Alert: Alert{ - Action: test, - ActionLocKey: test, - Body: test, - LaunchImage: test, - LocArgs: []string{"a", "b"}, - LocKey: test, - Subtitle: test, - TitleLocArgs: []string{"a", "b"}, - TitleLocKey: test, + PushNotification: structs.PushNotification{ + Message: "Welcome", + Title: test, + Alert: structs.Alert{ + Action: test, + ActionLocKey: test, + Body: test, + LaunchImage: test, + LocArgs: []string{"a", "b"}, + LocKey: test, + Subtitle: test, + TitleLocArgs: []string{"a", "b"}, + TitleLocKey: test, + }, }, } @@ -524,15 +547,19 @@ func TestDisabledIosNotifications(t *testing.T) { Notifications: []PushNotification{ //ios { - Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, - Platform: PlatFormIos, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, + Platform: consts.PlatFormIos, + Message: "Welcome", + }, }, // android { - Tokens: []string{androidToken, androidToken + "_"}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken, androidToken + "_"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, }, }, } @@ -706,9 +733,11 @@ func TestPushToIOS(t *testing.T) { assert.Nil(t, err) req := PushNotification{ - Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, - Platform: 1, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, + Platform: 1, + Message: "Welcome", + }, } // send fail @@ -727,13 +756,17 @@ func TestApnsHostFromRequest(t *testing.T) { assert.Nil(t, err) req := PushNotification{ - Production: true, + PushNotification: structs.PushNotification{ + Production: true, + }, } client := getApnsClient(req) assert.Equal(t, apns2.HostProduction, client.Host) req = PushNotification{ - Development: true, + PushNotification: structs.PushNotification{ + Development: true, + }, } client = getApnsClient(req) assert.Equal(t, apns2.HostDevelopment, client.Host) diff --git a/gorush/notification_fcm.go b/gorush/notification_fcm.go index 1d08c035d..ccbd40541 100644 --- a/gorush/notification_fcm.go +++ b/gorush/notification_fcm.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/go-fcm" "github.com/sirupsen/logrus" ) @@ -48,8 +50,8 @@ func GetAndroidNotification(req PushNotification) *fcm.Message { notification.RegistrationIDs = req.Tokens } - if len(req.Priority) > 0 && req.Priority == "high" { - notification.Priority = "high" + if len(req.Priority) > 0 && req.Priority == consts.PriorityHigh { + notification.Priority = string(consts.PriorityHigh) } // Add another field @@ -171,21 +173,21 @@ Retry: } isError = true - LogPush(FailedPush, to, req, result.Error) + LogPush(consts.FailedPush, to, req, result.Error) if PushConf.Core.Sync { - req.AddLog(getLogPushEntry(FailedPush, to, req, result.Error)) + req.AddLog(getLogPushEntry(consts.FailedPush, to, req, result.Error)) } else if PushConf.Core.FeedbackURL != "" { go func(logger *logrus.Logger, log LogPushEntry, url string, timeout int64) { err := DispatchFeedback(log, url, timeout) if err != nil { logger.Error(err) } - }(LogError, getLogPushEntry(FailedPush, to, req, result.Error), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout) + }(LogError, getLogPushEntry(consts.FailedPush, to, req, result.Error), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout) } continue } - LogPush(SucceededPush, to, req, nil) + LogPush(consts.SucceededPush, to, req, nil) } // result from Send messages to topics @@ -199,13 +201,13 @@ Retry: LogAccess.Debug("Send Topic Message: ", to) // Success if res.MessageID != 0 { - LogPush(SucceededPush, to, req, nil) + LogPush(consts.SucceededPush, to, req, nil) } else { isError = true // failure - LogPush(FailedPush, to, req, res.Error) + LogPush(consts.FailedPush, to, req, res.Error) if PushConf.Core.Sync { - req.AddLog(getLogPushEntry(FailedPush, to, req, res.Error)) + req.AddLog(getLogPushEntry(consts.FailedPush, to, req, res.Error)) } } } @@ -215,9 +217,9 @@ Retry: isError = true newTokens = append(newTokens, res.FailedRegistrationIDs...) - LogPush(FailedPush, notification.To, req, errors.New("device group: partial success or all fails")) + LogPush(consts.FailedPush, notification.To, req, errors.New("device group: partial success or all fails")) if PushConf.Core.Sync { - req.AddLog(getLogPushEntry(FailedPush, notification.To, req, errors.New("device group: partial success or all fails"))) + req.AddLog(getLogPushEntry(consts.FailedPush, notification.To, req, errors.New("device group: partial success or all fails"))) } } diff --git a/gorush/notification_fcm_test.go b/gorush/notification_fcm_test.go index 23494afb5..a9e12db4d 100644 --- a/gorush/notification_fcm_test.go +++ b/gorush/notification_fcm_test.go @@ -7,8 +7,11 @@ import ( "sync" "testing" - "github.com/appleboy/go-fcm" "github.com/appleboy/gorush/config" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" + + "github.com/appleboy/go-fcm" "github.com/stretchr/testify/assert" ) @@ -54,9 +57,11 @@ func TestPushToAndroidWrongToken(t *testing.T) { PushConf.Android.APIKey = os.Getenv("ANDROID_API_KEY") req := PushNotification{ - Tokens: []string{"aaaaaa", "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"aaaaaa", "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, } // Android Success count: 0, Failure count: 2 @@ -75,9 +80,11 @@ func TestPushToAndroidRightTokenForJSONLog(t *testing.T) { androidToken := os.Getenv("ANDROID_TEST_TOKEN") req := PushNotification{ - Tokens: []string{androidToken}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, } isError := PushToAndroid(req) @@ -93,9 +100,11 @@ func TestPushToAndroidRightTokenForStringLog(t *testing.T) { androidToken := os.Getenv("ANDROID_TEST_TOKEN") req := PushNotification{ - Tokens: []string{androidToken}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, } isError := PushToAndroid(req) @@ -111,11 +120,13 @@ func TestOverwriteAndroidAPIKey(t *testing.T) { androidToken := os.Getenv("ANDROID_TEST_TOKEN") req := PushNotification{ - Tokens: []string{androidToken, "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", - // overwrite android api key - APIKey: "1234", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken, "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + // overwrite android api key + APIKey: "1234", + }, } // FCM server error: 401 error: 401 Unauthorized (Wrong API Key) @@ -129,8 +140,10 @@ func TestFCMMessage(t *testing.T) { // the message must specify at least one registration ID req = PushNotification{ - Message: "Test", - Tokens: []string{}, + PushNotification: structs.PushNotification{ + Message: "Test", + Tokens: []string{}, + }, } err = CheckMessage(req) @@ -138,8 +151,10 @@ func TestFCMMessage(t *testing.T) { // the token must not be empty req = PushNotification{ - Message: "Test", - Tokens: []string{""}, + PushNotification: structs.PushNotification{ + Message: "Test", + Tokens: []string{""}, + }, } err = CheckMessage(req) @@ -147,9 +162,11 @@ func TestFCMMessage(t *testing.T) { // ignore check token length if send topic message req = PushNotification{ - Message: "Test", - Platform: PlatFormAndroid, - To: "/topics/foo-bar", + PushNotification: structs.PushNotification{ + Message: "Test", + Platform: consts.PlatFormAndroid, + To: "/topics/foo-bar", + }, } err = CheckMessage(req) @@ -157,9 +174,11 @@ func TestFCMMessage(t *testing.T) { // "condition": "'dogs' in topics || 'cats' in topics", req = PushNotification{ - Message: "Test", - Platform: PlatFormAndroid, - Condition: "'dogs' in topics || 'cats' in topics", + PushNotification: structs.PushNotification{ + Message: "Test", + Platform: consts.PlatFormAndroid, + Condition: "'dogs' in topics || 'cats' in topics", + }, } err = CheckMessage(req) @@ -167,9 +186,11 @@ func TestFCMMessage(t *testing.T) { // the message may specify at most 1000 registration IDs req = PushNotification{ - Message: "Test", - Platform: PlatFormAndroid, - Tokens: make([]string, 1001), + PushNotification: structs.PushNotification{ + Message: "Test", + Platform: consts.PlatFormAndroid, + Tokens: make([]string, 1001), + }, } err = CheckMessage(req) @@ -179,10 +200,12 @@ func TestFCMMessage(t *testing.T) { // between 0 and 2419200 (4 weeks) timeToLive := uint(2419201) req = PushNotification{ - Message: "Test", - Platform: PlatFormAndroid, - Tokens: []string{"XXXXXXXXX"}, - TimeToLive: &timeToLive, + PushNotification: structs.PushNotification{ + Message: "Test", + Platform: consts.PlatFormAndroid, + Tokens: []string{"XXXXXXXXX"}, + TimeToLive: &timeToLive, + }, } err = CheckMessage(req) @@ -191,10 +214,12 @@ func TestFCMMessage(t *testing.T) { // Pass timeToLive = uint(86400) req = PushNotification{ - Message: "Test", - Platform: PlatFormAndroid, - Tokens: []string{"XXXXXXXXX"}, - TimeToLive: &timeToLive, + PushNotification: structs.PushNotification{ + Message: "Test", + Platform: consts.PlatFormAndroid, + Tokens: []string{"XXXXXXXXX"}, + TimeToLive: &timeToLive, + }, } err = CheckMessage(req) @@ -209,10 +234,12 @@ func TestCheckAndroidMessage(t *testing.T) { timeToLive := uint(2419201) req := PushNotification{ - Tokens: []string{"aaaaaa", "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", - TimeToLive: &timeToLive, + PushNotification: structs.PushNotification{ + Tokens: []string{"aaaaaa", "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + TimeToLive: &timeToLive, + }, } err := PushToAndroid(req) @@ -220,30 +247,31 @@ func TestCheckAndroidMessage(t *testing.T) { } func TestAndroidNotificationStructure(t *testing.T) { - test := "test" timeToLive := uint(100) req := PushNotification{ - Tokens: []string{"a", "b"}, - Message: "Welcome", - To: test, - Priority: "high", - CollapseKey: "1", - ContentAvailable: true, - DelayWhileIdle: true, - TimeToLive: &timeToLive, - RestrictedPackageName: test, - DryRun: true, - Title: test, - Sound: test, - Data: D{ - "a": "1", - "b": 2, - }, - Notification: &fcm.Notification{ - Color: test, - Tag: test, - Body: "", + PushNotification: structs.PushNotification{ + Tokens: []string{"a", "b"}, + Message: "Welcome", + To: test, + Priority: consts.PriorityHigh, + CollapseKey: "1", + ContentAvailable: true, + DelayWhileIdle: true, + TimeToLive: &timeToLive, + RestrictedPackageName: test, + DryRun: true, + Title: test, + Sound: test, + Data: structs.D{ + "a": "1", + "b": 2, + }, + Notification: &fcm.Notification{ + Color: test, + Tag: test, + Body: "", + }, }, } @@ -267,10 +295,12 @@ func TestAndroidNotificationStructure(t *testing.T) { // test empty body req = PushNotification{ - Tokens: []string{"a", "b"}, - To: test, - Notification: &fcm.Notification{ - Body: "", + PushNotification: structs.PushNotification{ + Tokens: []string{"a", "b"}, + To: test, + Notification: &fcm.Notification{ + Body: "", + }, }, } notification = GetAndroidNotification(req) diff --git a/gorush/notification_test.go b/gorush/notification_test.go index c7b41caf2..658e4fd39 100644 --- a/gorush/notification_test.go +++ b/gorush/notification_test.go @@ -6,6 +6,9 @@ import ( "testing" "github.com/appleboy/gorush/config" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" + "github.com/stretchr/testify/assert" ) @@ -41,15 +44,19 @@ func TestSenMultipleNotifications(t *testing.T) { Notifications: []PushNotification{ //ios { - Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, - Platform: PlatFormIos, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, + Platform: consts.PlatFormIos, + Message: "Welcome", + }, }, // android { - Tokens: []string{androidToken, "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken, "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, }, }, } @@ -77,15 +84,19 @@ func TestDisabledAndroidNotifications(t *testing.T) { Notifications: []PushNotification{ //ios { - Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, - Platform: PlatFormIos, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, + Platform: consts.PlatFormIos, + Message: "Welcome", + }, }, // android { - Tokens: []string{androidToken, "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken, "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, }, }, } @@ -116,15 +127,19 @@ func TestSyncModeForNotifications(t *testing.T) { Notifications: []PushNotification{ //ios { - Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, - Platform: PlatFormIos, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, + Platform: consts.PlatFormIos, + Message: "Welcome", + }, }, // android { - Tokens: []string{androidToken, "bbbbb"}, - Platform: PlatFormAndroid, - Message: "Welcome", + PushNotification: structs.PushNotification{ + Tokens: []string{androidToken, "bbbbb"}, + Platform: consts.PlatFormAndroid, + Message: "Welcome", + }, }, }, } @@ -149,25 +164,31 @@ func TestSyncModeForTopicNotification(t *testing.T) { Notifications: []PushNotification{ // android { - // error:InvalidParameters - // Check that the provided parameters have the right name and type. - To: "/topics/foo-bar@@@##", - Platform: PlatFormAndroid, - Message: "This is a Firebase Cloud Messaging Topic Message!", + PushNotification: structs.PushNotification{ + // error:InvalidParameters + // Check that the provided parameters have the right name and type. + To: "/topics/foo-bar@@@##", + Platform: consts.PlatFormAndroid, + Message: "This is a Firebase Cloud Messaging Topic Message!", + }, }, // android { - // success - To: "/topics/foo-bar", - Platform: PlatFormAndroid, - Message: "This is a Firebase Cloud Messaging Topic Message!", + PushNotification: structs.PushNotification{ + // success + To: "/topics/foo-bar", + Platform: consts.PlatFormAndroid, + Message: "This is a Firebase Cloud Messaging Topic Message!", + }, }, // android { - // success - Condition: "'dogs' in topics || 'cats' in topics", - Platform: PlatFormAndroid, - Message: "This is a Firebase Cloud Messaging Topic Message!", + PushNotification: structs.PushNotification{ + // success + Condition: "'dogs' in topics || 'cats' in topics", + Platform: consts.PlatFormAndroid, + Message: "This is a Firebase Cloud Messaging Topic Message!", + }, }, }, } @@ -192,9 +213,11 @@ func TestSyncModeForDeviceGroupNotification(t *testing.T) { Notifications: []PushNotification{ // android { - To: "aUniqueKey", - Platform: PlatFormAndroid, - Message: "This is a Firebase Cloud Messaging Device Group Message!", + PushNotification: structs.PushNotification{ + To: "aUniqueKey", + Platform: consts.PlatFormAndroid, + Message: "This is a Firebase Cloud Messaging Device Group Message!", + }, }, }, } diff --git a/gorush/server_test.go b/gorush/server_test.go index e8b8b8e67..493cedf59 100644 --- a/gorush/server_test.go +++ b/gorush/server_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/appleboy/gorush/config" + "github.com/appleboy/gorush/gorush/consts" "github.com/appleboy/gofight/v2" "github.com/buger/jsonparser" @@ -257,7 +258,7 @@ func TestMutableContent(t *testing.T) { "notifications": []gofight.D{ { "tokens": []string{"aaaaa", "bbbbb"}, - "platform": PlatFormAndroid, + "platform": consts.PlatFormAndroid, "message": "Welcome", "mutable_content": 1, "topic": "test", @@ -288,12 +289,12 @@ func TestOutOfRangeMaxNotifications(t *testing.T) { "notifications": []gofight.D{ { "tokens": []string{"aaaaa", "bbbbb"}, - "platform": PlatFormAndroid, + "platform": consts.PlatFormAndroid, "message": "Welcome", }, { "tokens": []string{"aaaaa", "bbbbb"}, - "platform": PlatFormAndroid, + "platform": consts.PlatFormAndroid, "message": "Welcome", }, }, @@ -320,7 +321,7 @@ func TestSuccessPushHandler(t *testing.T) { "notifications": []gofight.D{ { "tokens": []string{androidToken, "bbbbb"}, - "platform": PlatFormAndroid, + "platform": consts.PlatFormAndroid, "message": "Welcome", }, }, diff --git a/gorush/structs/structs.go b/gorush/structs/structs.go new file mode 100644 index 000000000..184703ee0 --- /dev/null +++ b/gorush/structs/structs.go @@ -0,0 +1,79 @@ +package structs + +import ( + "github.com/appleboy/go-fcm" +) + +// D provide string array +type D map[string]interface{} + +// Alert is APNs payload +type Alert struct { + Action string `json:"action,omitempty"` + ActionLocKey string `json:"action-loc-key,omitempty"` + Body string `json:"body,omitempty"` + LaunchImage string `json:"launch-image,omitempty"` + LocArgs []string `json:"loc-args,omitempty"` + LocKey string `json:"loc-key,omitempty"` + Title string `json:"title,omitempty"` + Subtitle string `json:"subtitle,omitempty"` + TitleLocArgs []string `json:"title-loc-args,omitempty"` + TitleLocKey string `json:"title-loc-key,omitempty"` + SummaryArg string `json:"summary-arg,omitempty"` + SummaryArgCount int `json:"summary-arg-count,omitempty"` +} + +// PushNotification is single notification request +type PushNotification struct { + // Common + ID string `json:"notif_id,omitempty"` + Tokens []string `json:"tokens" binding:"required"` + Platform Platform `json:"platform" binding:"required"` + Message string `json:"message,omitempty"` + Title string `json:"title,omitempty"` + Image string `json:"image,omitempty"` + Priority Priority `json:"priority,omitempty"` + ContentAvailable bool `json:"content_available,omitempty"` + MutableContent bool `json:"mutable_content,omitempty"` + Sound interface{} `json:"sound,omitempty"` + Data D `json:"data,omitempty"` + Retry int `json:"retry,omitempty"` + + // Android + APIKey string `json:"api_key,omitempty"` + To string `json:"to,omitempty"` + CollapseKey string `json:"collapse_key,omitempty"` + DelayWhileIdle bool `json:"delay_while_idle,omitempty"` + TimeToLive *uint `json:"time_to_live,omitempty"` + RestrictedPackageName string `json:"restricted_package_name,omitempty"` + DryRun bool `json:"dry_run,omitempty"` + Condition string `json:"condition,omitempty"` + Notification *fcm.Notification `json:"notification,omitempty"` + + // iOS + Expiration *int64 `json:"expiration,omitempty"` + ApnsID string `json:"apns_id,omitempty"` + CollapseID string `json:"collapse_id,omitempty"` + Topic string `json:"topic,omitempty"` + PushType APNSPushType `json:"push_type,omitempty"` + Badge *int `json:"badge,omitempty"` + Category string `json:"category,omitempty"` + ThreadID string `json:"thread-id,omitempty"` + URLArgs []string `json:"url-args,omitempty"` + Alert Alert `json:"alert,omitempty"` + Production bool `json:"production,omitempty"` + Development bool `json:"development,omitempty"` + SoundName string `json:"name,omitempty"` + SoundVolume float32 `json:"volume,omitempty"` + Apns D `json:"apns,omitempty"` +} + +type ( + // Platform push notification receiver's platform (iOS/Android) + Platform int + // Priority push notification's priority + Priority string + + // APNSPushType is an iOS13 required field describing the type of push notification + APNSPushType string +) diff --git a/gorush/worker.go b/gorush/worker.go index 6eb608183..567fc9b5c 100644 --- a/gorush/worker.go +++ b/gorush/worker.go @@ -4,6 +4,8 @@ import ( "context" "errors" "sync" + + "github.com/appleboy/gorush/gorush/consts" ) // InitWorkers for initialize all workers. @@ -22,9 +24,9 @@ func SendNotification(ctx context.Context, req PushNotification) { } switch req.Platform { - case PlatFormIos: + case consts.PlatFormIos: PushToIOS(req) - case PlatFormAndroid: + case consts.PlatFormAndroid: PushToAndroid(req) } } @@ -41,7 +43,7 @@ func startWorker(ctx context.Context, wg *sync.WaitGroup, num int64) { func markFailedNotification(notification *PushNotification, reason string) { LogError.Error(reason) for _, token := range notification.Tokens { - notification.AddLog(getLogPushEntry(FailedPush, token, *notification, errors.New(reason))) + notification.AddLog(getLogPushEntry(consts.FailedPush, token, *notification, errors.New(reason))) } notification.WaitDone() } @@ -54,11 +56,11 @@ func queueNotification(ctx context.Context, req RequestPush) (int, []LogPushEntr for i := range req.Notifications { notification := &req.Notifications[i] switch notification.Platform { - case PlatFormIos: + case consts.PlatFormIos: if !PushConf.Ios.Enabled { continue } - case PlatFormAndroid: + case consts.PlatFormAndroid: if !PushConf.Android.Enabled { continue } diff --git a/main.go b/main.go index bee755a2c..75ef8f6c5 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "context" "flag" "fmt" + "github.com/appleboy/gorush/gorush/consts" + "github.com/appleboy/gorush/gorush/structs" "log" "net" "net/http" @@ -172,9 +174,11 @@ func main() { if opts.Android.Enabled { gorush.PushConf.Android.Enabled = opts.Android.Enabled req := gorush.PushNotification{ - Platform: gorush.PlatFormAndroid, - Message: message, - Title: title, + PushNotification: structs.PushNotification{ + Platform: consts.PlatFormAndroid, + Message: message, + Title: title, + }, } // send message to single device @@ -210,9 +214,11 @@ func main() { gorush.PushConf.Ios.Enabled = opts.Ios.Enabled req := gorush.PushNotification{ - Platform: gorush.PlatFormIos, - Message: message, - Title: title, + PushNotification: structs.PushNotification{ + Platform: consts.PlatFormIos, + Message: message, + Title: title, + }, } // send message to single device diff --git a/rpc/server.go b/rpc/server.go index aedb82049..8a277c7a0 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/appleboy/gorush/gorush" + "github.com/appleboy/gorush/gorush/structs" "github.com/appleboy/gorush/rpc/proto" "google.golang.org/grpc" @@ -50,18 +51,20 @@ func (s *Server) Check(ctx context.Context, in *proto.HealthCheckRequest) (*prot func (s *Server) Send(ctx context.Context, in *proto.NotificationRequest) (*proto.NotificationReply, error) { var badge = int(in.Badge) notification := gorush.PushNotification{ - Platform: int(in.Platform), - Tokens: in.Tokens, - Message: in.Message, - Title: in.Title, - Topic: in.Topic, - APIKey: in.Key, - Category: in.Category, - Sound: in.Sound, - ContentAvailable: in.ContentAvailable, - ThreadID: in.ThreadID, - MutableContent: in.MutableContent, - Image: in.Image, + PushNotification: structs.PushNotification{ + Platform: structs.Platform(in.Platform), + Tokens: in.Tokens, + Message: in.Message, + Title: in.Title, + Topic: in.Topic, + APIKey: in.Key, + Category: in.Category, + Sound: in.Sound, + ContentAvailable: in.ContentAvailable, + ThreadID: in.ThreadID, + MutableContent: in.MutableContent, + Image: in.Image, + }, } if badge > 0 { @@ -69,7 +72,7 @@ func (s *Server) Send(ctx context.Context, in *proto.NotificationRequest) (*prot } if in.Alert != nil { - notification.Alert = gorush.Alert{ + notification.Alert = structs.Alert{ Title: in.Alert.Title, Body: in.Alert.Body, Subtitle: in.Alert.Subtitle,