Documentation ¶
Index ¶
- Constants
- Variables
- func AddMemberMuteRole(config *Config, id int64, currentRoles []int64) (removedRoles []int64, err error)
- func AdvancedDeleteMessages(channelID int64, filterUser int64, regex string, maxAge time.Duration, ...) (int, error)
- func BanUser(config *Config, guildID, channelID int64, author *discordgo.User, ...) error
- func BanUserWithDuration(config *Config, guildID, channelID int64, author *discordgo.User, ...) error
- func CreateLogs(guildID, channelID int64, user *discordgo.User) string
- func CreateModlogEmbed(channelID int64, author *discordgo.User, action ModlogAction, ...) error
- func DeleteMessages(channelID int64, filterUser int64, deleteNum, fetchNum int) (int, error)
- func FindAuditLogEntry(guildID int64, typ int, targetUser int64, within time.Duration) (author *discordgo.User, entry *discordgo.AuditLogEntry)
- func HandleChannelCreateUpdate(evt *eventsystem.EventData)
- func HandleClearServerWarnings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
- func HandleGuildBanAddRemove(evt *eventsystem.EventData)
- func HandleGuildCreate(evt *eventsystem.EventData)
- func HandleGuildMemberRemove(evt *eventsystem.EventData)
- func HandleGuildMemberUpdate(evt *eventsystem.EventData)
- func HandleMemberJoin(evt *eventsystem.EventData)
- func HandleModeration(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
- func HandlePostModeration(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
- func HandleRefreshMuteOverrides(evt *pubsub.Event)
- func KickUser(config *Config, guildID, channelID int64, author *discordgo.User, ...) error
- func LockMemberMuteMW(next func(evt *eventsystem.EventData)) func(evt *eventsystem.EventData)
- func LockMute(uID int64)
- func ModBaseCmd(neededPerm, cmd int, inner dcmd.RunFunc) dcmd.RunFunc
- func MuteUnmuteUser(config *Config, mute bool, guildID, channelID int64, author *discordgo.User, ...) error
- func RedisKeyBannedUser(guildID, userID int64) string
- func RedisKeyLockedMute(guildID, userID int64) string
- func RedisKeyMutedUser(guildID, userID int64) string
- func RedisKeyUnbannedUser(guildID, userID int64) string
- func RefreshMuteOverrideForChannel(config *Config, channel *discordgo.Channel)
- func RefreshMuteOverrides(guildID int64)
- func RegisterPlugin()
- func RemoveMemberMuteRole(config *Config, id int64, currentRoles []int64, mute MuteModel) (err error)
- func SafeArgString(data *dcmd.Data, arg int) string
- func UnlockMute(uID int64)
- func WarnUser(config *Config, guildID, channelID int64, author *discordgo.User, ...) error
- type Config
- type ContextKey
- type ModlogAction
- type MuteModel
- type Plugin
- type Punishment
- type WarningModel
Constants ¶
View Source
const ( ModCmdBan int = iota ModCmdKick ModCmdMute ModCmdUnMute ModCmdClean ModCmdReport ModCmdReason ModCmdWarn )
View Source
const ( ActionMuted = "Muted" ActionUnMuted = "Unmuted" ActionKicked = "Kicked" ActionBanned = "Banned" ActionUnbanned = "Unbanned" ActionWarned = "Warned" )
View Source
const MuteDeniedChannelPerms = discordgo.PermissionSendMessages | discordgo.PermissionVoiceSpeak
Variables ¶
View Source
var ( MAMute = ModlogAction{Prefix: "Muted", Emoji: "🔇", Color: 0x57728e} MAUnmute = ModlogAction{Prefix: "Unmuted", Emoji: "🔊", Color: 0x62c65f} MAKick = ModlogAction{Prefix: "Kicked", Emoji: "👢", Color: 0xf2a013} MABanned = ModlogAction{Prefix: "Banned", Emoji: "🔨", Color: 0xd64848} MAUnbanned = ModlogAction{Prefix: "Unbanned", Emoji: "🔓", Color: 0x62c65f} MAWarned = ModlogAction{Prefix: "Warned", Emoji: "⚠", Color: 0xfca253} )
View Source
var (
ErrFailedPerms = errors.New("Failed retrieving perms")
)
View Source
var (
ErrNoMuteRole = errors.New("No mute role")
)
View Source
var ModerationCommands = []*commands.YAGCommand{ &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Ban", Aliases: []string{"banid"}, Description: "Bans a member, specify a duration with -d", RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserID}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, ArgSwitches: []*dcmd.ArgDef{ &dcmd.ArgDef{Switch: "d", Default: time.Duration(0), Name: "Duration", Type: &commands.DurationArg{}}, }, RunFunc: ModBaseCmd(discordgo.PermissionBanMembers, ModCmdBan, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) reason := SafeArgString(parsed, 1) targetID := parsed.Args[0].Int64() targetMember := parsed.GS.MemberCopy(true, targetID) var target *discordgo.User if targetMember == nil || !targetMember.MemberSet { target = &discordgo.User{ Username: "unknown", Discriminator: "????", ID: targetID, } } else { target = targetMember.DGoUser() } err := BanUserWithDuration(config, parsed.GS.ID, parsed.Msg.ChannelID, parsed.Msg.Author, reason, target, parsed.Switches["d"].Value.(time.Duration)) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Kick", Description: "Kicks a member", RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserID}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, RunFunc: ModBaseCmd(discordgo.PermissionKickMembers, ModCmdKick, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) reason := SafeArgString(parsed, 1) targetID := parsed.Args[0].Int64() targetMember := parsed.GS.MemberCopy(true, targetID) var target *discordgo.User if targetMember == nil || !targetMember.MemberSet { target = &discordgo.User{ Username: "unknown", Discriminator: "????", ID: targetID, } } else { target = targetMember.DGoUser() } err := KickUser(config, parsed.GS.ID, parsed.Msg.ChannelID, parsed.Msg.Author, reason, target) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Mute", Description: "Mutes a member", Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserReqMention}, &dcmd.ArgDef{Name: "Duration", Default: time.Minute * 10, Type: &commands.DurationArg{Max: time.Hour * 24 * 7}}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, ArgumentCombos: [][]int{[]int{0, 1, 2}, []int{0, 2, 1}, []int{0, 1}, []int{0, 2}, []int{0}}, RunFunc: ModBaseCmd(discordgo.PermissionKickMembers, ModCmdMute, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) if config.MuteRole == "" { return "No mute role set up, assign a mute role in the control panel", nil } target := parsed.Args[0].Value.(*discordgo.User) muteDuration := int(parsed.Args[1].Value.(time.Duration).Minutes()) reason := parsed.Args[2].Str() member, err := bot.GetMember(parsed.GS.ID, target.ID) if err != nil || member == nil { return "Member not found", err } err = MuteUnmuteUser(config, true, parsed.GS.ID, parsed.Msg.ChannelID, parsed.Msg.Author, reason, member, muteDuration) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Unmute", Description: "Unmutes a member", RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserReqMention}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, RunFunc: ModBaseCmd(discordgo.PermissionKickMembers, ModCmdUnMute, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) if config.MuteRole == "" { return "No mute role set up, assign a mute role in the control panel", nil } target := parsed.Args[0].Value.(*discordgo.User) reason := parsed.Args[1].Str() member, err := bot.GetMember(parsed.GS.ID, target.ID) if err != nil || member == nil { return "Member not found", err } err = MuteUnmuteUser(config, false, parsed.GS.ID, parsed.Msg.ChannelID, parsed.Msg.Author, reason, member, 0) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, Cooldown: 5, CmdCategory: commands.CategoryModeration, Name: "Report", Description: "Reports a member to the server's staff", RequiredArgs: 2, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserID}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, RunFunc: ModBaseCmd(0, ModCmdReport, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) target := parsed.Args[0].Int64() logLink := CreateLogs(parsed.GS.ID, parsed.CS.ID, parsed.Msg.Author) channelID := config.IntReportChannel() if channelID == 0 { return "No report channel set up", nil } reportBody := fmt.Sprintf("<@%d> Reported <@%d> in <#%d> For `%s`\nLast 100 messages from channel: <%s>", parsed.Msg.Author.ID, target, parsed.Msg.ChannelID, parsed.Args[1].Str(), logLink) _, err := common.BotSession.ChannelMessageSend(channelID, common.EscapeSpecialMentions(reportBody)) if err != nil { return nil, err } if channelID != parsed.Msg.ChannelID { return "User reported to the proper authorities", nil } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Clean", Description: "Delete the last number of messages from chat, optionally filtering by user, max age and regex.", LongDescription: "Specify a regex with \"-r regex_here\" and max age with \"-ma 1h10m\"\nNote: Will only look in the last 1k messages", Aliases: []string{"clear", "cl"}, RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "Num", Type: &dcmd.IntArg{Min: 1, Max: 100}}, &dcmd.ArgDef{Name: "User", Type: dcmd.UserReqMention}, }, ArgSwitches: []*dcmd.ArgDef{ &dcmd.ArgDef{Switch: "r", Name: "Regex", Type: dcmd.String}, &dcmd.ArgDef{Switch: "ma", Default: time.Duration(0), Name: "Max age", Type: &commands.DurationArg{}}, &dcmd.ArgDef{Switch: "i", Name: "Regex case insensitive"}, }, ArgumentCombos: [][]int{[]int{0}, []int{0, 1}, []int{1, 0}}, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdClean, func(parsed *dcmd.Data) (interface{}, error) { var userFilter int64 if parsed.Args[1].Value != nil { userFilter = parsed.Args[1].Value.(*discordgo.User).ID } num := parsed.Args[0].Int() if userFilter == 0 || userFilter == parsed.Msg.Author.ID { num++ } if num > 100 { num = 100 } if num < 1 { if num < 0 { return errors.New("Bot is having a stroke <https://www.youtube.com/watch?v=dQw4w9WgXcQ>"), nil } return errors.New("Can't delete nothing"), nil } filtered := false re := "" if parsed.Switches["r"].Value != nil { filtered = true re = parsed.Switches["r"].Str() if parsed.Switches["i"].Value != nil && parsed.Switches["i"].Value.(bool) { if !strings.HasPrefix(re, "(?i)") { re = "(?i)" + re } } } ma := parsed.Switches["ma"].Value.(time.Duration) if ma != 0 { filtered = true } limitFetch := num if userFilter != 0 || filtered { limitFetch = num * 50 } if limitFetch > 1000 { limitFetch = 1000 } time.Sleep(time.Second) numDeleted, err := AdvancedDeleteMessages(parsed.Msg.ChannelID, userFilter, re, ma, num, limitFetch) return dcmd.NewTemporaryResponse(time.Second*5, fmt.Sprintf("Deleted %d message(s)! :')", numDeleted), true), err }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Reason", Description: "Add/Edit a modlog reason", RequiredArgs: 2, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "ID", Type: dcmd.Int}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, RunFunc: ModBaseCmd(discordgo.PermissionKickMembers, ModCmdReason, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) if config.ActionChannel == "" { return "No mod log channel set up", nil } msg, err := common.BotSession.ChannelMessage(config.IntActionChannel(), parsed.Args[0].Int64()) if err != nil { return nil, err } if msg.Author.ID != common.Conf.BotID { return "I didn't make that message", nil } if len(msg.Embeds) < 1 { return "This entry is either too old or you're trying to mess with me...", nil } embed := msg.Embeds[0] updateEmbedReason(parsed.Msg.Author, parsed.Args[1].Str(), embed) _, err = common.BotSession.ChannelMessageEditEmbed(config.IntActionChannel(), msg.ID, embed) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Warn", Description: "Warns a user, warnings are saved using the bot. Use -warnings to view them.", RequiredArgs: 2, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserReqMention}, &dcmd.ArgDef{Name: "Reason", Type: dcmd.String}, }, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdWarn, func(parsed *dcmd.Data) (interface{}, error) { config := parsed.Context().Value(ContextKeyConfig).(*Config) err := WarnUser(config, parsed.GS.ID, parsed.CS.ID, parsed.Msg.Author, parsed.Args[0].Value.(*discordgo.User), parsed.Args[1].Str()) if err != nil { return nil, err } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "Warnings", Description: "Lists warning of a user.", Aliases: []string{"Warns"}, RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserID}, }, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdWarn, func(parsed *dcmd.Data) (interface{}, error) { userID := parsed.Args[0].Int64() var result []*WarningModel err := common.GORM.Where("user_id = ? AND guild_id = ?", userID, parsed.GS.ID).Order("id desc").Find(&result).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if len(result) < 1 { return "This user has not received any warnings", nil } out := "" for _, entry := range result { out += fmt.Sprintf("#%d: `%20s` **%s** (%13s) - **%s**\n", entry.ID, entry.CreatedAt.Format(time.RFC822), entry.AuthorUsernameDiscrim, entry.AuthorID, entry.Message) if entry.LogsLink != "" { out += "^logs: <" + entry.LogsLink + ">\n" } } return out, nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "EditWarning", Description: "Edit a warning, id is the first number of each warning from the warnings command", RequiredArgs: 2, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "Id", Type: dcmd.Int}, &dcmd.ArgDef{Name: "NewMessage", Type: dcmd.String}, }, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdWarn, func(parsed *dcmd.Data) (interface{}, error) { rows := common.GORM.Model(WarningModel{}).Where("guild_id = ? AND id = ?", parsed.GS.ID, parsed.Args[0].Int()).Update( "message", fmt.Sprintf("%s (updated by %s#%s (%d))", parsed.Args[1].Str(), parsed.Msg.Author.Username, parsed.Msg.Author.Discriminator, parsed.Msg.Author.ID)).RowsAffected if rows < 1 { return "Failed updating, most likely couldn't find the warning", nil } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "DelWarning", Aliases: []string{"dw"}, Description: "Deletes a warning, id is the first number of each warning from the warnings command", RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "Id", Type: dcmd.Int}, }, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdWarn, func(parsed *dcmd.Data) (interface{}, error) { rows := common.GORM.Where("guild_id = ? AND id = ?", parsed.GS.ID, parsed.Args[0].Int()).Delete(WarningModel{}).RowsAffected if rows < 1 { return "Failed deleting, most likely couldn't find the warning", nil } return "👌", nil }), }, &commands.YAGCommand{ CustomEnabled: true, CmdCategory: commands.CategoryModeration, Name: "ClearWarnings", Aliases: []string{"clw"}, Description: "Clears the warnings of a user", RequiredArgs: 1, Arguments: []*dcmd.ArgDef{ &dcmd.ArgDef{Name: "User", Type: dcmd.UserID}, }, RunFunc: ModBaseCmd(discordgo.PermissionManageMessages, ModCmdWarn, func(parsed *dcmd.Data) (interface{}, error) { userID := parsed.Args[0].Int64() rows := common.GORM.Where("guild_id = ? AND user_id = ?", parsed.GS.ID, userID).Delete(WarningModel{}).RowsAffected return fmt.Sprintf("Deleted %d warnings.", rows), nil }), }, }
Functions ¶
func AddMemberMuteRole ¶ added in v0.29.1
func AdvancedDeleteMessages ¶
func BanUserWithDuration ¶
func CreateModlogEmbed ¶
func DeleteMessages ¶
func FindAuditLogEntry ¶ added in v1.4.1
func HandleChannelCreateUpdate ¶ added in v1.4.1
func HandleChannelCreateUpdate(evt *eventsystem.EventData)
func HandleClearServerWarnings ¶ added in v1.4.1
func HandleClearServerWarnings(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
Clear all server warnigns
func HandleGuildBanAddRemove ¶
func HandleGuildBanAddRemove(evt *eventsystem.EventData)
func HandleGuildCreate ¶ added in v1.4.1
func HandleGuildCreate(evt *eventsystem.EventData)
func HandleGuildMemberRemove ¶ added in v1.4.1
func HandleGuildMemberRemove(evt *eventsystem.EventData)
func HandleGuildMemberUpdate ¶ added in v0.29.1
func HandleGuildMemberUpdate(evt *eventsystem.EventData)
func HandleMemberJoin ¶
func HandleMemberJoin(evt *eventsystem.EventData)
func HandleModeration ¶
func HandleModeration(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
The moderation page itself
func HandlePostModeration ¶
func HandlePostModeration(w http.ResponseWriter, r *http.Request) (web.TemplateData, error)
Update the settings
func HandleRefreshMuteOverrides ¶ added in v1.4.1
func LockMemberMuteMW ¶ added in v0.29.1
func LockMemberMuteMW(next func(evt *eventsystem.EventData)) func(evt *eventsystem.EventData)
Since updating mutes are now a complex operation with removing roles and whatnot, to avoid weird bugs from happening we lock it so it can only be updated one place per user
func ModBaseCmd ¶
ModBaseCmd is the base command for moderation commands, it makes sure proper permissions are there for the user invoking it and that the command is required and the reason is specified if required
func MuteUnmuteUser ¶
func MuteUnmuteUser(config *Config, mute bool, guildID, channelID int64, author *discordgo.User, reason string, member *dstate.MemberState, duration int) error
Unmut or mute a user, ignore duration if unmuting
func RedisKeyBannedUser ¶
func RedisKeyLockedMute ¶ added in v0.29.1
func RedisKeyMutedUser ¶
func RedisKeyUnbannedUser ¶
func RefreshMuteOverrideForChannel ¶ added in v1.4.1
func RefreshMuteOverrides ¶ added in v1.4.1
func RefreshMuteOverrides(guildID int64)
Refreshes the mute override on the channel, currently it only adds it.
func RegisterPlugin ¶
func RegisterPlugin()
func RemoveMemberMuteRole ¶ added in v0.29.1
func UnlockMute ¶ added in v1.4.1
func UnlockMute(uID int64)
Types ¶
type Config ¶
type Config struct { configstore.GuildConfigModel // Kick command KickEnabled bool KickCmdRoles pq.Int64Array `gorm:"type:bigint[]" valid:"role,true"` DeleteMessagesOnKick bool KickReasonOptional bool KickMessage string `valid:"template,1900"` // Ban BanEnabled bool BanCmdRoles pq.Int64Array `gorm:"type:bigint[]" valid:"role,true"` BanReasonOptional bool BanMessage string `valid:"template,1900"` // Mute/unmute MuteEnabled bool MuteCmdRoles pq.Int64Array `gorm:"type:bigint[]" valid:"role,true"` MuteRole string `valid:"role,true"` MuteReasonOptional bool UnmuteReasonOptional bool MuteManageRole bool MuteRemoveRoles pq.Int64Array `gorm:"type:bigint[]" valid:"role,true"` MuteIgnoreChannels pq.Int64Array `gorm:"type:bigint[]" valid:"channel,true"` // Warn WarnCommandsEnabled bool WarnCmdRoles pq.Int64Array `gorm:"type:bigint[]" valid:"role,true"` WarnIncludeChannelLogs bool WarnSendToModlog bool // Misc CleanEnabled bool ReportEnabled bool ActionChannel string `valid:"channel,true"` ReportChannel string `valid:"channel,true"` LogUnbans bool LogBans bool }
func (*Config) IntActionChannel ¶
func (*Config) IntMuteRole ¶
func (*Config) IntReportChannel ¶
type ModlogAction ¶ added in v0.29.1
func (ModlogAction) String ¶ added in v0.29.1
func (m ModlogAction) String() string
type MuteModel ¶ added in v0.29.1
type MuteModel struct { common.SmallModel ExpiresAt time.Time GuildID int64 `gorm:"index"` UserID int64 AuthorID int64 Reason string RemovedRoles pq.Int64Array `gorm:"type:bigint[]"` }
type Plugin ¶
type Plugin struct{}
func (*Plugin) AddCommands ¶ added in v1.4.1
func (p *Plugin) AddCommands()
func (*Plugin) GuildMigrated ¶ added in v1.4.1
func (p *Plugin) GuildMigrated(gs *dstate.GuildState, toThisSlave bool)
type WarningModel ¶
type WarningModel struct { common.SmallModel GuildID int64 `gorm:"index"` UserID string AuthorID string // Username and discrim for author incase he/she leaves AuthorUsernameDiscrim string Message string LogsLink string }
func (*WarningModel) TableName ¶
func (w *WarningModel) TableName() string
Click to show internal directories.
Click to hide internal directories.