push

package
v1.36.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrCurrencyStateNotFound = errors.New("currency state not found")

ErrCurrencyStateNotFound is returned when no push state exists for a mint.

Functions

func SendContactJoinedFlipcashPush added in v1.11.0

func SendContactJoinedFlipcashPush(ctx context.Context, pusher Pusher, joinedPhone *commonpb.PhoneNumber, users ...*commonpb.UserId) error

func SendFlipcashCurrencyBoughtPush added in v0.4.0

func SendFlipcashCurrencyBoughtPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, mint *commonpb.PublicKey, currencyName string, region ocp_currency.Code, nativeAmount float64) error

func SendFlipcashCurrencyDepositedPush added in v0.4.0

func SendFlipcashCurrencyDepositedPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, mint *commonpb.PublicKey, currencyName string, usdMarketValue float64) error

func SendFlipcashCurrencyGainPush added in v1.1.0

func SendFlipcashCurrencyGainPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, mint *commonpb.PublicKey, currencyName string, gainRegion ocp_currency.Code, gainAmount float64) error

func SendFlipcashCurrencySoldPush added in v0.4.0

func SendFlipcashCurrencySoldPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, mint *commonpb.PublicKey, currencyName string, region ocp_currency.Code, nativeAmount float64) error

func SendLobbyAdmittedPush added in v1.36.0

func SendLobbyAdmittedPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, chatId *commonpb.ChatId, chatTitle string) error

SendLobbyAdmittedPush tells a user that a private group's creator let them in from its lobby, titled by the group. Like SendLobbyEnteredPush it is a chat push that is not for a message, and moves no badge.

func SendLobbyEnteredPush added in v1.36.0

func SendLobbyEnteredPush(ctx context.Context, pusher Pusher, creator *commonpb.UserId, chatId *commonpb.ChatId, chatTitle, requesterDisplayName string) error

SendLobbyEnteredPush tells a private group's creator that a user is waiting in its lobby to be let in ("Alice requested to join", titled by the group). The requester is named by display name, or as "Someone" when they have none. It is a chat push that is not for a message: it navigates to the group and carries its type, but no message and no sender (see pushpb.ChatMetadata), and it moves no badge, like every push that is not a message's.

func SendUsdfDepositProcessingPush added in v1.9.0

func SendUsdfDepositProcessingPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, dollarAmount float64) error

func SendUsdfDepositedPush added in v0.4.0

func SendUsdfDepositedPush(ctx context.Context, pusher Pusher, user *commonpb.UserId, dollarAmount float64) error

Types

type BadgeCounts added in v1.30.0

type BadgeCounts map[string]uint64

BadgeCounts maps a user (keyed by string(userID.Value)) to the app-icon badge to display alongside a push. A user absent from the map gets no badge.

type BadgeResolver added in v1.30.0

type BadgeResolver func(ctx context.Context, users []*commonpb.UserId) (BadgeCounts, error)

BadgeResolver produces the badge count to display for each of the given users. A pusher invokes it with only the users who own at least one iOS device — APNs is the only platform with a server-settable numeric badge — so any per-user work behind it (e.g. bumping a stored count) is skipped for users who could never display the result.

A resolver may return a partial map alongside an error: the counts it did produce are still carried on the push, and the error is reported after the push is sent.

type ChatMessagePush added in v1.32.0

type ChatMessagePush struct {
	// contains filtered or unexported fields
}

ChatMessagePush is a chat message's notification, rendered once and sent to any number of recipient pages (see Send). Rendering is the part of a push that is the same for every recipient — the title, the body, the payload, and for cash content a currency-name lookup — so a fan-out that walks a large group's roster in pages builds it once per message rather than once per page. A nil *ChatMessagePush from a builder means the message's content earns no push (an unsupported content type, or a sender missing what the title needs), which is not an error.

func BuildContactDmPush added in v1.32.0

func BuildContactDmPush(ctx context.Context, ocpData ocp_data.Provider, chatId *commonpb.ChatId, message *messagingpb.Message, senderID *commonpb.UserId, senderContact *commonpb.PhoneNumber) (*ChatMessagePush, error)

BuildContactDmPush renders a new message in a contact DM. The title is a contact substitution on the sender's phone number, which the recipient's client resolves against their address book.

func BuildDmPush added in v1.33.0

func BuildDmPush(ctx context.Context, ocpData ocp_data.Provider, chatId *commonpb.ChatId, message *messagingpb.Message, senderID *commonpb.UserId, senderDisplayName string) (*ChatMessagePush, error)

BuildDmPush renders a new message in a DM (ChatType_DM). The sender is typically not in the recipient's contacts, so the title carries the sender's display name directly rather than a contact substitution — and never the sender's phone number, which is private outside contact DMs.

