From e641e3b3c490bdea97fa86e3c4594ce82e1b65a1 Mon Sep 17 00:00:00 2001 From: David Florness Date: Fri, 5 Feb 2021 20:05:37 -0500 Subject: [PATCH] Use the elections map as our mautrix store --- cmd/tallyard/main.go | 15 +++----- election/election.go | 2 +- election/map.go | 47 +++++++++++++++++------ matrix/rooms_disk_store.go | 56 --------------------------- matrix/store.go | 78 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 77 deletions(-) delete mode 100644 matrix/rooms_disk_store.go create mode 100644 matrix/store.go diff --git a/cmd/tallyard/main.go b/cmd/tallyard/main.go index 6279e0a..c7453a9 100644 --- a/cmd/tallyard/main.go +++ b/cmd/tallyard/main.go @@ -60,25 +60,22 @@ func main() { if err != nil { panic(err) } - store, err := matrix.NewRoomsDiskStore() + + // setup the elections store + elections, err := election.GetElectionsMap(client.UserID) if err != nil { panic(err) } + client.Store = matrix.NewTallyardStore(elections) defer func() { - err := store.Save() + err := elections.Save() if err != nil { panic(err) } }() - client.Store = store - - elections, err := election.GetElectionsMap() - if err != nil { - panic(err) - } syncer := client.Syncer.(*mautrix.DefaultSyncer) - syncer.OnEvent(client.Store.(*matrix.RoomsDiskStore).UpdateState) + syncer.OnEvent(client.Store.(*matrix.TallyardStore).UpdateState) election.SetupEventHooks(client, syncer, elections) go func() { diff --git a/election/election.go b/election/election.go index 5085167..afbef08 100644 --- a/election/election.go +++ b/election/election.go @@ -19,7 +19,7 @@ type Election struct { Joins map[id.EventID]*Voter `json:"joins"` LocalVoter *LocalVoter `json:"local_voter,omitempty"` RoomID id.RoomID `json:"room_id"` - Save func() `json:"-"` + Save func() error `json:"-"` StartEvt *event.Event `json:"start_evt,omitempty"` Title string `json:"title"` } diff --git a/election/map.go b/election/map.go index 3711582..cf27992 100644 --- a/election/map.go +++ b/election/map.go @@ -9,9 +9,8 @@ import ( "sync" "time" - log "github.com/sirupsen/logrus" - "github.com/kyoh86/xdg" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/id" ) @@ -37,16 +36,21 @@ type ElectionsMap struct { // maps join event ID to voter. This is a convenience in scenarios when // we don't know the exact election Joins map[id.EventID]*Voter `json:"-"` + + // needed by the state store + NextBatch string `json:"next_batch"` + Rooms map[id.RoomID]*mautrix.Room `json:"rooms"` + UserID id.UserID `json:"userid"` } -const electionsMapVersion = 1 +const electionsMapVersion = 2 var electionsFname = xdg.DataHome() + "/tallyard/elections.json" -func GetElectionsMap() (em *ElectionsMap, err error) { +func GetElectionsMap(userID id.UserID) (em *ElectionsMap, err error) { em = &ElectionsMap{} if _, err = os.Stat(electionsFname); os.IsNotExist(err) { - return newElectionsMap(), nil + return newElectionsMap(userID), nil } jsonBytes, err := ioutil.ReadFile(electionsFname) if err != nil { @@ -60,6 +64,10 @@ func GetElectionsMap() (em *ElectionsMap, err error) { return nil, fmt.Errorf("Your stored elections schema version (%d) is incompitable with the latest schema version (%d).", em.Version, electionsMapVersion) } + if userID != em.UserID { + // TODO support multiple users? + return nil, fmt.Errorf("user IDs don't match") + } em.L = make(map[id.RoomID][]*Election, 0) em.Ltime = time.Now() for createEventId, el := range em.M { @@ -75,17 +83,35 @@ func GetElectionsMap() (em *ElectionsMap, err error) { return } -func newElectionsMap() *ElectionsMap { +func (em *ElectionsMap) UnmarshalJSON(b []byte) error { + type Alias ElectionsMap + err := json.Unmarshal(b, (*Alias)(em)) + if err != nil { + return err + } + for _, room := range em.Rooms { + for eventType, events := range room.State { + for _, evt := range events { + evt.Content.ParseRaw(eventType) + } + } + } + return nil +} + +func newElectionsMap(userID id.UserID) *ElectionsMap { return &ElectionsMap{ Version: electionsMapVersion, M: make(map[id.EventID]*Election), L: make(map[id.RoomID][]*Election), Ltime: time.Now(), Joins: make(map[id.EventID]*Voter), + Rooms: make(map[id.RoomID]*mautrix.Room), + UserID: userID, } } -func (em *ElectionsMap) Save() { +func (em *ElectionsMap) Save() error { em.RLock() defer em.RUnlock() for _, el := range em.M { @@ -94,14 +120,13 @@ func (em *ElectionsMap) Save() { } jsonBytes, err := json.Marshal(em) if err != nil { - log.Errorf("couldn't marshal elections: %s", err) - return + return fmt.Errorf("couldn't marshal elections: %s", err) } err = ioutil.WriteFile(electionsFname, jsonBytes, 0600) if err != nil { - log.Errorf("couldn't save elections: %s", err) - return + return fmt.Errorf("couldn't save elections: %s", err) } + return nil } func (em *ElectionsMap) Get(createEventID id.EventID) *Election { diff --git a/matrix/rooms_disk_store.go b/matrix/rooms_disk_store.go deleted file mode 100644 index e852d62..0000000 --- a/matrix/rooms_disk_store.go +++ /dev/null @@ -1,56 +0,0 @@ -package matrix - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - - "github.com/kyoh86/xdg" - "maunium.net/go/mautrix" -) - -// TODO: this sucks; this is not what JSON is for - -type RoomsDiskStore struct { - *mautrix.InMemoryStore -} - -var roomsFname = xdg.DataHome() + "/tallyard/rooms.json" - -func NewRoomsDiskStore() (*RoomsDiskStore, error) { - t := &RoomsDiskStore{ - mautrix.NewInMemoryStore(), - } - if _, err := os.Stat(roomsFname); os.IsNotExist(err) { - return t, nil - } - roomBytes, err := ioutil.ReadFile(roomsFname) - if err != nil { - return t, fmt.Errorf("couldn't read rooms file: %s", err) - } - err = json.Unmarshal(roomBytes, &t.Rooms) - if err != nil { - return t, fmt.Errorf("couldn't unmarshal rooms file: %s", err) - } - for _, room := range t.Rooms { - for stateEventType, m := range room.State { - for _, evt := range m { - evt.Content.ParseRaw(stateEventType) - } - } - } - return t, nil -} - -func (t *RoomsDiskStore) Save() error { - jsonBytes, err := json.Marshal(t.Rooms) - if err != nil { - return fmt.Errorf("couldn't marshal rooms: %s", err) - } - err = ioutil.WriteFile(roomsFname, jsonBytes, 0600) - if err != nil { - return fmt.Errorf("couldn't write rooms: %s", err) - } - return nil -} diff --git a/matrix/store.go b/matrix/store.go new file mode 100644 index 0000000..70b3d6d --- /dev/null +++ b/matrix/store.go @@ -0,0 +1,78 @@ +package matrix + +import ( + log "github.com/sirupsen/logrus" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" + "tallyard.xyz/election" +) + +type TallyardStore struct { + electionsMap *election.ElectionsMap + filters map[id.UserID]string +} + +func NewTallyardStore(electionsMap *election.ElectionsMap) *TallyardStore { + return &TallyardStore{ + electionsMap: electionsMap, + filters: make(map[id.UserID]string), + } +} + +func (s *TallyardStore) SaveFilterID(userID id.UserID, filterID string) { + s.filters[userID] = filterID +} + +func (s *TallyardStore) LoadFilterID(userID id.UserID) string { + return s.filters[userID] +} + +func (s *TallyardStore) SaveNextBatch(userID id.UserID, nextBatchToken string) { + if userID == s.electionsMap.UserID { + s.electionsMap.Lock() + defer s.electionsMap.Unlock() + s.electionsMap.NextBatch = nextBatchToken + } else { + // *should* never happen + log.Warnf("SaveNextBatch: %s is not the local user %s; please report to devs", + userID, s.electionsMap.UserID) + } +} + +func (s *TallyardStore) LoadNextBatch(userID id.UserID) string { + if userID == s.electionsMap.UserID { + s.electionsMap.RLock() + defer s.electionsMap.RUnlock() + return s.electionsMap.NextBatch + } else { + // *should* never happen + log.Warnf("LoadNextBatch: %s is not the local user %s; please report to devs", + userID, s.electionsMap.UserID) + return "" + } +} + +func (s *TallyardStore) SaveRoom(room *mautrix.Room) { + s.electionsMap.Lock() + defer s.electionsMap.Unlock() + s.electionsMap.Rooms[room.ID] = room +} + +func (s *TallyardStore) LoadRoom(roomID id.RoomID) *mautrix.Room { + s.electionsMap.RLock() + defer s.electionsMap.RUnlock() + return s.electionsMap.Rooms[roomID] +} + +func (s *TallyardStore) UpdateState(_ mautrix.EventSource, evt *event.Event) { + if !evt.Type.IsState() { + return + } + room := s.LoadRoom(evt.RoomID) + if room == nil { + room = mautrix.NewRoom(evt.RoomID) + s.SaveRoom(room) + } + room.UpdateState(evt) +} -- 2.38.4