Send messages to topics for android (#296)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
parent
4df24202cf
commit
115ee18560
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/appleboy/go-fcm"
|
"github.com/appleboy/go-fcm"
|
||||||
|
@ -69,6 +70,7 @@ type PushNotification struct {
|
||||||
TimeToLive *uint `json:"time_to_live,omitempty"`
|
TimeToLive *uint `json:"time_to_live,omitempty"`
|
||||||
RestrictedPackageName string `json:"restricted_package_name,omitempty"`
|
RestrictedPackageName string `json:"restricted_package_name,omitempty"`
|
||||||
DryRun bool `json:"dry_run,omitempty"`
|
DryRun bool `json:"dry_run,omitempty"`
|
||||||
|
Condition string `json:"condition,omitempty"`
|
||||||
Notification fcm.Notification `json:"notification,omitempty"`
|
Notification fcm.Notification `json:"notification,omitempty"`
|
||||||
|
|
||||||
// iOS
|
// iOS
|
||||||
|
@ -103,11 +105,19 @@ 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/")) ||
|
||||||
|
p.Condition != ""
|
||||||
|
}
|
||||||
|
|
||||||
// CheckMessage for check request message
|
// CheckMessage for check request message
|
||||||
func CheckMessage(req PushNotification) error {
|
func CheckMessage(req PushNotification) error {
|
||||||
var msg string
|
var msg string
|
||||||
|
|
||||||
if len(req.Tokens) == 0 {
|
// ignore send topic mesaage from FCM
|
||||||
|
if !req.IsTopic() && len(req.Tokens) == 0 {
|
||||||
msg = "the message must specify at least one registration ID"
|
msg = "the message must specify at least one registration ID"
|
||||||
LogAccess.Debug(msg)
|
LogAccess.Debug(msg)
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
|
|
|
@ -33,6 +33,7 @@ func InitFCMClient(key string) (*fcm.Client, error) {
|
||||||
func GetAndroidNotification(req PushNotification) *fcm.Message {
|
func GetAndroidNotification(req PushNotification) *fcm.Message {
|
||||||
notification := &fcm.Message{
|
notification := &fcm.Message{
|
||||||
To: req.To,
|
To: req.To,
|
||||||
|
Condition: req.Condition,
|
||||||
CollapseKey: req.CollapseKey,
|
CollapseKey: req.CollapseKey,
|
||||||
ContentAvailable: req.ContentAvailable,
|
ContentAvailable: req.ContentAvailable,
|
||||||
DelayWhileIdle: req.DelayWhileIdle,
|
DelayWhileIdle: req.DelayWhileIdle,
|
||||||
|
@ -122,11 +123,15 @@ Retry:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !req.IsTopic() {
|
||||||
LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
|
LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
|
||||||
|
}
|
||||||
|
|
||||||
StatStorage.AddAndroidSuccess(int64(res.Success))
|
StatStorage.AddAndroidSuccess(int64(res.Success))
|
||||||
StatStorage.AddAndroidError(int64(res.Failure))
|
StatStorage.AddAndroidError(int64(res.Failure))
|
||||||
|
|
||||||
var newTokens []string
|
var newTokens []string
|
||||||
|
// result from Send messages to specific devices
|
||||||
for k, result := range res.Results {
|
for k, result := range res.Results {
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
isError = true
|
isError = true
|
||||||
|
@ -141,6 +146,28 @@ Retry:
|
||||||
LogPush(SucceededPush, req.Tokens[k], req, nil)
|
LogPush(SucceededPush, req.Tokens[k], req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// result from Send messages to topics
|
||||||
|
if req.IsTopic() {
|
||||||
|
to := ""
|
||||||
|
if req.To != "" {
|
||||||
|
to = req.To
|
||||||
|
} else {
|
||||||
|
to = req.Condition
|
||||||
|
}
|
||||||
|
LogAccess.Debug("Send Topic Message: ", to)
|
||||||
|
// Success
|
||||||
|
if res.MessageID != 0 {
|
||||||
|
LogPush(SucceededPush, to, req, nil)
|
||||||
|
} else {
|
||||||
|
isError = true
|
||||||
|
// failure
|
||||||
|
LogPush(FailedPush, to, req, res.Error)
|
||||||
|
if PushConf.Core.Sync {
|
||||||
|
req.AddLog(getLogPushEntry(FailedPush, to, req, res.Error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if isError && retryCount < maxRetry {
|
if isError && retryCount < maxRetry {
|
||||||
retryCount++
|
retryCount++
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,26 @@ func TestFCMMessage(t *testing.T) {
|
||||||
err = CheckMessage(req)
|
err = CheckMessage(req)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// ignore check token length if send topic message
|
||||||
|
req = PushNotification{
|
||||||
|
Message: "Test",
|
||||||
|
Platform: PlatFormAndroid,
|
||||||
|
To: "/topics/foo-bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CheckMessage(req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// "condition": "'dogs' in topics || 'cats' in topics",
|
||||||
|
req = PushNotification{
|
||||||
|
Message: "Test",
|
||||||
|
Platform: PlatFormAndroid,
|
||||||
|
Condition: "'dogs' in topics || 'cats' in topics",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CheckMessage(req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// the message may specify at most 1000 registration IDs
|
// the message may specify at most 1000 registration IDs
|
||||||
req = PushNotification{
|
req = PushNotification{
|
||||||
Message: "Test",
|
Message: "Test",
|
||||||
|
|
|
@ -132,6 +132,48 @@ func TestSyncModeForNotifications(t *testing.T) {
|
||||||
assert.Equal(t, 2, len(logs))
|
assert.Equal(t, 2, len(logs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncModeForTopicNotification(t *testing.T) {
|
||||||
|
PushConf, _ = config.LoadConf("")
|
||||||
|
|
||||||
|
PushConf.Android.Enabled = true
|
||||||
|
PushConf.Android.APIKey = os.Getenv("ANDROID_API_KEY")
|
||||||
|
PushConf.Log.HideToken = false
|
||||||
|
|
||||||
|
// enable sync mode
|
||||||
|
PushConf.Core.Sync = true
|
||||||
|
|
||||||
|
req := RequestPush{
|
||||||
|
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!",
|
||||||
|
},
|
||||||
|
// android
|
||||||
|
{
|
||||||
|
// success
|
||||||
|
To: "/topics/foo-bar",
|
||||||
|
Platform: 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!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
count, logs := queueNotification(req)
|
||||||
|
assert.Equal(t, 2, count)
|
||||||
|
assert.Equal(t, 1, len(logs))
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetProxyURL(t *testing.T) {
|
func TestSetProxyURL(t *testing.T) {
|
||||||
|
|
||||||
err := SetProxy("87.236.233.92:8080")
|
err := SetProxy("87.236.233.92:8080")
|
||||||
|
|
|
@ -58,6 +58,10 @@ func queueNotification(req RequestPush) (int, []LogPushEntry) {
|
||||||
}
|
}
|
||||||
QueueNotification <- notification
|
QueueNotification <- notification
|
||||||
count += len(notification.Tokens)
|
count += len(notification.Tokens)
|
||||||
|
// Count topic message
|
||||||
|
if notification.To != "" {
|
||||||
|
count++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if PushConf.Core.Sync {
|
if PushConf.Core.Sync {
|
||||||
|
|
20
main.go
20
main.go
|
@ -130,12 +130,21 @@ func main() {
|
||||||
if opts.Android.Enabled {
|
if opts.Android.Enabled {
|
||||||
gorush.PushConf.Android.Enabled = opts.Android.Enabled
|
gorush.PushConf.Android.Enabled = opts.Android.Enabled
|
||||||
req := gorush.PushNotification{
|
req := gorush.PushNotification{
|
||||||
Tokens: []string{token},
|
|
||||||
Platform: gorush.PlatFormAndroid,
|
Platform: gorush.PlatFormAndroid,
|
||||||
Message: message,
|
Message: message,
|
||||||
Title: title,
|
Title: title,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send message to single device
|
||||||
|
if token != "" {
|
||||||
|
req.Tokens = []string{token}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send topic message
|
||||||
|
if topic != "" {
|
||||||
|
req.To = topic
|
||||||
|
}
|
||||||
|
|
||||||
err := gorush.CheckMessage(req)
|
err := gorush.CheckMessage(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -159,12 +168,17 @@ func main() {
|
||||||
|
|
||||||
gorush.PushConf.Ios.Enabled = opts.Ios.Enabled
|
gorush.PushConf.Ios.Enabled = opts.Ios.Enabled
|
||||||
req := gorush.PushNotification{
|
req := gorush.PushNotification{
|
||||||
Tokens: []string{token},
|
|
||||||
Platform: gorush.PlatFormIos,
|
Platform: gorush.PlatFormIos,
|
||||||
Message: message,
|
Message: message,
|
||||||
Title: title,
|
Title: title,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send message to single device
|
||||||
|
if token != "" {
|
||||||
|
req.Tokens = []string{token}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send topic message
|
||||||
if topic != "" {
|
if topic != "" {
|
||||||
req.Topic = topic
|
req.Topic = topic
|
||||||
}
|
}
|
||||||
|
@ -259,13 +273,13 @@ Server Options:
|
||||||
iOS Options:
|
iOS Options:
|
||||||
-i, --key <file> certificate key file path
|
-i, --key <file> certificate key file path
|
||||||
-P, --password <password> certificate key password
|
-P, --password <password> certificate key password
|
||||||
--topic <topic> iOS topic
|
|
||||||
--ios enabled iOS (default: false)
|
--ios enabled iOS (default: false)
|
||||||
--production iOS production mode (default: false)
|
--production iOS production mode (default: false)
|
||||||
Android Options:
|
Android Options:
|
||||||
-k, --apikey <api_key> Android API Key
|
-k, --apikey <api_key> Android API Key
|
||||||
--android enabled android (default: false)
|
--android enabled android (default: false)
|
||||||
Common Options:
|
Common Options:
|
||||||
|
--topic <topic> iOS or Android topic message
|
||||||
-h, --help Show this message
|
-h, --help Show this message
|
||||||
-v, --version Show version
|
-v, --version Show version
|
||||||
`
|
`
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# go-fcm
|
# go-fcm
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/edganiukov/fcm?status.svg)](https://godoc.org/github.com/edganiukov/fcm)
|
[![GoDoc](https://godoc.org/github.com/appleboy/go-fcm?status.svg)](https://godoc.org/github.com/edganiukov/fcm)
|
||||||
[![Build Status](https://travis-ci.org/edganiukov/fcm.svg?branch=master)](https://travis-ci.org/edganiukov/fcm)
|
[![Build Status](https://travis-ci.org/appleboy/go-fcm.svg?branch=master)](https://travis-ci.org/edganiukov/fcm)
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/edganiukov/fcm)](https://goreportcard.com/report/github.com/edganiukov/fcm)
|
[![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/go-fcm)](https://goreportcard.com/report/github.com/edganiukov/fcm)
|
||||||
|
|
||||||
This project was forked from [github.com/edganiukov/fcmfcm](https://github.com/edganiukov/fcm).
|
This project was forked from [github.com/edganiukov/fcmfcm](https://github.com/edganiukov/fcm).
|
||||||
|
|
||||||
|
@ -10,6 +10,12 @@ Golang client library for Firebase Cloud Messaging. Implemented only [HTTP clien
|
||||||
|
|
||||||
More information on [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/)
|
More information on [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/)
|
||||||
|
|
||||||
|
## Feature
|
||||||
|
|
||||||
|
* [x] Send messages to a topic
|
||||||
|
* [x] Send messages to a device list
|
||||||
|
* [x] Supports condition attribute (fcm only)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
To install fcm, use `go get`:
|
To install fcm, use `go get`:
|
||||||
|
@ -34,31 +40,32 @@ Here is a simple example illustrating how to use FCM library:
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/edganiukov/fcm"
|
"log"
|
||||||
|
|
||||||
|
"github.com/appleboy/go-fcm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create the message to be sent.
|
// Create the message to be sent.
|
||||||
msg := &fcm.Message{
|
msg := &fcm.Message{
|
||||||
Token: "sample_device_token",
|
To: "sample_device_token",
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a FCM client to send the message.
|
// Create a FCM client to send the message.
|
||||||
client := fcm.NewClient("sample_api_key")
|
client, err := fcm.NewClient("sample_api_key")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Send the message and receive the response without retries.
|
// Send the message and receive the response without retries.
|
||||||
response, err := client.Send(msg)
|
response, err := client.Send(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
/* ... */
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
/* ... */
|
|
||||||
|
log.Printf("%#v\n", response)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### TODO:
|
|
||||||
---------
|
|
||||||
- [ ] Retry only failed messages while multicast messaging.
|
|
||||||
|
|
|
@ -46,6 +46,9 @@ var (
|
||||||
// ErrTopicsMessageRateExceeded occurs when client sent to many requests to
|
// ErrTopicsMessageRateExceeded occurs when client sent to many requests to
|
||||||
// the topics.
|
// the topics.
|
||||||
ErrTopicsMessageRateExceeded = errors.New("topics message rate exceeded")
|
ErrTopicsMessageRateExceeded = errors.New("topics message rate exceeded")
|
||||||
|
|
||||||
|
// ErrInvalidParameters occurs when provided parameters have the right name and type
|
||||||
|
ErrInvalidParameters = errors.New("check that the provided parameters have the right name and type")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -62,6 +65,7 @@ var (
|
||||||
"InternalServerError": ErrInternalServerError,
|
"InternalServerError": ErrInternalServerError,
|
||||||
"DeviceMessageRateExceeded": ErrDeviceMessageRateExceeded,
|
"DeviceMessageRateExceeded": ErrDeviceMessageRateExceeded,
|
||||||
"TopicsMessageRateExceeded": ErrTopicsMessageRateExceeded,
|
"TopicsMessageRateExceeded": ErrTopicsMessageRateExceeded,
|
||||||
|
"InvalidParameters": ErrInvalidParameters,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,6 +109,47 @@ type Response struct {
|
||||||
Failure int `json:"failure"`
|
Failure int `json:"failure"`
|
||||||
CanonicalIDs int `json:"canonical_ids"`
|
CanonicalIDs int `json:"canonical_ids"`
|
||||||
Results []Result `json:"results"`
|
Results []Result `json:"results"`
|
||||||
|
|
||||||
|
// Device Group HTTP Response
|
||||||
|
FailedRegistrationIDs []string `json:"failed_registration_ids"`
|
||||||
|
|
||||||
|
// Topic HTTP response
|
||||||
|
MessageID int64 `json:"message_id"`
|
||||||
|
Error error `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
|
func (r *Response) UnmarshalJSON(data []byte) error {
|
||||||
|
var response struct {
|
||||||
|
MulticastID int64 `json:"multicast_id"`
|
||||||
|
Success int `json:"success"`
|
||||||
|
Failure int `json:"failure"`
|
||||||
|
CanonicalIDs int `json:"canonical_ids"`
|
||||||
|
Results []Result `json:"results"`
|
||||||
|
|
||||||
|
// Device Group HTTP Response
|
||||||
|
FailedRegistrationIDs []string `json:"failed_registration_ids"`
|
||||||
|
|
||||||
|
// Topic HTTP response
|
||||||
|
MessageID int64 `json:"message_id"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &response); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.MulticastID = response.MulticastID
|
||||||
|
r.Success = response.Success
|
||||||
|
r.Failure = response.Failure
|
||||||
|
r.CanonicalIDs = response.CanonicalIDs
|
||||||
|
r.Results = response.Results
|
||||||
|
r.Success = response.Success
|
||||||
|
r.FailedRegistrationIDs = response.FailedRegistrationIDs
|
||||||
|
r.MessageID = response.MessageID
|
||||||
|
r.Error = errMap[response.Error]
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result represents the status of a processed message.
|
// Result represents the status of a processed message.
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
"ignore": "test",
|
"ignore": "test",
|
||||||
"package": [
|
"package": [
|
||||||
{
|
{
|
||||||
"checksumSHA1": "DuVew6znkXuUG1wjGQF+pUyXrHU=",
|
"checksumSHA1": "TVTjsXflagrWbTXmbxPJdCtTFRo=",
|
||||||
"path": "github.com/appleboy/go-fcm",
|
"path": "github.com/appleboy/go-fcm",
|
||||||
"revision": "5f2cb2866531e4e37c7a9ff5fb7a1536e1ffc566",
|
"revision": "c12f9e2e95b14802da2b4d3807dd12ef0dd80a42",
|
||||||
"revisionTime": "2017-06-01T07:42:50Z"
|
"revisionTime": "2017-10-24T08:00:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Ab7MUtqX0iq2PUzzBxWpgzPSydw=",
|
"checksumSHA1": "Ab7MUtqX0iq2PUzzBxWpgzPSydw=",
|
||||||
|
|
Loading…
Reference in New Issue