Expand upon flood detection and chat rate limiting. Closes #1349

This commit is contained in:
Gabe Kangas 2021-08-27 14:43:09 -07:00
parent 99a3aae843
commit e3b0af1b67

View File

@ -25,6 +25,8 @@ type ChatClient struct {
// Buffered channel of outbound messages.
send chan []byte
rateLimiter *rate.Limiter
timeoutTimer *time.Timer
inTimeout bool
Geo *geoip.GeoDetails `json:"geo"`
MessageCount int `json:"messageCount"`
UserAgent string `json:"userAgent"`
@ -72,7 +74,9 @@ func (c *ChatClient) sendConnectedClientInfo() {
}
func (c *ChatClient) readPump() {
c.rateLimiter = rate.NewLimiter(0.6, 5)
// Allow 3 messages every two seconds.
limit := rate.Every(2 * time.Second / 3)
c.rateLimiter = rate.NewLimiter(limit, 1)
defer func() {
c.close()
@ -100,8 +104,16 @@ func (c *ChatClient) readPump() {
continue
}
// Check if this client is temporarily blocked from sending messages.
if c.inTimeout {
continue
}
// Guard against floods.
if !c.passesRateLimit() {
log.Warnln("Client", c.id, c.User.DisplayName, "has exceeded the messaging rate limiting thresholds and messages are being rejected temporarily.")
c.startChatRejectionTimeout()
continue
}
@ -171,12 +183,24 @@ func (c *ChatClient) close() {
}
func (c *ChatClient) passesRateLimit() bool {
if !c.rateLimiter.Allow() {
log.Debugln("Client", c.id, c.User.DisplayName, "has exceeded the messaging rate limiting thresholds.")
return false
return c.rateLimiter.Allow() && !c.inTimeout
}
func (c *ChatClient) startChatRejectionTimeout() {
if c.timeoutTimer != nil {
return
}
return true
c.inTimeout = true
c.timeoutTimer = time.NewTimer(10 * time.Second)
go func(c *ChatClient) {
for range c.timeoutTimer.C {
c.inTimeout = false
c.timeoutTimer = nil
}
}(c)
c.sendAction("You are temporarily blocked from sending chat messages due to perceived flooding.")
}
func (c *ChatClient) sendPayload(payload events.EventPayload) {