refactor(notification): separate ios and android (#250)
This commit is contained in:
parent
6a64b42ab0
commit
460b74d8a6
|
@ -2,18 +2,12 @@ package gorush
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/appleboy/go-fcm"
|
||||
apns "github.com/sideshow/apns2"
|
||||
"github.com/sideshow/apns2/certificate"
|
||||
"github.com/sideshow/apns2/payload"
|
||||
)
|
||||
|
||||
// D provide string array
|
||||
|
@ -182,402 +176,3 @@ func CheckPushConf() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitAPNSClient use for initialize APNs Client.
|
||||
func InitAPNSClient() error {
|
||||
if PushConf.Ios.Enabled {
|
||||
var err error
|
||||
ext := filepath.Ext(PushConf.Ios.KeyPath)
|
||||
|
||||
switch ext {
|
||||
case ".p12":
|
||||
CertificatePemIos, err = certificate.FromP12File(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
||||
case ".pem":
|
||||
CertificatePemIos, err = certificate.FromPemFile(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
||||
default:
|
||||
err = errors.New("wrong certificate key extension")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
LogError.Error("Cert Error:", err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if PushConf.Ios.Production {
|
||||
ApnsClient = apns.NewClient(CertificatePemIos).Production()
|
||||
} else {
|
||||
ApnsClient = apns.NewClient(CertificatePemIos).Development()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitWorkers for initialize all workers.
|
||||
func InitWorkers(workerNum int64, queueNum int64) {
|
||||
LogAccess.Debug("worker number is ", workerNum, ", queue number is ", queueNum)
|
||||
QueueNotification = make(chan PushNotification, queueNum)
|
||||
for i := int64(0); i < workerNum; i++ {
|
||||
go startWorker()
|
||||
}
|
||||
}
|
||||
|
||||
func startWorker() {
|
||||
for {
|
||||
notification := <-QueueNotification
|
||||
switch notification.Platform {
|
||||
case PlatFormIos:
|
||||
PushToIOS(notification)
|
||||
case PlatFormAndroid:
|
||||
PushToAndroid(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// queueNotification add notification to queue list.
|
||||
func queueNotification(req RequestPush) (int, []LogPushEntry) {
|
||||
var count int
|
||||
wg := sync.WaitGroup{}
|
||||
newNotification := []PushNotification{}
|
||||
for _, notification := range req.Notifications {
|
||||
switch notification.Platform {
|
||||
case PlatFormIos:
|
||||
if !PushConf.Ios.Enabled {
|
||||
continue
|
||||
}
|
||||
case PlatFormAndroid:
|
||||
if !PushConf.Android.Enabled {
|
||||
continue
|
||||
}
|
||||
}
|
||||
newNotification = append(newNotification, notification)
|
||||
}
|
||||
|
||||
log := make([]LogPushEntry, 0, count)
|
||||
for _, notification := range newNotification {
|
||||
if PushConf.Core.Sync {
|
||||
notification.wg = &wg
|
||||
notification.log = &log
|
||||
notification.AddWaitCount()
|
||||
}
|
||||
QueueNotification <- notification
|
||||
count += len(notification.Tokens)
|
||||
}
|
||||
|
||||
if PushConf.Core.Sync {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
StatStorage.AddTotalCount(int64(count))
|
||||
|
||||
return count, log
|
||||
}
|
||||
|
||||
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
||||
// Alert dictionary
|
||||
|
||||
if len(req.Title) > 0 {
|
||||
payload.AlertTitle(req.Title)
|
||||
}
|
||||
|
||||
if len(req.Alert.Title) > 0 {
|
||||
payload.AlertTitle(req.Alert.Title)
|
||||
}
|
||||
|
||||
// Apple Watch & Safari display this string as part of the notification interface.
|
||||
if len(req.Alert.Subtitle) > 0 {
|
||||
payload.AlertSubtitle(req.Alert.Subtitle)
|
||||
}
|
||||
|
||||
if len(req.Alert.TitleLocKey) > 0 {
|
||||
payload.AlertTitleLocKey(req.Alert.TitleLocKey)
|
||||
}
|
||||
|
||||
if len(req.Alert.LocArgs) > 0 {
|
||||
payload.AlertLocArgs(req.Alert.LocArgs)
|
||||
}
|
||||
|
||||
if len(req.Alert.TitleLocArgs) > 0 {
|
||||
payload.AlertTitleLocArgs(req.Alert.TitleLocArgs)
|
||||
}
|
||||
|
||||
if len(req.Alert.Body) > 0 {
|
||||
payload.AlertBody(req.Alert.Body)
|
||||
}
|
||||
|
||||
if len(req.Alert.LaunchImage) > 0 {
|
||||
payload.AlertLaunchImage(req.Alert.LaunchImage)
|
||||
}
|
||||
|
||||
if len(req.Alert.LocKey) > 0 {
|
||||
payload.AlertLocKey(req.Alert.LocKey)
|
||||
}
|
||||
|
||||
if len(req.Alert.Action) > 0 {
|
||||
payload.AlertAction(req.Alert.Action)
|
||||
}
|
||||
|
||||
if len(req.Alert.ActionLocKey) > 0 {
|
||||
payload.AlertActionLocKey(req.Alert.ActionLocKey)
|
||||
}
|
||||
|
||||
// General
|
||||
|
||||
if len(req.Category) > 0 {
|
||||
payload.Category(req.Category)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
// GetIOSNotification use for define iOS notification.
|
||||
// The iOS Notification Payload
|
||||
// ref: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1
|
||||
func GetIOSNotification(req PushNotification) *apns.Notification {
|
||||
notification := &apns.Notification{
|
||||
ApnsID: req.ApnsID,
|
||||
Topic: req.Topic,
|
||||
}
|
||||
|
||||
if req.Expiration > 0 {
|
||||
notification.Expiration = time.Unix(req.Expiration, 0)
|
||||
}
|
||||
|
||||
if len(req.Priority) > 0 && req.Priority == "normal" {
|
||||
notification.Priority = apns.PriorityLow
|
||||
}
|
||||
|
||||
payload := payload.NewPayload()
|
||||
|
||||
// add alert object if message length > 0
|
||||
if len(req.Message) > 0 {
|
||||
payload.Alert(req.Message)
|
||||
}
|
||||
|
||||
// zero value for clear the badge on the app icon.
|
||||
if req.Badge != nil && *req.Badge >= 0 {
|
||||
payload.Badge(*req.Badge)
|
||||
}
|
||||
|
||||
if req.MutableContent {
|
||||
payload.MutableContent()
|
||||
}
|
||||
|
||||
if len(req.Sound) > 0 {
|
||||
payload.Sound(req.Sound)
|
||||
}
|
||||
|
||||
if req.ContentAvailable {
|
||||
payload.ContentAvailable()
|
||||
}
|
||||
|
||||
if len(req.URLArgs) > 0 {
|
||||
payload.URLArgs(req.URLArgs)
|
||||
}
|
||||
|
||||
for k, v := range req.Data {
|
||||
payload.Custom(k, v)
|
||||
}
|
||||
|
||||
payload = iosAlertDictionary(payload, req)
|
||||
|
||||
notification.Payload = payload
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
// PushToIOS provide send notification to APNs server.
|
||||
func PushToIOS(req PushNotification) bool {
|
||||
LogAccess.Debug("Start push notification for iOS")
|
||||
if PushConf.Core.Sync {
|
||||
defer req.WaitDone()
|
||||
}
|
||||
|
||||
var (
|
||||
retryCount = 0
|
||||
maxRetry = PushConf.Ios.MaxRetry
|
||||
)
|
||||
|
||||
if req.Retry > 0 && req.Retry < maxRetry {
|
||||
maxRetry = req.Retry
|
||||
}
|
||||
|
||||
Retry:
|
||||
var (
|
||||
isError = false
|
||||
newTokens []string
|
||||
)
|
||||
|
||||
notification := GetIOSNotification(req)
|
||||
|
||||
for _, token := range req.Tokens {
|
||||
notification.DeviceToken = token
|
||||
|
||||
// send ios notification
|
||||
res, err := ApnsClient.Push(notification)
|
||||
|
||||
if err != nil {
|
||||
// apns server error
|
||||
LogPush(FailedPush, token, req, err)
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, token, req, err))
|
||||
}
|
||||
StatStorage.AddIosError(1)
|
||||
newTokens = append(newTokens, token)
|
||||
isError = true
|
||||
continue
|
||||
}
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
// error message:
|
||||
// ref: https://github.com/sideshow/apns2/blob/master/response.go#L14-L65
|
||||
LogPush(FailedPush, token, req, errors.New(res.Reason))
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, token, req, errors.New(res.Reason)))
|
||||
}
|
||||
StatStorage.AddIosError(1)
|
||||
newTokens = append(newTokens, token)
|
||||
isError = true
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Sent() {
|
||||
LogPush(SucceededPush, token, req, nil)
|
||||
StatStorage.AddIosSuccess(1)
|
||||
}
|
||||
}
|
||||
|
||||
if isError == true && retryCount < maxRetry {
|
||||
retryCount++
|
||||
|
||||
// resend fail token
|
||||
req.Tokens = newTokens
|
||||
goto Retry
|
||||
}
|
||||
|
||||
return isError
|
||||
}
|
||||
|
||||
// GetAndroidNotification use for define Android notification.
|
||||
// HTTP Connection Server Reference for Android
|
||||
// https://firebase.google.com/docs/cloud-messaging/http-server-ref
|
||||
func GetAndroidNotification(req PushNotification) *fcm.Message {
|
||||
notification := &fcm.Message{
|
||||
To: req.To,
|
||||
CollapseKey: req.CollapseKey,
|
||||
ContentAvailable: req.ContentAvailable,
|
||||
DelayWhileIdle: req.DelayWhileIdle,
|
||||
TimeToLive: req.TimeToLive,
|
||||
RestrictedPackageName: req.RestrictedPackageName,
|
||||
DryRun: req.DryRun,
|
||||
}
|
||||
|
||||
notification.RegistrationIDs = req.Tokens
|
||||
|
||||
if len(req.Priority) > 0 && req.Priority == "high" {
|
||||
notification.Priority = "high"
|
||||
}
|
||||
|
||||
// Add another field
|
||||
if len(req.Data) > 0 {
|
||||
notification.Data = make(map[string]interface{})
|
||||
for k, v := range req.Data {
|
||||
notification.Data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
notification.Notification = &req.Notification
|
||||
|
||||
// Set request message if body is empty
|
||||
if len(req.Message) > 0 {
|
||||
notification.Notification.Body = req.Message
|
||||
}
|
||||
|
||||
if len(req.Title) > 0 {
|
||||
notification.Notification.Title = req.Title
|
||||
}
|
||||
|
||||
if len(req.Sound) > 0 {
|
||||
notification.Notification.Sound = req.Sound
|
||||
}
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
// PushToAndroid provide send notification to Android server.
|
||||
func PushToAndroid(req PushNotification) bool {
|
||||
LogAccess.Debug("Start push notification for Android")
|
||||
if PushConf.Core.Sync {
|
||||
defer req.WaitDone()
|
||||
}
|
||||
|
||||
var (
|
||||
APIKey string
|
||||
retryCount = 0
|
||||
maxRetry = PushConf.Android.MaxRetry
|
||||
)
|
||||
|
||||
if req.Retry > 0 && req.Retry < maxRetry {
|
||||
maxRetry = req.Retry
|
||||
}
|
||||
|
||||
// check message
|
||||
err := CheckMessage(req)
|
||||
|
||||
if err != nil {
|
||||
LogError.Error("request error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
Retry:
|
||||
var isError = false
|
||||
notification := GetAndroidNotification(req)
|
||||
|
||||
if APIKey = PushConf.Android.APIKey; req.APIKey != "" {
|
||||
APIKey = req.APIKey
|
||||
}
|
||||
|
||||
client, err := fcm.NewClient(APIKey)
|
||||
if err != nil {
|
||||
// FCM server error
|
||||
LogError.Error("FCM server error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
res, err := client.Send(notification)
|
||||
if err != nil {
|
||||
// FCM server error
|
||||
LogError.Error("FCM server error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
|
||||
StatStorage.AddAndroidSuccess(int64(res.Success))
|
||||
StatStorage.AddAndroidError(int64(res.Failure))
|
||||
|
||||
var newTokens []string
|
||||
for k, result := range res.Results {
|
||||
if result.Error != nil {
|
||||
isError = true
|
||||
newTokens = append(newTokens, req.Tokens[k])
|
||||
LogPush(FailedPush, req.Tokens[k], req, result.Error)
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, req.Tokens[k], req, result.Error))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
LogPush(SucceededPush, req.Tokens[k], req, nil)
|
||||
}
|
||||
|
||||
if isError == true && retryCount < maxRetry {
|
||||
retryCount++
|
||||
|
||||
// resend fail token
|
||||
req.Tokens = newTokens
|
||||
goto Retry
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
package gorush
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
apns "github.com/sideshow/apns2"
|
||||
"github.com/sideshow/apns2/certificate"
|
||||
"github.com/sideshow/apns2/payload"
|
||||
)
|
||||
|
||||
// InitAPNSClient use for initialize APNs Client.
|
||||
func InitAPNSClient() error {
|
||||
if PushConf.Ios.Enabled {
|
||||
var err error
|
||||
ext := filepath.Ext(PushConf.Ios.KeyPath)
|
||||
|
||||
switch ext {
|
||||
case ".p12":
|
||||
CertificatePemIos, err = certificate.FromP12File(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
||||
case ".pem":
|
||||
CertificatePemIos, err = certificate.FromPemFile(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
||||
default:
|
||||
err = errors.New("wrong certificate key extension")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
LogError.Error("Cert Error:", err.Error())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if PushConf.Ios.Production {
|
||||
ApnsClient = apns.NewClient(CertificatePemIos).Production()
|
||||
} else {
|
||||
ApnsClient = apns.NewClient(CertificatePemIos).Development()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
||||
// Alert dictionary
|
||||
|
||||
if len(req.Title) > 0 {
|
||||
payload.AlertTitle(req.Title)
|
||||
}
|
||||
|
||||
if len(req.Alert.Title) > 0 {
|
||||
payload.AlertTitle(req.Alert.Title)
|
||||
}
|
||||
|
||||
// Apple Watch & Safari display this string as part of the notification interface.
|
||||
if len(req.Alert.Subtitle) > 0 {
|
||||
payload.AlertSubtitle(req.Alert.Subtitle)
|
||||
}
|
||||
|
||||
if len(req.Alert.TitleLocKey) > 0 {
|
||||
payload.AlertTitleLocKey(req.Alert.TitleLocKey)
|
||||
}
|
||||
|
||||
if len(req.Alert.LocArgs) > 0 {
|
||||
payload.AlertLocArgs(req.Alert.LocArgs)
|
||||
}
|
||||
|
||||
if len(req.Alert.TitleLocArgs) > 0 {
|
||||
payload.AlertTitleLocArgs(req.Alert.TitleLocArgs)
|
||||
}
|
||||
|
||||
if len(req.Alert.Body) > 0 {
|
||||
payload.AlertBody(req.Alert.Body)
|
||||
}
|
||||
|
||||
if len(req.Alert.LaunchImage) > 0 {
|
||||
payload.AlertLaunchImage(req.Alert.LaunchImage)
|
||||
}
|
||||
|
||||
if len(req.Alert.LocKey) > 0 {
|
||||
payload.AlertLocKey(req.Alert.LocKey)
|
||||
}
|
||||
|
||||
if len(req.Alert.Action) > 0 {
|
||||
payload.AlertAction(req.Alert.Action)
|
||||
}
|
||||
|
||||
if len(req.Alert.ActionLocKey) > 0 {
|
||||
payload.AlertActionLocKey(req.Alert.ActionLocKey)
|
||||
}
|
||||
|
||||
// General
|
||||
|
||||
if len(req.Category) > 0 {
|
||||
payload.Category(req.Category)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
// GetIOSNotification use for define iOS notification.
|
||||
// The iOS Notification Payload
|
||||
// ref: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1
|
||||
func GetIOSNotification(req PushNotification) *apns.Notification {
|
||||
notification := &apns.Notification{
|
||||
ApnsID: req.ApnsID,
|
||||
Topic: req.Topic,
|
||||
}
|
||||
|
||||
if req.Expiration > 0 {
|
||||
notification.Expiration = time.Unix(req.Expiration, 0)
|
||||
}
|
||||
|
||||
if len(req.Priority) > 0 && req.Priority == "normal" {
|
||||
notification.Priority = apns.PriorityLow
|
||||
}
|
||||
|
||||
payload := payload.NewPayload()
|
||||
|
||||
// add alert object if message length > 0
|
||||
if len(req.Message) > 0 {
|
||||
payload.Alert(req.Message)
|
||||
}
|
||||
|
||||
// zero value for clear the badge on the app icon.
|
||||
if req.Badge != nil && *req.Badge >= 0 {
|
||||
payload.Badge(*req.Badge)
|
||||
}
|
||||
|
||||
if req.MutableContent {
|
||||
payload.MutableContent()
|
||||
}
|
||||
|
||||
if len(req.Sound) > 0 {
|
||||
payload.Sound(req.Sound)
|
||||
}
|
||||
|
||||
if req.ContentAvailable {
|
||||
payload.ContentAvailable()
|
||||
}
|
||||
|
||||
if len(req.URLArgs) > 0 {
|
||||
payload.URLArgs(req.URLArgs)
|
||||
}
|
||||
|
||||
for k, v := range req.Data {
|
||||
payload.Custom(k, v)
|
||||
}
|
||||
|
||||
payload = iosAlertDictionary(payload, req)
|
||||
|
||||
notification.Payload = payload
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
// PushToIOS provide send notification to APNs server.
|
||||
func PushToIOS(req PushNotification) bool {
|
||||
LogAccess.Debug("Start push notification for iOS")
|
||||
if PushConf.Core.Sync {
|
||||
defer req.WaitDone()
|
||||
}
|
||||
|
||||
var (
|
||||
retryCount = 0
|
||||
maxRetry = PushConf.Ios.MaxRetry
|
||||
)
|
||||
|
||||
if req.Retry > 0 && req.Retry < maxRetry {
|
||||
maxRetry = req.Retry
|
||||
}
|
||||
|
||||
Retry:
|
||||
var (
|
||||
isError = false
|
||||
newTokens []string
|
||||
)
|
||||
|
||||
notification := GetIOSNotification(req)
|
||||
|
||||
for _, token := range req.Tokens {
|
||||
notification.DeviceToken = token
|
||||
|
||||
// send ios notification
|
||||
res, err := ApnsClient.Push(notification)
|
||||
|
||||
if err != nil {
|
||||
// apns server error
|
||||
LogPush(FailedPush, token, req, err)
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, token, req, err))
|
||||
}
|
||||
StatStorage.AddIosError(1)
|
||||
newTokens = append(newTokens, token)
|
||||
isError = true
|
||||
continue
|
||||
}
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
// error message:
|
||||
// ref: https://github.com/sideshow/apns2/blob/master/response.go#L14-L65
|
||||
LogPush(FailedPush, token, req, errors.New(res.Reason))
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, token, req, errors.New(res.Reason)))
|
||||
}
|
||||
StatStorage.AddIosError(1)
|
||||
newTokens = append(newTokens, token)
|
||||
isError = true
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Sent() {
|
||||
LogPush(SucceededPush, token, req, nil)
|
||||
StatStorage.AddIosSuccess(1)
|
||||
}
|
||||
}
|
||||
|
||||
if isError == true && retryCount < maxRetry {
|
||||
retryCount++
|
||||
|
||||
// resend fail token
|
||||
req.Tokens = newTokens
|
||||
goto Retry
|
||||
}
|
||||
|
||||
return isError
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package gorush
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/appleboy/go-fcm"
|
||||
)
|
||||
|
||||
// GetAndroidNotification use for define Android notification.
|
||||
// HTTP Connection Server Reference for Android
|
||||
// https://firebase.google.com/docs/cloud-messaging/http-server-ref
|
||||
func GetAndroidNotification(req PushNotification) *fcm.Message {
|
||||
notification := &fcm.Message{
|
||||
To: req.To,
|
||||
CollapseKey: req.CollapseKey,
|
||||
ContentAvailable: req.ContentAvailable,
|
||||
DelayWhileIdle: req.DelayWhileIdle,
|
||||
TimeToLive: req.TimeToLive,
|
||||
RestrictedPackageName: req.RestrictedPackageName,
|
||||
DryRun: req.DryRun,
|
||||
}
|
||||
|
||||
notification.RegistrationIDs = req.Tokens
|
||||
|
||||
if len(req.Priority) > 0 && req.Priority == "high" {
|
||||
notification.Priority = "high"
|
||||
}
|
||||
|
||||
// Add another field
|
||||
if len(req.Data) > 0 {
|
||||
notification.Data = make(map[string]interface{})
|
||||
for k, v := range req.Data {
|
||||
notification.Data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
notification.Notification = &req.Notification
|
||||
|
||||
// Set request message if body is empty
|
||||
if len(req.Message) > 0 {
|
||||
notification.Notification.Body = req.Message
|
||||
}
|
||||
|
||||
if len(req.Title) > 0 {
|
||||
notification.Notification.Title = req.Title
|
||||
}
|
||||
|
||||
if len(req.Sound) > 0 {
|
||||
notification.Notification.Sound = req.Sound
|
||||
}
|
||||
|
||||
return notification
|
||||
}
|
||||
|
||||
// PushToAndroid provide send notification to Android server.
|
||||
func PushToAndroid(req PushNotification) bool {
|
||||
LogAccess.Debug("Start push notification for Android")
|
||||
if PushConf.Core.Sync {
|
||||
defer req.WaitDone()
|
||||
}
|
||||
|
||||
var (
|
||||
APIKey string
|
||||
retryCount = 0
|
||||
maxRetry = PushConf.Android.MaxRetry
|
||||
)
|
||||
|
||||
if req.Retry > 0 && req.Retry < maxRetry {
|
||||
maxRetry = req.Retry
|
||||
}
|
||||
|
||||
// check message
|
||||
err := CheckMessage(req)
|
||||
|
||||
if err != nil {
|
||||
LogError.Error("request error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
Retry:
|
||||
var isError = false
|
||||
notification := GetAndroidNotification(req)
|
||||
|
||||
if APIKey = PushConf.Android.APIKey; req.APIKey != "" {
|
||||
APIKey = req.APIKey
|
||||
}
|
||||
|
||||
client, err := fcm.NewClient(APIKey)
|
||||
if err != nil {
|
||||
// FCM server error
|
||||
LogError.Error("FCM server error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
res, err := client.Send(notification)
|
||||
if err != nil {
|
||||
// FCM server error
|
||||
LogError.Error("FCM server error: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
|
||||
StatStorage.AddAndroidSuccess(int64(res.Success))
|
||||
StatStorage.AddAndroidError(int64(res.Failure))
|
||||
|
||||
var newTokens []string
|
||||
for k, result := range res.Results {
|
||||
if result.Error != nil {
|
||||
isError = true
|
||||
newTokens = append(newTokens, req.Tokens[k])
|
||||
LogPush(FailedPush, req.Tokens[k], req, result.Error)
|
||||
if PushConf.Core.Sync {
|
||||
req.AddLog(getLogPushEntry(FailedPush, req.Tokens[k], req, result.Error))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
LogPush(SucceededPush, req.Tokens[k], req, nil)
|
||||
}
|
||||
|
||||
if isError == true && retryCount < maxRetry {
|
||||
retryCount++
|
||||
|
||||
// resend fail token
|
||||
req.Tokens = newTokens
|
||||
goto Retry
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package gorush
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// InitWorkers for initialize all workers.
|
||||
func InitWorkers(workerNum int64, queueNum int64) {
|
||||
LogAccess.Debug("worker number is ", workerNum, ", queue number is ", queueNum)
|
||||
QueueNotification = make(chan PushNotification, queueNum)
|
||||
for i := int64(0); i < workerNum; i++ {
|
||||
go startWorker()
|
||||
}
|
||||
}
|
||||
|
||||
func startWorker() {
|
||||
for {
|
||||
notification := <-QueueNotification
|
||||
switch notification.Platform {
|
||||
case PlatFormIos:
|
||||
PushToIOS(notification)
|
||||
case PlatFormAndroid:
|
||||
PushToAndroid(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// queueNotification add notification to queue list.
|
||||
func queueNotification(req RequestPush) (int, []LogPushEntry) {
|
||||
var count int
|
||||
wg := sync.WaitGroup{}
|
||||
newNotification := []PushNotification{}
|
||||
for _, notification := range req.Notifications {
|
||||
switch notification.Platform {
|
||||
case PlatFormIos:
|
||||
if !PushConf.Ios.Enabled {
|
||||
continue
|
||||
}
|
||||
case PlatFormAndroid:
|
||||
if !PushConf.Android.Enabled {
|
||||
continue
|
||||
}
|
||||
}
|
||||
newNotification = append(newNotification, notification)
|
||||
}
|
||||
|
||||
log := make([]LogPushEntry, 0, count)
|
||||
for _, notification := range newNotification {
|
||||
if PushConf.Core.Sync {
|
||||
notification.wg = &wg
|
||||
notification.log = &log
|
||||
notification.AddWaitCount()
|
||||
}
|
||||
QueueNotification <- notification
|
||||
count += len(notification.Tokens)
|
||||
}
|
||||
|
||||
if PushConf.Core.Sync {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
StatStorage.AddTotalCount(int64(count))
|
||||
|
||||
return count, log
|
||||
}
|
Loading…
Reference in New Issue