forked from mautrix/go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
syncstore.go
167 lines (145 loc) · 4.9 KB
/
syncstore.go
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package mautrix
import (
"context"
"errors"
"github.com/globekeeper/gomautrix/id"
)
// SyncStore is an interface which must be satisfied to store client data.
//
// You can either write a struct which persists this data to disk, or you can use the
// provided "MemorySyncStore" which just keeps data around in-memory which is lost on
// restarts.
type SyncStore interface {
SaveFilterID(userID id.UserID, filterID string)
LoadFilterID(userID id.UserID) string
SaveNextBatch(userID id.UserID, nextBatchToken string)
LoadNextBatch(userID id.UserID) string
}
// Deprecated: renamed to SyncStore
type Storer = SyncStore
// MemorySyncStore implements the Storer interface.
//
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
// or next batch tokens on any goroutine other than the syncing goroutine: the one
// which called Client.Sync().
type MemorySyncStore struct {
Filters map[id.UserID]string
NextBatch map[id.UserID]string
}
// SaveFilterID to memory.
func (s *MemorySyncStore) SaveFilterID(userID id.UserID, filterID string) {
s.Filters[userID] = filterID
}
// LoadFilterID from memory.
func (s *MemorySyncStore) LoadFilterID(userID id.UserID) string {
return s.Filters[userID]
}
// SaveNextBatch to memory.
func (s *MemorySyncStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
s.NextBatch[userID] = nextBatchToken
}
// LoadNextBatch from memory.
func (s *MemorySyncStore) LoadNextBatch(userID id.UserID) string {
return s.NextBatch[userID]
}
// NewMemorySyncStore constructs a new MemorySyncStore.
func NewMemorySyncStore() *MemorySyncStore {
return &MemorySyncStore{
Filters: make(map[id.UserID]string),
NextBatch: make(map[id.UserID]string),
}
}
// AccountDataStore uses account data to store the next batch token, and stores the filter ID in memory
// (as filters can be safely recreated every startup).
type AccountDataStore struct {
FilterID string
EventType string
client *Client
nextBatch string
}
type accountData struct {
NextBatch string `json:"next_batch"`
}
func (s *AccountDataStore) SaveFilterID(userID id.UserID, filterID string) {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with a single account")
}
s.FilterID = filterID
}
func (s *AccountDataStore) LoadFilterID(userID id.UserID) string {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with a single account")
}
return s.FilterID
}
func (s *AccountDataStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with a single account")
} else if nextBatchToken == s.nextBatch {
return
}
data := accountData{
NextBatch: nextBatchToken,
}
err := s.client.SetAccountData(context.Background(), s.EventType, data)
if err != nil {
s.client.Log.Warn().Err(err).Msg("Failed to save next batch token to account data")
} else {
s.client.Log.Debug().
Str("old_token", s.nextBatch).
Str("new_token", nextBatchToken).
Msg("Saved next batch token")
s.nextBatch = nextBatchToken
}
}
func (s *AccountDataStore) LoadNextBatch(userID id.UserID) string {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with a single account")
}
data := &accountData{}
err := s.client.GetAccountData(context.Background(), s.EventType, data)
if err != nil {
if errors.Is(err, MNotFound) {
s.client.Log.Debug().Msg("No next batch token found in account data")
} else {
s.client.Log.Warn().Err(err).Msg("Failed to load next batch token from account data")
}
return ""
}
s.nextBatch = data.NextBatch
s.client.Log.Debug().Str("next_batch", data.NextBatch).Msg("Loaded next batch token from account data")
return s.nextBatch
}
// NewAccountDataStore returns a new AccountDataStore, which stores
// the next_batch token as a custom event in account data in the
// homeserver.
//
// AccountDataStore is only appropriate for bots, not appservices.
//
// The event type should be a reversed DNS name like tld.domain.sub.internal and
// must be unique for a client. The data stored in it is considered internal
// and must not be modified through outside means. You should also add a filter
// for account data changes of this event type, to avoid ending up in a sync
// loop:
//
// filter := mautrix.Filter{
// AccountData: mautrix.FilterPart{
// Limit: 20,
// NotTypes: []event.Type{
// event.NewEventType(eventType),
// },
// },
// }
// // If you use a custom Syncer, set the filter there, not like this
// client.Syncer.(*mautrix.DefaultSyncer).FilterJSON = &filter
// client.Store = mautrix.NewAccountDataStore("com.example.mybot.store", client)
// go func() {
// err := client.Sync()
// // don't forget to check err
// }()
func NewAccountDataStore(eventType string, client *Client) *AccountDataStore {
return &AccountDataStore{
EventType: eventType,
client: client,
}
}