push

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package push runs the account's client side of push notifications: device-token persistence + registration with the push node, the subscription sync loop (desired-topic reconcile → SubscribeAll full replace), and a buffered notify queue so HTTP handlers never block on the push server. Structural twin of internal/indexer — per-account, SDK-consuming, background-looping; constructed by the engine only when config.Push.Active().

The SDK owns crypto + transport (space.PushAPI); this package owns the account policy: WHICH topics (space-level and per-chat modes, topics.go), WHEN to re-sync, WHAT survives a restart (the token file), and the chat notify hooks the HTTP handlers call after send/edit/read (chatpush.go — heart-interoperable payloads).

Index

Constants

View Source
const (
	ModeAll      = "all"
	ModeMentions = "mentions"
	ModeNone     = "none"
)

notifyMode values. Heart parity: All is the default (also the fallback for an absent or invalid value).

View Source
const SettingNotifyMode = "notifyMode"

SettingNotifyMode is the per-space settings key the sync loop reads (SpaceInfo.Settings["notifyMode"], written via PATCH /v1/spaces/:spaceId/settings).

View Source
const TopicChats = "chats"

TopicChats is the space-wide "all messages" topic (heart's chats topic). The account identity doubles as the bulk mentions topic.

Variables

This section is empty.

Functions

This section is empty.

Types

type Attachment

type Attachment struct {
	Layout int `json:"layout"`
}

Attachment mirrors heart's per-attachment stub. Layout is heart's resolvedLayout from object details; any's chat attachments are {type, link} hints with no layout notion, so it stays 0.

type CaptureFunc

type CaptureFunc func(spaceId string, topics []string, payload []byte, groupId string, silent bool)

CaptureFunc receives one would-be-enqueued notification. Test seam; see CaptureNotifications.

type NewMessagePayload

type NewMessagePayload struct {
	ChatId         string        `json:"chatId"`
	MsgId          string        `json:"msgId"`
	SpaceName      string        `json:"spaceName"`
	ChatName       string        `json:"chatName"`
	SenderName     string        `json:"senderName"`
	Text           string        `json:"text"`
	HasAttachments bool          `json:"hasAttachments"`
	Attachments    []*Attachment `json:"attachments"`
}

NewMessagePayload carries the renderable message preview.

type Payload

type Payload struct {
	SpaceId string `json:"spaceId,omitempty"`
	// SpaceUxType / SpaceType are heart's model.SpaceUxType /
	// model.SpaceType enums, filled by heartSpaceKinds from the space's
	// type: the payload is the only thing a receiver that has never
	// seen the space can classify on (a 1-1 renders as a direct
	// message, not a channel). Unmapped space types stay 0 (unknown).
	SpaceUxType       int                `json:"spaceUxType"`
	SpaceType         int                `json:"spaceType"`
	SenderId          string             `json:"senderId"`
	Type              Type               `json:"type"`
	NewMessagePayload *NewMessagePayload `json:"newMessage,omitempty"`
}

Payload is the outer (pre-encryption) notification body.

type Service

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

Service is the per-account push service. New → Start → Close; all exported methods are safe for concurrent use. The chat handler hooks (NotifyChatMessage / NotifyChatEdit / NotifyChatRead in chatpush.go) feed Enqueue.

func New

func New(sdk *anysyncsdk.SDK, accountDir string) *Service

New constructs the service. accountDir is the per-account data dir (wallet.key, server.pid, …) where push-token.json lives. Call Start to begin background work; the token/notify surfaces are inert but safe before that.

func (*Service) CaptureNotifications

func (s *Service) CaptureNotifications(fn CaptureFunc)

CaptureNotifications diverts every subsequent Enqueue into fn instead of the delivery queue — the test seam for asserting the chat-hook outputs (topics / payload / groupId) without a reachable push node. Pass nil to restore normal delivery.

func (*Service) ChatMentionsBefore

func (s *Service) ChatMentionsBefore(ctx context.Context, sp space.Space, objectId, msgId string) (mentions []string, ok bool)

ChatMentionsBefore snapshots a message's server-derived mentions — the chatEdit hook's "before" set. SYNCHRONOUS by necessity (it must read the pre-edit record, so the handler calls it before the edit lands); one local indexed read, so the handler cost is negligible. ok=false means the read failed and the edit push should be skipped entirely — without a before set the diff would over-notify every existing mention on every edit.

func (*Service) Close

func (s *Service) Close() error

Close stops the loops. Best-effort teardown: an in-flight RPC finishes or context-cancels; queued notifications are dropped. Idempotent.

func (*Service) Enqueue

func (s *Service) Enqueue(spaceId string, topics []string, payload []byte, groupId string, silent bool)

Enqueue schedules one notification for buffered async delivery (notifyAttempts × notifyRetryDelay, break early when the server reports no valid topics). silent routes through NotifySilent (own-devices wakeup; topics ignored). Never blocks: on queue overflow the notification is dropped with a warning — push is a best-effort side channel, the message itself is already synced. Fed by the chat handler hooks in chatpush.go.

func (*Service) Kick

func (s *Service) Kick()

Kick schedules a subscription re-sync (non-blocking, coalesced). SetToken uses it so a fresh token gets its subscriptions immediately instead of waiting for the tick.

func (*Service) NotifyChatEdit

func (s *Service) NotifyChatEdit(sp space.Space, objectId, msgId string, before []string)

NotifyChatEdit is the chatEdit hook: re-read the message, diff its mentions against the pre-edit snapshot, and push only to the NEWLY-ADDED mentions — bare identity + per-chat mention topics only, never the "chats" / per-chat broadcast topics (an edit is not a new message for the room; only a fresh ping notifies, decision 1 in docs/20-push.md § Triggers). Asynchronous and non-blocking.

func (*Service) NotifyChatMessage

func (s *Service) NotifyChatMessage(sp space.Space, objectId, msgId string)

NotifyChatMessage is the chatSend hook: read the just-written message back (for the server-derived `mentions` + text), then enqueue a loud notification on the full topic superset —

chats                                   space-wide "all"
chats/<sha256hex(chatId)>               per-chat "all"
chats/<sha256hex(chatId)>/<mention>     per-chat mention
<mention>                               bare identity (bulk mentions)

groupId = sha256hex(chatId). Asynchronous and non-blocking — safe to call on the handler goroutine after chat.Send succeeds.

func (*Service) NotifyChatRead

func (s *Service) NotifyChatRead(spaceId, objectId string)

NotifyChatRead is the chatRead / chatReadAll hook: a SILENT notification with the chat's groupId (topics nil — the server targets the caller's own-identity topic on the silent path), so the account's other devices wake and refresh their badges. Heart hooks its read RPCs the same way. Non-blocking (plain Enqueue).

