From 8d51fb02b859c1db34e91af45376d6f561d27cbf Mon Sep 17 00:00:00 2001 From: Luther Wen Xu Date: Thu, 10 Oct 2019 18:38:30 +0800 Subject: [PATCH] go: Implement voting There are currently no ways to start vote so the code cannot be tested currently. --- GoBot/db.go | 8 ++++++ GoBot/db_vote.go | 68 ++++++++++++++++++++++++++++++++++++++++++++ GoBot/main.go | 1 + GoBot/messageUtil.go | 15 ++++++++++ GoBot/vote.go | 62 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 GoBot/db_vote.go create mode 100644 GoBot/vote.go diff --git a/GoBot/db.go b/GoBot/db.go index ea9c366..3c8a5fa 100644 --- a/GoBot/db.go +++ b/GoBot/db.go @@ -23,4 +23,12 @@ func init() { if err != nil { panic(err) } + _, err = db.Exec("CREATE TABLE IF NOT EXISTS vote (id INTEGER PRIMARY KEY, messageId TEXT, name TEXT, finished BOOLEAN, UNIQUE(id))") + if err != nil { + panic(err) + } + _, err = db.Exec("CREATE TABLE IF NOT EXISTS choices (voteId INTEGER, userId INTEGER, date TIMEDATE, value INTEGER, UNIQUE(voteId, userId))") + if err != nil { + panic(err) + } } diff --git a/GoBot/db_vote.go b/GoBot/db_vote.go new file mode 100644 index 0000000..67914d0 --- /dev/null +++ b/GoBot/db_vote.go @@ -0,0 +1,68 @@ +package main + +import ( + "errors" + "time" +) + +const ( + forceRejectionVote = -1 + nuclearOptionVote = 10 +) + +var errForceRejectionVoteReuse = errors.New("db: the user has used force rejection vote in the last month") +var errVoteIsOver = errors.New("db: the vote cannot be changed once it's over") + +func getVoteName(id int) (string, error) { + rows, err := db.Query("SELECT name FROM vote WHERE id=?", id) + if err != nil { + return "", err + } + if rows.Next() { + var name string + err := rows.Scan(&name) + if err != nil { + return "", err + } + return name, nil + } + return "", errNotFound +} + +func getVoteFromMessageID(msgID string) (int, error) { + rows, err := db.Query("SELECT id FROM vote WHERE messageId=?", msgID) + if err != nil { + return 0, err + } + if rows.Next() { + var id int + err := rows.Scan(&id) + if err != nil { + return 0, err + } + return id, nil + } + return 0, errNotFound +} + +func updateVote(voteID int, userID string, voteValue int) error { + if voteValue == forceRejectionVote { + //Check if they used it within a month. + rows, err := db.Query("SELECT voteId FROM choices WHERE date >= ? AND value=?", time.Now().AddDate(0, -1, 0)) + if err != nil { + return err + } + if rows.Next() { + return errForceRejectionVoteReuse + } + } + + //Check if the vote is finished and don't allow change of vote that way. + rows, err := db.Query("SELECT finished FROM vote WHERE id=? AND finished=?", voteID, true) + if rows.Next() { + return errVoteIsOver + } + + _, err = db.Exec("REPLACE INTO choices (voteId, userId, date, value) VALUES (?, ?, ?, ?)", voteID, userID, time.Now(), voteValue) + return err +} diff --git a/GoBot/main.go b/GoBot/main.go index ddf4728..a3eebc7 100644 --- a/GoBot/main.go +++ b/GoBot/main.go @@ -18,6 +18,7 @@ func main() { panic(err) } dg.AddHandler(messageCreate) + dg.AddHandler(checkForVote) if err := dg.Open(); err != nil { panic(err) } diff --git a/GoBot/messageUtil.go b/GoBot/messageUtil.go index fb4c911..ac2b3b1 100644 --- a/GoBot/messageUtil.go +++ b/GoBot/messageUtil.go @@ -5,6 +5,8 @@ import "github.com/bwmarrin/discordgo" const guildID = "626424729234046987" const memberRoleID = "626434632614805526" +const auditChannel = "631789849929711627" + func enforceDM(s *discordgo.Session, m *discordgo.MessageCreate) bool { if m.GuildID != "" { //This command can only be used in DM to protect the invite creator. @@ -38,3 +40,16 @@ func membersOnly(s *discordgo.Session, m *discordgo.MessageCreate) bool { ) return false } + +func sendPrivateMessage(s *discordgo.Session, recipient, message string) error { + channel, err := s.UserChannelCreate(recipient) + if err != nil { + return err + } + _, err = s.ChannelMessageSend(channel.ID, message) + return err +} + +func auditLog(s *discordgo.Session, message string) { + s.ChannelMessageSend(auditChannel, message) +} diff --git a/GoBot/vote.go b/GoBot/vote.go new file mode 100644 index 0000000..dc908ed --- /dev/null +++ b/GoBot/vote.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" +) + +func checkForVote(s *discordgo.Session, r *discordgo.MessageReactionAdd) { + voteID, err := getVoteFromMessageID(r.MessageID) + if err != nil { + return + } + + s.MessageReactionRemove(r.ChannelID, r.MessageID, r.Emoji.ID, r.UserID) + + var value int + switch r.Emoji.Name { + case "x": + value = forceRejectionVote + case "one": + value = 1 + case "two": + value = 2 + case "three": + value = 3 + case "four": + value = 4 + case "five": + value = 5 + case "white_check_mark": + value = nuclearOptionVote + default: + return + } + + err = updateVote(voteID, r.UserID, value) + + if err == errForceRejectionVoteReuse { + sendPrivateMessage(s, r.UserID, "您在这个月内已使用过:x:。请选择其他选项。\nYou have used :x: this month. Please choose another option.") + return + } + if err == errVoteIsOver { + sendPrivateMessage(s, r.UserID, "这个投票已结束。你投的票没有被记录。\nThe vote is over so your vote is not recorded.") + return + } + + var voteName string + if err == nil { + voteName, err = getVoteName(voteID) + } + + if err != nil { + sendPrivateMessage(s, r.UserID, "一个错误已发生,请重新尝试。\nAn error has occurred while voting. Please try again.") + auditLog(s, + fmt.Sprintf("Error occurred while processing vote for <@%s>.\n%v\nError: %s", r.UserID, r, err.Error()), + ) + return + } + + sendPrivateMessage(s, r.UserID, "您投票已成功。\nYou have voted successfully.\n名字Vote Name: "+voteName+"\n目前投的票: :"+r.Emoji.Name+":") +}