Skip to content

Commit

Permalink
moderation: migrate to sqlboiler
Browse files Browse the repository at this point in the history
Largely uncontroversial other than the choice to introduce the Config struct (in addition to the autogenerated models.ModerationConfig.) See long comment in config.go for details.

Unlike botlabs-gg#1652, I generally avoided any unrelated refactors since the moderation plugin is considerably more complicated and changes are high-stakes.
  • Loading branch information
jo3-l committed May 24, 2024
1 parent a8a42cc commit 44087a5
Show file tree
Hide file tree
Showing 11 changed files with 467 additions and 273 deletions.
2 changes: 1 addition & 1 deletion automod/automod_web.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ func (p *Plugin) handlePostAutomodUpdateRule(w http.ResponseWriter, r *http.Requ
}
if anyMute {
conf, err := moderation.GetCachedConfigOrDefault(g.ID)
if err != nil || conf.MuteRole == "" {
if err != nil || conf.MuteRole == 0 {
tx.Rollback()
tmpl.AddAlerts(web.ErrorAlert("No mute role set, please configure one."))
return tmpl, nil
Expand Down
23 changes: 14 additions & 9 deletions lib/discordgo/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ var ErrMFA = errors.New("account has 2FA enabled")
// tasks if given enough information to do so. Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
// With a single auth token - All requests will use the token blindly,
// no verification of the token will be done and requests may fail.
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
// eg: `"Bot <token>"`
// With an email and password - Discord will sign in with the provided
// credentials.
// With an email, password and auth token - Discord will verify the auth
// token, if it is invalid it will sign in with the provided
// credentials. This is the Discord recommended way to sign in.
//
// With a single auth token - All requests will use the token blindly,
// no verification of the token will be done and requests may fail.
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
// eg: `"Bot <token>"`
// With an email and password - Discord will sign in with the provided
// credentials.
// With an email, password and auth token - Discord will verify the auth
// token, if it is invalid it will sign in with the provided
// credentials. This is the Discord recommended way to sign in.
//
// NOTE: While email/pass authentication is supported by DiscordGo it is
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
Expand Down Expand Up @@ -153,3 +154,7 @@ func CheckRetry(_ context.Context, resp *http.Response, err error) (bool, error)
func StrID(id int64) string {
return strconv.FormatInt(id, 10)
}

func ParseID(s string) (int64, error) {
return strconv.ParseInt(s, 10, 64)
}
102 changes: 64 additions & 38 deletions moderation/commands.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package moderation

import (
"database/sql"
"fmt"
"regexp"
"strconv"
Expand All @@ -19,8 +20,9 @@ import (
"github.com/botlabs-gg/yagpdb/v2/lib/discordgo"
"github.com/botlabs-gg/yagpdb/v2/lib/dstate"
"github.com/botlabs-gg/yagpdb/v2/logs"
"github.com/botlabs-gg/yagpdb/v2/moderation/models"
"github.com/botlabs-gg/yagpdb/v2/web"
"github.com/jinzhu/gorm"
"github.com/volatiletech/sqlboiler/v4/queries/qm"
)

func MBaseCmd(cmdData *dcmd.Data, targetID int64) (config *Config, targetUser *discordgo.User, err error) {
Expand Down Expand Up @@ -337,7 +339,7 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

if config.MuteRole == "" {
if config.MuteRole == 0 {
return fmt.Sprintf("No mute role selected. Select one at <%s/moderation>", web.ManageServerURL(parsed.GuildData)), nil
}

Expand Down Expand Up @@ -396,7 +398,7 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

if config.MuteRole == "" {
if config.MuteRole == 0 {
return "No mute role set up, assign a mute role in the control panel", nil
}

Expand Down Expand Up @@ -562,7 +564,7 @@ var ModerationCommands = []*commands.YAGCommand{

logLink := CreateLogs(parsed.GuildData.GS.ID, parsed.GuildData.CS.ID, parsed.Author)

channelID := config.IntReportChannel()
channelID := config.ReportChannel
if channelID == 0 {
return "No report channel set up", nil
}
Expand Down Expand Up @@ -780,11 +782,11 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

if config.ActionChannel == "" {
if config.ActionChannel == 0 {
return "No mod log channel set up", nil
}

msg, err := common.BotSession.ChannelMessage(config.IntActionChannel(), parsed.Args[0].Int64())
msg, err := common.BotSession.ChannelMessage(config.ActionChannel, parsed.Args[0].Int64())
if err != nil {
return nil, err
}
Expand All @@ -799,7 +801,7 @@ var ModerationCommands = []*commands.YAGCommand{

embed := msg.Embeds[0]
updateEmbedReason(parsed.Author, parsed.Args[1].Str(), embed)
_, err = common.BotSession.ChannelMessageEditEmbed(config.IntActionChannel(), msg.ID, embed)
_, err = common.BotSession.ChannelMessageEditEmbed(config.ActionChannel, msg.ID, embed)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -878,19 +880,19 @@ var ModerationCommands = []*commands.YAGCommand{
}

if parsed.Switches["id"].Value != nil {
var warn []*WarningModel
err = common.GORM.Where("guild_id = ? AND id = ?", parsed.GuildData.GS.ID, parsed.Switches["id"].Int()).First(&warn).Error
if err != nil && err != gorm.ErrRecordNotFound {
id := parsed.Switches["id"].Int()
warning, err := models.FindModerationWarningG(parsed.Context(), id)
if err != nil {
if err == sql.ErrNoRows {
return fmt.Sprintf("Could not find warning with id `%d`", id), nil
}
return nil, err
}
if len(warn) == 0 {
return fmt.Sprintf("Warning with given id : `%d` does not exist.", parsed.Switches["id"].Int()), nil
}

return &discordgo.MessageEmbed{
Title: fmt.Sprintf("Warning#%d - User : %s", warn[0].ID, warn[0].UserID),
Description: fmt.Sprintf("<t:%d:f> - **Reason** : %s", warn[0].CreatedAt.Unix(), warn[0].Message),
Footer: &discordgo.MessageEmbedFooter{Text: fmt.Sprintf("By: %s (%13s)", warn[0].AuthorUsernameDiscrim, warn[0].AuthorID)},
Title: fmt.Sprintf("Warning#%d - User : %s", warning.ID, warning.UserID),
Description: fmt.Sprintf("<t:%d:f> - **Reason** : %s", warning.CreatedAt.Unix(), warning.Message),
Footer: &discordgo.MessageEmbedFooter{Text: fmt.Sprintf("By: %s (%13s)", warning.AuthorUsernameDiscrim, warning.AuthorID)},
}, nil
}
page := parsed.Args[1].Int()
Expand Down Expand Up @@ -928,11 +930,16 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

rows := common.GORM.Model(WarningModel{}).Where("guild_id = ? AND id = ?", parsed.GuildData.GS.ID, parsed.Args[0].Int()).Update(
"message", fmt.Sprintf("%s (updated by %s (%d))", parsed.Args[1].Str(), parsed.Author.String(), parsed.Author.ID)).RowsAffected

if rows < 1 {
return "Failed updating, most likely couldn't find the warning", nil
warningID := parsed.Args[0].Int()
updatedMessage := fmt.Sprintf("%s (updated by %s (%d))", parsed.Args[1].Str(), parsed.Author.String(), parsed.Author.ID)
numUpdated, err := models.
ModerationWarnings(models.ModerationWarningWhere.ID.EQ(warningID)).
UpdateAllG(parsed.Context(), models.M{"message": updatedMessage})
if err != nil {
return "Failed editing warning", err
}
if numUpdated == 0 {
return fmt.Sprintf("Could not find warning with id `%d`", warningID), nil
}

return "👌", nil
Expand Down Expand Up @@ -963,9 +970,13 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

rows := common.GORM.Where("guild_id = ? AND id = ?", parsed.GuildData.GS.ID, parsed.Args[0].Int()).Delete(WarningModel{}).RowsAffected
if rows < 1 {
return "Failed deleting, most likely couldn't find the warning", nil
warningID := parsed.Args[0].Int()
numDeleted, err := models.ModerationWarnings(models.ModerationWarningWhere.ID.EQ(warningID)).DeleteAllG(parsed.Context())
if err != nil {
return "Failed deleting warning", err
}
if numDeleted == 0 {
return fmt.Sprintf("Could not find warning with id `%d`", warningID), nil
}

return "👌", nil
Expand Down Expand Up @@ -997,15 +1008,21 @@ var ModerationCommands = []*commands.YAGCommand{
return nil, err
}

rows := common.GORM.Where("guild_id = ? AND user_id = ?", parsed.GuildData.GS.ID, target.ID).Delete(WarningModel{}).RowsAffected
numDeleted, err := models.ModerationWarnings(
models.ModerationWarningWhere.GuildID.EQ(parsed.GuildData.GS.ID),
models.ModerationWarningWhere.UserID.EQ(discordgo.StrID(target.ID)),
).DeleteAllG(parsed.Context())
if err != nil {
return "Failed deleting warnings", err
}

reason := parsed.Args[1].Str()
err = CreateModlogEmbed(config, parsed.Author, MAClearWarnings, target, reason, "")
if err != nil {
return "failed sending modlog", err
return "Failed sending modlog", err
}

return fmt.Sprintf("Deleted %d warnings.", rows), nil
return fmt.Sprintf("Deleted %d warnings.", numDeleted), nil
},
},
{
Expand Down Expand Up @@ -1143,7 +1160,7 @@ var ModerationCommands = []*commands.YAGCommand{

action := MAGiveRole
action.Prefix = "Gave the role " + role.Name + " to "
if config.GiveRoleCmdModlog && config.IntActionChannel() != 0 {
if config.GiveRoleCmdModlog && config.ActionChannel != 0 {
if dur > 0 {
action.Footer = "Duration: " + common.HumanizeDuration(common.DurationPrecisionMinutes, dur)
}
Expand Down Expand Up @@ -1204,7 +1221,7 @@ var ModerationCommands = []*commands.YAGCommand{

action := MARemoveRole
action.Prefix = "Removed the role " + role.Name + " from "
if config.GiveRoleCmdModlog && config.IntActionChannel() != 0 {
if config.GiveRoleCmdModlog && config.ActionChannel != 0 {
CreateModlogEmbed(config, parsed.Author, action, target, "", "")
}

Expand Down Expand Up @@ -1345,22 +1362,31 @@ func FindRole(gs *dstate.GuildSet, roleS string) *discordgo.Role {
}

func PaginateWarnings(parsed *dcmd.Data) func(p *paginatedmessages.PaginatedMessage, page int) (*discordgo.MessageEmbed, error) {

return func(p *paginatedmessages.PaginatedMessage, page int) (*discordgo.MessageEmbed, error) {

var err error
skip := (page - 1) * 6
userID := parsed.Args[0].Int64()
limit := 6

var result []*WarningModel
var count int
err = common.GORM.Table("moderation_warnings").Where("user_id = ? AND guild_id = ?", userID, parsed.GuildData.GS.ID).Count(&count).Error
if err != nil && err != gorm.ErrRecordNotFound {
userIDStr := discordgo.StrID(userID)
count, err := models.ModerationWarnings(
models.ModerationWarningWhere.UserID.EQ(userIDStr),
models.ModerationWarningWhere.GuildID.EQ(parsed.GuildData.GS.ID),
).CountG(parsed.Context())
if err != nil {
return nil, err
}
err = common.GORM.Where("user_id = ? AND guild_id = ?", userID, parsed.GuildData.GS.ID).Order("id desc").Offset(skip).Limit(limit).Find(&result).Error
if err != nil && err != gorm.ErrRecordNotFound {

result, err := models.ModerationWarnings(
models.ModerationWarningWhere.UserID.EQ(userIDStr),
models.ModerationWarningWhere.GuildID.EQ(parsed.GuildData.GS.ID),

qm.OrderBy("id desc"),
qm.Offset(skip),
qm.Limit(limit),
).AllG(parsed.Context())
if err != nil {
return nil, err
}

Expand All @@ -1385,8 +1411,8 @@ func PaginateWarnings(parsed *dcmd.Data) func(p *paginatedmessages.PaginatedMess
}
entry_formatted += "\n"
purgedWarnLogs := logs.ConfEnableMessageLogPurge.GetBool() && entry.CreatedAt.Before(time.Now().AddDate(0, 0, -30))
if entry.LogsLink != "" && !purgedWarnLogs {
entry_formatted += fmt.Sprintf("> logs: [`link`](%s)\n", entry.LogsLink)
if entry.LogsLink.String != "" && !purgedWarnLogs {
entry_formatted += fmt.Sprintf("> logs: [`link`](%s)\n", entry.LogsLink.String)
}
if len([]rune(currentField.Value+entry_formatted)) > 1023 {
currentField = &discordgo.MessageEmbedField{
Expand Down
Loading

0 comments on commit 44087a5

Please sign in to comment.