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
|
package mautrix
import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// Storer 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 "InMemoryStore" which just keeps data around in-memory which is lost on
// restarts.
type Storer interface {
SaveFilterID(userID id.UserID, filterID string)
LoadFilterID(userID id.UserID) string
SaveNextBatch(userID id.UserID, nextBatchToken string)
LoadNextBatch(userID id.UserID) string
SaveRoom(room *Room)
LoadRoom(roomID id.RoomID) *Room
}
// InMemoryStore 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 InMemoryStore struct {
Filters map[id.UserID]string
NextBatch map[id.UserID]string
Rooms map[id.RoomID]*Room
}
// SaveFilterID to memory.
func (s *InMemoryStore) SaveFilterID(userID id.UserID, filterID string) {
s.Filters[userID] = filterID
}
// LoadFilterID from memory.
func (s *InMemoryStore) LoadFilterID(userID id.UserID) string {
return s.Filters[userID]
}
// SaveNextBatch to memory.
func (s *InMemoryStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
s.NextBatch[userID] = nextBatchToken
}
// LoadNextBatch from memory.
func (s *InMemoryStore) LoadNextBatch(userID id.UserID) string {
return s.NextBatch[userID]
}
// SaveRoom to memory.
func (s *InMemoryStore) SaveRoom(room *Room) {
s.Rooms[room.ID] = room
}
// LoadRoom from memory.
func (s *InMemoryStore) LoadRoom(roomID id.RoomID) *Room {
return s.Rooms[roomID]
}
// UpdateState stores a state event. This can be passed to DefaultSyncer.OnEvent to keep all room state cached.
func (s *InMemoryStore) UpdateState(_ EventSource, evt *event.Event) {
if !evt.Type.IsState() {
return
}
room := s.LoadRoom(evt.RoomID)
if room == nil {
room = NewRoom(evt.RoomID)
s.SaveRoom(room)
}
room.UpdateState(evt)
}
// NewInMemoryStore constructs a new InMemoryStore.
func NewInMemoryStore() *InMemoryStore {
return &InMemoryStore{
Filters: make(map[id.UserID]string),
NextBatch: make(map[id.UserID]string),
Rooms: make(map[id.RoomID]*Room),
}
}
// AccountDataStore uses account data to store the next batch token, and
// reuses the InMemoryStore for all other operations.
type AccountDataStore struct {
*InMemoryStore
eventType string
client *Client
}
type accountData struct {
NextBatch string `json:"next_batch"`
}
// SaveNextBatch to account data.
func (s *AccountDataStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with bots")
}
data := accountData{
NextBatch: nextBatchToken,
}
err := s.client.SetAccountData(s.eventType, data)
if err != nil {
if s.client.Logger != nil {
s.client.Logger.Debugfln("failed to save next batch token to account data: %s", err.Error())
}
}
}
// LoadNextBatch from account data.
func (s *AccountDataStore) LoadNextBatch(userID id.UserID) string {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with bots")
}
data := &accountData{}
err := s.client.GetAccountData(s.eventType, data)
if err != nil {
if s.client.Logger != nil {
s.client.Logger.Debugfln("failed to load next batch token to account data: %s", err.Error())
}
return ""
}
return data.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.
//
// eventType 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:
//
// mautrix.Filter{
// AccountData: mautrix.FilterPart{
// Limit: 20,
// NotTypes: []event.Type{
// event.NewEventType(eventType),
// },
// },
// }
// mautrix.Client.CreateFilter(...)
//
func NewAccountDataStore(eventType string, client *Client) *AccountDataStore {
return &AccountDataStore{
InMemoryStore: NewInMemoryStore(),
eventType: eventType,
client: client,
}
}
|