From 3918fab9081f8d7db74d058dd8d52d18e2d6f393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Salih=20Karaka=C5=9Fl=C4=B1?= Date: Fri, 4 Sep 2020 06:01:21 +0300 Subject: [PATCH] Add Huawei Mobile Services Support to Gorush (#523) Co-authored-by: Bo-Yi Wu --- README.md | 155 +++++++++++++++++++- config/config.go | 21 +++ config/testdata/config.yml | 6 + go.mod | 1 + go.sum | 10 ++ gorush/const.go | 4 + gorush/global.go | 3 + gorush/log.go | 4 + gorush/metrics.go | 24 ++++ gorush/notification.go | 42 +++++- gorush/notification_apns_test.go | 3 +- gorush/notification_hms.go | 238 +++++++++++++++++++++++++++++++ gorush/notification_hms_test.go | 67 +++++++++ gorush/status.go | 9 ++ gorush/worker.go | 8 +- main.go | 57 +++++++- storage/badger/badger.go | 30 ++++ storage/boltdb/boltdb.go | 30 ++++ storage/buntdb/buntdb.go | 30 ++++ storage/leveldb/leveldb.go | 30 ++++ storage/memory/memory.go | 33 +++++ storage/redis/redis.go | 28 ++++ storage/storage.go | 10 ++ 23 files changed, 829 insertions(+), 14 deletions(-) create mode 100644 gorush/notification_hms.go create mode 100644 gorush/notification_hms_test.go diff --git a/README.md b/README.md index 9de151e..7bcea4f 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ A push notification micro server using [Gin](https://github.com/gin-gonic/gin) f - [APNS](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html) - [FCM](https://firebase.google.com/) +- [HMS](https://developer.huawei.com/consumer/en/hms/) [A live demo on Netlify](https://gorush.netlify.com/). @@ -71,6 +72,7 @@ A push notification micro server using [Gin](https://github.com/gin-gonic/gin) f - Support [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) using [go-fcm](https://github.com/appleboy/go-fcm) library for Android. - Support [HTTP/2](https://http2.github.io/) Apple Push Notification Service using [apns2](https://github.com/sideshow/apns2) library. +- Support [HMS Push Service](https://developer.huawei.com/consumer/en/hms/huawei-pushkit) using [go-hms-push](https://github.com/msalihkarakasli/go-hms-push) library for Huawei Devices. - Support [YAML](https://github.com/go-yaml/yaml) configuration. - Support command line to send single Android or iOS notification. - Support Web API to send push notification. @@ -138,6 +140,12 @@ android: apikey: "YOUR_API_KEY" max_retry: 0 # resend fail notification, default value zero is disabled +huawei: + enabled: true + apikey: "YOUR_API_KEY" + appid: "YOUR_APP_ID" + max_retry: 0 # resend fail notification, default value zero is disabled + ios: enabled: false key_path: "key.pem" @@ -275,6 +283,10 @@ iOS Options: Android Options: -k, --apikey Android API Key --android enabled android (default: false) +Huawei Options: + -hk, --hmskey HMS API Key + -hid, --hmsid HMS APP Id + --huawei enabled huawei (default: false) Common Options: --topic iOS or Android topic message -h, --help Show this message @@ -304,6 +316,31 @@ gorush --android --topic "/topics/foo-bar" \ - `--topic`: Send messages to topics. note: don't add device token. - `--proxy`: Set `http`, `https` or `socks5` proxy url. +### Send Huawei (HMS) notification + +Send single notification with the following command. + +```bash +gorush -huawei -title "Gorush with HMS" -m "your message" -hk "API Key" -hid "APP Id" -t "Device token" +``` + +Send messages to topics. + +```bash +gorush --huawei --topic "foo-bar" \ + -title "Gorush with HMS" \ + -m "This is a Huawei Mobile Services Topic Message" \ + -hk "API Key" \ + -hid "APP Id" +``` + +- `-m`: Notification message. +- `-hk`: [Huawei Mobile Services](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/Preparations) api secret key +- `-t`: Device token. +- `--title`: Notification title. +- `--topic`: Send messages to topics. note: don't add device token. +- `--proxy`: Set `http`, `https` or `socks5` proxy url. + ### Send iOS notification Send single notification with the following command. @@ -360,7 +397,7 @@ Gorush support the following API. - **GET** `/api/stat/go` Golang cpu, memory, gc, etc information. Thanks for [golang-stats-api-handler](https://github.com/fukata/golang-stats-api-handler). - **GET** `/api/stat/app` show notification success and failure counts. - **GET** `/api/config` show server yml config file. -- **POST** `/api/push` push ios and android notifications. +- **POST** `/api/push` push ios, android or huawei notifications. ### GET /api/stat/go @@ -418,6 +455,10 @@ Show success or failure counts information of notification. "android": { "push_success": 10, "push_error": 10 + }, + "huawei": { + "push_success": 3, + "push_error": 1 } } ``` @@ -482,6 +523,21 @@ Simple send Android notification example, the `platform` value is `2`: } ``` +Simple send Huawei notification example, the `platform` value is `3`: + +```json +{ + "notifications": [ + { + "tokens": ["token_a", "token_b"], + "platform": 3, + "title": "Gorush with HMS", + "message": "Hello World Huawei!" + } + ] +} +``` + Simple send notification on Android and iOS devices using Firebase, the `platform` value is `2`: ```json @@ -513,15 +569,16 @@ Send multiple notifications as below: }, { "tokens": ["token_a", "token_b"], - "platform": 2, - "message": "Hello World!" + "platform": 3, + "message": "Hello World Huawei!", + "title": "Gorush with HMS" }, ..... ] } ``` -See more example about [iOS](#ios-example) or [Android](#android-example). +See more example about [iOS](#ios-example), [Android](#android-example) or [Huawei](#huawei-example) ### Request body @@ -531,24 +588,31 @@ The Request body must have a notifications array. The following is a parameter t |-------------------------|--------------|---------------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------| | notif_id | string | A unique string that identifies the notification for async feedback | - | | | tokens | string array | device tokens | o | | -| platform | int | platform(iOS,Android) | o | 1=iOS, 2=Android (Firebase) | +| platform | int | platform(iOS,Android) | o | 1=iOS, 2=Android (Firebase), 3=Huawei (HMS) | | message | string | message for notification | - | | | title | string | notification title | - | | | priority | string | Sets the priority of the message. | - | `normal` or `high` | | content_available | bool | data messages wake the app by default. | - | | | sound | interface{} | sound type | - | | -| data | string array | extensible partition | - | | +| data | string array | extensible partition | - | only Android and IOS | +| huawei_data | string | JSON object as string to extensible partition partition | - | only Huawei. See the [detail](#huawei-notification) | | retry | int | retry send notification if fail response from server. Value must be small than `max_retry` field. | - | | | topic | string | send messages to topics | | | -| image | string | image url to show in notification | - | only Android | +| image | string | image url to show in notification | - | only Android and Huawei | | api_key | string | api key for firebase cloud message | - | only Android | | to | string | The value must be a registration token, notification key, or topic. | - | only Android | | collapse_key | string | a key for collapsing notifications | - | only Android | +| huawei_collapse_key | int | a key integer for collapsing notifications | - | only Huawei See the [detail](#huawei-notification) | | delay_while_idle | bool | a flag for device idling | - | only Android | | time_to_live | uint | expiration of message kept on FCM storage | - | only Android | +| huawei_ttl | string | expiration of message kept on HMS storage | - | only Huawei See the [detail](#huawei-notification) | | restricted_package_name | string | the package name of the application | - | only Android | | dry_run | bool | allows developers to test a request without actually sending a message | - | only Android | | notification | string array | payload of a FCM message | - | only Android. See the [detail](#android-notification-payload) | +| huawei_notification | string array | payload of a HMS message | - | only Huawei. See the [detail](#huawei-notification) | +| app_id | string | hms app id | - | only Huawei. See the [detail](#huawei-notification) | +| bi_tag | string | Tag of a message in a batch delivery task | - | only Huawei. See the [detail](#huawei-notification) | +| fast_app_target | int | State of a mini program when a quick app sends a data message. | - | only Huawei. See the [detail](#huawei-notification) | | expiration | int | expiration for notification | - | only iOS | | apns_id | string | A canonical UUID that identifies the notification | - | only iOS | | collapse_id | string | An identifier you use to coalesce multiple notifications into a single notification for the user | - | only iOS | @@ -612,6 +676,18 @@ request format: See more detail about [Firebase Cloud Messaging HTTP Protocol reference](https://firebase.google.com/docs/cloud-messaging/http-server-ref#send-downstream). +### Huawei notification + +* app_id: app id from huawei developer console +* huawei_data: mapped to data +* huawei_notification: mapped to notification +* huawei_ttl: mapped to ttl +* huawei_collapse_key: mapped to collapse_key +* bi_tag: +* fast_app_target: + +See more detail about [Huawei Mobulse Services Push API reference](https://developer.huawei.com/consumer/en/doc/development/HMS-References/push-sendapi). + ### iOS Example Send normal notification. @@ -776,6 +852,71 @@ Send messages to topics } ``` +### Huawei Example + +Send normal notification. + +```json +{ + "notifications": [ + { + "tokens": ["token_a", "token_b"], + "platform": 3, + "message": "Hello World Huawei!", + "title": "You got message" + } + ] +} +``` + +Add `notification` payload. + +```json +{ + "notifications": [ + { + "tokens": ["token_a", "token_b"], + "platform": 3, + "message": "Hello World Huawei!", + "title": "You got message", + "huawei_notification" : { + "icon": "myicon", + "color": "#112244" + } + } + ] +} +``` + +Add other fields which user defined via `huawei_data` field. + +```json +{ + "notifications": [ + { + "tokens": ["token_a", "token_b"], + "platform": 3, + "huawei_data": "{'title' : 'Mario','message' : 'great match!', 'Room' : 'PortugalVSDenmark'}" + } + ] +} +``` + +Send messages to topics + +```json +{ + "notifications": [ + { + "topic": "foo-bar", + "platform": 3, + "message": "This is a Huawei Mobile Services Topic Message", + "title": "You got message" + } + ] +} +``` + ### Response body Error response message table: diff --git a/config/config.go b/config/config.go index c4c32be..3fa5997 100644 --- a/config/config.go +++ b/config/config.go @@ -56,6 +56,12 @@ android: apikey: "YOUR_API_KEY" max_retry: 0 # resend fail notification, default value zero is disabled +huawei: + enabled: true + apikey: "YOUR_API_KEY" + appid: "YOUR_APP_ID" + max_retry: 0 # resend fail notification, default value zero is disabled + ios: enabled: false key_path: "" @@ -98,6 +104,7 @@ type ConfYaml struct { Core SectionCore `yaml:"core"` API SectionAPI `yaml:"api"` Android SectionAndroid `yaml:"android"` + Huawei SectionHuawei `yaml:"huawei"` Ios SectionIos `yaml:"ios"` Log SectionLog `yaml:"log"` Stat SectionStat `yaml:"stat"` @@ -152,6 +159,14 @@ type SectionAndroid struct { MaxRetry int `yaml:"max_retry"` } +// SectionHuawei is sub section of config. +type SectionHuawei struct { + Enabled bool `yaml:"enabled"` + APIKey string `yaml:"apikey"` + APPId string `yaml:"appid"` + MaxRetry int `yaml:"max_retry"` +} + // SectionIos is sub section of config. type SectionIos struct { Enabled bool `yaml:"enabled"` @@ -303,6 +318,12 @@ func LoadConf(confPath string) (ConfYaml, error) { conf.Android.APIKey = viper.GetString("android.apikey") conf.Android.MaxRetry = viper.GetInt("android.max_retry") + // Huawei + conf.Huawei.Enabled = viper.GetBool("huawei.enabled") + conf.Huawei.APIKey = viper.GetString("huawei.apikey") + conf.Huawei.APPId = viper.GetString("huawei.appid") + conf.Huawei.MaxRetry = viper.GetInt("huawei.max_retry") + // iOS conf.Ios.Enabled = viper.GetBool("ios.enabled") conf.Ios.KeyPath = viper.GetString("ios.key_path") diff --git a/config/testdata/config.yml b/config/testdata/config.yml index 9680aa8..4ddad6b 100644 --- a/config/testdata/config.yml +++ b/config/testdata/config.yml @@ -43,6 +43,12 @@ android: apikey: "YOUR_API_KEY" max_retry: 0 # resend fail notification, default value zero is disabled +huawei: + enabled: true + apikey: "YOUR_API_KEY" + appid: "YOUR_APP_ID" + max_retry: 0 # resend fail notification, default value zero is disabled + ios: enabled: false key_path: "key.pem" diff --git a/go.mod b/go.mod index 279f261..ff6a43e 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/mattn/go-isatty v0.0.12 github.com/mitchellh/mapstructure v1.1.2 + github.com/msalihkarakasli/go-hms-push v0.0.0-20200616114002-91cd23dfeed4 github.com/pelletier/go-toml v1.6.0 // indirect github.com/prometheus/client_golang v1.2.1 github.com/prometheus/client_model v0.0.0-20191202183732-d1d2010b5bee // indirect diff --git a/go.sum b/go.sum index 9c92e1d..8cc13cf 100644 --- a/go.sum +++ b/go.sum @@ -180,6 +180,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200615195759-7a970b71c22c h1:OG1NL81rADpFFYqfWQFGWQZLgxajiOS9cZApwDaPpNg= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200615195759-7a970b71c22c/go.mod h1:4X2lQHsWGt+e3uRK124A6ndq3IIVymTAzEI9A1kIQKc= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616075053-ff4aeca924ee h1:GhSc8Bz6jeSlPukx9t3eyWK30udyAoACCR9jtci4tVI= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616075053-ff4aeca924ee/go.mod h1:4X2lQHsWGt+e3uRK124A6ndq3IIVymTAzEI9A1kIQKc= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616081907-aa182701f7a8 h1:anVdFSIgATqyCKkgRV8TZs8JLvzvlocQmYdW4/QXlfw= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616081907-aa182701f7a8/go.mod h1:4X2lQHsWGt+e3uRK124A6ndq3IIVymTAzEI9A1kIQKc= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616090445-02629345434a h1:8RhjWJXeVuHBGjGDbjNVT9/k1oOblgYkZR11hTfbHXM= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616090445-02629345434a/go.mod h1:4X2lQHsWGt+e3uRK124A6ndq3IIVymTAzEI9A1kIQKc= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616114002-91cd23dfeed4 h1:3cAiZwpOmIXbP+3/SAriDi9XlxBwZCGhS0UGhnlYhi0= +github.com/msalihkarakasli/go-hms-push v0.0.0-20200616114002-91cd23dfeed4/go.mod h1:4X2lQHsWGt+e3uRK124A6ndq3IIVymTAzEI9A1kIQKc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/gorush/const.go b/gorush/const.go index cf8e5da..ac1a82c 100644 --- a/gorush/const.go +++ b/gorush/const.go @@ -5,6 +5,8 @@ const ( PlatFormIos = iota + 1 // PlatFormAndroid constant is 2 for Android PlatFormAndroid + // PlatFormHuawei constant is 3 for Huawei + PlatFormHuawei ) const ( @@ -21,4 +23,6 @@ const ( IosErrorKey = "gorush-ios-error-count" AndroidSuccessKey = "gorush-android-success-count" AndroidErrorKey = "gorush-android-error-count" + HuaweiSuccessKey = "gorush-huawei-success-count" + HuaweiErrorKey = "gorush-huawei-error-count" ) diff --git a/gorush/global.go b/gorush/global.go index b97130d..11a2d60 100644 --- a/gorush/global.go +++ b/gorush/global.go @@ -5,6 +5,7 @@ import ( "github.com/appleboy/gorush/storage" "github.com/appleboy/go-fcm" + "github.com/msalihkarakasli/go-hms-push/push/core" "github.com/sideshow/apns2" "github.com/sirupsen/logrus" ) @@ -18,6 +19,8 @@ var ( ApnsClient *apns2.Client // FCMClient is apns client FCMClient *fcm.Client + // HMSClient is Huawei push client + HMSClient *core.HMSClient // LogAccess is log server request log LogAccess *logrus.Logger // LogError is log server error log diff --git a/gorush/log.go b/gorush/log.go index d333071..76fcbcb 100644 --- a/gorush/log.go +++ b/gorush/log.go @@ -122,6 +122,8 @@ func colorForPlatForm(platform int) string { return blue case PlatFormAndroid: return yellow + case PlatFormHuawei: + return green default: return reset } @@ -133,6 +135,8 @@ func typeForPlatForm(platform int) string { return "ios" case PlatFormAndroid: return "android" + case PlatFormHuawei: + return "huawei" default: return "" } diff --git a/gorush/metrics.go b/gorush/metrics.go index 74e0922..71a9f2a 100644 --- a/gorush/metrics.go +++ b/gorush/metrics.go @@ -14,6 +14,8 @@ type Metrics struct { IosError *prometheus.Desc AndroidSuccess *prometheus.Desc AndroidError *prometheus.Desc + HuaweiSuccess *prometheus.Desc + HuaweiError *prometheus.Desc QueueUsage *prometheus.Desc } @@ -46,6 +48,16 @@ func NewMetrics() Metrics { "Number of android fail count", nil, nil, ), + HuaweiSuccess: prometheus.NewDesc( + namespace+"huawei_success", + "Number of huawei success count", + nil, nil, + ), + HuaweiError: prometheus.NewDesc( + namespace+"huawei_fail", + "Number of huawei fail count", + nil, nil, + ), QueueUsage: prometheus.NewDesc( namespace+"queue_usage", "Length of internal queue", @@ -61,6 +73,8 @@ func (c Metrics) Describe(ch chan<- *prometheus.Desc) { ch <- c.IosError ch <- c.AndroidSuccess ch <- c.AndroidError + ch <- c.HuaweiSuccess + ch <- c.HuaweiError ch <- c.QueueUsage } @@ -91,6 +105,16 @@ func (c Metrics) Collect(ch chan<- prometheus.Metric) { prometheus.GaugeValue, float64(StatStorage.GetAndroidError()), ) + ch <- prometheus.MustNewConstMetric( + c.HuaweiSuccess, + prometheus.GaugeValue, + float64(StatStorage.GetHuaweiSuccess()), + ) + ch <- prometheus.MustNewConstMetric( + c.HuaweiError, + prometheus.GaugeValue, + float64(StatStorage.GetHuaweiError()), + ) ch <- prometheus.MustNewConstMetric( c.QueueUsage, prometheus.GaugeValue, diff --git a/gorush/notification.go b/gorush/notification.go index f38e66d..c363726 100644 --- a/gorush/notification.go +++ b/gorush/notification.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/appleboy/go-fcm" + "github.com/msalihkarakasli/go-hms-push/push/model" ) // D provide string array @@ -79,6 +80,15 @@ type PushNotification struct { Condition string `json:"condition,omitempty"` Notification *fcm.Notification `json:"notification,omitempty"` + // Huawei + APPId string `json:"app_id,omitempty"` + HuaweiNotification *model.AndroidNotification `json:"huawei_notification,omitempty"` + HuaweiData string `json:"huawei_data,omitempty"` + HuaweiCollapseKey int `json:"huawei_collapse_key,omitempty"` + HuaweiTTL string `json:"huawei_ttl,omitempty"` + BiTag string `json:"bi_tag,omitempty"` + FastAppTarget int `json:"fast_app_target,omitempty"` + // iOS Expiration *int64 `json:"expiration,omitempty"` ApnsID string `json:"apns_id,omitempty"` @@ -121,8 +131,16 @@ 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 != "" + + if p.Platform == PlatFormAndroid { + return p.To != "" && strings.HasPrefix(p.To, "/topics/") || p.Condition != "" + } + + if p.Platform == PlatFormHuawei { + return p.Topic != "" || p.Condition != "" + } + + return false } // CheckMessage for check request message @@ -148,6 +166,12 @@ func CheckMessage(req PushNotification) error { return errors.New(msg) } + if req.Platform == PlatFormHuawei && len(req.Tokens) > 500 { + msg = "the message may specify at most 500 registration IDs for Huawei" + 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) { msg = "the message's TimeToLive field must be an integer " + @@ -176,8 +200,8 @@ func SetProxy(proxy string) error { // CheckPushConf provide check your yml config. func CheckPushConf() error { - if !PushConf.Ios.Enabled && !PushConf.Android.Enabled { - return errors.New("Please enable iOS or Android config in yml config") + if !PushConf.Ios.Enabled && !PushConf.Android.Enabled && !PushConf.Huawei.Enabled { + return errors.New("Please enable iOS, Android or Huawei config in yml config") } if PushConf.Ios.Enabled { @@ -199,5 +223,15 @@ func CheckPushConf() error { } } + if PushConf.Huawei.Enabled { + if PushConf.Huawei.APIKey == "" { + return errors.New("Missing Huawei API Key") + } + + if PushConf.Huawei.APPId == "" { + return errors.New("Missing Huawei APP Id") + } + } + return nil } diff --git a/gorush/notification_apns_test.go b/gorush/notification_apns_test.go index 35ec3be..a24b4f1 100644 --- a/gorush/notification_apns_test.go +++ b/gorush/notification_apns_test.go @@ -27,11 +27,12 @@ const authkeyValidP8 = `LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5 func TestDisabledAndroidIosConf(t *testing.T) { PushConf, _ = config.LoadConf("") PushConf.Android.Enabled = false + PushConf.Huawei.Enabled = false err := CheckPushConf() assert.Error(t, err) - assert.Equal(t, "Please enable iOS or Android config in yml config", err.Error()) + assert.Equal(t, "Please enable iOS, Android or Huawei config in yml config", err.Error()) } func TestMissingIOSCertificate(t *testing.T) { diff --git a/gorush/notification_hms.go b/gorush/notification_hms.go new file mode 100644 index 0000000..b84e155 --- /dev/null +++ b/gorush/notification_hms.go @@ -0,0 +1,238 @@ +package gorush + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "sync" + + "github.com/msalihkarakasli/go-hms-push/push/config" + "github.com/msalihkarakasli/go-hms-push/push/core" + "github.com/msalihkarakasli/go-hms-push/push/model" +) + +var ( + pushError error + pushClient *core.HMSClient + once sync.Once +) + +// GetPushClient use for create HMS Push +func GetPushClient(conf *config.Config) (*core.HMSClient, error) { + once.Do(func() { + client, err := core.NewHttpClient(conf) + if err != nil { + fmt.Printf("Failed to new common client! Error is %s\n", err.Error()) + panic(err) + } + pushClient = client + pushError = err + }) + + return pushClient, pushError +} + +// InitHMSClient use for initialize HMS Client. +func InitHMSClient(apiKey string, appID string) (*core.HMSClient, error) { + + if apiKey == "" { + return nil, errors.New("Missing Huawei API Key") + } + + if appID == "" { + return nil, errors.New("Missing Huawei APP Id") + } + + var conf = &config.Config{ + AppId: appID, + AppSecret: apiKey, + AuthUrl: "https://login.cloud.huawei.com/oauth2/v2/token", + PushUrl: "https://api.push.hicloud.com", + } + + if apiKey != PushConf.Huawei.APIKey || appID != PushConf.Huawei.APPId { + return GetPushClient(conf) + } + + if HMSClient == nil { + return GetPushClient(conf) + } + + return HMSClient, nil +} + +// GetHuaweiNotification use for define HMS notification. +// HTTP Connection Server Reference for HMS +// https://developer.huawei.com/consumer/en/doc/development/HMS-References/push-sendapi +func GetHuaweiNotification(req PushNotification) (*model.MessageRequest, error) { + + msgRequest := model.NewNotificationMsgRequest() + + msgRequest.Message.Android = model.GetDefaultAndroid() + + if len(req.Tokens) > 0 { + msgRequest.Message.Token = req.Tokens + } + + if len(req.Topic) > 0 { + msgRequest.Message.Topic = req.Topic + } + + if len(req.To) > 0 { + msgRequest.Message.Topic = req.To + } + + if len(req.Condition) > 0 { + msgRequest.Message.Condition = req.Condition + } + + if req.Priority == "high" { + msgRequest.Message.Android.Urgency = "HIGH" + } + + //if req.HuaweiCollapseKey != nil { + msgRequest.Message.Android.CollapseKey = req.HuaweiCollapseKey + //} + + if len(req.Category) > 0 { + msgRequest.Message.Android.Category = req.Category + } + + if len(req.HuaweiTTL) > 0 { + msgRequest.Message.Android.TTL = req.HuaweiTTL + } + + if len(req.BiTag) > 0 { + msgRequest.Message.Android.BiTag = req.BiTag + } + + //if req.FastAppTarget != nil { + msgRequest.Message.Android.FastAppTarget = req.FastAppTarget + //} + + //Add data fields + if len(req.HuaweiData) > 0 { + msgRequest.Message.Data = req.HuaweiData + } else { + //Notification Message + msgRequest.Message.Android.Notification = model.GetDefaultAndroidNotification() + + n := msgRequest.Message.Android.Notification + isNotificationSet := false + + if req.HuaweiNotification != nil { + isNotificationSet = true + n = req.HuaweiNotification + + if n.ClickAction == nil { + n.ClickAction = model.GetDefaultClickAction() + } + } + + if len(req.Message) > 0 { + isNotificationSet = true + n.Body = req.Message + } + + if len(req.Title) > 0 { + isNotificationSet = true + n.Title = req.Title + } + + if len(req.Image) > 0 { + isNotificationSet = true + n.Image = req.Image + } + + if v, ok := req.Sound.(string); ok && len(v) > 0 { + isNotificationSet = true + n.Sound = v + } else { + n.DefaultSound = true + } + + if isNotificationSet { + msgRequest.Message.Android.Notification = n + } + } + + // if len(req.Apns) > 0 { + // notification.Apns = req.Apns + // } + + b, err := json.Marshal(msgRequest) + if err != nil { + fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) + return nil, err + } + + fmt.Printf("Default message is %s\n", string(b)) + return msgRequest, nil +} + +// PushToHuawei provide send notification to Android server. +func PushToHuawei(req PushNotification) bool { + LogAccess.Debug("Start push notification for Huawei") + + var ( + client *core.HMSClient + retryCount = 0 + maxRetry = PushConf.Huawei.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, err := GetHuaweiNotification(req) + + client, err = InitHMSClient(PushConf.Huawei.APIKey, PushConf.Huawei.APPId) + + if err != nil { + // HMS server error + LogError.Error("HMS server error: " + err.Error()) + return false + } + + res, err := client.SendMessage(context.Background(), notification) + if err != nil { + // Send Message error + LogError.Error("HMS server send message error: " + err.Error()) + return false + } + + fmt.Println(res.Code) + fmt.Println(res.Msg) + fmt.Println(res.RequestId) + + // Huawei Push Send API does not support exact results for each token + if res.Code == "80000000" { + StatStorage.AddHuaweiSuccess(int64(1)) + LogAccess.Debug(fmt.Sprintf("Huwaei Send Notification is completed successfully!")) + } else { + isError = true + StatStorage.AddHuaweiError(int64(1)) + LogAccess.Debug(fmt.Sprintf("Huwaei Send Notification is failed!")) + } + + if isError && retryCount < maxRetry { + retryCount++ + + // resend all tokens + goto Retry + } + + return isError +} diff --git a/gorush/notification_hms_test.go b/gorush/notification_hms_test.go new file mode 100644 index 0000000..abdc1f3 --- /dev/null +++ b/gorush/notification_hms_test.go @@ -0,0 +1,67 @@ +package gorush + +import ( + "context" + "log" + "sync" + "testing" + + "github.com/appleboy/gorush/config" + "github.com/stretchr/testify/assert" +) + +func init() { + PushConf, _ = config.LoadConf("") + if err := InitLog(); err != nil { + log.Fatal(err) + } + + ctx := context.Background() + wg := &sync.WaitGroup{} + wg.Add(int(PushConf.Core.WorkerNum)) + InitWorkers(ctx, wg, PushConf.Core.WorkerNum, PushConf.Core.QueueNum) + + if err := InitAppStatus(); err != nil { + log.Fatal(err) + } +} + +func TestMissingHuaweiAPIKey(t *testing.T) { + PushConf, _ = config.LoadConf("") + + PushConf.Huawei.Enabled = true + PushConf.Huawei.APIKey = "" + + err := CheckPushConf() + + assert.Error(t, err) + assert.Equal(t, "Missing Huawei API Key", err.Error()) +} + +func TestMissingHuaweiAPPId(t *testing.T) { + PushConf, _ = config.LoadConf("") + + PushConf.Huawei.Enabled = true + PushConf.Huawei.APPId = "" + + err := CheckPushConf() + + assert.Error(t, err) + assert.Equal(t, "Missing Huawei APP Id", err.Error()) +} + +func TestMissingKeyForInitHMSClient(t *testing.T) { + client, err := InitHMSClient("", "APP_ID") + + assert.Nil(t, client) + assert.Error(t, err) + assert.Equal(t, "Missing Huawei API Key", err.Error()) +} + +func TestMissingAppIDForInitHMSClient(t *testing.T) { + client, err := InitHMSClient("APP_KEY", "") + + assert.Nil(t, client) + assert.Error(t, err) + assert.Equal(t, "Missing Huawei APP Id", err.Error()) +} diff --git a/gorush/status.go b/gorush/status.go index 7078d52..7495411 100644 --- a/gorush/status.go +++ b/gorush/status.go @@ -26,6 +26,7 @@ type StatusApp struct { TotalCount int64 `json:"total_count"` Ios IosStatus `json:"ios"` Android AndroidStatus `json:"android"` + Huawei HuaweiStatus `json:"huawei"` } // AndroidStatus is android structure @@ -40,6 +41,12 @@ type IosStatus struct { PushError int64 `json:"push_error"` } +// HuaweiStatus is huawei structure +type HuaweiStatus struct { + PushSuccess int64 `json:"push_success"` + PushError int64 `json:"push_error"` +} + // InitAppStatus for initialize app status func InitAppStatus() error { LogAccess.Info("Init App Status Engine as ", PushConf.Stat.Engine) @@ -81,6 +88,8 @@ func appStatusHandler(c *gin.Context) { result.Ios.PushError = StatStorage.GetIosError() result.Android.PushSuccess = StatStorage.GetAndroidSuccess() result.Android.PushError = StatStorage.GetAndroidError() + result.Huawei.PushSuccess = StatStorage.GetHuaweiSuccess() + result.Huawei.PushError = StatStorage.GetHuaweiError() c.JSON(http.StatusOK, result) } diff --git a/gorush/worker.go b/gorush/worker.go index 6eb6081..50cf7c0 100644 --- a/gorush/worker.go +++ b/gorush/worker.go @@ -15,7 +15,7 @@ func InitWorkers(ctx context.Context, wg *sync.WaitGroup, workerNum int64, queue } } -// SendNotification is send message to iOS or Android +// SendNotification is send message to iOS, Android or Huawei func SendNotification(ctx context.Context, req PushNotification) { if PushConf.Core.Sync { defer req.WaitDone() @@ -26,6 +26,8 @@ func SendNotification(ctx context.Context, req PushNotification) { PushToIOS(req) case PlatFormAndroid: PushToAndroid(req) + case PlatFormHuawei: + PushToHuawei(req) } } @@ -62,6 +64,10 @@ func queueNotification(ctx context.Context, req RequestPush) (int, []LogPushEntr if !PushConf.Android.Enabled { continue } + case PlatFormHuawei: + if !PushConf.Huawei.Enabled { + continue + } } newNotification = append(newNotification, notification) } diff --git a/main.go b/main.go index bee755a..9cd8cdc 100644 --- a/main.go +++ b/main.go @@ -66,6 +66,10 @@ func main() { flag.StringVar(&opts.Ios.Password, "password", "", "iOS certificate password for gorush") flag.StringVar(&opts.Android.APIKey, "k", "", "Android api key configuration for gorush") flag.StringVar(&opts.Android.APIKey, "apikey", "", "Android api key configuration for gorush") + flag.StringVar(&opts.Huawei.APIKey, "hk", "", "Huawei api key configuration for gorush") + flag.StringVar(&opts.Huawei.APIKey, "hmskey", "", "Huawei api key configuration for gorush") + flag.StringVar(&opts.Huawei.APPId, "hid", "", "HMS app id configuration for gorush") + flag.StringVar(&opts.Huawei.APPId, "hmsid", "", "HMS app id configuration for gorush") flag.StringVar(&opts.Core.Address, "A", "", "address to bind") flag.StringVar(&opts.Core.Address, "address", "", "address to bind") flag.StringVar(&opts.Core.Port, "p", "", "port number for gorush") @@ -79,6 +83,7 @@ func main() { flag.StringVar(&message, "message", "", "notification message") flag.StringVar(&title, "title", "", "notification title") flag.BoolVar(&opts.Android.Enabled, "android", false, "send android notification") + flag.BoolVar(&opts.Huawei.Enabled, "huawei", false, "send huawei notification") flag.BoolVar(&opts.Ios.Enabled, "ios", false, "send ios notification") flag.BoolVar(&opts.Ios.Production, "production", false, "production mode in iOS") flag.StringVar(&topic, "topic", "", "apns topic in iOS") @@ -129,6 +134,14 @@ func main() { gorush.PushConf.Android.APIKey = opts.Android.APIKey } + if opts.Huawei.APIKey != "" { + gorush.PushConf.Huawei.APIKey = opts.Huawei.APIKey + } + + if opts.Huawei.APPId != "" { + gorush.PushConf.Huawei.APPId = opts.Huawei.APPId + } + if opts.Stat.Engine != "" { gorush.PushConf.Stat.Engine = opts.Stat.Engine } @@ -202,6 +215,40 @@ func main() { return } + // send huawei notification + if opts.Huawei.Enabled { + gorush.PushConf.Huawei.Enabled = opts.Huawei.Enabled + req := gorush.PushNotification{ + Platform: gorush.PlatFormHuawei, + Message: message, + 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) + + if err != nil { + gorush.LogError.Fatal(err) + } + + if err := gorush.InitAppStatus(); err != nil { + return + } + + gorush.PushToHuawei(req) + + return + } + // send ios notification if opts.Ios.Enabled { if opts.Ios.Production { @@ -287,6 +334,10 @@ func main() { gorush.LogError.Fatal(err) } + if _, err = gorush.InitHMSClient(gorush.PushConf.Huawei.APIKey, gorush.PushConf.Huawei.APPId); err != nil { + gorush.LogError.Fatal(err) + } + var g errgroup.Group // Run httpd server @@ -345,8 +396,12 @@ iOS Options: Android Options: -k, --apikey Android API Key --android enabled android (default: false) +Huawei Options: + -hk, --hmskey HMS API Key + -hid, --hmsid HMS APP Id + --huawei enabled huawei (default: false) Common Options: - --topic iOS or Android topic message + --topic iOS, Android or Huawei topic message -h, --help Show this message -v, --version Show version ` diff --git a/storage/badger/badger.go b/storage/badger/badger.go index c40aea8..b017d69 100644 --- a/storage/badger/badger.go +++ b/storage/badger/badger.go @@ -59,6 +59,8 @@ func (s *Storage) Reset() { s.setBadger(storage.IosErrorKey, 0) s.setBadger(storage.AndroidSuccessKey, 0) s.setBadger(storage.AndroidErrorKey, 0) + s.setBadger(storage.HuaweiSuccessKey, 0) + s.setBadger(storage.HuaweiErrorKey, 0) } func (s *Storage) setBadger(key string, count int64) { @@ -129,6 +131,18 @@ func (s *Storage) AddAndroidError(count int64) { s.setBadger(storage.AndroidErrorKey, total) } +// AddHuaweiSuccess record counts of success Huawei push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + total := s.GetHuaweiSuccess() + count + s.setBadger(storage.HuaweiSuccessKey, total) +} + +// AddHuaweiError record counts of error Huawei push notification. +func (s *Storage) AddHuaweiError(count int64) { + total := s.GetHuaweiError() + count + s.setBadger(storage.HuaweiErrorKey, total) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { var count int64 @@ -168,3 +182,19 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + var count int64 + s.getBadger(storage.HuaweiSuccessKey, &count) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + var count int64 + s.getBadger(storage.HuaweiErrorKey, &count) + + return count +} diff --git a/storage/boltdb/boltdb.go b/storage/boltdb/boltdb.go index 0e46b62..b1f3425 100644 --- a/storage/boltdb/boltdb.go +++ b/storage/boltdb/boltdb.go @@ -45,6 +45,8 @@ func (s *Storage) Reset() { s.setBoltDB(storage.IosErrorKey, 0) s.setBoltDB(storage.AndroidSuccessKey, 0) s.setBoltDB(storage.AndroidErrorKey, 0) + s.setBoltDB(storage.HuaweiSuccessKey, 0) + s.setBoltDB(storage.HuaweiErrorKey, 0) } func (s *Storage) setBoltDB(key string, count int64) { @@ -91,6 +93,18 @@ func (s *Storage) AddAndroidError(count int64) { s.setBoltDB(storage.AndroidErrorKey, total) } +// AddHuaweiSuccess record counts of success Huawei push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + total := s.GetHuaweiSuccess() + count + s.setBoltDB(storage.HuaweiSuccessKey, total) +} + +// AddHuaweiError record counts of error Huawei push notification. +func (s *Storage) AddHuaweiError(count int64) { + total := s.GetHuaweiError() + count + s.setBoltDB(storage.HuaweiErrorKey, total) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { var count int64 @@ -130,3 +144,19 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + var count int64 + s.getBoltDB(storage.HuaweiSuccessKey, &count) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + var count int64 + s.getBoltDB(storage.HuaweiErrorKey, &count) + + return count +} diff --git a/storage/buntdb/buntdb.go b/storage/buntdb/buntdb.go index 259f96d..0dda8cd 100644 --- a/storage/buntdb/buntdb.go +++ b/storage/buntdb/buntdb.go @@ -47,6 +47,8 @@ func (s *Storage) Reset() { s.setBuntDB(storage.IosErrorKey, 0) s.setBuntDB(storage.AndroidSuccessKey, 0) s.setBuntDB(storage.AndroidErrorKey, 0) + s.setBuntDB(storage.HuaweiSuccessKey, 0) + s.setBuntDB(storage.HuaweiErrorKey, 0) } func (s *Storage) setBuntDB(key string, count int64) { @@ -104,6 +106,18 @@ func (s *Storage) AddAndroidError(count int64) { s.setBuntDB(storage.AndroidErrorKey, total) } +// AddHuaweiSuccess record counts of success Huawei push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + total := s.GetHuaweiSuccess() + count + s.setBuntDB(storage.HuaweiSuccessKey, total) +} + +// AddHuaweiError record counts of error Huawei push notification. +func (s *Storage) AddHuaweiError(count int64) { + total := s.GetHuaweiError() + count + s.setBuntDB(storage.HuaweiErrorKey, total) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { var count int64 @@ -143,3 +157,19 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + var count int64 + s.getBuntDB(storage.HuaweiSuccessKey, &count) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + var count int64 + s.getBuntDB(storage.HuaweiErrorKey, &count) + + return count +} diff --git a/storage/leveldb/leveldb.go b/storage/leveldb/leveldb.go index 4f4288d..82d3601 100644 --- a/storage/leveldb/leveldb.go +++ b/storage/leveldb/leveldb.go @@ -56,6 +56,8 @@ func (s *Storage) Reset() { s.setLevelDB(storage.IosErrorKey, 0) s.setLevelDB(storage.AndroidSuccessKey, 0) s.setLevelDB(storage.AndroidErrorKey, 0) + s.setLevelDB(storage.HuaweiSuccessKey, 0) + s.setLevelDB(storage.HuaweiErrorKey, 0) } // AddTotalCount record push notification count. @@ -88,6 +90,18 @@ func (s *Storage) AddAndroidError(count int64) { s.setLevelDB(storage.AndroidErrorKey, total) } +// AddHuaweiSuccess record counts of success Huawei push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + total := s.GetHuaweiSuccess() + count + s.setLevelDB(storage.HuaweiSuccessKey, total) +} + +// AddHuaweiError record counts of error Huawei push notification. +func (s *Storage) AddHuaweiError(count int64) { + total := s.GetHuaweiError() + count + s.setLevelDB(storage.HuaweiErrorKey, total) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { var count int64 @@ -127,3 +141,19 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + var count int64 + s.getLevelDB(storage.HuaweiSuccessKey, &count) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + var count int64 + s.getLevelDB(storage.HuaweiErrorKey, &count) + + return count +} diff --git a/storage/memory/memory.go b/storage/memory/memory.go index c677654..7c0e961 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -9,6 +9,7 @@ type statApp struct { TotalCount int64 `json:"total_count"` Ios IosStatus `json:"ios"` Android AndroidStatus `json:"android"` + Huawei HuaweiStatus `json:"huawei"` } // AndroidStatus is android structure @@ -23,6 +24,12 @@ type IosStatus struct { PushError int64 `json:"push_error"` } +// HuaweiStatus is android structure +type HuaweiStatus struct { + PushSuccess int64 `json:"push_success"` + PushError int64 `json:"push_error"` +} + // New func implements the storage interface for gorush (https://github.com/appleboy/gorush) func New() *Storage { return &Storage{ @@ -52,6 +59,8 @@ func (s *Storage) Reset() { atomic.StoreInt64(&s.stat.Ios.PushError, 0) atomic.StoreInt64(&s.stat.Android.PushSuccess, 0) atomic.StoreInt64(&s.stat.Android.PushError, 0) + atomic.StoreInt64(&s.stat.Huawei.PushSuccess, 0) + atomic.StoreInt64(&s.stat.Huawei.PushError, 0) } // AddTotalCount record push notification count. @@ -79,6 +88,16 @@ func (s *Storage) AddAndroidError(count int64) { atomic.AddInt64(&s.stat.Android.PushError, count) } +// AddHuaweiSuccess record counts of success Huawei push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + atomic.AddInt64(&s.stat.Huawei.PushSuccess, count) +} + +// AddHuaweiError record counts of error Huawei push notification. +func (s *Storage) AddHuaweiError(count int64) { + atomic.AddInt64(&s.stat.Huawei.PushError, count) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { count := atomic.LoadInt64(&s.stat.TotalCount) @@ -113,3 +132,17 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + count := atomic.LoadInt64(&s.stat.Huawei.PushSuccess) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + count := atomic.LoadInt64(&s.stat.Huawei.PushError) + + return count +} diff --git a/storage/redis/redis.go b/storage/redis/redis.go index 2fe5241..7df20e4 100644 --- a/storage/redis/redis.go +++ b/storage/redis/redis.go @@ -55,6 +55,8 @@ func (s *Storage) Reset() { s.client.Set(storage.IosErrorKey, int64(0), 0) s.client.Set(storage.AndroidSuccessKey, int64(0), 0) s.client.Set(storage.AndroidErrorKey, int64(0), 0) + s.client.Set(storage.HuaweiSuccessKey, int64(0), 0) + s.client.Set(storage.HuaweiErrorKey, int64(0), 0) } // AddTotalCount record push notification count. @@ -82,6 +84,16 @@ func (s *Storage) AddAndroidError(count int64) { s.client.IncrBy(storage.AndroidErrorKey, count) } +// AddHuaweiSuccess record counts of success Android push notification. +func (s *Storage) AddHuaweiSuccess(count int64) { + s.client.IncrBy(storage.HuaweiSuccessKey, count) +} + +// AddHuaweiError record counts of error Android push notification. +func (s *Storage) AddHuaweiError(count int64) { + s.client.IncrBy(storage.HuaweiErrorKey, count) +} + // GetTotalCount show counts of all notification. func (s *Storage) GetTotalCount() int64 { var count int64 @@ -121,3 +133,19 @@ func (s *Storage) GetAndroidError() int64 { return count } + +// GetHuaweiSuccess show success counts of Huawei notification. +func (s *Storage) GetHuaweiSuccess() int64 { + var count int64 + s.getInt64(storage.HuaweiSuccessKey, &count) + + return count +} + +// GetHuaweiError show error counts of Huawei notification. +func (s *Storage) GetHuaweiError() int64 { + var count int64 + s.getInt64(storage.HuaweiErrorKey, &count) + + return count +} diff --git a/storage/storage.go b/storage/storage.go index ad51928..c89c514 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -15,6 +15,12 @@ const ( // AndroidErrorKey is key name for android error count of storage AndroidErrorKey = "gorush-android-error-count" + + // HuaweiSuccessKey is key name for huawei success count of storage + HuaweiSuccessKey = "gorush-huawei-success-count" + + // HuaweiErrorKey is key name for huawei error count of storage + HuaweiErrorKey = "gorush-huawei-error-count" ) // Storage interface @@ -26,10 +32,14 @@ type Storage interface { AddIosError(int64) AddAndroidSuccess(int64) AddAndroidError(int64) + AddHuaweiSuccess(int64) + AddHuaweiError(int64) GetTotalCount() int64 GetIosSuccess() int64 GetIosError() int64 GetAndroidSuccess() int64 GetAndroidError() int64 + GetHuaweiSuccess() int64 + GetHuaweiError() int64 Close() error }