|
|
|
@ -25,6 +25,8 @@ type ChatClient struct {
@@ -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() {
@@ -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() {
@@ -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() {
@@ -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) { |
|
|
|
|