<commit_before>package sweetiebot

import (
	"strconv"
	"strings"
	"time"

	"github.com/bwmarrin/discordgo"
)

// The emote module detects banned emotes and deletes them
type SpamModule struct {
	tracker     map[uint64]*SaturationLimit
	lastraid    int64
	AutoSilence int8
}

func (w *SpamModule) Name() string {
	return "Anti-Spam"
}

func (w *SpamModule) Register(info *GuildInfo) {
	w.tracker = make(map[uint64]*SaturationLimit)
	w.lastraid = 0
	w.AutoSilence = 0
	info.hooks.OnMessageCreate = append(info.hooks.OnMessageCreate, w)
	info.hooks.OnMessageUpdate = append(info.hooks.OnMessageUpdate, w)
	info.hooks.OnCommand = append(info.hooks.OnCommand, w)
	info.hooks.OnGuildMemberAdd = append(info.hooks.OnGuildMemberAdd, w)
	info.hooks.OnGuildMemberUpdate = append(info.hooks.OnGuildMemberUpdate, w)
}

func SilenceMember(u *discordgo.User, info *GuildInfo) {
	// Manually set our internal state to say this user has the Silent role, to prevent race conditions
	m, err := sb.dg.State.Member(info.Guild.ID, u.ID)
	if err == nil {
		srole := strconv.FormatUint(info.config.SilentRole, 10)
		for _, v := range m.Roles {
			if v == srole {
				return // Spammer was already killed, so don't try killing it again
			}
		}
		m.Roles = append(m.Roles, srole)
	} else {
		info.log.Log("Tried to kill spammer ", u.Username, " but they were already banned??? (Error: ", err.Error(), ")")
		return
	}
	sb.dg.GuildMemberEdit(info.Guild.ID, u.ID, m.Roles) // Tell discord to make this spammer silent
}
func KillSpammer(u *discordgo.User, info *GuildInfo, msg *discordgo.Message, reason string) {
	info.log.Log("Killing spammer ", u.Username, ". Last message sent: \n", msg.ContentWithMentionsReplaced())
	SilenceMember(u, info)

	if sb.IsMainGuild(info) {
		messages := sb.db.GetRecentMessages(SBatoi(u.ID), 60) // Retrieve all messages in the past 60 seconds and delete them.

		for _, v := range messages {
			sb.dg.ChannelMessageDelete(strconv.FormatUint(v.channel, 10), strconv.FormatUint(v.message, 10))
		}
	}

	info.SendMessage(strconv.FormatUint(info.config.ModChannel, 10), "Alert: <@"+u.ID+"> was silenced for "+reason+". Please investigate.") // Alert admins
}
func (w *SpamModule) CheckSpam(info *GuildInfo, m *discordgo.Message) bool {
	if m.Author != nil {
		if info.UserHasRole(m.Author.ID, strconv.FormatUint(info.config.SilentRole, 10)) {
			sb.dg.ChannelMessageDelete(m.ChannelID, m.ID)
			return true
		}
		id := SBatoi(m.Author.ID)
		_, ok := w.tracker[id]
		if !ok {
			w.tracker[id] = &SaturationLimit{make([]int64, 20, 20), 0, AtomicFlag{0}}
		}
		limit := w.tracker[id]
		limit.append(time.Now().UTC().Unix())
		if limit.checkafter(5, 1) || limit.checkafter(7, 4) || limit.checkafter(10, 9) {
			KillSpammer(m.Author, info, m, "spamming too many messages")
			return true
		}
		if len(m.Mentions) > 4 {
			KillSpammer(m.Author, info, m, "pinging too many people")<commit_msg>Fix mention count.<commit_after>package sweetiebot

import (
	"strconv"
	"strings"
	"time"

	"github.com/bwmarrin/discordgo"
)

// The emote module detects banned emotes and deletes them
type SpamModule struct {
	tracker     map[uint64]*SaturationLimit
	lastraid    int64
	AutoSilence int8
}

func (w *SpamModule) Name() string {
	return "Anti-Spam"
}

func (w *SpamModule) Register(info *GuildInfo) {
	w.tracker = make(map[uint64]*SaturationLimit)
	w.lastraid = 0
	w.AutoSilence = 0
	info.hooks.OnMessageCreate = append(info.hooks.OnMessageCreate, w)
	info.hooks.OnMessageUpdate = append(info.hooks.OnMessageUpdate, w)
	info.hooks.OnCommand = append(info.hooks.OnCommand, w)
	info.hooks.OnGuildMemberAdd = append(info.hooks.OnGuildMemberAdd, w)
	info.hooks.OnGuildMemberUpdate = append(info.hooks.OnGuildMemberUpdate, w)
}

func SilenceMember(u *discordgo.User, info *GuildInfo) {
	// Manually set our internal state to say this user has the Silent role, to prevent race conditions
	m, err := sb.dg.State.Member(info.Guild.ID, u.ID)
	if err == nil {
		srole := strconv.FormatUint(info.config.SilentRole, 10)
		for _, v := range m.Roles {
			if v == srole {
				return // Spammer was already killed, so don't try killing it again
			}
		}
		m.Roles = append(m.Roles, srole)
	} else {
		info.log.Log("Tried to kill spammer ", u.Username, " but they were already banned??? (Error: ", err.Error(), ")")
		return
	}
	sb.dg.GuildMemberEdit(info.Guild.ID, u.ID, m.Roles) // Tell discord to make this spammer silent
}
func KillSpammer(u *discordgo.User, info *GuildInfo, msg *discordgo.Message, reason string) {
	info.log.Log("Killing spammer ", u.Username, ". Last message sent: \n", msg.ContentWithMentionsReplaced())
	SilenceMember(u, info)

	if sb.IsMainGuild(info) {
		messages := sb.db.GetRecentMessages(SBatoi(u.ID), 60) // Retrieve all messages in the past 60 seconds and delete them.

		for _, v := range messages {
			sb.dg.ChannelMessageDelete(strconv.FormatUint(v.channel, 10), strconv.FormatUint(v.message, 10))
		}
	}

	info.SendMessage(strconv.FormatUint(info.config.ModChannel, 10), "Alert: <@"+u.ID+"> was silenced for "+reason+". Please investigate.") // Alert admins
}
func (w *SpamModule) CheckSpam(info *GuildInfo, m *discordgo.Message) bool {
	if m.Author != nil {
		if info.UserHasRole(m.Author.ID, strconv.FormatUint(info.config.SilentRole, 10)) {
			sb.dg.ChannelMessageDelete(m.ChannelID, m.ID)
			return true
		}
		id := SBatoi(m.Author.ID)
		_, ok := w.tracker[id]
		if !ok {
			w.tracker[id] = &SaturationLimit{make([]int64, 20, 20), 0, AtomicFlag{0}}
		}
		limit := w.tracker[id]
		limit.append(time.Now().UTC().Unix())
		if limit.checkafter(5, 1) || limit.checkafter(7, 4) || limit.checkafter(10, 9) {
			KillSpammer(m.Author, info, m, "spamming too many messages")
			return true
		}
		if len(m.Mentions) > 24 {
			KillSpammer(m.Author, info, m, "pinging too many people")
{'fraction_non_alphanumeric': 0.14115411195577057, 'fraction_numerical': 0.013648928818244643, 'mean_word_length': 4.234177215189874, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4500832', 'n_tokens_mistral': 2310, 'n_tokens_neox': 2128, 'n_words': 501}