func (a *Aps) UnmarshalJSON(b []byte) error {
	type apsInternal Aps
	temp := struct {
		AlertObject         *json.RawMessage `json:"alert,omitempty"`
		SoundObject         *json.RawMessage `json:"sound,omitempty"`
		ContentAvailableInt int              `json:"content-available,omitempty"`
		MutableContentInt   int              `json:"mutable-content,omitempty"`
		*apsInternal
	}{
		apsInternal: (*apsInternal)(a),
	}
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	a.ContentAvailable = (temp.ContentAvailableInt == 1)
	a.MutableContent = (temp.MutableContentInt == 1)
	if temp.AlertObject != nil {
		if err := json.Unmarshal(*temp.AlertObject, &a.Alert); err != nil {
			a.Alert = nil
			if err := json.Unmarshal(*temp.AlertObject, &a.AlertString); err != nil {
				return fmt.Errorf("failed to unmarshal alert as a struct or a string: %v", err)
			}
		}
	}
	if temp.SoundObject != nil {
		if err := json.Unmarshal(*temp.SoundObject, &a.CriticalSound); err != nil {
			a.CriticalSound = nil
			if err := json.Unmarshal(*temp.SoundObject, &a.Sound); err != nil {
				return fmt.Errorf("failed to unmarshal sound as a struct or a string")
			}
		}
	}
	allFields := make(map[string]interface{})
	if err := json.Unmarshal(b, &allFields); err != nil {
		return err
	}
	for k := range a.standardFields() {
		delete(allFields, k)
	}
	if len(allFields) > 0 {
		a.CustomData = allFields
	}
	return nil
}
UnmarshalJSON unmarshals a JSON string into an Aps (for internal use only).
package messaging
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
	"firebase.google.com/go/v4/internal"
	"google.golang.org/api/transport"
)
const (
	defaultMessagingEndpoint = "https://fcm.googleapis.com/v1"
	defaultBatchEndpoint     = "https://fcm.googleapis.com/batch"
	firebaseClientHeader   = "X-Firebase-Client"
	apiFormatVersionHeader = "X-GOOG-API-FORMAT-VERSION"
	apiFormatVersion       = "2"
	apnsAuthError       = "APNS_AUTH_ERROR"
	internalError       = "INTERNAL"
	thirdPartyAuthError = "THIRD_PARTY_AUTH_ERROR"
	invalidArgument     = "INVALID_ARGUMENT"
	quotaExceeded       = "QUOTA_EXCEEDED"
	senderIDMismatch    = "SENDER_ID_MISMATCH"
	unregistered        = "UNREGISTERED"
	unavailable         = "UNAVAILABLE"
	rfc3339Zulu = "2006-01-02T15:04:05.000000000Z"
)
var (
	topicNamePattern = regexp.MustCompile("^(/topics/)?(private/)?[a-zA-Z0-9-_.~%]+$")
)
type Message struct {
	Data         map[string]string `json:"data,omitempty"`
	Notification *Notification     `json:"notification,omitempty"`
	Android      *AndroidConfig    `json:"android,omitempty"`
	Webpush      *WebpushConfig    `json:"webpush,omitempty"`
	APNS         *APNSConfig       `json:"apns,omitempty"`
	FCMOptions   *FCMOptions       `json:"fcm_options,omitempty"`
	Token        string            `json:"token,omitempty"`
	Topic        string            `json:"-"`
	Condition    string            `json:"condition,omitempty"`
}
func (m *Message) MarshalJSON() ([]byte, error) {
	type messageInternal Message
	temp := &struct {
		BareTopic string `json:"topic,omitempty"`
		*messageInternal
	}{
		BareTopic:       strings.TrimPrefix(m.Topic, "/topics/"),
		messageInternal: (*messageInternal)(m),
	}
	return json.Marshal(temp)
}
func (m *Message) UnmarshalJSON(b []byte) error {
	type messageInternal Message
	s := struct {
		BareTopic string `json:"topic,omitempty"`
		*messageInternal
	}{
		messageInternal: (*messageInternal)(m),
	}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	m.Topic = s.BareTopic
	return nil
}
type Notification struct {
	Title    string `json:"title,omitempty"`
	Body     string `json:"body,omitempty"`
	ImageURL string `json:"image,omitempty"`
}
type AndroidConfig struct {
	CollapseKey           string               `json:"collapse_key,omitempty"`
	Priority              string               `json:"priority,omitempty"`  
	TTL                   *time.Duration       `json:"-"`
	RestrictedPackageName string               `json:"restricted_package_name,omitempty"`
	Data                  map[string]string    `json:"data,omitempty"`  
	Notification          *AndroidNotification `json:"notification,omitempty"`
	FCMOptions            *AndroidFCMOptions   `json:"fcm_options,omitempty"`
}
func (a *AndroidConfig) MarshalJSON() ([]byte, error) {
	var ttl string
	if a.TTL != nil {
		ttl = durationToString(*a.TTL)
	}
	type androidInternal AndroidConfig
	temp := &struct {
		TTL string `json:"ttl,omitempty"`
		*androidInternal
	}{
		TTL:             ttl,
		androidInternal: (*androidInternal)(a),
	}
	return json.Marshal(temp)
}
func (a *AndroidConfig) UnmarshalJSON(b []byte) error {
	type androidInternal AndroidConfig
	temp := struct {
		TTL string `json:"ttl,omitempty"`
		*androidInternal
	}{
		androidInternal: (*androidInternal)(a),
	}
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	if temp.TTL != "" {
		ttl, err := stringToDuration(temp.TTL)
		if err != nil {
			return err
		}
		a.TTL = &ttl
	}
	return nil
}
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"`
	Tag                   string                        `json:"tag,omitempty"`
	ClickAction           string                        `json:"click_action,omitempty"`
	BodyLocKey            string                        `json:"body_loc_key,omitempty"`
	BodyLocArgs           []string                      `json:"body_loc_args,omitempty"`
	TitleLocKey           string                        `json:"title_loc_key,omitempty"`
	TitleLocArgs          []string                      `json:"title_loc_args,omitempty"`
	ChannelID             string                        `json:"channel_id,omitempty"`
	ImageURL              string                        `json:"image,omitempty"`
	Ticker                string                        `json:"ticker,omitempty"`
	Sticky                bool                          `json:"sticky,omitempty"`
	EventTimestamp        *time.Time                    `json:"-"`
	LocalOnly             bool                          `json:"local_only,omitempty"`
	Priority              AndroidNotificationPriority   `json:"-"`
	VibrateTimingMillis   []int64                       `json:"-"`
	DefaultVibrateTimings bool                          `json:"default_vibrate_timings,omitempty"`
	DefaultSound          bool                          `json:"default_sound,omitempty"`
	LightSettings         *LightSettings                `json:"light_settings,omitempty"`
	DefaultLightSettings  bool                          `json:"default_light_settings,omitempty"`
	Visibility            AndroidNotificationVisibility `json:"-"`
	NotificationCount     *int                          `json:"notification_count,omitempty"`
}
func (a *AndroidNotification) MarshalJSON() ([]byte, error) {
	var priority string
	if a.Priority != priorityUnspecified {
		priorities := map[AndroidNotificationPriority]string{
			PriorityMin:     "PRIORITY_MIN",
			PriorityLow:     "PRIORITY_LOW",
			PriorityDefault: "PRIORITY_DEFAULT",
			PriorityHigh:    "PRIORITY_HIGH",
			PriorityMax:     "PRIORITY_MAX",
		}
		priority, _ = priorities[a.Priority]
	}
	var visibility string
	if a.Visibility != visibilityUnspecified {
		visibilities := map[AndroidNotificationVisibility]string{
			VisibilityPrivate: "PRIVATE",
			VisibilityPublic:  "PUBLIC",
			VisibilitySecret:  "SECRET",
		}
		visibility, _ = visibilities[a.Visibility]
	}
	var timestamp string
	if a.EventTimestamp != nil {
		timestamp = a.EventTimestamp.UTC().Format(rfc3339Zulu)
	}
	var vibTimings []string
	for _, t := range a.VibrateTimingMillis {
		vibTimings = append(vibTimings, durationToString(time.Duration(t)*time.Millisecond))
	}
	type androidInternal AndroidNotification
	temp := &struct {
		EventTimestamp string   `json:"event_time,omitempty"`
		Priority       string   `json:"notification_priority,omitempty"`
		Visibility     string   `json:"visibility,omitempty"`
		VibrateTimings []string `json:"vibrate_timings,omitempty"`
		*androidInternal
	}{
		EventTimestamp:  timestamp,
		Priority:        priority,
		Visibility:      visibility,
		VibrateTimings:  vibTimings,
		androidInternal: (*androidInternal)(a),
	}
	return json.Marshal(temp)
}
func (a *AndroidNotification) UnmarshalJSON(b []byte) error {
	type androidInternal AndroidNotification
	temp := struct {
		EventTimestamp string   `json:"event_time,omitempty"`
		Priority       string   `json:"notification_priority,omitempty"`
		Visibility     string   `json:"visibility,omitempty"`
		VibrateTimings []string `json:"vibrate_timings,omitempty"`
		*androidInternal
	}{
		androidInternal: (*androidInternal)(a),
	}
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	if temp.Priority != "" {
		priorities := map[string]AndroidNotificationPriority{
			"PRIORITY_MIN":     PriorityMin,
			"PRIORITY_LOW":     PriorityLow,
			"PRIORITY_DEFAULT": PriorityDefault,
			"PRIORITY_HIGH":    PriorityHigh,
			"PRIORITY_MAX":     PriorityMax,
		}
		if prio, ok := priorities[temp.Priority]; ok {
			a.Priority = prio
		} else {
			return fmt.Errorf("unknown priority value: %q", temp.Priority)
		}
	}
	if temp.Visibility != "" {
		visibilities := map[string]AndroidNotificationVisibility{
			"PRIVATE": VisibilityPrivate,
			"PUBLIC":  VisibilityPublic,
			"SECRET":  VisibilitySecret,
		}
		if vis, ok := visibilities[temp.Visibility]; ok {
			a.Visibility = vis
		} else {
			return fmt.Errorf("unknown visibility value: %q", temp.Visibility)
		}
	}
	if temp.EventTimestamp != "" {
		ts, err := time.Parse(rfc3339Zulu, temp.EventTimestamp)
		if err != nil {
			return err
		}
		a.EventTimestamp = &ts
	}
	var vibTimings []int64
	for _, t := range temp.VibrateTimings {
		vibTime, err := stringToDuration(t)
		if err != nil {
			return err
		}
		millis := int64(vibTime / time.Millisecond)
		vibTimings = append(vibTimings, millis)
	}
	a.VibrateTimingMillis = vibTimings
	return nil
}
type AndroidNotificationPriority int
const (
	priorityUnspecified AndroidNotificationPriority = iota
	PriorityMin
	PriorityLow
	PriorityDefault
	PriorityHigh
	PriorityMax
)
type AndroidNotificationVisibility int
const (
	visibilityUnspecified AndroidNotificationVisibility = iota
	VisibilityPrivate
	VisibilityPublic
	VisibilitySecret
)
type LightSettings struct {
	Color                  string
	LightOnDurationMillis  int64
	LightOffDurationMillis int64
}
func (l *LightSettings) MarshalJSON() ([]byte, error) {
	clr, err := newColor(l.Color)
	if err != nil {
		return nil, err
	}
	temp := struct {
		Color            *color `json:"color"`
		LightOnDuration  string `json:"light_on_duration"`
		LightOffDuration string `json:"light_off_duration"`
	}{
		Color:            clr,
		LightOnDuration:  durationToString(time.Duration(l.LightOnDurationMillis) * time.Millisecond),
		LightOffDuration: durationToString(time.Duration(l.LightOffDurationMillis) * time.Millisecond),
	}
	return json.Marshal(temp)
}
func (l *LightSettings) UnmarshalJSON(b []byte) error {
	temp := struct {
		Color            *color `json:"color"`
		LightOnDuration  string `json:"light_on_duration"`
		LightOffDuration string `json:"light_off_duration"`
	}{}
	if err := json.Unmarshal(b, &temp); err != nil {
		return err
	}
	on, err := stringToDuration(temp.LightOnDuration)
	if err != nil {
		return err
	}
	off, err := stringToDuration(temp.LightOffDuration)
	if err != nil {
		return err
	}
	l.Color = temp.Color.toString()
	l.LightOnDurationMillis = int64(on / time.Millisecond)
	l.LightOffDurationMillis = int64(off / time.Millisecond)
	return nil
}
func durationToString(ms time.Duration) string {
	seconds := int64(ms / time.Second)
	nanos := int64((ms - time.Duration(seconds)*time.Second) / time.Nanosecond)
	if nanos > 0 {
		return fmt.Sprintf("%d.%09ds", seconds, nanos)
	}
	return fmt.Sprintf("%ds", seconds)
}
func stringToDuration(s string) (time.Duration, error) {
	segments := strings.Split(strings.TrimSuffix(s, "s"), ".")
	if len(segments) != 1 && len(segments) != 2 {
		return 0, fmt.Errorf("incorrect number of segments in ttl: %q", s)
	}
	seconds, err := strconv.ParseInt(segments[0], 10, 64)
	if err != nil {
		return 0, fmt.Errorf("failed to parse %s: %v", s, err)
	}
	ttl := time.Duration(seconds) * time.Second
	if len(segments) == 2 {
		nanos, err := strconv.ParseInt(strings.TrimLeft(segments[1], "0"), 10, 64)
		if err != nil {
			return 0, fmt.Errorf("failed to parse %s: %v", s, err)
		}
		ttl += time.Duration(nanos) * time.Nanosecond
	}
	return ttl, nil
}
type color struct {
	Red   float64 `json:"red"`
	Green float64 `json:"green"`
	Blue  float64 `json:"blue"`
	Alpha float64 `json:"alpha"`
}
func newColor(clr string) (*color, error) {
	red, err := strconv.ParseInt(clr[1:3], 16, 32)
	if err != nil {
		return nil, fmt.Errorf("failed to parse %s: %v", clr, err)
	}
	green, err := strconv.ParseInt(clr[3:5], 16, 32)
	if err != nil {
		return nil, fmt.Errorf("failed to parse %s: %v", clr, err)
	}
	blue, err := strconv.ParseInt(clr[5:7], 16, 32)
	if err != nil {
		return nil, fmt.Errorf("failed to parse %s: %v", clr, err)
	}
	alpha := int64(255)
	if len(clr) == 9 {
		alpha, err = strconv.ParseInt(clr[7:9], 16, 32)
		if err != nil {
			return nil, fmt.Errorf("failed to parse %s: %v", clr, err)
		}
	}
	return &color{
		Red:   float64(red) / 255.0,
		Green: float64(green) / 255.0,
		Blue:  float64(blue) / 255.0,
		Alpha: float64(alpha) / 255.0,
	}, nil
}
func (c *color) toString() string {
	red := int(c.Red * 255.0)
	green := int(c.Green * 255.0)
	blue := int(c.Blue * 255.0)
	alpha := int(c.Alpha * 255.0)
	if alpha == 255 {
		return fmt.Sprintf("#%X%X%X", red, green, blue)
	}
	return fmt.Sprintf("#%X%X%X%X", red, green, blue, alpha)
}
type AndroidFCMOptions struct {
	AnalyticsLabel string `json:"analytics_label,omitempty"`
}
type WebpushConfig struct {
	Headers      map[string]string    `json:"headers,omitempty"`
	Data         map[string]string    `json:"data,omitempty"`
	Notification *WebpushNotification `json:"notification,omitempty"`
	FCMOptions   *WebpushFCMOptions   `json:"fcm_options,omitempty"`
}
type WebpushNotificationAction struct {
	Action string `json:"action,omitempty"`
	Title  string `json:"title,omitempty"`
	Icon   string `json:"icon,omitempty"`
}
type WebpushNotification struct {
	Actions            []*WebpushNotificationAction `json:"actions,omitempty"`
	Title              string                       `json:"title,omitempty"`  
	Body               string                       `json:"body,omitempty"`   
	Icon               string                       `json:"icon,omitempty"`
	Badge              string                       `json:"badge,omitempty"`
	Direction          string                       `json:"dir,omitempty"`  
	Data               interface{}                  `json:"data,omitempty"`
	Image              string                       `json:"image,omitempty"`
	Language           string                       `json:"lang,omitempty"`
	Renotify           bool                         `json:"renotify,omitempty"`
	RequireInteraction bool                         `json:"requireInteraction,omitempty"`
	Silent             bool                         `json:"silent,omitempty"`
	Tag                string                       `json:"tag,omitempty"`
	TimestampMillis    *int64                       `json:"timestamp,omitempty"`
	Vibrate            []int                        `json:"vibrate,omitempty"`
	CustomData         map[string]interface{}
}
func (n *WebpushNotification) standardFields() map[string]interface{} {
	m := make(map[string]interface{})
	addNonEmpty := func(key, value string) {
		if value != "" {
			m[key] = value
		}
	}
	addTrue := func(key string, value bool) {
		if value {
			m[key] = value
		}
	}
	if len(n.Actions) > 0 {
		m["actions"] = n.Actions
	}
	addNonEmpty("title", n.Title)
	addNonEmpty("body", n.Body)
	addNonEmpty("icon", n.Icon)
	addNonEmpty("badge", n.Badge)
	addNonEmpty("dir", n.Direction)
	addNonEmpty("image", n.Image)
	addNonEmpty("lang", n.Language)
	addTrue("renotify", n.Renotify)
	addTrue("requireInteraction", n.RequireInteraction)
	addTrue("silent", n.Silent)
	addNonEmpty("tag", n.Tag)
	if n.Data != nil {
		m["data"] = n.Data
	}
	if n.TimestampMillis != nil {
		m["timestamp"] = *n.TimestampMillis
	}
	if len(n.Vibrate) > 0 {
		m["vibrate"] = n.Vibrate
	}
	return m
}
func (n *WebpushNotification) MarshalJSON() ([]byte, error) {
	m := n.standardFields()
	for k, v := range n.CustomData {
		m[k] = v
	}
	return json.Marshal(m)
}
func (n *WebpushNotification) UnmarshalJSON(b []byte) error {
	type webpushNotificationInternal WebpushNotification
	var temp = (*webpushNotificationInternal)(n)
	if err := json.Unmarshal(b, temp); err != nil {
		return err
	}
	allFields := make(map[string]interface{})
	if err := json.Unmarshal(b, &allFields); err != nil {
		return err
	}
	for k := range n.standardFields() {
		delete(allFields, k)
	}
	if len(allFields) > 0 {
		n.CustomData = allFields
	}
	return nil
}
type WebpushFCMOptions struct {
	Link string `json:"link,omitempty"`
}
type APNSConfig struct {
	Headers    map[string]string `json:"headers,omitempty"`
	Payload    *APNSPayload      `json:"payload,omitempty"`
	FCMOptions *APNSFCMOptions   `json:"fcm_options,omitempty"`
}
type APNSPayload struct {
	Aps        *Aps                   `json:"aps,omitempty"`
	CustomData map[string]interface{} `json:"-"`
}
func (p *APNSPayload) standardFields() map[string]interface{} {
	return map[string]interface{}{"aps": p.Aps}
}
func (p *APNSPayload) MarshalJSON() ([]byte, error) {
	m := p.standardFields()
	for k, v := range p.CustomData {
		m[k] = v
	}
	return json.Marshal(m)
}
func (p *APNSPayload) UnmarshalJSON(b []byte) error {
	type apnsPayloadInternal APNSPayload
	var temp = (*apnsPayloadInternal)(p)
	if err := json.Unmarshal(b, temp); err != nil {
		return err
	}
	allFields := make(map[string]interface{})
	if err := json.Unmarshal(b, &allFields); err != nil {
		return err
	}
	for k := range p.standardFields() {
		delete(allFields, k)
	}
	if len(allFields) > 0 {
		p.CustomData = allFields
	}
	return nil
}
type Aps struct {
	AlertString      string                 `json:"-"`
	Alert            *ApsAlert              `json:"-"`
	Badge            *int                   `json:"badge,omitempty"`
	Sound            string                 `json:"-"`
	CriticalSound    *CriticalSound         `json:"-"`
	ContentAvailable bool                   `json:"-"`
	MutableContent   bool                   `json:"-"`
	Category         string                 `json:"category,omitempty"`
	ThreadID         string                 `json:"thread-id,omitempty"`
	CustomData       map[string]interface{} `json:"-"`
}
func (a *Aps) standardFields() map[string]interface{} {
	m := make(map[string]interface{})
	if a.Alert != nil {
		m["alert"] = a.Alert
	} else if a.AlertString != "" {
		m["alert"] = a.AlertString
	}
	if a.ContentAvailable {
		m["content-available"] = 1
	}
	if a.MutableContent {
		m["mutable-content"] = 1
	}
	if a.Badge != nil {
		m["badge"] = *a.Badge
	}
	if a.CriticalSound != nil {
		m["sound"] = a.CriticalSound
	} else if a.Sound != "" {
		m["sound"] = a.Sound
	}
	if a.Category != "" {
		m["category"] = a.Category
	}
	if a.ThreadID != "" {
		m["thread-id"] = a.ThreadID
	}
	return m
}
func (a *Aps) MarshalJSON() ([]byte, error) {
	m := a.standardFields()
	for k, v := range a.CustomData {
		m[k] = v
	}
	return json.Marshal(m)
}