aboutsummaryrefslogtreecommitdiff
path: root/matrix/sync.go
blob: 9eeb87ad4fe780e24c99011cbb4eaffd61970964 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package matrix

import (
	"encoding/json"
	"fmt"
	"runtime/debug"
	"time"

	"maunium.net/go/gomatrix"
	"maunium.net/go/gomuks/config"
)

// GomuksSyncer is the default syncing implementation. You can either write your own syncer, or selectively
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
// pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information.
type GomuksSyncer struct {
	Session   *config.Session
	listeners map[string][]gomatrix.OnEventListener // event type to listeners array
}

// NewGomuksSyncer returns an instantiated GomuksSyncer
func NewGomuksSyncer(session *config.Session) *GomuksSyncer {
	return &GomuksSyncer{
		Session:   session,
		listeners: make(map[string][]gomatrix.OnEventListener),
	}
}

func (s *GomuksSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (err error) {
	if !s.shouldProcessResponse(res, since) {
		return
	}
	// gdebug.Print("Processing sync response", since, res)

	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.Session.MXID, since, r, debug.Stack())
		}
	}()

	for _, event := range res.Presence.Events {
		s.notifyListeners(event)
	}
	for _, event := range res.AccountData.Events {
		s.notifyListeners(event)
	}
	for roomID, roomData := range res.Rooms.Join {
		room := s.Session.GetRoom(roomID)
		for _, event := range roomData.State.Events {
			event.RoomID = roomID
			room.UpdateState(event)
			s.notifyListeners(event)
		}
		for _, event := range roomData.Timeline.Events {
			event.RoomID = roomID
			s.notifyListeners(event)
		}
		for _, event := range roomData.Ephemeral.Events {
			event.RoomID = roomID
			s.notifyListeners(event)
		}

		if len(room.PrevBatch) == 0 {
			room.PrevBatch = roomData.Timeline.PrevBatch
		}
	}
	for roomID, roomData := range res.Rooms.Invite {
		room := s.Session.GetRoom(roomID)
		for _, event := range roomData.State.Events {
			event.RoomID = roomID
			room.UpdateState(event)
			s.notifyListeners(event)
		}
	}
	for roomID, roomData := range res.Rooms.Leave {
		room := s.Session.GetRoom(roomID)
		for _, event := range roomData.Timeline.Events {
			if event.StateKey != nil {
				event.RoomID = roomID
				room.UpdateState(event)
				s.notifyListeners(event)
			}
		}

		if len(room.PrevBatch) == 0 {
			room.PrevBatch = roomData.Timeline.PrevBatch
		}
	}
	return
}

// OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks.
func (s *GomuksSyncer) OnEventType(eventType string, callback gomatrix.OnEventListener) {
	_, exists := s.listeners[eventType]
	if !exists {
		s.listeners[eventType] = []gomatrix.OnEventListener{}
	}
	s.listeners[eventType] = append(s.listeners[eventType], callback)
}

// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
// stuff that shouldn't be processed.
func (s *GomuksSyncer) shouldProcessResponse(resp *gomatrix.RespSync, since string) bool {
	if since == "" {
		return false
	}
	return true
}

func (s *GomuksSyncer) notifyListeners(event *gomatrix.Event) {
	listeners, exists := s.listeners[event.Type]
	if !exists {
		return
	}
	for _, fn := range listeners {
		fn(event)
	}
}

// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
func (s *GomuksSyncer) OnFailedSync(res *gomatrix.RespSync, err error) (time.Duration, error) {
	return 10 * time.Second, nil
}

// GetFilterJSON returns a filter with a timeline limit of 50.
func (s *GomuksSyncer) GetFilterJSON(userID string) json.RawMessage {
	return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
}