huawei_push/push.go

360 lines
10 KiB
Go

// See documentation here:
//
// Sending notifications
//
// https://developer.huawei.com/consumer/en/doc/development/HMSCore-References/https-send-api-0000001050986197
//
// OAuth2.0
//
// https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/oauth2-0000001212610981
package huawei_push
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"git.bit5.ru/backend/errors"
"golang.org/x/oauth2"
"github.com/go-logr/logr"
)
func MakeSendEndpointV1(appId string) string {
return fmt.Sprintf("https://push-api.cloud.huawei.com/v1/%s/messages:send", appId)
}
func MakeSendEndpointV2(projectId string) string {
return fmt.Sprintf("https://push-api.cloud.huawei.com/v2/%s/messages:send", projectId)
}
type ClientConfig struct {
SendEndpoint string
}
type Client struct {
cfg ClientConfig
ts oauth2.TokenSource
httpClient *http.Client
logger logr.Logger
}
func NewClient(cfg ClientConfig, ts oauth2.TokenSource, httpClient *http.Client, logger logr.Logger) *Client {
return &Client{
cfg: cfg,
ts: ts,
httpClient: httpClient,
logger: logger,
}
}
func (c *Client) SendMessage(msg Message) (SendResponse, error) {
sendRequest := SendRequest{
ValidateOnly: false,
Message: msg,
}
return c.doSend(sendRequest)
}
func (c *Client) ValidateMessage(msg Message) (SendResponse, error) {
sendRequest := SendRequest{
ValidateOnly: true,
Message: msg,
}
return c.doSend(sendRequest)
}
func (c *Client) doSend(req SendRequest) (SendResponse, error) {
accessToken, err := c.ts.Token()
if err != nil {
return SendResponse{}, err
}
data, err := json.Marshal(req)
if err != nil {
return SendResponse{}, errors.WithStack(err)
}
request, err := http.NewRequest(http.MethodPost, c.cfg.SendEndpoint, bytes.NewReader(data))
if err != nil {
return SendResponse{}, errors.WithStack(err)
}
accessToken.SetAuthHeader(request)
request.Header.Set("Content-Type", "application/json; charset=UTF-8")
response, err := c.httpClient.Do(request)
if err != nil {
return SendResponse{}, errors.WithStack(err)
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return SendResponse{}, errors.WithStack(err)
}
if response.StatusCode != http.StatusOK {
return SendResponse{}, errors.New(string(body))
}
var resp SendResponse
if err := json.Unmarshal(body, &resp); err != nil {
return SendResponse{}, errors.WithStack(err)
}
if resp.IsPartialSuccess() {
if err := json.Unmarshal([]byte(resp.Message), &resp.PartialResponse); err != nil {
return SendResponse{}, errors.WithStack(err)
}
}
return resp, nil
}
type SendRequest struct {
ValidateOnly bool `json:"validate_only"`
Message Message `json:"message"`
}
type ResultCode string
const (
ResultCode_Success ResultCode = "80000000"
ResultCode_PartialSuccess ResultCode = "80100000"
)
type SendResponse struct {
Code ResultCode `json:"code"`
Message string `json:"msg"`
RequestId string `json:"requestId"`
PartialResponse PartialSuccessResponse `json:"-"`
}
type PartialSuccessResponse struct {
Success int `json:"success"`
Failure int `json:"failure"`
IllegalTokens []string `json:"illegal_tokens"`
}
func (r SendResponse) IsSuccess() bool {
return r.Code == ResultCode_Success
}
func (r SendResponse) IsPartialSuccess() bool {
return r.Code == ResultCode_PartialSuccess
}
func (r SendResponse) IsError() bool {
return !r.IsSuccess() && !r.IsPartialSuccess()
}
type Message struct {
Data json.RawMessage `json:"data,omitempty"`
Notification *Notification `json:"notification,omitempty"`
Android *AndroidConfig `json:"android,omitempty"`
Apns *ApnsConfig `json:"apns,omitempty"`
Webpush *WebPushConfig `json:"webpush,omitempty"`
Token []string `json:"token,omitempty"`
Topic string `json:"topic,omitempty"`
Condition string `json:"condition,omitempty"`
}
type Notification struct {
Title string `json:"title"`
Body string `json:"body"`
Image string `json:"image,omitempty"`
}
type AndroidConfig struct {
CollapseKey int `json:"collapse_key,omitempty"`
Ttl string `json:"ttl,omitempty"`
BiTag string `json:"bi_tag,omitempty"`
ReceiptId string `json:"receipt_id,omitempty"`
FastAppTarget FastAppState `json:"fast_app_target,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
Notification *AndroidNotification `json:"notification,omitempty"`
}
type FastAppState uint8
const (
FastAppState_Dev FastAppState = 1
FastAppState_Prod FastAppState = 2
)
type AndroidNotification struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
Icon string `json:"icon,omitempty"`
Color string `json:"color,omitempty"`
Sound string `json:"sound,omitempty"`
UseDefaultSound bool `json:"default_sound,omitempty"`
Tag string `json:"tag,omitempty"`
ClickAction ClickAction `json:"click_action"`
BodyLocalizationKey string `json:"body_loc_key,omitempty"`
BodyLocalizationArgs []string `json:"body_loc_args,omitempty"`
TitleLocalizationKey string `json:"title_loc_key,omitempty"`
TitleLocalizationArgs []string `json:"title_loc_args,omitempty"`
MultiLanguageKey *MessageLocalization `json:"multi_lang_key,omitempty"`
ChannelID string `json:"channel_id,omitempty"`
NotifySummary string `json:"notify_summary,omitempty"`
Image string `json:"image,omitempty"`
Style NotificationStyle `json:"style,omitempty"`
BigTitle string `json:"big_title,omitempty"`
BigBody string `json:"big_body,omitempty"`
ClearAfter time.Duration `json:"auto_clear,omitempty"`
NotifyId int `json:"notify_id,omitempty"`
Group string `json:"group,omitempty"`
Badge *NotificationBadge `json:"badge,omitempty"`
Ticker string `json:"ticker,omitempty"`
When string `json:"when,omitempty"`
Importance NotificationImportance `json:"importance,omitempty"`
UseDefaultVibrate bool `json:"use_default_vibrate,omitempty"`
UseDefaultLight bool `json:"use_default_light,omitempty"`
VibrateTimings []string `json:"vibrate_config,omitempty"`
Visibility NotificationVisibility `json:"visibility,omitempty"`
LightSettings *LightSettings `json:"light_settings,omitempty"`
ShowInForeground bool `json:"foreground_show,omitempty"`
ProfileId string `json:"profile_id,omitempty"`
InboxContent []string `json:"inbox_content,omitempty"`
Buttons []ActionButton `json:"buttons,omitempty"`
}
type MessageLocalization struct {
TitleKey map[string]string `json:"title_key"`
BodyKey map[string]string `json:"body_key"`
}
type NotificationStyle uint8
const (
NotificationStyle_Default NotificationStyle = 0
NotificationStyle_Large NotificationStyle = 1
NotificationStyle_Inbox NotificationStyle = 3
)
type NotificationImportance string
const (
NotificationImportance_Low NotificationImportance = "LOW"
NotificationImportance_Normal NotificationImportance = "NORMAL"
)
type NotificationVisibility string
const (
NotificationVisibility_Unspecified NotificationVisibility = "VISIBILITY_UNSPECIFIED"
NotificationVisibility_Public NotificationVisibility = "PUBLIC"
NotificationVisibility_Secret NotificationVisibility = "SECRET"
NotificationVisibility_Private NotificationVisibility = "PRIVATE"
)
type ActionButton struct {
Name string `json:"name"`
Type ButtonActionType `json:"action_type"`
IntentType IntentType `json:"intent_type,omitempty"`
Intent string `json:"intent,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
}
type ButtonActionType uint8
const (
ButtonActionType_AppHome ButtonActionType = 0
ButtonActionType_AppPage ButtonActionType = 1
ButtonActionType_Url ButtonActionType = 2
ButtonActionType_Delete ButtonActionType = 3
ButtonActionType_Share ButtonActionType = 4
)
type IntentType uint8
const (
IntentType_Intent IntentType = 0
IntentType_Action IntentType = 1
)
type ClickAction struct {
Type ClickActionType `json:"type"`
Intent string `json:"intent,omitempty"`
Url string `json:"url,omitempty"`
Action string `json:"action,omitempty"`
}
type ClickActionType uint8
const (
ClickActionType_AppPage ClickActionType = 1
ClickActionType_Url ClickActionType = 2
ClickActionType_StartApp ClickActionType = 3
)
type NotificationBadge struct {
ActivityClass string `json:"class"`
AddNum uint8 `json:"add_num,omitempty"`
SetNum uint8 `json:"set_num,omitempty"`
}
type LightSettings struct {
Color Color `json:"color"`
OnDuration string `json:"light_on_duration"`
OffDuration string `json:"light_off_duration"`
}
type Color struct {
Red float32 `json:"red"`
Green float32 `json:"green"`
Blue float32 `json:"blue"`
Alpha float32 `json:"alpha"`
}
type ApnsConfig struct {
Headers *ApnsHeaders `json:"headers,omitempty"`
Notification ApnsNotification `json:"payload"`
HmsOptions ApnsHmsOptions `json:"hms_options"`
}
// TODO
type ApnsHeaders struct{}
// TODO
type ApnsNotification struct{}
type ApnsHmsOptions struct {
UserType ApnsUserType `json:"target_user_type"`
}
type ApnsUserType uint8
const (
ApnsUserType_Test ApnsUserType = 1
ApnsUserType_Formal ApnsUserType = 2
ApnsUserType_VoIP ApnsUserType = 3
)
type WebPushConfig struct {
Headers *WebPushHeaders `json:"headers,omitempty"`
Notification WebPushNotification `json:"notification"`
HmsOptions *WebPushHmsOptions `json:"hms_options,omitempty"`
}
type WebPushHeaders struct {
Ttl string `json:"ttl"`
Topic string `json:"topic"`
}
// TODO
type WebPushNotification struct{}
type WebPushHmsOptions struct {
Link string `json:"link"`
}