func BuildGroupChatPush added in v1.32.0

func BuildGroupChatPush(ctx context.Context, ocpData ocp_data.Provider, chatId *commonpb.ChatId, message *messagingpb.Message, senderID *commonpb.UserId, senderDisplayName, receiverDisplayName, chatTitle string) (*ChatMessagePush, error)

BuildGroupChatPush renders a new message in a group chat. The notification is titled by the group ("Untitled Group" when it has none), with the sender identified by display name in the body ("Alice: hello"; "Alice sent a message" for encrypted content, see renderGroupChatMessagePushBody). A cash message paying one member also names its receiver, by receiverDisplayName ("Alice sent Bob $5 of USDF"), which is ignored for any other content; one paying several names no one and counts them instead ("Alice sent $20 of USDF to 4 people"). Receivers are meant to get BuildGroupChatReceiverPushes instead. Like a DM, a group push never carries the sender's phone number, which is private outside contact DMs.

func (*ChatMessagePush) Send added in v1.32.0

func (p *ChatMessagePush) Send(ctx context.Context, pusher Pusher, badges badge.Store, recipients ChatRecipients) error

Send delivers the push to both halves of one page of its audience: the unmuted half with each recipient's badge bumped and the new total carried on the notification, so a recipient's icon updates with the same push that announces the message rather than a second, badge-only push per recipient; the muted half flagged, unbadged, as a copy of the same payload, to Android devices only. Either half may be empty and costs nothing then. The two sends are independent, and a failure in one is reported alongside the other's. Send may be called once per page of a large audience; each call is its own token lookup, badge batch and FCM send, so what a call holds is bounded by its page.

type ChatRecipients added in v1.32.0

type ChatRecipients struct {
	Unmuted []*commonpb.UserId
	Muted   []*commonpb.UserId
}

ChatRecipients is a chat push's audience, split by whether each recipient has the chat muted. The unmuted half receives the alert with a badge bump. The muted half receives the same payload flagged (ChatMetadata.muted) and without a badge bump, since a muted chat must not move the app icon's count — and only on Android, where the client can deliver the message quietly; the FCMPusher drops a muted recipient's iOS devices, which could not suppress the alert, so an iOS user hears nothing from a chat they muted. Which half a recipient lands in is the sender's best-effort call at send time; the client's own copy of its mute is the last word.

type CurrencyState added in v1.19.0

type CurrencyState struct {
	Mint              *commonpb.PublicKey
	AllTimeHighSupply uint64
	AllTimeHighSlot   uint64
	LastGainPushAt    *time.Time // nil if a gain push has never been granted for the mint
}

CurrencyState captures per-currency state used to gate push notifications.

AllTimeHighSupply tracks the circulating supply (in quarks) observed at the last granted gain push, NOT a true high-water mark: it only advances when a gain push is actually granted (see CurrencyStateStore.ClaimGainPush).

type CurrencyStateStore added in v1.19.0

type CurrencyStateStore interface {
	// ClaimGainPush atomically gates a "currency gain" push wave for a mint.
	//
	// granted is true only when both conditions hold against the currently stored
	// state: (1) supply is strictly greater than the stored all-time high, and
	// (2) at least cooldown has elapsed since the last granted gain push (or none
	// has ever been granted). When granted, the stored all-time high (supply,
	// slot) and last-gain-push timestamp are advanced to the provided values / now.
	// When not granted, the stored state is left unchanged — in particular a new
	// high observed during cooldown is NOT recorded.
	//
	// Whenever err is nil, the returned CurrencyState reflects the resulting stored
	// state — the freshly written values when granted, or the existing values when
	// not — so callers can populate a local cache regardless of the outcome.
	ClaimGainPush(ctx context.Context, mint *commonpb.PublicKey, supply, slot uint64, cooldown time.Duration) (granted bool, state *CurrencyState, err error)

	// GetCurrencyState returns the stored push state for a mint, or
	// ErrCurrencyStateNotFound if none has been recorded yet.
	GetCurrencyState(ctx context.Context, mint *commonpb.PublicKey) (*CurrencyState, error)
}

type FCMClient

type FCMClient interface {
	SendEach(ctx context.Context, messages []*messaging.Message) (*messaging.BatchResponse, error)
}

FCMClient is the subset of *messaging.Client the pusher depends on. SendEach takes one fully independent message per device, which is what lets each recipient carry its own badge; the SDK fans it out one request per message over a worker pool, exactly as it does for a multicast.

type FCMPusher

type FCMPusher struct {
	// contains filtered or unexported fields
}

func NewFCMPusher

func NewFCMPusher(log *zap.Logger, tokens TokenStore, client FCMClient) *FCMPusher

