gosora/common/promotions.go
Azareal 77291e4b44 Add ScheduledTasks and TaskSet interfaces.
Add unlisted LogLongTick developer setting.
Rearrange the order of the shutdown signal handler to figure out where it might fail.
2021-05-03 10:36:29 +10:00

133 lines
4.1 KiB
Go

package common
import (
"database/sql"
//"log"
"time"
qgen "github.com/Azareal/Gosora/query_gen"
)
var GroupPromotions GroupPromotionStore
type GroupPromotion struct {
ID int
From int
To int
TwoWay bool
Level int
Posts int
MinTime int
RegisteredFor int
}
type GroupPromotionStore interface {
GetByGroup(gid int) (gps []*GroupPromotion, err error)
Get(id int) (*GroupPromotion, error)
PromoteIfEligible(u *User, level, posts int, registeredAt time.Time) error
Delete(id int) error
Create(from, to int, twoWay bool, level, posts, registeredFor int) (int, error)
}
type DefaultGroupPromotionStore struct {
getByGroup *sql.Stmt
get *sql.Stmt
delete *sql.Stmt
create *sql.Stmt
getByUser *sql.Stmt
getByUserMins *sql.Stmt
updateUser *sql.Stmt
updateGeneric *sql.Stmt
}
func NewDefaultGroupPromotionStore(acc *qgen.Accumulator) (*DefaultGroupPromotionStore, error) {
ugp := "users_groups_promotions"
prs := &DefaultGroupPromotionStore{
getByGroup: acc.Select(ugp).Columns("pid, from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? OR to_gid=?").Prepare(),
get: acc.Select(ugp).Columns("from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Where("pid=?").Prepare(),
delete: acc.Delete(ugp).Where("pid=?").Prepare(),
create: acc.Insert(ugp).Columns("from_gid, to_gid, two_way, level, posts, minTime, registeredFor").Fields("?,?,?,?,?,?,?").Prepare(),
getByUserMins: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? AND level<=? AND posts<=? AND registeredFor<=?").Orderby("level DESC").Limit("1").Prepare(),
getByUser: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime, registeredFor").Where("from_gid=? AND level<=? AND posts<=?").Orderby("level DESC").Limit("1").Prepare(),
updateUser: acc.Update("users").Set("group=?").Where("group=? AND uid=?").Prepare(),
updateGeneric: acc.Update("users").Set("group=?").Where("group=? AND level>=? AND posts>=?").Prepare(),
}
Tasks.FifteenMin.Add(prs.Tick)
return prs, acc.FirstError()
}
func (s *DefaultGroupPromotionStore) Tick() error {
return nil
}
func (s *DefaultGroupPromotionStore) GetByGroup(gid int) (gps []*GroupPromotion, err error) {
rows, err := s.getByGroup.Query(gid, gid)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
g := &GroupPromotion{}
err := rows.Scan(&g.ID, &g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err != nil {
return nil, err
}
gps = append(gps, g)
}
return gps, rows.Err()
}
// TODO: Cache the group promotions to avoid hitting the database as much
func (s *DefaultGroupPromotionStore) Get(id int) (*GroupPromotion, error) {
/*g, err := s.cache.Get(id)
if err == nil {
return u, nil
}*/
g := &GroupPromotion{ID: id}
err := s.get.QueryRow(id).Scan(&g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err == nil {
//s.cache.Set(u)
}
return g, err
}
// TODO: Optimise this to avoid the query
func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level, posts int, registeredAt time.Time) error {
mins := time.Since(registeredAt).Minutes()
g := &GroupPromotion{From: u.Group}
//log.Printf("pre getByUserMins: %+v\n", u)
err := s.getByUserMins.QueryRow(u.Group, level, posts, mins).Scan(&g.ID, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime, &g.RegisteredFor)
if err == sql.ErrNoRows {
//log.Print("no matches found")
return nil
} else if err != nil {
return err
}
//log.Printf("g: %+v\n", g)
if g.RegisteredFor == 0 {
_, err = s.updateGeneric.Exec(g.To, g.From, g.Level, g.Posts)
} else {
_, err = s.updateUser.Exec(g.To, g.From, u.ID)
}
return err
}
func (s *DefaultGroupPromotionStore) Delete(id int) error {
_, err := s.delete.Exec(id)
return err
}
func (s *DefaultGroupPromotionStore) Create(from, to int, twoWay bool, level, posts, registeredFor int) (int, error) {
res, err := s.create.Exec(from, to, twoWay, level, posts, 0, registeredFor)
if err != nil {
return 0, err
}
lastID, err := res.LastInsertId()
return int(lastID), err
}