M cmd/tallyard/main.go => cmd/tallyard/main.go +6 -9
@@ 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() {
M election/election.go => election/election.go +1 -1
@@ 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"`
}
M election/map.go => election/map.go +36 -11
@@ 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 {
D matrix/rooms_disk_store.go => matrix/rooms_disk_store.go +0 -56
@@ 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
-}
A matrix/store.go => matrix/store.go +78 -0
@@ 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)
+}