package storage import ( "context" "errors" ) // ErrNoSuchPusher is a pusher that does not exist. var ErrNoSuchPusher = errors.New("no such pusher") // Pusher is one place a user's notifications are pushed to: an app on a // device, reached through its push gateway. type Pusher struct { Localpart string // DeviceID is the session that registered the pusher, so that logging // the device out removes it. Empty for a pusher whose session is not // known. DeviceID string Kind string AppID string PushKey string AppDisplayName string DeviceDisplayName string ProfileTag string Lang string // Data is the pusher's data object as the client sent it: the gateway // URL, the payload format, and anything else the app wants passed to // its gateway. Data []byte // PushKeyTSMS is when the push key was last set, which a gateway uses // to tell an old registration of a key from a newer one. PushKeyTSMS int64 } // NewNotification is one event that notifies one user. type NewNotification struct { UserID string RoomID string EventID string // EventPos is the event's position in the room event stream, which is // what a read receipt is compared against. EventPos int64 // ThreadID is the thread the event is in: the root its m.relates_to // names when rel_type is m.thread, or ThreadMain for an event on the // main timeline. A thread root is itself on the main timeline. ThreadID string TimestampMS int64 Highlight bool // Actions are the matched rule's actions, as JSON, which GET // /notifications reports. Actions []byte } // Notification is a stored notification. type Notification struct { NewNotification // ID orders a user's notifications, newest highest, and is what GET // /notifications pages by. ID int64 Read bool } // Threads a read receipt can be for, as the thread argument of // MarkNotificationsRead. Any other value is a thread root's event ID. const ( // ThreadMain is the main timeline: the thread ID of an event in no // thread, and what a receipt with thread_id "main" reads. ThreadMain = "" // AllThreads is an unthreaded receipt, which reads the main timeline // and every thread together. It cannot be mistaken for a thread root: // an event ID starts with '$'. AllThreads = "*" ) // NotificationCounts is a room's unread notifications for one user. type NotificationCounts struct { Notifications int Highlights int } // PushStore persists pushers and the notifications push rules produce. // // Notification counts are a stream of their own: every write that changes // a user's counts in a room stamps the room with a new position, inside // the transaction that changes them, so that an incremental sync can ask // which rooms' counts moved since its token. A count moves without an // event — a read receipt sent from another device sets it back to zero — // and a sync that only looked at events would go on showing the old one. type PushStore interface { // Pushers lists a user's pushers. Pushers(ctx context.Context, localpart string) ([]Pusher, error) // SetPusher creates or replaces the user's pusher with the same app ID // and push key. Unless appendOthers is set, any other user's pusher // with the same app ID and push key is removed: a push key names a // device, and the device is now this user's. SetPusher(ctx context.Context, p Pusher, appendOthers bool) error // DeletePusher removes one of a user's pushers. Removing one that is // not there is not an error. DeletePusher(ctx context.Context, localpart, appID, pushKey string) error // DeletePushersForDevices removes the pushers registered by the given // sessions, or by every session of the user when deviceIDs is empty. DeletePushersForDevices(ctx context.Context, localpart string, deviceIDs []string) error // AddNotifications records notifications and moves the counts of // each (user, room) they are for, returning the position the last // count moved to. AddNotifications(ctx context.Context, ns []NewNotification) (int64, error) // MarkNotificationsRead marks a user's notifications in a room up to // and including an event position as read, and reports whether the // counts moved. thread limits it to one thread — ThreadMain for the // main timeline, a root's event ID for a thread — or is AllThreads. MarkNotificationsRead(ctx context.Context, userID, roomID, thread string, upTo int64) (pos int64, moved bool, err error) // NotificationCounts reads a user's unread counts in the given rooms. // A room with nothing unread is in the result with zero counts. NotificationCounts(ctx context.Context, userID string, roomIDs []string) (map[string]NotificationCounts, error) // ThreadNotificationCounts reads a user's unread counts in the given // rooms one thread at a time, keyed by room and then by thread ID, with // the main timeline under ThreadMain. Only threads with something // unread are in the result. The room's move in the notification stream // covers its threads: any change to a thread's counts is a change to // the room's. ThreadNotificationCounts(ctx context.Context, userID string, roomIDs []string) (map[string]map[string]NotificationCounts, error) // NotificationCountChanges lists the rooms whose counts for a user // moved after from and at or before to. NotificationCountChanges(ctx context.Context, userID string, from, to int64) ([]string, error) // UnreadNotifications is a user's unread notifications across every // room, which a push tells the device to show as its badge. UnreadNotifications(ctx context.Context, userID string) (int, error) // Notifications pages through a user's notifications, newest first, // starting below the ID before (0 for the newest). Notifications(ctx context.Context, userID string, before int64, limit int, onlyHighlight bool) ([]Notification, error) // PruneNotifications removes read notifications older than a time, // returning how many went. PruneNotifications(ctx context.Context, beforeMS int64) (int64, error) }