sync: add support for MSC4222

This commit is contained in:
Tulir Asokan 2025-03-09 14:29:54 +02:00
commit 52c8a2e1de
4 changed files with 27 additions and 13 deletions

View file

@ -696,6 +696,7 @@ type ReqSync struct {
FullState bool
SetPresence event.Presence
StreamResponse bool
UseStateAfter bool
BeeperStreaming bool
Client *http.Client
}
@ -716,6 +717,9 @@ func (req *ReqSync) BuildQuery() map[string]string {
if req.FullState {
query["full_state"] = "true"
}
if req.UseStateAfter {
query["org.matrix.msc4222.use_state_after"] = "true"
}
if req.BeeperStreaming {
query["com.beeper.streaming"] = "true"
}

View file

@ -118,6 +118,9 @@ type MautrixInfo struct {
DecryptionDuration time.Duration
CheckpointSent bool
// When using MSC4222 and the state_after field, this field is set
// for timeline events to indicate they shouldn't update room state.
IgnoreState bool
}
func (evt *Event) GetStateKey() string {

View file

@ -393,6 +393,7 @@ type BeeperInboxPreviewEvent struct {
type SyncJoinedRoom struct {
Summary LazyLoadSummary `json:"summary"`
State SyncEventsList `json:"state"`
StateAfter *SyncEventsList `json:"org.matrix.msc4222.state_after,omitempty"`
Timeline SyncTimeline `json:"timeline"`
Ephemeral SyncEventsList `json:"ephemeral"`
AccountData SyncEventsList `json:"account_data"`

32
sync.go
View file

@ -97,33 +97,38 @@ func (s *DefaultSyncer) ProcessResponse(ctx context.Context, res *RespSync, sinc
}
}
s.processSyncEvents(ctx, "", res.ToDevice.Events, event.SourceToDevice)
s.processSyncEvents(ctx, "", res.Presence.Events, event.SourcePresence)
s.processSyncEvents(ctx, "", res.AccountData.Events, event.SourceAccountData)
s.processSyncEvents(ctx, "", res.ToDevice.Events, event.SourceToDevice, false)
s.processSyncEvents(ctx, "", res.Presence.Events, event.SourcePresence, false)
s.processSyncEvents(ctx, "", res.AccountData.Events, event.SourceAccountData, false)
for roomID, roomData := range res.Rooms.Join {
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceJoin|event.SourceState)
s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceJoin|event.SourceTimeline)
s.processSyncEvents(ctx, roomID, roomData.Ephemeral.Events, event.SourceJoin|event.SourceEphemeral)
s.processSyncEvents(ctx, roomID, roomData.AccountData.Events, event.SourceJoin|event.SourceAccountData)
if roomData.StateAfter == nil {
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceJoin|event.SourceState, false)
s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceJoin|event.SourceTimeline, false)
} else {
s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceJoin|event.SourceTimeline, true)
s.processSyncEvents(ctx, roomID, roomData.StateAfter.Events, event.SourceJoin|event.SourceState, false)
}
s.processSyncEvents(ctx, roomID, roomData.Ephemeral.Events, event.SourceJoin|event.SourceEphemeral, false)
s.processSyncEvents(ctx, roomID, roomData.AccountData.Events, event.SourceJoin|event.SourceAccountData, false)
}
for roomID, roomData := range res.Rooms.Invite {
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceInvite|event.SourceState)
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceInvite|event.SourceState, false)
}
for roomID, roomData := range res.Rooms.Leave {
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceLeave|event.SourceState)
s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceLeave|event.SourceTimeline)
s.processSyncEvents(ctx, roomID, roomData.State.Events, event.SourceLeave|event.SourceState, false)
s.processSyncEvents(ctx, roomID, roomData.Timeline.Events, event.SourceLeave|event.SourceTimeline, false)
}
return
}
func (s *DefaultSyncer) processSyncEvents(ctx context.Context, roomID id.RoomID, events []*event.Event, source event.Source) {
func (s *DefaultSyncer) processSyncEvents(ctx context.Context, roomID id.RoomID, events []*event.Event, source event.Source, ignoreState bool) {
for _, evt := range events {
s.processSyncEvent(ctx, roomID, evt, source)
s.processSyncEvent(ctx, roomID, evt, source, ignoreState)
}
}
func (s *DefaultSyncer) processSyncEvent(ctx context.Context, roomID id.RoomID, evt *event.Event, source event.Source) {
func (s *DefaultSyncer) processSyncEvent(ctx context.Context, roomID id.RoomID, evt *event.Event, source event.Source, ignoreState bool) {
evt.RoomID = roomID
// Ensure the type class is correct. It's safe to mutate the class since the event type is not a pointer.
@ -149,6 +154,7 @@ func (s *DefaultSyncer) processSyncEvent(ctx context.Context, roomID id.RoomID,
}
evt.Mautrix.EventSource = source
evt.Mautrix.IgnoreState = ignoreState
s.Dispatch(ctx, evt)
}