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-07-29 00:48:24 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2017-04-06 02:52:57 +00:00
|
|
|
"os"
|
2017-10-24 09:00:08 +00:00
|
|
|
"strings"
|
2017-04-06 07:00:49 +00:00
|
|
|
"sync"
|
2017-01-19 09:08:12 +00:00
|
|
|
|
2017-06-01 07:52:01 +00:00
|
|
|
"github.com/appleboy/go-fcm"
|
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 {
|
2018-11-20 03:02:26 +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"`
|
|
|
|
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"`
|
2016-03-25 02:39:59 +00:00
|
|
|
}
|
|
|
|
|
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
|
2018-08-28 03:02:13 +00:00
|
|
|
Tokens []string `json:"tokens" binding:"required"`
|
|
|
|
Platform int `json:"platform" binding:"required"`
|
|
|
|
Message string `json:"message,omitempty"`
|
|
|
|
Title string `json:"title,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"`
|
2017-04-06 07:00:49 +00:00
|
|
|
wg *sync.WaitGroup
|
2017-04-10 03:46:48 +00:00
|
|
|
log *[]LogPushEntry
|
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"`
|
2017-10-24 09:00:08 +00:00
|
|
|
Condition string `json:"condition,omitempty"`
|
2017-06-01 03:56:10 +00:00
|
|
|
Notification fcm.Notification `json:"notification,omitempty"`
|
2016-03-25 01:34:22 +00:00
|
|
|
|
2016-03-24 16:46:37 +00:00
|
|
|
// iOS
|
2018-08-15 03:47:15 +00:00
|
|
|
Expiration int64 `json:"expiration,omitempty"`
|
|
|
|
ApnsID string `json:"apns_id,omitempty"`
|
|
|
|
CollapseID string `json:"collapse_id,omitempty"`
|
|
|
|
Topic string `json:"topic,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"`
|
2018-08-28 03:02:13 +00:00
|
|
|
SoundName string `json:"name,omitempty"`
|
|
|
|
SoundVolume float32 `json:"volume,omitempty"`
|
2016-03-24 16:46:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-10 03:46:48 +00:00
|
|
|
// WaitDone decrements the WaitGroup counter.
|
|
|
|
func (p *PushNotification) WaitDone() {
|
2017-04-06 07:00:49 +00:00
|
|
|
if p.wg != nil {
|
|
|
|
p.wg.Done()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-10 03:46:48 +00:00
|
|
|
// AddWaitCount increments the WaitGroup counter.
|
|
|
|
func (p *PushNotification) AddWaitCount() {
|
|
|
|
if p.wg != nil {
|
|
|
|
p.wg.Add(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddLog record fail log of notification
|
|
|
|
func (p *PushNotification) AddLog(log LogPushEntry) {
|
|
|
|
if p.log != nil {
|
|
|
|
*p.log = append(*p.log, log)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-24 09:00:08 +00:00
|
|
|
// 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/")) ||
|
|
|
|
p.Condition != ""
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
2017-10-24 09:00:08 +00:00
|
|
|
// ignore send topic mesaage from FCM
|
2017-10-25 02:37:53 +00:00
|
|
|
if !req.IsTopic() && len(req.Tokens) == 0 && len(req.To) == 0 {
|
2016-04-24 06:30:17 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-06-01 07:52:01 +00:00
|
|
|
// ref: https://firebase.google.com/docs/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
|
|
|
|
}
|
|
|
|
|
2017-06-01 03:56:10 +00:00
|
|
|
// SetProxy only working for FCM 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 {
|
2018-02-18 09:12:51 +00:00
|
|
|
if PushConf.Ios.KeyPath == "" && PushConf.Ios.KeyBase64 == "" {
|
|
|
|
return errors.New("Missing iOS certificate key")
|
2016-04-02 15:51:42 +00:00
|
|
|
}
|
2017-04-06 02:52:57 +00:00
|
|
|
|
|
|
|
// check certificate file exist
|
2018-02-18 09:12:51 +00:00
|
|
|
if PushConf.Ios.KeyPath != "" {
|
|
|
|
if _, err := os.Stat(PushConf.Ios.KeyPath); os.IsNotExist(err) {
|
|
|
|
return errors.New("certificate file does not exist")
|
|
|
|
}
|
2017-04-06 02:52:57 +00:00
|
|
|
}
|
2016-04-02 15:51:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|