go: Implement vote ending

master
Luther Wen Xu 2019-10-10 21:59:00 +07:00
parent 3709d0698b
commit 0033c68067
Signed by untrusted user: chanbakjsd
GPG Key ID: B7D77E3E9D102B70
5 changed files with 205 additions and 4 deletions

@ -56,6 +56,40 @@ func getVoteFromMessageID(msgID string) (int, error) {
return 0, errNotFound
}
func getMessageIDFromVote(voteID int) (string, error) {
rows, err := db.Query("SELECT messageId FROM vote WHERE id=?", voteID)
if err != nil {
return "", err
}
defer rows.Close()
if rows.Next() {
var id string
err := rows.Scan(&id)
if err != nil {
return "", err
}
return id, nil
}
return "", errNotFound
}
func getVoteType(voteID int) (string, error) {
rows, err := db.Query("SELECT type FROM vote WHERE id=?", voteID)
if err != nil {
return "", err
}
defer rows.Close()
if rows.Next() {
var messageType string
err := rows.Scan(&messageType)
if err != nil {
return "", err
}
return messageType, nil
}
return "", errNotFound
}
func updateVote(voteID int, userID string, voteValue int) error {
if voteValue == forceRejectionVote {
//Check if they used it within a month.
@ -79,3 +113,27 @@ func updateVote(voteID int, userID string, voteValue int) error {
_, err = db.Exec("REPLACE INTO choices (voteId, userId, date, value) VALUES (?, ?, ?, ?)", voteID, userID, time.Now(), voteValue)
return err
}
func finishVote(voteID int) error {
_, err := db.Exec("UPDATE vote SET finished=true WHERE id=?", voteID)
return err
}
func getAllVoteChoices(voteID int) ([]voteChoice, error) {
rows, err := db.Query("SELECT userId, value FROM choices WHERE voteId=?", voteID)
if err != nil {
return []voteChoice{}, err
}
defer rows.Close()
array := make([]voteChoice, 0)
for rows.Next() {
var userID string
var value int
rows.Scan(&userID, &value)
array = append(array, voteChoice{
UserID: userID,
Value: value,
})
}
return array, nil
}

@ -19,13 +19,16 @@ func main() {
if err != nil {
panic(err)
}
dg.AddHandler(messageCreate)
dg.AddHandler(checkForVote)
if err := dg.Open(); err != nil {
panic(err)
}
fmt.Println("Bot is now running. Press CTRL-C to exit.")
go listenToVoteFinishes(dg)
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc

@ -0,0 +1,11 @@
package main
func getTrust(discordID string) int {
//TODO This is a stub. Everyone has same weight for now.
return 1
}
func getTotalTrust() int {
//TODO This is a stub. It returns 1 for easier testing for now.
return 1
}

@ -8,7 +8,20 @@ import (
"github.com/bwmarrin/discordgo"
)
type voteType struct {
EmbedBuilder func(id int, name string) *discordgo.MessageEmbed
ResultHandler func(s *discordgo.Session, id int, name string, isPositive bool)
}
var voteTypes = map[string]voteType{
"custom": voteType{
EmbedBuilder: createCustomEmbed,
ResultHandler: announceCustomResult,
},
}
const voteChannel = "627164246056239104"
const announceCustomChannel = "627165467269922864"
const (
emojiOne = "1⃣"
@ -81,6 +94,8 @@ func checkForVote(s *discordgo.Session, r *discordgo.MessageReactionAdd) {
}
sendPrivateMessage(s, r.UserID, "您投票已成功。\nYou have voted successfully.\n名字Vote Name: "+voteName+"\n目前投的票 :"+r.Emoji.Name+":")
checkForVoteResult(s, voteID)
}
func voteSuggestion(s *discordgo.Session, m *discordgo.MessageCreate) {
@ -112,10 +127,7 @@ func voteSuggestion(s *discordgo.Session, m *discordgo.MessageCreate) {
}
auditLog(s, fmt.Sprintf("Vote ID %d has been created by <@%s>.", id, m.Author.ID))
s.ChannelMessageEdit(voteChannel, msg.ID, "")
s.ChannelMessageEditEmbed(
voteChannel, msg.ID,
newEmbed().SetColour(0x00FFFF).SetTitle("自定义投票Custom Vote").AddField("ID", strconv.Itoa(id)).AddField("内容Content", args[1]).Build(),
)
s.ChannelMessageEditEmbed(voteChannel, msg.ID, createCustomEmbed(id, args[1]))
s.MessageReactionAdd(voteChannel, msg.ID, emojiOne)
s.MessageReactionAdd(voteChannel, msg.ID, emojiTwo)
s.MessageReactionAdd(voteChannel, msg.ID, emojiThree)
@ -125,3 +137,11 @@ func voteSuggestion(s *discordgo.Session, m *discordgo.MessageCreate) {
sendPrivateMessage(s, m.Author.ID, "未知投票种类:"+args[0]+"\nUnknown vote type: "+args[0])
}
}
func createCustomEmbed(id int, name string) *discordgo.MessageEmbed {
return newEmbed().SetColour(0x00FFFF).SetTitle("自定义投票Custom Vote").AddField("ID", strconv.Itoa(id)).AddField("内容Content", name).Build()
}
func announceCustomResult(s *discordgo.Session, id int, name string, isPositive bool) {
s.ChannelMessageSendEmbed(announceCustomChannel, showVoteStatus(createCustomEmbed(id, name), isPositive))
}

@ -0,0 +1,109 @@
package main
import (
"fmt"
"sync"
"time"
"github.com/bwmarrin/discordgo"
)
type voteChoice struct {
UserID string
Value int
}
type confirmedResult struct {
VoteID int
IsPositive bool
}
var voteMutex sync.Mutex
var toAnnounceResultList []confirmedResult
func listenToVoteFinishes(s *discordgo.Session) {
for {
voteMutex.Lock()
for _, v := range toAnnounceResultList {
auditLog(s, fmt.Sprintf("Announcing the result of vote %d.", v.VoteID))
if err := finishVote(v.VoteID); err != nil {
auditLog(s, fmt.Sprintf("Error while finishing vote %d.", v.VoteID))
}
voteType, _ := getVoteType(v.VoteID)
voteName, _ := getVoteName(v.VoteID)
messageID, _ := getMessageIDFromVote(v.VoteID)
s.ChannelMessageEditEmbed(voteChannel, messageID, showVoteStatus(voteTypes[voteType].EmbedBuilder(v.VoteID, voteName), v.IsPositive))
voteTypes[voteType].ResultHandler(s, v.VoteID, voteName, v.IsPositive)
}
toAnnounceResultList = toAnnounceResultList[:0]
voteMutex.Unlock()
time.Sleep(60 * time.Second)
}
}
func checkForVoteResult(s *discordgo.Session, id int) {
votes, err := getAllVoteChoices(id)
if err != nil {
auditLog(s, fmt.Sprintf("Error while calculating vote result for ID %d: %s", id, err.Error()))
return
}
var currentScore, totalTrust float64
var absoluteRejectionVote bool
for _, vote := range votes {
if vote.Value == forceRejectionVote {
absoluteRejectionVote = true
}
currentScore += float64(vote.Value * getTrust(vote.UserID))
totalTrust += float64(getTrust(vote.UserID))
}
remainingTrust := float64(getTotalTrust()) - totalTrust
lowestPossible := (currentScore + remainingTrust) / float64(getTotalTrust())
highestPossible := (currentScore + remainingTrust*5) / float64(getTotalTrust())
if highestPossible <= 3.5 || absoluteRejectionVote {
//Rejection confirmed.
voteMutex.Lock()
for _, v := range toAnnounceResultList {
if v.VoteID == id {
//It's already queued to be announced in the next announcement cycle.
voteMutex.Unlock()
return
}
}
auditLog(s, fmt.Sprintf("Rejection of vote ID %d has been confirmed.", id))
toAnnounceResultList = append(toAnnounceResultList, confirmedResult{
VoteID: id,
IsPositive: false,
})
voteMutex.Unlock()
}
if lowestPossible >= 3.5 {
//Acceptance confirmed.
voteMutex.Lock()
for _, v := range toAnnounceResultList {
if v.VoteID == id {
//It's already queued to be announced in the next announcement cycle.
voteMutex.Unlock()
return
}
}
auditLog(s, fmt.Sprintf("Acceptance of vote ID %d has been confirmed.", id))
toAnnounceResultList = append(toAnnounceResultList, confirmedResult{
VoteID: id,
IsPositive: true,
})
voteMutex.Unlock()
}
}
func showVoteStatus(embed *discordgo.MessageEmbed, isPositive bool) *discordgo.MessageEmbed {
if isPositive {
embed.Color = 0x00F000
embed.Title = "投票通过 Vote Passed"
} else {
embed.Color = 0xF00000
embed.Title = "投票不通过 Vote Rejected"
}
return embed
}