github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/poll.go (about) 1 package common 2 3 import ( 4 "database/sql" 5 6 qgen "github.com/Azareal/Gosora/query_gen" 7 ) 8 9 var pollStmts PollStmts 10 11 type Poll struct { 12 ID int 13 ParentID int 14 ParentTable string 15 Type int // 0: Single choice, 1: Multiple choice, 2: Multiple choice w/ points 16 AntiCheat bool // Apply various mitigations for cheating 17 // GroupPower map[gid]points // The number of points a group can spend in this poll, defaults to 1 18 19 Options map[int]string 20 Results map[int]int // map[optionIndex]points 21 QuickOptions []PollOption // TODO: Fix up the template transpiler so we don't need to use this hack anymore 22 VoteCount int 23 } 24 25 // TODO: Use a transaction for this? 26 // TODO: Add a voters table with castAt / IP data and only populate it when poll anti-cheat is on 27 func (p *Poll) CastVote(optionIndex, uid int, ip string) error { 28 if Config.DisablePollIP || !p.AntiCheat { 29 ip = "" 30 } 31 _, e := pollStmts.addVote.Exec(p.ID, uid, optionIndex, ip) 32 if e != nil { 33 return e 34 } 35 _, e = pollStmts.incVoteCount.Exec(p.ID) 36 if e != nil { 37 return e 38 } 39 _, e = pollStmts.incVoteCountForOption.Exec(optionIndex, p.ID) 40 return e 41 } 42 43 func (p *Poll) Delete() error { 44 _, e := pollStmts.deletePollVotes.Exec(p.ID) 45 if e != nil { 46 return e 47 } 48 _, e = pollStmts.deletePollOptions.Exec(p.ID) 49 if e != nil { 50 return e 51 } 52 _, e = pollStmts.deletePoll.Exec(p.ID) 53 _ = Polls.GetCache().Remove(p.ID) 54 return e 55 } 56 57 func (p *Poll) Resultsf(f func(votes int) error) error { 58 rows, e := pollStmts.getResults.Query(p.ID) 59 if e != nil { 60 return e 61 } 62 defer rows.Close() 63 64 var votes int 65 for rows.Next() { 66 if e := rows.Scan(&votes); e != nil { 67 return e 68 } 69 if e := f(votes); e != nil { 70 return e 71 } 72 } 73 return rows.Err() 74 } 75 76 func (p *Poll) Copy() Poll { 77 return *p 78 } 79 80 type PollStmts struct { 81 getResults *sql.Stmt 82 83 addVote *sql.Stmt 84 incVoteCount *sql.Stmt 85 incVoteCountForOption *sql.Stmt 86 87 deletePoll *sql.Stmt 88 deletePollOptions *sql.Stmt 89 deletePollVotes *sql.Stmt 90 } 91 92 func init() { 93 DbInits.Add(func(acc *qgen.Accumulator) error { 94 p := "polls" 95 wh := "pollID=?" 96 pollStmts = PollStmts{ 97 getResults: acc.Select("polls_options").Columns("votes").Where("pollID=?").Orderby("option ASC").Prepare(), 98 99 addVote: acc.Insert("polls_votes").Columns("pollID,uid,option,castAt,ip").Fields("?,?,?,UTC_TIMESTAMP(),?").Prepare(), 100 incVoteCount: acc.Update(p).Set("votes=votes+1").Where(wh).Prepare(), 101 incVoteCountForOption: acc.Update("polls_options").Set("votes=votes+1").Where("option=? AND pollID=?").Prepare(), 102 103 deletePoll: acc.Delete(p).Where(wh).Prepare(), 104 deletePollOptions: acc.Delete("polls_options").Where(wh).Prepare(), 105 deletePollVotes: acc.Delete("polls_votes").Where(wh).Prepare(), 106 } 107 return acc.FirstError() 108 }) 109 }