2016-04-13 07:22:04 +00:00
|
|
|
package gorush
|
2016-03-24 16:46:37 +00:00
|
|
|
|
2016-03-24 16:53:45 +00:00
|
|
|
import (
|
2016-04-02 23:27:10 +00:00
|
|
|
"errors"
|
2016-04-08 10:11:36 +00:00
|
|
|
"fmt"
|
2016-03-25 08:41:47 +00:00
|
|
|
"github.com/google/go-gcm"
|
2016-03-24 16:46:37 +00:00
|
|
|
apns "github.com/sideshow/apns2"
|
2016-03-27 05:08:31 +00:00
|
|
|
"github.com/sideshow/apns2/certificate"
|
2016-03-24 16:53:45 +00:00
|
|
|
"github.com/sideshow/apns2/payload"
|
2016-07-29 00:48:24 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2016-06-12 07:54:17 +00:00
|
|
|
"path/filepath"
|
2016-04-09 04:15:39 +00:00
|
|
|
"time"
|
2016-03-24 16:46:37 +00:00
|
|
|
)
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// D provide string array
|
2016-04-13 03:15:24 +00:00
|
|
|
type D map[string]interface{}
|
2016-03-24 16:46:37 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
const (
|
2016-04-13 06:59:28 +00:00
|
|
|
// ApnsPriorityLow will tell APNs to send the push message at a time that takes
|
2016-04-01 07:45:24 +00:00
|
|
|
// 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
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// ApnsPriorityHigh will tell APNs to send the push message immediately.
|
2016-04-01 07:45:24 +00:00
|
|
|
// 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
|
|
|
|
)
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// Alert is APNs payload
|
2016-04-01 07:45:24 +00:00
|
|
|
type Alert struct {
|
2016-03-25 02:39:59 +00:00
|
|
|
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"`
|
2016-11-26 06:42:49 +00:00
|
|
|
Subtitle string `json:"subtitle,omitempty"`
|
2016-03-25 02:39:59 +00:00
|
|
|
TitleLocArgs []string `json:"title-loc-args,omitempty"`
|
|
|
|
TitleLocKey string `json:"title-loc-key,omitempty"`
|
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// RequestPush support multiple notification request.
|
2016-04-10 03:36:49 +00:00
|
|
|
type RequestPush struct {
|
2016-04-10 04:02:44 +00:00
|
|
|
Notifications []PushNotification `json:"notifications" binding:"required"`
|
2016-04-10 03:36:49 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// PushNotification is single notification request
|
2016-04-10 03:36:49 +00:00
|
|
|
type PushNotification struct {
|
2016-03-24 16:46:37 +00:00
|
|
|
// Common
|
2016-04-13 03:15:24 +00:00
|
|
|
Tokens []string `json:"tokens" binding:"required"`
|
|
|
|
Platform int `json:"platform" binding:"required"`
|
|
|
|
Message string `json:"message" binding:"required"`
|
|
|
|
Title string `json:"title,omitempty"`
|
|
|
|
Priority string `json:"priority,omitempty"`
|
|
|
|
ContentAvailable bool `json:"content_available,omitempty"`
|
|
|
|
Sound string `json:"sound,omitempty"`
|
|
|
|
Data D `json:"data,omitempty"`
|
2016-10-23 10:17:50 +00:00
|
|
|
Retry int `json:"retry,omitempty"`
|
2016-03-25 01:34:22 +00:00
|
|
|
|
2016-03-24 16:46:37 +00:00
|
|
|
// Android
|
2016-04-13 06:59:28 +00:00
|
|
|
APIKey string `json:"api_key,omitempty"`
|
2016-03-25 09:56:09 +00:00
|
|
|
To string `json:"to,omitempty"`
|
2016-03-25 08:41:47 +00:00
|
|
|
CollapseKey string `json:"collapse_key,omitempty"`
|
|
|
|
DelayWhileIdle bool `json:"delay_while_idle,omitempty"`
|
2016-06-04 12:01:04 +00:00
|
|
|
TimeToLive *uint `json:"time_to_live,omitempty"`
|
2016-03-25 08:41:47 +00:00
|
|
|
RestrictedPackageName string `json:"restricted_package_name,omitempty"`
|
|
|
|
DryRun bool `json:"dry_run,omitempty"`
|
|
|
|
Notification gcm.Notification `json:"notification,omitempty"`
|
2016-03-25 01:34:22 +00:00
|
|
|
|
2016-03-24 16:46:37 +00:00
|
|
|
// iOS
|
2016-04-13 02:06:21 +00:00
|
|
|
Expiration int64 `json:"expiration,omitempty"`
|
|
|
|
ApnsID string `json:"apns_id,omitempty"`
|
|
|
|
Topic string `json:"topic,omitempty"`
|
|
|
|
Badge int `json:"badge,omitempty"`
|
|
|
|
Category string `json:"category,omitempty"`
|
|
|
|
URLArgs []string `json:"url-args,omitempty"`
|
|
|
|
Alert Alert `json:"alert,omitempty"`
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
2016-04-24 07:57:38 +00:00
|
|
|
// CheckMessage for check request message
|
2016-04-24 07:54:41 +00:00
|
|
|
func CheckMessage(req PushNotification) error {
|
2016-04-24 06:30:17 +00:00
|
|
|
var msg string
|
|
|
|
if req.Message == "" {
|
|
|
|
msg = "the message must not be empty"
|
|
|
|
LogAccess.Debug(msg)
|
|
|
|
return errors.New(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Tokens) == 0 {
|
|
|
|
msg = "the message must specify at least one registration ID"
|
|
|
|
LogAccess.Debug(msg)
|
|
|
|
return errors.New(msg)
|
|
|
|
}
|
|
|
|
|
2016-04-24 07:54:41 +00:00
|
|
|
if len(req.Tokens) == PlatFormIos && 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 {
|
2016-04-24 06:30:17 +00:00
|
|
|
msg = "the message may specify at most 1000 registration IDs"
|
|
|
|
LogAccess.Debug(msg)
|
|
|
|
return errors.New(msg)
|
|
|
|
}
|
|
|
|
|
2016-04-24 07:57:38 +00:00
|
|
|
// ref: https://developers.google.com/cloud-messaging/http-server-ref
|
2016-06-04 12:01:04 +00:00
|
|
|
if req.Platform == PlatFormAndroid && req.TimeToLive != nil && (*req.TimeToLive < uint(0) || uint(2419200) < *req.TimeToLive) {
|
2016-04-24 06:30:17 +00:00
|
|
|
msg = "the message's TimeToLive field must be an integer " +
|
|
|
|
"between 0 and 2419200 (4 weeks)"
|
|
|
|
LogAccess.Debug(msg)
|
|
|
|
return errors.New(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-07-31 12:58:13 +00:00
|
|
|
// SetProxy only working for GCM server.
|
2016-07-29 00:48:24 +00:00
|
|
|
func SetProxy(proxy string) error {
|
|
|
|
|
2016-07-31 12:58:13 +00:00
|
|
|
proxyURL, err := url.ParseRequestURI(proxy)
|
2016-07-29 00:48:24 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-07-31 12:58:13 +00:00
|
|
|
http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(proxyURL)}
|
2016-07-29 01:38:06 +00:00
|
|
|
LogAccess.Debug("Set http proxy as " + proxy)
|
2016-07-29 00:48:24 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// CheckPushConf provide check your yml config.
|
2016-04-02 15:51:42 +00:00
|
|
|
func CheckPushConf() error {
|
|
|
|
if !PushConf.Ios.Enabled && !PushConf.Android.Enabled {
|
2016-04-13 06:59:28 +00:00
|
|
|
return errors.New("Please enable iOS or Android config in yml config")
|
2016-04-02 15:51:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if PushConf.Ios.Enabled {
|
2016-06-12 11:09:41 +00:00
|
|
|
if PushConf.Ios.KeyPath == "" {
|
2016-04-02 15:51:42 +00:00
|
|
|
return errors.New("Missing iOS certificate path")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if PushConf.Android.Enabled {
|
2016-04-13 06:59:28 +00:00
|
|
|
if PushConf.Android.APIKey == "" {
|
2016-04-02 15:51:42 +00:00
|
|
|
return errors.New("Missing Android API Key")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// InitAPNSClient use for initialize APNs Client.
|
2016-04-01 15:06:29 +00:00
|
|
|
func InitAPNSClient() error {
|
2016-03-27 05:08:31 +00:00
|
|
|
if PushConf.Ios.Enabled {
|
|
|
|
var err error
|
2016-06-12 11:09:41 +00:00
|
|
|
ext := filepath.Ext(PushConf.Ios.KeyPath)
|
2016-03-27 05:08:31 +00:00
|
|
|
|
2016-06-12 07:54:17 +00:00
|
|
|
switch ext {
|
|
|
|
case ".p12":
|
2016-06-12 11:09:41 +00:00
|
|
|
CertificatePemIos, err = certificate.FromP12File(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
2016-06-12 07:54:17 +00:00
|
|
|
case ".pem":
|
2016-06-12 11:09:41 +00:00
|
|
|
CertificatePemIos, err = certificate.FromPemFile(PushConf.Ios.KeyPath, PushConf.Ios.Password)
|
2016-06-12 07:54:17 +00:00
|
|
|
default:
|
|
|
|
err = errors.New("Wrong Certificate key extension.")
|
|
|
|
}
|
|
|
|
|
2016-03-27 05:08:31 +00:00
|
|
|
if err != nil {
|
2016-04-08 10:11:36 +00:00
|
|
|
LogError.Error("Cert Error:", err.Error())
|
2016-03-27 05:08:31 +00:00
|
|
|
|
2016-04-01 15:06:29 +00:00
|
|
|
return err
|
2016-03-27 05:08:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if PushConf.Ios.Production {
|
|
|
|
ApnsClient = apns.NewClient(CertificatePemIos).Production()
|
|
|
|
} else {
|
|
|
|
ApnsClient = apns.NewClient(CertificatePemIos).Development()
|
|
|
|
}
|
|
|
|
}
|
2016-04-01 15:06:29 +00:00
|
|
|
|
|
|
|
return nil
|
2016-03-27 05:08:31 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 12:34:23 +00:00
|
|
|
// InitWorkers for initialize all workers.
|
2016-09-02 07:55:47 +00:00
|
|
|
func InitWorkers(workerNum int64, queueNum int64) {
|
2016-04-14 12:34:23 +00:00
|
|
|
LogAccess.Debug("worker number is ", workerNum, ", queue number is ", queueNum)
|
|
|
|
QueueNotification = make(chan PushNotification, queueNum)
|
2016-09-02 07:55:47 +00:00
|
|
|
for i := int64(0); i < workerNum; i++ {
|
2016-04-14 12:34:23 +00:00
|
|
|
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 {
|
2016-04-10 03:36:49 +00:00
|
|
|
var count int
|
|
|
|
for _, notification := range req.Notifications {
|
|
|
|
switch notification.Platform {
|
|
|
|
case PlatFormIos:
|
|
|
|
if !PushConf.Ios.Enabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case PlatFormAndroid:
|
|
|
|
if !PushConf.Android.Enabled {
|
|
|
|
continue
|
|
|
|
}
|
2016-03-27 13:07:11 +00:00
|
|
|
}
|
2016-04-14 12:34:23 +00:00
|
|
|
QueueNotification <- notification
|
|
|
|
|
2016-04-15 01:41:10 +00:00
|
|
|
count += len(notification.Tokens)
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 09:03:08 +00:00
|
|
|
StatStorage.AddTotalCount(int64(count))
|
2016-04-15 01:41:10 +00:00
|
|
|
|
2016-04-10 03:36:49 +00:00
|
|
|
return count
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
2016-04-17 05:11:45 +00:00
|
|
|
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
2016-04-01 07:45:24 +00:00
|
|
|
// Alert dictionary
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-13 02:06:21 +00:00
|
|
|
if len(req.Title) > 0 {
|
|
|
|
payload.AlertTitle(req.Title)
|
2016-04-01 07:45:24 +00:00
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-11-26 06:42:49 +00:00
|
|
|
// Apple Watch & Safari display this string as part of the notification interface.
|
|
|
|
if len(req.Alert.Subtitle) > 0 {
|
|
|
|
payload.AlertSubtitle(req.Alert.Subtitle)
|
|
|
|
}
|
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.TitleLocKey) > 0 {
|
|
|
|
payload.AlertTitleLocKey(req.Alert.TitleLocKey)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-08 07:31:58 +00:00
|
|
|
if len(req.Alert.LocArgs) > 0 {
|
|
|
|
payload.AlertLocArgs(req.Alert.LocArgs)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.TitleLocArgs) > 0 {
|
|
|
|
payload.AlertTitleLocArgs(req.Alert.TitleLocArgs)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.Body) > 0 {
|
|
|
|
payload.AlertBody(req.Alert.Body)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.LaunchImage) > 0 {
|
|
|
|
payload.AlertLaunchImage(req.Alert.LaunchImage)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.LocKey) > 0 {
|
|
|
|
payload.AlertLocKey(req.Alert.LocKey)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.Action) > 0 {
|
|
|
|
payload.AlertAction(req.Alert.Action)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Alert.ActionLocKey) > 0 {
|
|
|
|
payload.AlertActionLocKey(req.Alert.ActionLocKey)
|
|
|
|
}
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
// General
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.Category) > 0 {
|
|
|
|
payload.Category(req.Category)
|
|
|
|
}
|
|
|
|
|
2016-04-17 05:11:45 +00:00
|
|
|
return payload
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetIOSNotification use for define iOS notificaiton.
|
|
|
|
// The iOS Notification Payload
|
|
|
|
// ref: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html
|
|
|
|
func GetIOSNotification(req PushNotification) *apns.Notification {
|
|
|
|
notification := &apns.Notification{
|
|
|
|
ApnsID: req.ApnsID,
|
2016-04-17 05:14:27 +00:00
|
|
|
Topic: req.Topic,
|
2016-04-17 05:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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().Alert(req.Message)
|
|
|
|
|
|
|
|
if req.Badge > 0 {
|
|
|
|
payload.Badge(req.Badge)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Sound) > 0 {
|
|
|
|
payload.Sound(req.Sound)
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.ContentAvailable {
|
|
|
|
payload.ContentAvailable()
|
|
|
|
}
|
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
if len(req.URLArgs) > 0 {
|
|
|
|
payload.URLArgs(req.URLArgs)
|
|
|
|
}
|
|
|
|
|
2016-04-17 05:11:45 +00:00
|
|
|
for k, v := range req.Data {
|
|
|
|
payload.Custom(k, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
payload = iosAlertDictionary(payload, req)
|
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
notification.Payload = payload
|
2016-03-25 02:39:59 +00:00
|
|
|
|
2016-04-01 07:45:24 +00:00
|
|
|
return notification
|
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// PushToIOS provide send notification to APNs server.
|
2016-04-14 15:58:25 +00:00
|
|
|
func PushToIOS(req PushNotification) bool {
|
2016-06-15 01:17:45 +00:00
|
|
|
LogAccess.Debug("Start push notification for iOS")
|
2016-04-14 15:58:25 +00:00
|
|
|
|
2016-10-23 13:52:23 +00:00
|
|
|
var retryCount = 0
|
|
|
|
var maxRetry = PushConf.Ios.MaxRetry
|
|
|
|
|
|
|
|
if req.Retry > 0 && req.Retry < maxRetry {
|
|
|
|
maxRetry = req.Retry
|
|
|
|
}
|
|
|
|
|
2016-10-23 10:17:50 +00:00
|
|
|
Retry:
|
|
|
|
var isError = false
|
|
|
|
var newTokens []string
|
2016-04-01 07:45:24 +00:00
|
|
|
|
|
|
|
notification := GetIOSNotification(req)
|
|
|
|
|
|
|
|
for _, token := range req.Tokens {
|
|
|
|
notification.DeviceToken = token
|
2016-03-24 16:46:37 +00:00
|
|
|
|
|
|
|
// send ios notification
|
2016-03-26 15:42:22 +00:00
|
|
|
res, err := ApnsClient.Push(notification)
|
2016-03-24 16:46:37 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2016-04-08 08:56:32 +00:00
|
|
|
// apns server error
|
2016-04-13 06:59:28 +00:00
|
|
|
LogPush(FailedPush, token, req, err)
|
2016-05-02 09:03:08 +00:00
|
|
|
StatStorage.AddIosError(1)
|
2016-10-23 10:17:50 +00:00
|
|
|
newTokens = append(newTokens, token)
|
|
|
|
isError = true
|
2016-04-14 12:34:23 +00:00
|
|
|
continue
|
2016-04-08 08:56:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
// error message:
|
|
|
|
// ref: https://github.com/sideshow/apns2/blob/master/response.go#L14-L65
|
2016-04-13 06:59:28 +00:00
|
|
|
LogPush(FailedPush, token, req, errors.New(res.Reason))
|
2016-05-02 09:03:08 +00:00
|
|
|
StatStorage.AddIosError(1)
|
2016-10-23 10:17:50 +00:00
|
|
|
newTokens = append(newTokens, token)
|
|
|
|
isError = true
|
2016-04-14 12:34:23 +00:00
|
|
|
continue
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if res.Sent() {
|
2016-04-13 06:59:28 +00:00
|
|
|
LogPush(SucceededPush, token, req, nil)
|
2016-05-02 09:03:08 +00:00
|
|
|
StatStorage.AddIosSuccess(1)
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-14 15:58:25 +00:00
|
|
|
|
2016-10-23 13:52:23 +00:00
|
|
|
if isError == true && retryCount < maxRetry {
|
|
|
|
retryCount++
|
2016-10-23 10:17:50 +00:00
|
|
|
|
2016-10-23 13:52:23 +00:00
|
|
|
// resend fail token
|
2016-10-23 10:17:50 +00:00
|
|
|
req.Tokens = newTokens
|
|
|
|
goto Retry
|
|
|
|
}
|
|
|
|
|
2016-04-14 15:58:25 +00:00
|
|
|
return isError
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// GetAndroidNotification use for define Android notificaiton.
|
2016-04-01 09:14:23 +00:00
|
|
|
// HTTP Connection Server Reference for Android
|
|
|
|
// https://developers.google.com/cloud-messaging/http-server-ref
|
2016-04-10 03:36:49 +00:00
|
|
|
func GetAndroidNotification(req PushNotification) gcm.HttpMessage {
|
2016-04-17 05:11:45 +00:00
|
|
|
notification := gcm.HttpMessage{
|
2016-04-17 05:14:27 +00:00
|
|
|
To: req.To,
|
|
|
|
CollapseKey: req.CollapseKey,
|
|
|
|
ContentAvailable: req.ContentAvailable,
|
|
|
|
DelayWhileIdle: req.DelayWhileIdle,
|
|
|
|
TimeToLive: req.TimeToLive,
|
2016-04-17 05:11:45 +00:00
|
|
|
RestrictedPackageName: req.RestrictedPackageName,
|
2016-04-17 05:14:27 +00:00
|
|
|
DryRun: req.DryRun,
|
2016-04-17 05:11:45 +00:00
|
|
|
}
|
2016-03-25 08:41:47 +00:00
|
|
|
|
|
|
|
notification.RegistrationIds = req.Tokens
|
|
|
|
|
|
|
|
if len(req.Priority) > 0 && req.Priority == "high" {
|
|
|
|
notification.Priority = "high"
|
|
|
|
}
|
|
|
|
|
2016-04-13 03:15:24 +00:00
|
|
|
// Add another field
|
|
|
|
if len(req.Data) > 0 {
|
2016-04-13 02:06:21 +00:00
|
|
|
notification.Data = make(map[string]interface{})
|
2016-04-13 03:15:24 +00:00
|
|
|
for k, v := range req.Data {
|
|
|
|
notification.Data[k] = v
|
2016-04-13 02:06:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-25 08:41:47 +00:00
|
|
|
notification.Notification = &req.Notification
|
|
|
|
|
2016-03-25 15:50:32 +00:00
|
|
|
// Set request message if body is empty
|
|
|
|
if len(notification.Notification.Body) == 0 {
|
|
|
|
notification.Notification.Body = req.Message
|
|
|
|
}
|
2016-03-25 08:41:47 +00:00
|
|
|
|
2016-04-13 02:06:21 +00:00
|
|
|
if len(req.Title) > 0 {
|
|
|
|
notification.Notification.Title = req.Title
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Sound) > 0 {
|
|
|
|
notification.Notification.Sound = req.Sound
|
|
|
|
}
|
|
|
|
|
2016-04-01 09:14:23 +00:00
|
|
|
return notification
|
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
// PushToAndroid provide send notification to Android server.
|
2016-04-10 03:36:49 +00:00
|
|
|
func PushToAndroid(req PushNotification) bool {
|
2016-06-15 01:17:45 +00:00
|
|
|
LogAccess.Debug("Start push notification for Android")
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
var APIKey string
|
2016-10-23 13:52:23 +00:00
|
|
|
var retryCount = 0
|
|
|
|
var maxRetry = PushConf.Android.MaxRetry
|
|
|
|
|
|
|
|
if req.Retry > 0 && req.Retry < maxRetry {
|
|
|
|
maxRetry = req.Retry
|
|
|
|
}
|
2016-04-01 09:14:23 +00:00
|
|
|
|
2016-04-24 06:30:17 +00:00
|
|
|
// check message
|
2016-04-24 07:54:41 +00:00
|
|
|
err := CheckMessage(req)
|
2016-04-24 06:30:17 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2016-04-24 07:54:41 +00:00
|
|
|
LogError.Error("request error: " + err.Error())
|
2016-04-24 06:30:17 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-10-23 10:17:50 +00:00
|
|
|
Retry:
|
2016-10-23 13:05:31 +00:00
|
|
|
var isError = false
|
2016-04-01 09:14:23 +00:00
|
|
|
notification := GetAndroidNotification(req)
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
if APIKey = PushConf.Android.APIKey; req.APIKey != "" {
|
|
|
|
APIKey = req.APIKey
|
2016-04-09 06:06:03 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
res, err := gcm.SendHttp(APIKey, notification)
|
2016-03-25 08:41:47 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2016-04-08 10:11:36 +00:00
|
|
|
// GCM server error
|
|
|
|
LogError.Error("GCM server error: " + err.Error())
|
2016-03-25 08:41:47 +00:00
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-04-08 10:11:36 +00:00
|
|
|
LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
|
2016-05-02 09:03:08 +00:00
|
|
|
StatStorage.AddAndroidSuccess(int64(res.Success))
|
|
|
|
StatStorage.AddAndroidError(int64(res.Failure))
|
2016-04-01 14:15:10 +00:00
|
|
|
|
2016-10-23 10:17:50 +00:00
|
|
|
var newTokens []string
|
2016-04-08 10:11:36 +00:00
|
|
|
for k, result := range res.Results {
|
|
|
|
if result.Error != "" {
|
2016-10-23 10:17:50 +00:00
|
|
|
isError = true
|
|
|
|
newTokens = append(newTokens, req.Tokens[k])
|
2016-04-13 06:59:28 +00:00
|
|
|
LogPush(FailedPush, req.Tokens[k], req, errors.New(result.Error))
|
2016-04-08 10:11:36 +00:00
|
|
|
continue
|
|
|
|
}
|
2016-03-25 08:41:47 +00:00
|
|
|
|
2016-04-13 06:59:28 +00:00
|
|
|
LogPush(SucceededPush, req.Tokens[k], req, nil)
|
2016-03-25 08:41:47 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 13:52:23 +00:00
|
|
|
if isError == true && retryCount < maxRetry {
|
|
|
|
retryCount++
|
2016-10-23 10:17:50 +00:00
|
|
|
|
2016-10-23 13:05:31 +00:00
|
|
|
// resend fail token
|
2016-10-23 10:17:50 +00:00
|
|
|
req.Tokens = newTokens
|
|
|
|
goto Retry
|
|
|
|
}
|
|
|
|
|
2016-03-24 16:46:37 +00:00
|
|
|
return true
|
|
|
|
}
|