151 lines
3.9 KiB
Go
151 lines
3.9 KiB
Go
package manager
|
|
|
|
import (
|
|
"crypto/sha512"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thoj/go-ircevent"
|
|
|
|
cache "github.com/patrickmn/go-cache"
|
|
)
|
|
|
|
func (m *Manager) initAntiflood() {
|
|
m.cache = cache.New(1*time.Minute, 5*time.Second)
|
|
}
|
|
|
|
func (m *Manager) TrackUser(target string, source string) (shouldIgnore bool) {
|
|
key := normalizeUserAntiflood(target, source)
|
|
|
|
if _, ok := m.cache.Get(key); ok {
|
|
// User just joined here recently, ignore them
|
|
shouldIgnore = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (m *Manager) NotifyUserJoined(target string, source string) {
|
|
key := normalizeUserAntiflood(target, source)
|
|
|
|
// When a user joins, he will be ignored for the first 30 seconds,
|
|
// enough to prevent parsing links from people who only join to spam their
|
|
// links immediately
|
|
if _, exists := m.cache.Get(key); !exists {
|
|
m.cache.Add(key, nil, 30*time.Second)
|
|
}
|
|
}
|
|
|
|
func (m *Manager) TrackUrl(target string, u *url.URL) (shouldIgnore bool) {
|
|
key := normalizeUrlAntiflood(target, u)
|
|
|
|
if _, ok := m.cache.Get(key); ok {
|
|
// The URL has been used recently, should ignore
|
|
shouldIgnore = true
|
|
} else {
|
|
m.cache.Add(key, nil, cache.DefaultExpiration)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (m *Manager) TrackOutput(target, t string) (shouldNotSend bool) {
|
|
key := normalizeTextAntiflood(target, t)
|
|
|
|
if _, ok := m.cache.Get(key); ok {
|
|
// The URL has been used recently, should ignore
|
|
shouldNotSend = true
|
|
} else {
|
|
m.cache.Add(key, nil, cache.DefaultExpiration)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (m *Manager) AntifloodIrcConn(c *irc.Connection) *ircConnectionProxy {
|
|
return &ircConnectionProxy{Connection: c, m: m}
|
|
}
|
|
|
|
func normalizeUrlAntiflood(target string, u *url.URL) string {
|
|
uc := &(*u)
|
|
|
|
// Normalize host
|
|
uc.Host = strings.ToLower(uc.Host)
|
|
if strings.HasPrefix(uc.Host, "www.") {
|
|
uc.Host = uc.Host[4:]
|
|
}
|
|
|
|
// Normalize query
|
|
uc.RawQuery = uc.Query().Encode()
|
|
|
|
s := sha512.New()
|
|
s.Write([]byte(u.String()))
|
|
return fmt.Sprintf("LINK/%s/%X", strings.ToUpper(target), s.Sum([]byte{}))
|
|
}
|
|
|
|
func normalizeTextAntiflood(target, text string) string {
|
|
s := sha512.New()
|
|
s.Write([]byte(text))
|
|
return fmt.Sprintf("TEXT/%s/%X", strings.ToUpper(target), s.Sum([]byte{}))
|
|
}
|
|
|
|
func normalizeUserAntiflood(target, source string) string {
|
|
sourceSplitHost := strings.SplitN(source, "@", 2)
|
|
sourceSplitHostname := strings.Split(sourceSplitHost[1], ".")
|
|
if len(sourceSplitHostname) > 1 &&
|
|
strings.EqualFold(sourceSplitHostname[len(sourceSplitHostname)-1], "IP") {
|
|
sourceSplitHostname[0] = "*"
|
|
}
|
|
source = fmt.Sprintf("%s!%s@%s", "*", "*", strings.Join(sourceSplitHostname, "."))
|
|
return fmt.Sprintf("USER/%s/%s", strings.ToUpper(target), source)
|
|
}
|
|
|
|
// Proxies several methods of the IRC connection in order to drop repeated messages
|
|
type ircConnectionProxy struct {
|
|
*irc.Connection
|
|
|
|
m *Manager
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Action(target, message string) {
|
|
if proxy.m.TrackOutput(target, message) {
|
|
log.Printf("WARNING: Output antiflood triggered, dropping message for %s: %s", target, message)
|
|
return
|
|
}
|
|
|
|
proxy.Connection.Action(target, message)
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Actionf(target, format string, a ...interface{}) {
|
|
proxy.Action(target, fmt.Sprintf(format, a...))
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Privmsg(target, message string) {
|
|
if proxy.m.TrackOutput(target, message) {
|
|
log.Printf("WARNING: Output antiflood triggered, dropping message for %s: %s", target, message)
|
|
return
|
|
}
|
|
|
|
proxy.Connection.Privmsg(target, message)
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Privmsgf(target, format string, a ...interface{}) {
|
|
proxy.Privmsg(target, fmt.Sprintf(format, a...))
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Notice(target, message string) {
|
|
if proxy.m.TrackOutput(target, message) {
|
|
log.Printf("WARNING: Output antiflood triggered, dropping message for %s: %s", target, message)
|
|
return
|
|
}
|
|
|
|
proxy.Connection.Notice(target, message)
|
|
}
|
|
|
|
func (proxy *ircConnectionProxy) Noticef(target, format string, a ...interface{}) {
|
|
proxy.Notice(target, fmt.Sprintf(format, a...))
|
|
}
|