2020-10-12 13:47:28 +00:00
|
|
|
package db
|
2020-10-11 19:32:58 +00:00
|
|
|
|
|
|
|
// Database is laid out like this:
|
|
|
|
// mapper/<mapper_id>/trackers/<channel_id> -> priority
|
|
|
|
// mapper/<mapper_id>/latestEvent
|
|
|
|
// channel/<channel_id>/tracks/<mapper_id> -> priority
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
|
2021-07-21 22:09:06 +00:00
|
|
|
"github.com/pkg/errors"
|
2020-10-11 19:32:58 +00:00
|
|
|
bolt "go.etcd.io/bbolt"
|
2021-07-21 22:09:06 +00:00
|
|
|
"gorm.io/driver/sqlite"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"gorm.io/gorm/clause"
|
2020-10-12 14:26:39 +00:00
|
|
|
|
|
|
|
"subscribe-bot/osuapi"
|
2020-10-11 19:32:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
LATEST_EVENT = []byte("latestEvent")
|
2020-10-14 19:33:23 +00:00
|
|
|
MAPPERS = []byte("mapper")
|
|
|
|
CHANNELS = []byte("channels")
|
2020-10-11 19:32:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Db struct {
|
2021-07-21 22:09:06 +00:00
|
|
|
gorm *gorm.DB
|
|
|
|
api *osuapi.Osuapi
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 13:47:28 +00:00
|
|
|
func OpenDb(path string, api *osuapi.Osuapi) (db *Db, err error) {
|
2021-07-21 22:09:06 +00:00
|
|
|
gorm, err := gorm.Open(sqlite.Open(path), &gorm.Config{})
|
|
|
|
if err != nil {
|
|
|
|
panic("failed to connect database")
|
|
|
|
}
|
|
|
|
|
|
|
|
// auto-migrate
|
|
|
|
gorm.AutoMigrate(&Config{})
|
|
|
|
gorm.AutoMigrate(&User{})
|
|
|
|
gorm.AutoMigrate(&Beatmapset{})
|
|
|
|
gorm.AutoMigrate(&DiscordChannel{})
|
|
|
|
|
|
|
|
db = &Db{gorm, api}
|
2020-10-11 19:32:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop over channels that are tracking this specific mapper
|
2021-07-21 22:09:06 +00:00
|
|
|
func (db *Db) IterTrackingChannels(mapperId int, fn func(channel DiscordChannel) error) (err error) {
|
|
|
|
var channels []DiscordChannel
|
|
|
|
db.gorm.Model(&User{ID: mapperId}).Association("TrackingChannels").Find(&channels)
|
|
|
|
for _, channel := range channels {
|
|
|
|
fn(channel)
|
|
|
|
}
|
2020-10-11 19:32:58 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-14 19:33:23 +00:00
|
|
|
// Loop over tracked mappers for this channel
|
2021-07-21 22:09:06 +00:00
|
|
|
func (db *Db) IterChannelTrackedMappers(channelId string, fn func(user User) error) (err error) {
|
|
|
|
var mappers []User
|
|
|
|
db.gorm.Model(&DiscordChannel{ID: channelId}).Association("TrackedMappers").Find(&mappers)
|
|
|
|
for _, mapper := range mappers {
|
|
|
|
fn(mapper)
|
|
|
|
}
|
2020-10-14 19:33:23 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop over all tracked mappers
|
2021-07-21 22:09:06 +00:00
|
|
|
func (db *Db) IterAllTrackedMappers(fn func(user User) error) (err error) {
|
|
|
|
var mappers []User
|
|
|
|
db.gorm.Find(&mappers)
|
|
|
|
for _, mapper := range mappers {
|
|
|
|
fn(mapper)
|
|
|
|
}
|
2020-10-11 19:32:58 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-21 22:09:06 +00:00
|
|
|
func (db *Db) UpdateMapperLastEvent(userId int, eventId int) (err error) {
|
|
|
|
if eventId == -1 {
|
|
|
|
var events []osuapi.Event
|
|
|
|
events, err = db.api.GetUserEvents(userId, 1, 0)
|
2020-10-11 19:32:58 +00:00
|
|
|
if err != nil {
|
2021-07-21 22:09:06 +00:00
|
|
|
err = errors.Wrap(err, "couldn't get user events from API")
|
|
|
|
return
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
2021-07-21 22:09:06 +00:00
|
|
|
eventId = events[0].ID
|
|
|
|
}
|
2020-10-11 19:32:58 +00:00
|
|
|
|
2021-07-21 22:09:06 +00:00
|
|
|
db.gorm.Model(&User{}).Where("id = ?", userId).Update("latest_event_id", eventId)
|
|
|
|
return nil
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the latest event ID of this mapper, if they have one
|
|
|
|
func (db *Db) MapperLastEvent(userId int) (has bool, id int) {
|
2021-07-21 22:09:06 +00:00
|
|
|
var user User
|
|
|
|
db.gorm.Select("latest_event_id").First(&user)
|
|
|
|
return true, user.LatestEventID
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Start tracking a new mapper (if they're not already tracked)
|
|
|
|
func (db *Db) ChannelTrackMapper(channelId string, mapperId int, priority int) (err error) {
|
2021-07-21 22:09:06 +00:00
|
|
|
err = db.gorm.Model(&DiscordChannel{ID: channelId}).Association("TrackedMappers").Append(db.getUser(mapperId))
|
2020-10-11 19:32:58 +00:00
|
|
|
if err != nil {
|
2021-07-21 22:09:06 +00:00
|
|
|
err = errors.Wrap(err, "could not add tracking for channel "+channelId)
|
2020-10-11 19:32:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-21 22:09:06 +00:00
|
|
|
err = db.UpdateMapperLastEvent(mapperId, -1)
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "could not update mapper latest event")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (db *Db) Close() {
|
2021-07-21 22:09:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (db *Db) getUser(userId int) (user *User, err error) {
|
|
|
|
// TODO: cache user info for some time?
|
|
|
|
|
|
|
|
apiUser, err := db.api.GetUser(strconv.Itoa(userId))
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "could not retrieve user from the API")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
user = &User{
|
|
|
|
ID: userId,
|
|
|
|
Username: apiUser.Username,
|
|
|
|
Country: apiUser.CountryCode,
|
|
|
|
}
|
|
|
|
db.gorm.Clauses(clause.OnConflict{UpdateAll: true}).Create(user)
|
|
|
|
|
|
|
|
return
|
2020-10-11 19:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getMapper(tx *bolt.Tx, userId int) (mapper *bolt.Bucket) {
|
2020-10-14 19:33:23 +00:00
|
|
|
mappers := tx.Bucket(MAPPERS)
|
2020-10-11 19:32:58 +00:00
|
|
|
if mappers == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
mapper = mappers.Bucket([]byte(strconv.Itoa(userId)))
|
|
|
|
if mapper == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getMapperMut(tx *bolt.Tx, userId int) (mapper *bolt.Bucket, err error) {
|
2020-10-14 19:33:23 +00:00
|
|
|
mappers, err := tx.CreateBucketIfNotExists(MAPPERS)
|
2020-10-11 19:32:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mapper, err = mappers.CreateBucketIfNotExists([]byte(strconv.Itoa(userId)))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|