diff --git a/commands/admin.go b/commands/admin.go index cd2ed11..ac8adec 100644 --- a/commands/admin.go +++ b/commands/admin.go @@ -32,6 +32,12 @@ var AdminCommand = discord.SlashCommandCreate{ Required: false, ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText}, }, + discord.ApplicationCommandOptionChannel{ + Name: "audit-log-channel", + Description: "The channel to set as the audit log channel", + Required: false, + ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText}, + }, }, }, @@ -164,9 +170,19 @@ func AdminShowAllButtonHandler(e *handler.ComponentEvent) error { } func modChannelInfo(settings *model.GuildSettings) string { - modChannelInfo := "> This is the channel in which notifications and other information for moderators and administrators are sent." - return fmt.Sprintf("**Moderator channel:** <#%d>\n%s", - settings.ModeratorChannel, modChannelInfo) + modChannelInfoHelp := "> This is the channel in which notifications and other information for moderators and administrators are sent." + modChannelInfo := fmt.Sprintf("**Moderator channel:** %s\n%s", + utils.Iif(settings.ModeratorChannel == 0, "not set", + fmt.Sprintf("<#%d>", settings.ModeratorChannel)), + modChannelInfoHelp) + + auditLogChannelInfoHelp := "> This is the channel in which audit logs are sent." + auditLogChannelInfo := fmt.Sprintf("**Audit log channel:** %s\n%s", + utils.Iif(settings.AuditLogChannel == 0, "not set", + fmt.Sprintf("<#%d>", settings.AuditLogChannel)), + auditLogChannelInfoHelp) + + return fmt.Sprintf("%s\n\n%s", modChannelInfo, auditLogChannelInfo) } func infractionInfo(settings *model.GuildSettings) string { @@ -235,13 +251,15 @@ func AdminModChannelHandler(e *handler.CommandEvent) error { return ErrEventNoGuildID } - channel, hasChannel := data.OptChannel("channel") + modChannel, hasModChannel := data.OptChannel("channel") + auditLogChannel, hasAuditLogChannel := data.OptChannel("audit-log-channel") + settings, err := model.GetGuildSettings(guild.ID) if err != nil { return err } - if !hasChannel { + if !hasModChannel && !hasAuditLogChannel { return e.CreateMessage(discord.NewMessageCreateBuilder(). SetContent(modChannelInfo(settings)). SetEphemeral(true). @@ -249,14 +267,24 @@ func AdminModChannelHandler(e *handler.CommandEvent) error { Build()) } - settings.ModeratorChannel = channel.ID + msg := "" + + if hasModChannel { + settings.ModeratorChannel = modChannel.ID + msg += fmt.Sprintf("Moderator channel set to <#%d>\n", modChannel.ID) + } + if hasAuditLogChannel { + settings.AuditLogChannel = auditLogChannel.ID + msg += fmt.Sprintf("Audit log channel set to <#%d>\n", auditLogChannel.ID) + } + err = model.SetGuildSettings(settings) if err != nil { return err } return e.CreateMessage(discord.NewMessageCreateBuilder(). - SetContentf("Moderator channel set to <#%d>", channel.ID). + SetContent(msg). SetEphemeral(true). SetAllowedMentions(&discord.AllowedMentions{}). Build()) diff --git a/commands/infractions.go b/commands/infractions.go index f36736f..9b0a20a 100644 --- a/commands/infractions.go +++ b/commands/infractions.go @@ -226,7 +226,7 @@ func UserInfractionsHandler(e *handler.CommandEvent) error { } func UserInfractionButtonHandler(e *handler.ComponentEvent) error { - offsetStr := e.Variables["offset"] + offsetStr := e.Vars["offset"] offset, err := strconv.Atoi(offsetStr) if err != nil { return fmt.Errorf("failed to parse offset: %w", err) @@ -415,11 +415,11 @@ func InfractionsListComponentHandler(e *handler.ComponentEvent) error { if !isGuild { return ErrEventNoGuildID } - offset, err := strconv.Atoi(e.Variables["offset"]) + offset, err := strconv.Atoi(e.Vars["offset"]) if err != nil { return fmt.Errorf("failed to parse offset: %w", err) } - userID, err := snowflake.Parse(e.Variables["userID"]) + userID, err := snowflake.Parse(e.Vars["userID"]) if err != nil { return fmt.Errorf("failed to parse user id: %w", err) } diff --git a/components/role_assign_button.go b/components/role_assign_button.go index e954672..690014c 100644 --- a/components/role_assign_button.go +++ b/components/role_assign_button.go @@ -23,12 +23,12 @@ func RoleAssignButtonHandler(e *handler.ComponentEvent) error { "user", e.User().ID, "roleID", - e.Variables["roleID"], + e.Vars["roleID"], ) - roleID, err := snowflake.Parse(e.Variables["roleID"]) + roleID, err := snowflake.Parse(e.Vars["roleID"]) if err != nil { - slog.Warn("Failed to parse roleID", "roleID", e.Variables["roleID"], "err", err) + slog.Warn("Failed to parse roleID", "roleID", e.Vars["roleID"], "err", err) return nil } diff --git a/go.mod b/go.mod index d90c88d..e9918e6 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.22.0 require ( github.com/cbroglie/mustache v1.4.0 - github.com/disgoorg/disgo v0.17.2 + github.com/disgoorg/disgo v0.18.10-0.20240811153006-637a92bd7191 github.com/disgoorg/json v1.1.0 - github.com/disgoorg/snowflake/v2 v2.0.1 + github.com/disgoorg/snowflake/v2 v2.0.3 github.com/glebarez/sqlite v1.10.0 github.com/spf13/viper v1.18.2 github.com/sqids/sqids-go v0.4.1 @@ -19,7 +19,7 @@ require ( github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -40,12 +40,12 @@ require ( github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 1a13717..2a0b191 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disgoorg/disgo v0.17.2 h1:RxiLq8guMtk+9tArFwve02iya2APQ9yZVtV30ySKNtw= github.com/disgoorg/disgo v0.17.2/go.mod h1:8r3h9fXSz7BbACxLPsPbtB6LX8gaQFUETgPKV/0gAKQ= +github.com/disgoorg/disgo v0.18.9 h1:Ey1HYrCvdFtxNj8QQE+13wgj7/JTBvf4rXOQcvnBftA= +github.com/disgoorg/disgo v0.18.9/go.mod h1:TN4tM8VaOaZebO+Ek3686RIATCL32uqPGSNdUYP8yVI= +github.com/disgoorg/disgo v0.18.10-0.20240811153006-637a92bd7191 h1:+h2mcpl5OyNfvUtSdtnRPPPsIzzYx6iPcEGqmjhL1i8= +github.com/disgoorg/disgo v0.18.10-0.20240811153006-637a92bd7191/go.mod h1:TN4tM8VaOaZebO+Ek3686RIATCL32uqPGSNdUYP8yVI= github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys= github.com/disgoorg/json v1.1.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= github.com/disgoorg/snowflake/v2 v2.0.1 h1:CuUxGLwggUxEswZOmZ+mZ5i0xSumQdXW9tXW7uGqe+0= github.com/disgoorg/snowflake/v2 v2.0.1/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs= +github.com/disgoorg/snowflake/v2 v2.0.3 h1:3B+PpFjr7j4ad7oeJu4RlQ+nYOTadsKapJIzgvSI2Ro= +github.com/disgoorg/snowflake/v2 v2.0.3/go.mod h1:W6r7NUA7DwfZLwr00km6G4UnZ0zcoLBRufhkFWgAc4c= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -28,6 +34,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -90,6 +98,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= @@ -99,10 +109,15 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/listeners/audit_log.go b/listeners/audit_log.go index eaa25ea..9e01293 100644 --- a/listeners/audit_log.go +++ b/listeners/audit_log.go @@ -3,9 +3,13 @@ package listeners import ( "fmt" "log/slog" + "strings" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" + "github.com/disgoorg/json" + "github.com/disgoorg/snowflake/v2" + "github.com/myrkvi/heimdallr/model" "github.com/myrkvi/heimdallr/utils" ) @@ -13,22 +17,51 @@ import ( func OnAuditLog(e *events.GuildAuditLogEntryCreate) { entry := e.AuditLogEntry + guildSettings, err := model.GetGuildSettings(e.GuildID) + if err != nil { + return + } + msg := "" switch entry.ActionType { case discord.AuditLogEventMemberKick: - targetUser, err := e.Client().Rest().GetUser(*entry.TargetID) - if err != nil { - return - } - user, err := e.Client().Rest().GetUser(entry.UserID) - if err != nil { - slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", entry.UserID) - return - } + msg = onKickEvent(e) + + case discord.AuditLogEventMemberBanAdd: + msg = onBanAddEvent(e) + + case discord.AuditLogEventMemberBanRemove: + msg = onBanRemoveEvent(e) + + case discord.AuditLogEventMemberRoleUpdate: + msg = onMemberRoleUpdateEvent(e) + + case discord.AuditLogEventBotAdd: + msg = onBotAddEvent(e) + + case discord.AuditLogEventChannelCreate: + msg = onChannelCreateEvent(e) + + case discord.AuditLogEventChannelUpdate: + msg = onChannelUpdateEvent(e) + + case discord.AuditLogEventChannelDelete: + msg = onChannelDeleteEvent(e) + + case discord.AuditLogEventChannelOverwriteCreate: + msg = onChannelOverwriteCreateEvent(e) - msg = fmt.Sprintf("User %s (`%d`) was kicked by %s.%s", targetUser.Username, targetUser.ID, - user.Mention(), - utils.Iif(entry.Reason != nil, fmt.Sprintf("\n\n>>> %s", *entry.Reason), "")) + case discord.AuditLogEventChannelOverwriteUpdate: + msg = onChannelOverwriteUpdateEvent(e) + + case discord.AuditLogEventChannelOverwriteDelete: + msg = onChannelOverwriteDeleteEvent(e) + + case discord.AuditLogEventMemberPrune: + msg = onMemberPruneEvent(e) + + case discord.AuditLogEventMemberUpdate: + msg = onMemberUpdateEvent(e) default: return @@ -38,12 +71,7 @@ func OnAuditLog(e *events.GuildAuditLogEntryCreate) { return } - guildSettings, err := model.GetGuildSettings(e.GuildID) - if err != nil { - return - } - - _, err = e.Client().Rest().CreateMessage(guildSettings.ModeratorChannel, discord.NewMessageCreateBuilder(). + _, err = e.Client().Rest().CreateMessage(guildSettings.AuditLogChannel, discord.NewMessageCreateBuilder(). SetContent(msg). SetAllowedMentions(&discord.AllowedMentions{ RepliedUser: false, @@ -54,3 +82,269 @@ func OnAuditLog(e *events.GuildAuditLogEntryCreate) { slog.Error("Failed to send audit log message.", "err", err, "guild", e.GuildID) } } + +func onKickEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + return fmt.Sprintf("%s kicked %s (`%d`).%s", + user.Mention(), + targetUser.Username, + targetUser.ID, + utils.Iif(e.AuditLogEntry.Reason != nil, fmt.Sprintf("\n\n>>> %s", *e.AuditLogEntry.Reason), ""), + ) +} + +func onBanAddEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + return fmt.Sprintf("%s banned %s (`%d`).%s", + user.Mention(), + targetUser.Username, + targetUser.ID, + utils.Iif(e.AuditLogEntry.Reason != nil, fmt.Sprintf("\n\n>>> %s", *e.AuditLogEntry.Reason), ""), + ) +} + +func onBanRemoveEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + return fmt.Sprintf("%s unbanned %s (`%d`).%s", + user.Mention(), + targetUser.Username, + targetUser.ID, + utils.Iif(e.AuditLogEntry.Reason != nil, fmt.Sprintf("\n\n>>> %s", *e.AuditLogEntry.Reason), ""), + ) +} + +func onMemberRoleUpdateEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + var addedRoles, removedRoles []string + for _, change := range e.AuditLogEntry.Changes { + if change.Key == "$add" { + var d []struct { + Name string `json:"name"` + ID snowflake.ID `json:"id"` + } + err := json.Unmarshal(change.NewValue, &d) + if err != nil { + fmt.Println("Failed to unmarshal role change: ", err) + continue + } + + for _, role := range d { + addedRoles = append(addedRoles, role.Name) + } + } else if change.Key == "$remove" { + var d []struct { + Name string `json:"name"` + ID snowflake.ID `json:"id"` + } + err := json.Unmarshal(change.NewValue, &d) + if err != nil { + fmt.Println("Failed to unmarshal role change: ", err) + continue + } + + for _, role := range d { + removedRoles = append(removedRoles, role.Name) + } + } + + } + + addedRolesStr := strings.Join(addedRoles, ", ") + removedRolesStr := strings.Join(removedRoles, ", ") + + return fmt.Sprintf("%s updated the roles of %s (%d)\n\n%s%s%s", + user.Mention(), + targetUser.Username, + targetUser.ID, + utils.Iif(addedRolesStr == "", "", fmt.Sprintf("**Added roles:** %s\n", addedRolesStr)), + // ↓ Add a newline if both added and removed roles are present + utils.Iif(addedRolesStr != "" && removedRolesStr != "", "\n", ""), + utils.Iif(removedRolesStr == "", "", fmt.Sprintf("**Removed roles:** %s\n", removedRolesStr)), + ) +} + +func onBotAddEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + return fmt.Sprintf("%s added bot %s (%d) to the server", user.Mention(), targetUser.Username, targetUser.ID) +} + +func onChannelCreateEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + return fmt.Sprintf("%s created channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onChannelUpdateEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + return fmt.Sprintf("%s updated channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onChannelDeleteEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + return fmt.Sprintf("%s deleted channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onChannelOverwriteCreateEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + return fmt.Sprintf("%s created overwrite in channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onChannelOverwriteUpdateEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + for _, change := range e.AuditLogEntry.Changes { + fmt.Println("old: ", string(change.OldValue)) + fmt.Println("new: ", string(change.NewValue)) + + } + + return fmt.Sprintf("%s updated overwrite for in channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onChannelOverwriteDeleteEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + channel, err := e.Client().Rest().GetChannel(*e.AuditLogEntry.TargetID) + if err != nil { + slog.Warn("Failed to get channel for audit log entry.", "err", err, "channel_id", *e.AuditLogEntry.TargetID) + return "" + } + + return fmt.Sprintf("%s deleted overwrite for in channel *%s* <#%d>", user.Mention(), channel.Name(), channel.ID()) +} + +func onMemberPruneEvent(e *events.GuildAuditLogEntryCreate) string { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + return fmt.Sprintf("%s pruned %s members.", user.Mention(), *e.AuditLogEntry.Options.MembersRemoved) +} + +func onMemberUpdateEvent(e *events.GuildAuditLogEntryCreate) string { + user, targetUser, err := getUserAndTargetUser(e) + if err != nil { + slog.Warn("Failed to get user for audit log entry.", "err", err, "user_id", e.AuditLogEntry.UserID) + return "" + } + + msg := fmt.Sprintf("%s updated the profile of %s (`%d`).\n\n>>> ", + user.Mention(), + targetUser.Username, + targetUser.ID, + ) + + for _, change := range e.AuditLogEntry.Changes { + fmt.Println(change) + if change.Key == "nick" { + var old, new string + err := json.Unmarshal(change.OldValue, &old) + if err != nil { + old = " " + } + err = json.Unmarshal(change.NewValue, &new) + if err != nil { + new = " " + } + msg += fmt.Sprintf("Nickname changed from `%s` to `%s`.\n", old, new) + } + } + + return msg +} + +func getUserAndTargetUser(e *events.GuildAuditLogEntryCreate) (*discord.User, *discord.User, error) { + user, err := e.Client().Rest().GetUser(e.AuditLogEntry.UserID) + if err != nil { + return nil, nil, err + } + targetUser, err := e.Client().Rest().GetUser(*e.AuditLogEntry.TargetID) + if err != nil { + return nil, nil, err + } + return user, targetUser, nil +} diff --git a/model/guild_settings.go b/model/guild_settings.go index 506710a..7d12092 100644 --- a/model/guild_settings.go +++ b/model/guild_settings.go @@ -14,6 +14,9 @@ type GuildSettings struct { // information for moderators and administrators are sent. ModeratorChannel snowflake.ID + // AuditLogChannel is the channel where audit log messages are sent. + AuditLogChannel snowflake.ID + // InfractionHalfLifeDays is the half-life time of infractions in days. InfractionHalfLifeDays float64 NotifyOnWarnedUserJoin bool