xmpp/design/110_mam.md

4.3 KiB

Message Archive Management

Author(s): Sam Whited sam@samwhited.com
Last updated: 2021-02-24
Discussion: https://mellium.im/issue/110

Abstract

An API to support history retrieval via XEP-0313: Message Archive Management.

Requirements

  • Ability to process messages from an archive in order in a seemingly synchronous fashion
  • The ability to process messages from an archive asynchronously using a user-defined handler that is not part of this package
  • Pagination in both directions
  • Support for non-standard form fields

Proposal

Implementing the current proposal would add three types, 11 methods, and 2 functions that would need to remain backwards compatible once we reach 1.0.

// The namespace used by this package, provided as a convenience.
const NS = `urn:xmpp:mam:2`

type Query struct{
  ID      string
  With    jid.JID
  Start   time.Time
  End     time.Time
  Before  string
  After   string
  IDs     []string
  Field   []form.Field
  Max     uint64
  Reverse bool
}

// TokenReader implements xmlstream.Marshaler.
func (*Query) TokenReader() xml.TokenReader {}

// WriteXML implements xmlstream.WriterTo.
func (*Query) WriteXML(w xmlstream.TokenWriter) (int, error) {}

// MarshalXML implements xml.Marshaler.
func (*Query) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {}

// UnmarshalXML implements xml.Unmarshaler.
func (*Query) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {}

// Handle returns an option that registers a Handler for archive responses.
func Handle(h *Handler) mux.Option

// Get can be called to send a query where the responses should be handled by a
// user defined handler.
// It will block until all messages sent in response have been received and
// processed.
func Get(ctx context.Context, q Query) error {}

// Handler listens for incoming responses from an archive and matches them to
// outgoing requests sent with Get or GetIQ.
type Handler struct{}

// HandleMessage implements mux.MessageHandler.
func (h *Handler) HandleMessage(msg stanza.Message, t xmlstream.TokenReadEncoder) error {}

// Get requests history just like the Query function, except that it associates
// responses to the original query and allows the user to iterate through them
// as they come in.
//
// Unlike most iterators, archive history may arrive interspersed with other
// traffic, causing the Iter to block waiting for the next response from the
// archive.
// Care should be taken not to block processing of the stream.
// When the iter is closed if any subsequent responses are received they are
// sent to the normal serve handler for processing.
func (Handler) Get(ctx context.Context, q Query) *Iter

// Iter is used to iterate through MAM responses.
type Iter struct{}

// Next returns true if we expect more results from the iterator.
// When Next is called it blocks until one of three things happens: the next
// response is received, the context used when creating the iter is canceled,
// the original queries result is sent back indicating that no more responses
// are expected.
// Next automatically fetches the next (or previous) page if the end of the
// current page has been reached.
func (*Iter) Next() bool {}

// Current returns the most resent response from the archive.
func (*Iter) Current() (Result, xml.TokenReader) {}

// Close stops the iterator from receiving further archive responses.
// It does not cancel the current query.
// Future responses will be sent to the normal handler and processed just like
// any other incoming message where they may be processed or ignored.
func (*Iter) Close() error {}

// Err returns any error that was encountered while iterating.
func (*Iter) Err() error {}

// Set returns information about the finished query.
func (*Iter) Set() paging.Set {}

Open Questions

  • How can we verify that urn:xmpp:mam:2#extended is in the disco features before allowing the user to make requests that include extended querying?
  • Instead can we discover the form first and check if it has these fields (which presumably also means it supports <flip-page/> as well)?
  • How do we query for metadata?
  • Can we recommend a good querying strategy for clients in this package and make it the default or a special easy to use function?