From d42b8a7f5b96b899dddef684766595a167f0eafe Mon Sep 17 00:00:00 2001 From: Luther Wen Xu Date: Sat, 12 Oct 2019 14:40:46 +0800 Subject: [PATCH] go: Implement '!validate' command --- GoBot/command_invite.go | 130 ++++++++++++++++++++++++++++++++++- GoBot/command_messageutil.go | 17 +++++ GoBot/command_trust.go | 2 + GoBot/command_vote.go | 4 ++ GoBot/db_trust.go | 30 ++++---- GoBot/db_vote.go | 9 +++ GoBot/main.go | 3 + GoBot/trust.go | 2 +- GoBot/vote_invite.go | 41 +++++++++++ 9 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 GoBot/vote_invite.go diff --git a/GoBot/command_invite.go b/GoBot/command_invite.go index af84e45..934252e 100644 --- a/GoBot/command_invite.go +++ b/GoBot/command_invite.go @@ -1,6 +1,18 @@ package main -import "github.com/bwmarrin/discordgo" +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" +) + +var pendingInviteConfirmation = make(map[string]invite) + +type invite struct { + User string + Reason string +} func createInvite(s *discordgo.Session, m *discordgo.MessageCreate, command []string) { if !enforceDM(s, m) { @@ -50,3 +62,119 @@ func createInvite(s *discordgo.Session, m *discordgo.MessageCreate, command []st ) } } + +func checkUseInvite(s *discordgo.Session, m *discordgo.MessageCreate, command []string) { + if !enforceDM(s, m) { + return + } + if !nonMembersOnly(s, m) { + return + } + s.ChannelMessageDelete(m.ChannelID, m.ID) + if len(command) < 3 { + sendPrivateMessage(s, m.Author.ID, "指令的使用方法是`!validate <验证码> <原因>`.\nUsage: `!validate `") + return + } + + messageSplit := strings.SplitN(m.Content, " ", 3) + inviter, err := getInviteOwner(messageSplit[1]) + if err == errInviteUsed { + sendPrivateMessage(s, m.Author.ID, "该验证码已被使用过。请向验证码制造者要求新的验证码。\nThis validation code has been used before. Please request a new one.") + return + } + if err == errNotFound { + sendPrivateMessage(s, m.Author.ID, "该验证码不存在。请使用存在的验证码。\nThis validation code doesn't exist. Please use an existing one.") + return + } + if err != nil { + sendPrivateMessage(s, m.Author.ID, "错误已发生。请稍候尝试。\nAn error has occurred. Please try again later.") + auditLog(s, err.Error()) + return + } + + member, err := s.GuildMember(guildID, inviter) + if err != nil { + sendPrivateMessage(s, m.Author.ID, "验证码制造者不是会员。\nValidation code creator is no longer a member.") + return + } + + isMember := false + for _, v := range member.Roles { + if v == memberRoleID { + isMember = true + } + } + if !isMember { + sendPrivateMessage(s, m.Author.ID, "验证码制造者不是会员。\nValidation code creator is no longer a member.") + return + } + + useInvite(messageSplit[1]) + + channel, err := s.UserChannelCreate(inviter) + if err != nil { + sendPrivateMessage(s, m.Author.ID, "验证码制造者关闭了私信。\nValidation code creator did not enable DMs.") + return + } + msg, err := s.ChannelMessageSend(channel.ID, fmt.Sprintf("<@%s>正在尝试使用验证码%s。请问是否同意该使用?", m.Author.ID, messageSplit[1])) + if err != nil { + sendPrivateMessage(s, m.Author.ID, "验证码制造者关闭了私信。\nValidation code creator did not enable DMs.") + return + } + pendingInviteConfirmation[msg.ID] = invite{ + User: m.Author.ID, + Reason: messageSplit[2], + } + s.MessageReactionAdd(channel.ID, msg.ID, emojiX) + s.MessageReactionAdd(channel.ID, msg.ID, emojiCheck) +} + +func checkForInvite(s *discordgo.Session, r *discordgo.MessageReactionAdd) { + if r.UserID == s.State.User.ID { + return + } + + invite, ok := pendingInviteConfirmation[r.MessageID] + if !ok { + return + } + delete(pendingInviteConfirmation, r.MessageID) + + switch r.Emoji.Name { + case emojiX: + s.ChannelMessageSend(r.ChannelID, "已成功拒绝该验证码的使用。为了保护你,该验证码已被无效化。\nThe use of this validation code has been rejected. This validation code has been invalidated to protect you.") + return + case emojiCheck: + default: + return + } + + s.ChannelMessageSend(r.ChannelID, "已同意验证码的使用。该验证码已被无效化。\nUse of validation code accepted. This validation code has been invalidated.") + + msg, err := s.ChannelMessageSend(voteChannel, "正在准备新的一个投票…… Preparing for the next vote...") + if err != nil { + sendPrivateMessage(s, r.UserID, "创造投票失败。Failed to create vote.") + auditLog(s, + fmt.Sprintf("Error occurred while creating vote for <@%s>.\n%v\nError: %s", r.UserID, *r, err.Error()), + ) + return + } + id, err := createInviteVote(msg.ID, invite.User, invite.Reason) + if err != nil { + sendPrivateMessage(s, r.UserID, "创造投票失败。Failed to create vote.") + auditLog(s, + fmt.Sprintf("Error occurred while creating vote for <@%s>.\n%v\nError: %s", r.UserID, *r, err.Error()), + ) + return + } + auditLog(s, fmt.Sprintf("Vote ID %d has been created by <@%s>.", id, r.UserID)) + s.ChannelMessageEdit(voteChannel, msg.ID, "") + s.ChannelMessageEditEmbed(voteChannel, msg.ID, createInviteEmbed(id, invite.User+":"+invite.Reason)) + s.MessageReactionAdd(voteChannel, msg.ID, emojiX) + s.MessageReactionAdd(voteChannel, msg.ID, emojiOne) + s.MessageReactionAdd(voteChannel, msg.ID, emojiTwo) + s.MessageReactionAdd(voteChannel, msg.ID, emojiThree) + s.MessageReactionAdd(voteChannel, msg.ID, emojiFour) + s.MessageReactionAdd(voteChannel, msg.ID, emojiFive) + s.MessageReactionAdd(voteChannel, msg.ID, emojiCheck) +} diff --git a/GoBot/command_messageutil.go b/GoBot/command_messageutil.go index ec9c45a..1da8399 100644 --- a/GoBot/command_messageutil.go +++ b/GoBot/command_messageutil.go @@ -48,6 +48,23 @@ func membersOnly(s *discordgo.Session, m *discordgo.MessageCreate) bool { return false } +func nonMembersOnly(s *discordgo.Session, m *discordgo.MessageCreate) bool { + member, err := s.GuildMember(guildID, m.Author.ID) + if err != nil { + return false + } + for _, v := range member.Roles { + if v == memberRoleID { + s.ChannelMessageSend( + m.ChannelID, + m.Author.Mention()+",该指令只能被非会员使用。\n"+m.Author.Mention()+", this command can only be used by non-member.", + ) + return false + } + } + return true +} + func sendPrivateMessage(s *discordgo.Session, recipient, message string) error { channel, err := s.UserChannelCreate(recipient) if err != nil { diff --git a/GoBot/command_trust.go b/GoBot/command_trust.go index 574ad95..9d3956a 100644 --- a/GoBot/command_trust.go +++ b/GoBot/command_trust.go @@ -69,6 +69,8 @@ func checkForTrustUpdate(s *discordgo.Session, r *discordgo.MessageReactionAdd) return } + delete(trustMessage, r.MessageID) + var value int switch r.Emoji.Name { case emojiOne: diff --git a/GoBot/command_vote.go b/GoBot/command_vote.go index b611eda..5b65636 100644 --- a/GoBot/command_vote.go +++ b/GoBot/command_vote.go @@ -18,6 +18,10 @@ var voteTypes = map[string]voteType{ EmbedBuilder: createCustomEmbed, ResultHandler: announceCustomResult, }, + "invite": voteType{ + EmbedBuilder: createInviteEmbed, + ResultHandler: handleInviteResult, + }, } const voteChannel = "627164246056239104" diff --git a/GoBot/db_trust.go b/GoBot/db_trust.go index 1a7743d..c2bac42 100644 --- a/GoBot/db_trust.go +++ b/GoBot/db_trust.go @@ -25,22 +25,24 @@ func getTrustVote(targetID string) ([]int, error) { return array, nil } -func updateDbTrust(sourceID, targetID string, voteValue int) error { - rows, err := db.Query("SELECT lastUpdated FROM trustVote WHERE sourceUser=? AND targetUser=?", sourceID, targetID) - if err != nil { - return err - } - if rows.Next() { - //Check that it was not changed within a month. - var lastUpdated time.Time - rows.Scan(&lastUpdated) - if time.Now().Sub(lastUpdated) > 24*30*time.Hour { - rows.Close() - return errRecentlyChanged +func updateDbTrust(sourceID, targetID string, voteValue int, force bool) error { + if !force { + rows, err := db.Query("SELECT lastUpdated FROM trustVote WHERE sourceUser=? AND targetUser=?", sourceID, targetID) + if err != nil { + return err + } + if rows.Next() { + //Check that it was not changed within a month. + var lastUpdated time.Time + rows.Scan(&lastUpdated) + if time.Now().Sub(lastUpdated) > 24*30*time.Hour { + rows.Close() + return errRecentlyChanged + } } + rows.Close() } - rows.Close() - _, err = db.Exec("REPLACE INTO trustVote(sourceUser,targetUser,lastUpdated,trust) VALUES(?,?,?,?)", sourceID, targetID, time.Now(), voteValue) + _, err := db.Exec("REPLACE INTO trustVote(sourceUser,targetUser,lastUpdated,trust) VALUES(?,?,?,?)", sourceID, targetID, time.Now(), voteValue) return err } diff --git a/GoBot/db_vote.go b/GoBot/db_vote.go index d48beaa..9d99af2 100644 --- a/GoBot/db_vote.go +++ b/GoBot/db_vote.go @@ -22,6 +22,15 @@ func createCustomVote(messageID, text string) (int, error) { return int(lastID), nil } +func createInviteVote(messageID, username, reason string) (int, error) { + result, err := db.Exec("INSERT INTO vote(messageId, name, type, finished) VALUES(?, ?, ?, ?)", messageID, username+":"+reason, "invite", false) + if err != nil { + return 0, err + } + lastID, err := result.LastInsertId() + return int(lastID), nil +} + func getVoteName(id int) (string, error) { rows, err := db.Query("SELECT name FROM vote WHERE id=?", id) if err != nil { diff --git a/GoBot/main.go b/GoBot/main.go index c695ea9..4052f75 100644 --- a/GoBot/main.go +++ b/GoBot/main.go @@ -23,6 +23,7 @@ func main() { dg.AddHandler(messageCreate) dg.AddHandler(checkForVote) dg.AddHandler(checkForTrustUpdate) + dg.AddHandler(checkForInvite) if err := dg.Open(); err != nil { panic(err) @@ -57,6 +58,8 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { viewTrustLevel(s, m, command) case "!invite": createInvite(s, m, command) + case "!validate": + checkUseInvite(s, m, command) case "!trust": changeTrust(s, m, command) } diff --git a/GoBot/trust.go b/GoBot/trust.go index 0b1b6e5..7ce1626 100644 --- a/GoBot/trust.go +++ b/GoBot/trust.go @@ -65,7 +65,7 @@ func getTotalTrust(s *discordgo.Session) (float64, error) { } func updateTrust(sourceID, targetID string, voteValue int) error { - err := updateDbTrust(sourceID, targetID, voteValue) + err := updateDbTrust(sourceID, targetID, voteValue, false) if err != nil { return err } diff --git a/GoBot/vote_invite.go b/GoBot/vote_invite.go new file mode 100644 index 0000000..d957d5d --- /dev/null +++ b/GoBot/vote_invite.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + + "github.com/bwmarrin/discordgo" +) + +const announceInviteChannel = "627165467269922864" + +func createInviteEmbed(id int, name string) *discordgo.MessageEmbed { + list := strings.SplitN(name, ":", 2) + return newEmbed().SetColour(0x00FFFF).SetTitle("加入申请Entry Application").AddField("ID", strconv.Itoa(id)).AddField("加入者Applicant", "<@"+list[0]+">").AddField("提供原因Provided Reason", list[1]).Build() +} + +func handleInviteResult(s *discordgo.Session, id int, name string, isPositive bool) { + if !isPositive { + return + } + member, err := getMemberFromUserFriendlyName(s, strings.SplitN(name, ":", 2)[0]) + if err != nil { + auditLog(s, "Error while getting member from user friendly name in invite result. "+err.Error()) + return + } + err = s.GuildMemberRoleAdd(guildID, member.User.ID, memberRoleID) + if err != nil { + auditLog(s, fmt.Sprintf("Fail to give user <@%s> the member role: %s", member.User.ID, err.Error())) + return + } + s.ChannelMessageSend(announceInviteChannel, fmt.Sprintf("欢迎<@%s>的加入!Welcome <@%s> to this server!", member.User.ID, member.User.ID)) + choices, err := getAllVoteChoices(id) + if err != nil { + auditLog(s, "Error while pulling vote choices. "+err.Error()) + return + } + for _, choice := range choices { + updateDbTrust(choice.UserID, member.User.ID, choice.Value, true) + } +}