Documentation
¶
Index ¶
- Constants
- Variables
- func IDFromPageToken(token *commonpb.PagingToken) (messageID uint64, ok bool)
- func IntentIdToClientMessageId(intentID *commonpb.IntentId) *messagingpb.ClientMessageId
- func NewChatMessagingReader(store Store) chat.MessagingReader
- func PageTokenFromID(messageID *messagingpb.MessageId) *commonpb.PagingToken
- type Message
- type MessageRef
- type PointerRef
- type Sender
- type Server
- func (s *Server) AdvancePointer(ctx context.Context, req *messagingpb.AdvancePointerRequest) (*messagingpb.AdvancePointerResponse, error)
- func (s *Server) GetMessage(ctx context.Context, req *messagingpb.GetMessageRequest) (*messagingpb.GetMessageResponse, error)
- func (s *Server) GetMessages(ctx context.Context, req *messagingpb.GetMessagesRequest) (*messagingpb.GetMessagesResponse, error)
- func (s *Server) NotifyIsTyping(ctx context.Context, req *messagingpb.NotifyIsTypingRequest) (*messagingpb.NotifyIsTypingResponse, error)
- func (s *Server) SendMessage(ctx context.Context, req *messagingpb.SendMessageRequest) (*messagingpb.SendMessageResponse, error)
- type Store
Constants ¶
const ClientMessageIDSize = 16
ClientMessageIDSize is the length, in bytes, of a client message ID.
Variables ¶
var ErrMessageNotFound = errors.New("message not found")
ErrMessageNotFound indicates that no message exists for the given chat and message ID.
var StoredPointerTypes = []messagingpb.Pointer_Type{ messagingpb.Pointer_DELIVERED, messagingpb.Pointer_READ, }
StoredPointerTypes are the only pointer types persisted, for any chat type: DELIVERED and READ. SENT is client-side and never stored, so enumerating these per member addresses every pointer that can exist for a chat. Treat as read-only.
Functions ¶
func IDFromPageToken ¶
func IDFromPageToken(token *commonpb.PagingToken) (messageID uint64, ok bool)
IDFromPageToken decodes the message ID from a token produced by PageTokenFromID. The ok return is false if the token is nil or malformed.
func IntentIdToClientMessageId ¶ added in v1.14.0
func IntentIdToClientMessageId(intentID *commonpb.IntentId) *messagingpb.ClientMessageId
IntentIdToClientMessageId derives a ClientMessageId deterministically from an intent ID. It lets a server-authored send tied to a payment (e.g. the cash message posted after an intent settles) be idempotent on (chatID, clientMessageID): re-deriving from the same intent yields the same ID, so a retried send returns the originally persisted message instead of duplicating it. The intent ID is hashed because a ClientMessageId must be exactly ClientMessageIDSize bytes, narrower than an intent ID.
func NewChatMessagingReader ¶
func NewChatMessagingReader(store Store) chat.MessagingReader
NewChatMessagingReader returns a chat.MessagingReader backed by the given messaging store, for wiring the Chat service.
func PageTokenFromID ¶
func PageTokenFromID(messageID *messagingpb.MessageId) *commonpb.PagingToken
PageTokenFromID encodes a message ID as a paging token. The token is the identifier of the last message in a page; the next request resumes strictly after it. Shared by the store implementations and callers.
Types ¶
type Message ¶
type Message struct {
ChatID *commonpb.ChatId
ID *messagingpb.MessageId
SenderID *commonpb.UserId // nil for system messages
Content []*messagingpb.Content
Timestamp time.Time
UnreadSeq uint64
}
Message is a stored chat message.
ID and UnreadSeq are server-assigned by the store at PutMessage time. ID is a per-chat gapless sequence number that is the message's canonical identity, sort key, and pagination cursor. UnreadSeq is a separate per-chat running count of unread-eligible messages (see messagingpb.Message for the full semantics).
func (*Message) ToProto ¶
func (m *Message) ToProto() *messagingpb.Message
ToProto projects the stored message onto a messagingpb.Message.
type MessageRef ¶
type MessageRef struct {
ChatID *commonpb.ChatId
MessageID *messagingpb.MessageId
}
MessageRef identifies a single message within a chat. It is the unit of a cross-chat batch read (see Store.GetMessagesByRefs) — e.g. one ref per chat to fetch every chat's last message for the feed.
type PointerRef ¶ added in v1.14.0
PointerRef requests a chat's stored pointers (StoredPointerTypes) for the given members. It is the unit of the batched cross-chat pointer read (see Store.GetPointersForChats), mirroring MessageRef on the message path: the caller enumerates exactly which pointers to hydrate so the store can address them by key rather than scanning each chat's partition.
type Sender ¶ added in v1.14.0
type Sender struct {
// contains filtered or unexported fields
}
Sender is the engine behind a message send: it persists the message and performs every side effect — advancing the sender's read pointer, bumping the chat's last message, and broadcasting (with pushes) to members. It carries no authentication or transport concerns, so internal callers (e.g. injecting a cash message after a payment) can construct just a Sender rather than the full gRPC Server. The Server holds one and delegates SendMessage to it.
func (*Sender) Send ¶ added in v1.14.0
func (s *Sender) Send( ctx context.Context, chatID *commonpb.ChatId, senderID *commonpb.UserId, content []*messagingpb.Content, clientMessageID *messagingpb.ClientMessageId, countsTowardUnread bool, ) (*Message, error)
Send persists content as a message in the chat and performs every side effect of a send: it advances the sender's own read pointer past the message, records the message as the chat's most recent, and broadcasts the resulting update to all members. It is the shared core behind the SendMessage RPC and internal, server-authored sends — the latter bypass the RPC's client-side content and membership checks (e.g. injecting a cash message after a payment settles).
senderID may be nil to denote a system message, in which case no read pointer is advanced. countsTowardUnread controls whether the message advances the chat's unread sequence: true for user-authored messages (the sender doesn't see their own message as unread because their read pointer is advanced past it), false for messages that shouldn't bump anyone's unread count. Sends are idempotent on (chatID, clientMessageID).
type Server ¶
type Server struct {
messagingpb.UnimplementedMessagingServer
// contains filtered or unexported fields
}
func (*Server) AdvancePointer ¶
func (s *Server) AdvancePointer(ctx context.Context, req *messagingpb.AdvancePointerRequest) (*messagingpb.AdvancePointerResponse, error)
func (*Server) GetMessage ¶
func (s *Server) GetMessage(ctx context.Context, req *messagingpb.GetMessageRequest) (*messagingpb.GetMessageResponse, error)
func (*Server) GetMessages ¶
func (s *Server) GetMessages(ctx context.Context, req *messagingpb.GetMessagesRequest) (*messagingpb.GetMessagesResponse, error)
func (*Server) NotifyIsTyping ¶
func (s *Server) NotifyIsTyping(ctx context.Context, req *messagingpb.NotifyIsTypingRequest) (*messagingpb.NotifyIsTypingResponse, error)
func (*Server) SendMessage ¶
func (s *Server) SendMessage(ctx context.Context, req *messagingpb.SendMessageRequest) (*messagingpb.SendMessageResponse, error)
type Store ¶
type Store interface {
// PutMessage assigns the next gapless message ID for the chat and persists
// the message, returning the persisted message with its assigned ID and
// unread sequence.
//
// It is idempotent on (chatID, clientMessageID): a retried send with the
// same client message ID returns the originally persisted message without
// assigning a new ID.
//
// countsTowardUnread controls the unread sequence: when true the message's
// unread_seq is the previous value + 1; when false it carries the previous
// value forward (for messages that shouldn't bump anyone's unread count).
//
// senderID may be nil to denote a system message.
PutMessage(
ctx context.Context,
chatID *commonpb.ChatId,
senderID *commonpb.UserId,
content []*messagingpb.Content,
ts time.Time,
clientMessageID *messagingpb.ClientMessageId,
countsTowardUnread bool,
) (*Message, error)
// GetMessage returns a single message by ID, or ErrMessageNotFound.
GetMessage(ctx context.Context, chatID *commonpb.ChatId, messageID *messagingpb.MessageId) (*Message, error)
// GetMessages returns a page of messages for a chat ordered by message ID
// (ascending by default), paged via the provided query options. The paging
// token's value is the message ID of the last message from the previous
// page (see PageTokenFromID). Returns an empty result (no error) when the
// chat has no messages.
GetMessages(ctx context.Context, chatID *commonpb.ChatId, opts ...database.QueryOption) ([]*Message, error)
// GetMessagesByRefs returns the messages identified by the given refs that
// exist, across any number of chats. Refs without a matching message are
// omitted and duplicate refs collapse. Results are ordered by
// (chatID, message ID), so refs within a single chat come back ascending by
// ID. It returns an empty result (no error) when refs is empty.
//
// It is the batch read behind the DM feed, where it fetches every chat's last
// message in one call (one ref per chat). For the single-chat case the caller
// builds refs from a shared chat ID.
GetMessagesByRefs(ctx context.Context, refs []MessageRef) ([]*Message, error)
// GetPointers returns all delivered/read pointers for a chat. Returns an
// empty result (no error) when the chat has no pointers.
GetPointers(ctx context.Context, chatID *commonpb.ChatId) ([]*messagingpb.Pointer, error)
// GetPointersForChats returns the stored pointers (StoredPointerTypes) for the
// members named in each ref, keyed by string(chatID.Value). It is the
// cross-chat batch counterpart to GetPointers, used to hydrate member pointers
// for the DM feed. Unlike GetPointers it addresses pointers by exact key
// (chat × member × StoredPointerTypes) in a single batched read rather than
// scanning each chat's partition; since those are the only types ever stored,
// this is exhaustive. Chats with no matching pointers are absent from the map
// and duplicate (chat, member) pairs collapse. Returns an empty map (no error)
// when refs is empty.
GetPointersForChats(ctx context.Context, refs []PointerRef) (map[string][]*messagingpb.Pointer, error)
// AdvancePointer moves a member's pointer of the given type forward to
// newValue. Pointers are monotonic: a request to move a pointer to a value
// at or before its current value is a no-op. It returns ErrMessageNotFound
// if newValue does not reference an existing message in the chat. The bool
// return reports whether the pointer advanced.
AdvancePointer(
ctx context.Context,
chatID *commonpb.ChatId,
userID *commonpb.UserId,
pointerType messagingpb.Pointer_Type,
newValue *messagingpb.MessageId,
) (bool, error)
// AdvancePointerUnchecked is AdvancePointer for a newValue the caller already
// knows references an existing message — e.g. the sender's own READ pointer
// right after PutMessage returns the message it just wrote. It skips the
// existence check (and the read backing it), saving a round trip; the
// monotonic forward-only behavior is otherwise identical to AdvancePointer.
// It must not be used with a caller-supplied newValue, whose existence is not
// guaranteed.
AdvancePointerUnchecked(
ctx context.Context,
chatID *commonpb.ChatId,
userID *commonpb.UserId,
pointerType messagingpb.Pointer_Type,
newValue *messagingpb.MessageId,
) (bool, error)
}
Store persists chat messages and message-history pointers.
Each chat has its own gapless message ID sequence. Sends are made idempotent by a client-generated message ID, so a retried send returns the originally persisted message rather than assigning a new ID.