2020-11-20 21:51:10 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"compress/gzip"
|
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2020-12-30 15:40:23 +00:00
|
|
|
const storeMaxRecent = 25
|
|
|
|
|
|
|
|
type subscriber struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Months int64 `json:"months"`
|
|
|
|
}
|
2020-11-20 21:51:10 +00:00
|
|
|
|
|
|
|
type storage struct {
|
2021-01-03 23:31:53 +00:00
|
|
|
BitDonations struct {
|
|
|
|
LastDonator *string `json:"last_donator"`
|
|
|
|
LastAmount int64 `json:"last_amount"`
|
|
|
|
TotalAmounts map[string]int64 `json:"total_amounts"`
|
|
|
|
} `json:"bit_donations"`
|
2020-11-20 21:51:10 +00:00
|
|
|
Donations struct {
|
|
|
|
LastDonator *string `json:"last_donator"`
|
|
|
|
LastAmount float64 `json:"last_amount"`
|
|
|
|
TotalAmount float64 `json:"total_amount"`
|
|
|
|
} `json:"donations"`
|
|
|
|
Followers struct {
|
|
|
|
Last *string `json:"last"`
|
|
|
|
Seen []string `json:"seen"`
|
|
|
|
Count int64 `json:"count"`
|
|
|
|
} `json:"followers"`
|
|
|
|
Subs struct {
|
2020-12-30 15:40:23 +00:00
|
|
|
Last *string `json:"last"`
|
|
|
|
LastDuration int64 `json:"last_duration"`
|
|
|
|
Count int64 `json:"count"`
|
|
|
|
Recent []subscriber `json:"recent"`
|
2020-11-20 21:51:10 +00:00
|
|
|
} `json:"subs"`
|
|
|
|
|
2020-12-30 15:40:23 +00:00
|
|
|
modLock sync.RWMutex
|
2020-11-20 21:51:10 +00:00
|
|
|
saveLock sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStorage() *storage { return &storage{} }
|
|
|
|
|
|
|
|
func (s *storage) Load(from string) error {
|
|
|
|
f, err := os.Open(from)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "opening storage file")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
gf, err := gzip.NewReader(f)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "create gzip reader")
|
|
|
|
}
|
|
|
|
defer gf.Close()
|
|
|
|
|
|
|
|
return errors.Wrap(
|
|
|
|
json.NewDecoder(gf).Decode(s),
|
|
|
|
"decode json",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) Save(to string) error {
|
2020-12-30 15:40:23 +00:00
|
|
|
s.modLock.RLock()
|
|
|
|
defer s.modLock.RUnlock()
|
|
|
|
|
2020-11-20 21:51:10 +00:00
|
|
|
s.saveLock.Lock()
|
|
|
|
defer s.saveLock.Unlock()
|
|
|
|
|
2020-12-30 15:40:23 +00:00
|
|
|
if len(s.Followers.Seen) > storeMaxRecent {
|
|
|
|
s.Followers.Seen = s.Followers.Seen[:storeMaxRecent]
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.Subs.Recent) > storeMaxRecent {
|
|
|
|
s.Subs.Recent = s.Subs.Recent[:storeMaxRecent]
|
2020-11-20 21:51:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(to)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "create file")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
gf := gzip.NewWriter(f)
|
|
|
|
defer gf.Close()
|
|
|
|
|
|
|
|
return errors.Wrap(
|
|
|
|
json.NewEncoder(gf).Encode(s),
|
|
|
|
"encode json",
|
|
|
|
)
|
|
|
|
}
|
2020-12-30 15:40:23 +00:00
|
|
|
|
|
|
|
func (s *storage) WithModLock(fn func() error) error {
|
|
|
|
s.modLock.Lock()
|
|
|
|
defer s.modLock.Unlock()
|
|
|
|
|
|
|
|
return fn()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storage) WithModRLock(fn func() error) error {
|
|
|
|
s.modLock.RLock()
|
|
|
|
defer s.modLock.RUnlock()
|
|
|
|
|
|
|
|
return fn()
|
|
|
|
}
|