// 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 push import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "time" "git.bit5.ru/backend/colog" "git.bit5.ru/backend/errors" "golang.org/x/oauth2" ) 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 *colog.CoLog } func NewClient(cfg ClientConfig, ts oauth2.TokenSource, httpClient *http.Client, logger *colog.CoLog) *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"` }