start building out automatic spam detection, push some stale changes, very incomplete

auto_spam_detection
Dallas Winger 2024-05-09 20:44:38 -04:00
parent e8f401801c
commit 574edde030
No known key found for this signature in database
GPG Key ID: 59CE387CBF3FCA01
6 changed files with 218 additions and 25 deletions

View File

@ -204,11 +204,15 @@ func (c *Commands) ProcessMessage(s *discordgo.Session, m interface{}) {
switch m.(type) {
case *discordgo.MessageCreate:
// Pass Messages to the command processor
track(m.(*discordgo.MessageCreate).Message)
err := c.ProcessCommand(s, m.(*discordgo.MessageCreate))
if err != nil {
log.Println("[!] Error: " + err.Error())
}
break
case *discordgo.MessageEdit:
//TODO logging
break
case *discordgo.MessageDelete:
// Log deleted messages to the logging channel.
err := c.ProcessMessageDelete(s, m.(*discordgo.MessageDelete))

View File

@ -2,12 +2,11 @@ package commands
import (
"errors"
"github.com/bwmarrin/discordgo"
"github.com/foxtrot/scuzzy/actions"
"strconv"
"strings"
"time"
"github.com/bwmarrin/discordgo"
"github.com/foxtrot/scuzzy/actions"
)
func (c *Commands) handleSetSlowmode(s *discordgo.Session, m *discordgo.MessageCreate) error {

116
commands/monitoring.go Normal file
View File

@ -0,0 +1,116 @@
package commands
import (
"github.com/bwmarrin/discordgo"
"github.com/foxtrot/scuzzy/models"
"hash/fnv"
"time"
)
var Tracking map[uint32]models.TrackedMessage
var windowToConsiderIrrelevant = time.Minute * 5
var windowToConsiderDuplicate = time.Second * 30
var crossChannelSpamCountThreshold = 1
var spamCountThreshold = 3
func hashContent(s string) uint32 {
h := fnv.New32a()
_, err := h.Write([]byte(s))
if err != nil {
return 0
}
return h.Sum32()
}
func track(m *discordgo.Message) bool {
//todo ignore self bot
hash := hashContent(m.Content)
record, exists := Tracking[hash]
if !exists {
newMessage := models.TrackedMessage{
Author: m.Author,
Instances: map[string]models.CachedMessage{},
MessageContent: m.Content,
}
newMessage.Instances[m.ChannelID] = models.CachedMessage{
ID: m.ID,
CreatedAt: time.Now(),
Count: 1,
}
Tracking[hash] = newMessage
} else {
//ModerationReason := "[DELETED + TIMEOUT] Multiple of the same exact message sent repeatedly in the same channel within a short period of time is considered spam and was removed"
chanMessage, existsInChannel := record.Instances[m.ChannelID]
if existsInChannel {
chanMessage.Count++
record.Instances[m.ChannelID] = chanMessage
Tracking[hash] = record
if time.Since(chanMessage.CreatedAt) > windowToConsiderIrrelevant {
// this tracking has "expired"
delete(record.Instances, m.ChannelID)
Tracking[hash] = record
return false
}
countViolation := chanMessage.Count > spamCountThreshold
frequencyViolation := time.Since(chanMessage.CreatedAt) < windowToConsiderDuplicate
switch true {
case countViolation && frequencyViolation:
// remove obviously spam qualified by both frequency and quantity
cleanupDuplicates(record.Instances)
timeoutUser()
return true
case countViolation:
cleanupDuplicates(record.Instances)
return true
case frequencyViolation:
cleanupDuplicates(record.Instances)
return true
default:
warnUser()
return false
}
} else {
channelCount := 0
smallestTimeSince := time.Duration(10000000)
for _, _ = range record.Instances {
channelCount++
since := time.Since(chanMessage.CreatedAt)
if smallestTimeSince < since {
smallestTimeSince = since
}
}
countViolation := channelCount > crossChannelSpamCountThreshold
frequencyViolation := smallestTimeSince < windowToConsiderDuplicate
switch true {
case countViolation && frequencyViolation:
// remove obviously spam qualified by both frequency and quantity
cleanupDuplicates(record.Instances)
timeoutUser()
return true
case countViolation:
cleanupDuplicates(record.Instances)
return true
case frequencyViolation:
cleanupDuplicates(record.Instances)
return true
default:
warnUser()
return false
}
}
}
return false
}
func cleanupDuplicates(instances map[string]models.CachedMessage) {
}
func warnUser() {
}
func timeoutUser() {
}

View File

@ -11,6 +11,46 @@ import (
"strings"
)
// TODO for use in command arguments
/* Match on one two or three word arguments*/
func aliasMatch(product models.Product, args []string) bool {
return strings.Contains(strings.Join(product.Aliases[:], " "), args[1]) ||
strings.Contains(strings.Join(product.Aliases, " "), strings.Join(args[1:3], " ")) ||
strings.Contains(strings.Join(product.Aliases, " "), strings.Join(args[1:4], " "))
}
func (c *Commands) matchAlias(args []string) models.Product {
switch true {
case aliasMatch(models.Ducky, args):
return models.Ducky
case aliasMatch(models.PayloadStudio, args):
return models.PayloadStudio
case aliasMatch(models.Bunny, args):
return models.Bunny
case aliasMatch(models.Croc, args):
return models.Croc
case aliasMatch(models.Shark, args):
return models.Shark
case aliasMatch(models.CloudC2, args):
return models.CloudC2
case aliasMatch(models.Crab, args):
return models.Crab
case aliasMatch(models.Pineapple, args):
return models.Pineapple
case aliasMatch(models.Coconut, args):
return models.Coconut
case aliasMatch(models.Squirrel, args):
return models.Squirrel
case aliasMatch(models.Turtle, args):
return models.Turtle
case aliasMatch(models.OMG, args):
return models.OMG
default:
return models.Default
}
}
// why i didnt just use a map? because im dumb
func (c *Commands) getProductFromChannelID(cid string) models.Product {
switch cid {
case models.Ducky.ChannelID:

18
models/monitoring.go Normal file
View File

@ -0,0 +1,18 @@
package models
import (
"github.com/bwmarrin/discordgo"
"time"
)
type CachedMessage struct {
ID string
CreatedAt time.Time
Count int
}
type TrackedMessage struct {
Author *discordgo.User `json:"author"`
Instances map[string]CachedMessage
MessageContent string
}

View File

@ -2,6 +2,7 @@ package models
type Product struct {
ProductName string `json:"productName"`
Aliases []string `json:"aliases"`
Urllabel string `json:"urllabel"`
Emoji string `json:"logo"`
ChannelID string `json:"channelid"`
@ -12,6 +13,7 @@ type Product struct {
Githublink string `json:"githublink"`
InviteLink string `json:"invitelink"`
SupportLink string `json:"supportlink"`
GettingStarted string `json:"gettingstarted"`
}
var GITBOOKAPI = "https://api.gitbook.com/v1/spaces/"
@ -19,6 +21,7 @@ var SEARCHENDPOINT = "/search/ask"
var Default = Product{
ProductName: "",
Aliases: []string{},
Urllabel: "",
Emoji: "<:hak5:1063661436187975701>",
ChannelID: "824326326076702770",
@ -29,10 +32,12 @@ var Default = Product{
Githublink: "https://github.com/hak5",
InviteLink: "https://discord.gg/hak5",
SupportLink: "https://hak5.org/support",
GettingStarted: "https://docs.hak5.org",
}
var Ducky = Product{
ProductName: "USB Rubber Ducky",
Aliases: []string{"usb rubber ducky", "ducky", "duck", "rubber ducky"},
Urllabel: "usb-rubber-ducky",
Emoji: "<:Rubber_Ducky:1063661661795385384>",
ChannelID: "522275837651714048",
@ -48,6 +53,7 @@ var Ducky = Product{
var PayloadStudio = Product{
ProductName: "PayloadStudio",
Urllabel: Default.Urllabel,
Aliases: []string{"PayloadStudio", "PS", "PSP", "payload studio"},
Emoji: "<:payload_studio:1053210968064262234>",
ChannelID: "1006233482957164634",
SpaceID: "RgTCQkzfO7AUWTT3gFAq",
@ -61,6 +67,7 @@ var PayloadStudio = Product{
var CloudC2 = Product{
ProductName: "Cloud C2",
Urllabel: Default.Urllabel,
Aliases: []string{"CloudC2", "Cloud C2", "C2"},
Emoji: "<:Cloud_C2:1063661578181943336>",
ChannelID: "522276096746717184",
SpaceID: "<:Cloud_C2:1063661578181943336>",
@ -74,6 +81,7 @@ var CloudC2 = Product{
var Crab = Product{
ProductName: "Screen Crab",
Urllabel: Default.Urllabel,
Aliases: []string{"screen crab", "crab"},
Emoji: "<:Screen_Crab:1063661831756988427>",
ChannelID: "608057573517557809",
SpaceID: "-MiWySN4BHDJlUatEfm3",
@ -87,6 +95,7 @@ var Crab = Product{
var Coconut = Product{
ProductName: "WiFi Coconut",
Urllabel: Default.Urllabel,
Aliases: []string{"wifi coconut", "coconut"},
Emoji: "<:WiFi_Coconut:1063661830309953606>",
ChannelID: "1007363521098551349",
SpaceID: "DkilLranx3TNqKgzbBZ9",
@ -100,6 +109,7 @@ var Coconut = Product{
var Pineapple = Product{
ProductName: "WiFi Pineapple",
Urllabel: Default.Urllabel,
Aliases: []string{"wifi pineapple", "pineapple"},
Emoji: "<:WiFi_Pineapple:1063661628207411251>",
ChannelID: "522275782731497473",
SpaceID: "-Mhuhsyl_byoEWXOc5EU",
@ -114,6 +124,7 @@ var Croc = Product{
ProductName: "Key Croc",
Urllabel: "key-croc",
Emoji: "<:Key_Croc:1063661834093199431>",
Aliases: []string{"key croc", "croc"},
ChannelID: "709235715120168970",
SpaceID: "-MhLOzjhonMdC6SLKqRt",
Docslink: "https://docs.hak5.org/key-croc/",
@ -127,6 +138,7 @@ var Bunny = Product{
ProductName: "Bash Bunny",
Urllabel: "bash-bunny",
Emoji: "<:Bash_Bunny:1063661828753858680>",
Aliases: []string{"bash bunny", "bunny", "bb"},
ChannelID: "1009919913877590146",
SpaceID: "nxJgJ9UdPfrcuL1U8DpL",
Docslink: "https://docs.hak5.org/bash-bunny/",
@ -140,6 +152,7 @@ var Squirrel = Product{
ProductName: "Packet Squirrel",
Urllabel: "packet-squirrel",
Emoji: "<:packet_squirrel:1063661837398315079>",
Aliases: []string{"packet squirrel", "squirrel"},
ChannelID: "522275913031745548",
SpaceID: "-MiX86qQFvjhg-a6RwWr",
Docslink: "https://docs.hak5.org/packet-squirrel",
@ -152,6 +165,7 @@ var Squirrel = Product{
var Turtle = Product{
ProductName: "LAN Turtle",
Urllabel: "lan-turtle",
Aliases: []string{"lan turtle", "turtle"},
Emoji: "<:lan_turtle:1063661838937620480>",
ChannelID: "522275913031745548",
SpaceID: "N8g6UIGOyv4mW7rOOuC8",
@ -165,6 +179,7 @@ var Turtle = Product{
var Shark = Product{
ProductName: "Shark Jack",
Urllabel: "shark-jack",
Aliases: []string{"shark jack", "shark"},
Emoji: "<:Shark_Jack:1063661835888381952>",
ChannelID: "610344558655438858",
SpaceID: "-MhxW6geyenAGJvaKW11",
@ -178,6 +193,7 @@ var Shark = Product{
var OMG = Product{
ProductName: "O.MG Devices",
Urllabel: "omg",
Aliases: []string{"omg", "omg cable", "omg adapter", "omg plug", "plug", "cable"},
Emoji: "<:omg:1063696145022468116>",
ChannelID: "953129985131040838",
SpaceID: Default.SpaceID,