func (*Service) RevokeToken

func (s *Service) RevokeToken(ctx context.Context) error

RevokeToken forwards the revoke (best-effort — the token file is removed locally regardless, so a device that logged out stays logged out even if the push node was unreachable at that moment) and deletes the persisted file.

func (*Service) SetToken

func (s *Service) SetToken(ctx context.Context, platform space.PushPlatform, token string) error

SetToken persists the device token and forwards it to the push node. The persist is the durable part; the forward is bounded by forwardBudget and, on transient failure, retried in the background by the sync loop — so a slow/unreachable push node never fails the call. Only ErrPushNotConfigured (and local disk errors) surface.

func (*Service) Start

func (s *Service) Start(ctx context.Context)

Start loads the persisted device token, subscribes to space-list changes, and spawns the sync + notify loops. Non-blocking; a persisted token is re-registered by the first sync round in the background (never fails boot). The passed ctx bounds all background work — cancel it (or call Close) to stop.

func (*Service) Subscriptions

func (s *Service) Subscriptions(ctx context.Context) ([]space.PushSubscription, error)

Subscriptions returns the account's server-held topic set (raw {spaceKey, topic} rows, unsigned) — SDK passthrough.

func (*Service) TokenStatus

func (s *Service) TokenStatus() (registered bool, platform string)

TokenStatus reports the LOCAL registration state — whether a token file exists on this device and for which platform. It does not round-trip the push node.

type Type

type Type int

Type discriminates payload kinds. Heart parity.

const ChatMessage Type = 1

ChatMessage is the only payload type v1 emits (message create + mention-adding edits; reads go through the silent path with no payload).

Jump to

Keyboard shortcuts

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