func (*FCMPusher) SendPushes added in v0.7.0

func (p *FCMPusher) SendPushes(ctx context.Context, title, body string, customPayload *pushpb.Payload, users ...*commonpb.UserId) error

func (*FCMPusher) SendPushesWithBadges added in v1.30.0

func (p *FCMPusher) SendPushesWithBadges(ctx context.Context, title, body string, customPayload *pushpb.Payload, resolveBadges BadgeResolver, users ...*commonpb.UserId) error

type NoOpPusher

type NoOpPusher struct{}

func (*NoOpPusher) SendPushes added in v0.7.0

func (n *NoOpPusher) SendPushes(_ context.Context, _, _ string, _ *pushpb.Payload, _ ...*commonpb.UserId) error

func (*NoOpPusher) SendPushesWithBadges added in v1.30.0

func (n *NoOpPusher) SendPushesWithBadges(_ context.Context, _, _ string, _ *pushpb.Payload, _ BadgeResolver, _ ...*commonpb.UserId) error

type Pusher

type Pusher interface {
	// SendPushes sends the same notification to every device of the given users.
	SendPushes(ctx context.Context, title, body string, customPayload *pushpb.Payload, users ...*commonpb.UserId) error

	// SendPushesWithBadges is SendPushes with a per-user app-icon badge carried
	// on the notification itself, resolved on demand for the recipients whose
	// devices can display one (see BadgeResolver).
	SendPushesWithBadges(ctx context.Context, title, body string, customPayload *pushpb.Payload, resolveBadges BadgeResolver, users ...*commonpb.UserId) error
}

func NewNoOpPusher

func NewNoOpPusher() Pusher

type ReceiverPush added in v1.36.0

type ReceiverPush struct {
	Receivers []*commonpb.UserId
	Push      *ChatMessagePush
}

ReceiverPush is a group cash message's notification for some of its receivers, and who they are: every receiver whose own push reads the same, so they can be sent it together.

func BuildGroupChatReceiverPushes added in v1.36.0

func BuildGroupChatReceiverPushes(ctx context.Context, ocpData ocp_data.Provider, chatId *commonpb.ChatId, message *messagingpb.Message, senderID *commonpb.UserId, senderDisplayName, chatTitle string) ([]ReceiverPush, error)

BuildGroupChatReceiverPushes renders a cash message in a group chat for its receivers alone, each addressed with their own part ("Alice sent you $5 of USDF"), with the title and payload every other member's push has (see BuildGroupChatPush). A payment to one member has one receiver, paid the whole; one to several has one per entry of its multi receiver, each paid their part. Receivers whose push reads the same share one ReceiverPush, in the order the first of them appears in the message. It is empty for any message that is not cash naming a receiver.

type Server

type Server struct {
	pushpb.UnimplementedPushServer
	// contains filtered or unexported fields
}

func NewServer

func NewServer(log *zap.Logger, authz auth.Authorizer, tokens TokenStore) *Server

func (*Server) AddToken

func (*Server) DeleteTokens

type Store added in v1.19.0

type Store interface {
	TokenStore
	CurrencyStateStore
}

Store is the combined push persistence surface, implemented by each backend.

type Token

type Token struct {
	UserID       *commonpb.UserId
	Type         pushpb.TokenType
	Token        string
	AppInstallID string
}

Token represents a push notification token.

Tokens are bound to a (user, device) pair, identified by the AppInstallID.

type TokenStore

type TokenStore interface {
	// GetTokens returns all tokens for a user.
	GetTokens(ctx context.Context, userID *commonpb.UserId) ([]Token, error)

	// GetTokensBatch returns all tokens for a batch of users.
	GetTokensBatch(ctx context.Context, userIDs ...*commonpb.UserId) ([]Token, error)

	// AddToken adds a token for a user.
	//
	// If the token already exists for the same user and device, it will be updated.
	AddToken(ctx context.Context, userID *commonpb.UserId, appInstallID *commonpb.AppInstallId, tokenType pushpb.TokenType, token string) error

	// DeleteToken deletes a token for a user.
	DeleteToken(ctx context.Context, tokenType pushpb.TokenType, token string) error

	// DeleteTokens deletes every given (type, token) pair in one operation. It
	// exists for the pusher's cleanup of tokens FCM reports as unregistered,
	// which a large send can surface many of at once. A pair that no longer
	// exists is skipped, not an error.
	DeleteTokens(ctx context.Context, tokens ...Token) error

	// FilterUsersWithTokens returns the subset of user IDs that have at least one push token.
	FilterUsersWithTokens(ctx context.Context, userIDs ...*commonpb.UserId) ([]*commonpb.UserId, error)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL