.BANCHO. Add spam protection, better silence handling

This commit is contained in:
Nyo 2016-06-07 22:46:31 +02:00
parent c8f9825e6b
commit ef35697772
7 changed files with 103 additions and 17 deletions

View File

@ -62,3 +62,9 @@ class apiException(Exception):
class invalidArgumentsException(Exception): class invalidArgumentsException(Exception):
pass pass
class messageTooLongWarnException(Exception):
pass
class messageTooLongException(Exception):
pass

View File

@ -164,16 +164,10 @@ def silence(fro, chan, message):
if silenceTime > 604800: if silenceTime > 604800:
return "Invalid silence time. Max silence time is 7 days." return "Invalid silence time. Max silence time is 7 days."
# Calculate silence end time
endTime = int(time.time())+silenceTime
# Update silence end in db
userHelper.silence(targetUserID, endTime, reason)
# Send silence packet to target if he's connected # Send silence packet to target if he's connected
targetToken = glob.tokens.getTokenFromUsername(target) targetToken = glob.tokens.getTokenFromUsername(target)
if targetToken != None: if targetToken != None:
targetToken.enqueue(serverPackets.silenceEndTime(silenceTime)) targetToken.silence(silenceTime, reason)
# Log message # Log message
msg = "{} has been silenced for the following reason: {}".format(target, reason) msg = "{} has been silenced for the following reason: {}".format(target, reason)

View File

@ -171,6 +171,9 @@ def channelInfoEnd():
def channelKicked(chan): def channelKicked(chan):
return packetHelper.buildPacket(packetIDs.server_channelKicked, [[chan, dataTypes.string]]) return packetHelper.buildPacket(packetIDs.server_channelKicked, [[chan, dataTypes.string]])
def userSilenced(userID):
return packetHelper.buildPacket(packetIDs.server_userSilenced, [[userID, dataTypes.uInt32]])
""" Spectator packets """ """ Spectator packets """
def addSpectator(userID): def addSpectator(userID):

View File

@ -7,7 +7,9 @@ from objects import fokabot
from constants import exceptions from constants import exceptions
from constants import messageTemplates from constants import messageTemplates
from helpers import generalFunctions from helpers import generalFunctions
from helpers import userHelper
from helpers import logHelper as log from helpers import logHelper as log
import time
def handle(userToken, packetData): def handle(userToken, packetData):
""" """
@ -20,10 +22,18 @@ def handle(userToken, packetData):
try: try:
# Get usertoken username # Get usertoken username
username = userToken.username username = userToken.username
userID = userToken.userID
# Private message packet # Private message packet
packetData = clientPackets.sendPrivateMessage(packetData) packetData = clientPackets.sendPrivateMessage(packetData)
# Check message length
if len(packetData["message"]) > 256:
if userToken.longMessageWarning == True:
raise exceptions.messageTooLongException
else:
raise exceptions.messageTooLongWarnException
if packetData["to"] == "FokaBot": if packetData["to"] == "FokaBot":
# FokaBot command check # FokaBot command check
fokaMessage = fokabot.fokabotResponse(username, packetData["to"], packetData["message"]) fokaMessage = fokabot.fokabotResponse(username, packetData["to"], packetData["message"])
@ -47,12 +57,18 @@ def handle(userToken, packetData):
if token.awayMessage != "": if token.awayMessage != "":
userToken.enqueue(serverPackets.sendMessage(packetData["to"], username, "This user is away: {}".format(token.awayMessage))) userToken.enqueue(serverPackets.sendMessage(packetData["to"], username, "This user is away: {}".format(token.awayMessage)))
# Spam protection
userToken.spamProtection()
# Console and file output # Console and file output
log.pm("{} -> {}: {}".format(username, packetData["to"], packetData["message"])) log.pm("{} -> {}: {}".format(username, packetData["to"], packetData["message"]))
# Log to chatlog_private
#with open(".data/chatlog_private.txt", "a") as f:
# f.write("[{date}] {fro} -> {to}: {message}\n".format(date=generalFunctions.getTimestamp(), fro=username, to=packetData["to"], message=str(packetData["message"].encode("utf-8"))[2:-1]))
except exceptions.tokenNotFoundException: except exceptions.tokenNotFoundException:
# Token not found, user disconnected # Token not found, user disconnected
log.warning("{} tried to send a message to {}, but their token couldn't be found".format(username, packetData["to"])) log.warning("{} tried to send a message to {}, but their token couldn't be found".format(username, packetData["to"]))
except exceptions.messageTooLongWarnException:
# Message > 256 warn
userToken.longMessageWarning = True
userToken.enqueue(serverPackets.sendMessage("FokaBot", username, "Your message was too long and has not been sent. Please keep your messages under 256 characters. This is your last warning."))
except exceptions.messageTooLongException:
# Message > 256 silence
userToken.silence(2*3600, "Sending messages longer than 256 characters")

View File

@ -5,6 +5,8 @@ from objects import fokabot
from constants import serverPackets from constants import serverPackets
from helpers import discordBotHelper from helpers import discordBotHelper
from helpers import logHelper as log from helpers import logHelper as log
from helpers import userHelper
import time
def handle(userToken, packetData): def handle(userToken, packetData):
""" """
@ -26,6 +28,14 @@ def handle(userToken, packetData):
# Receivers # Receivers
who = [] who = []
# Check message length
if len(packetData["message"]) > 256:
if userToken.longMessageWarning == True:
raise exceptions.messageTooLongException
else:
raise exceptions.messageTooLongWarnException
# Get receivers list
# Check #spectator # Check #spectator
if packetData["to"] == "#spectator": if packetData["to"] == "#spectator":
# Spectator channel # Spectator channel
@ -87,7 +97,7 @@ def handle(userToken, packetData):
if userID in who: if userID in who:
who.remove(userID) who.remove(userID)
# We have receivers
# Send packet to required users # Send packet to required users
glob.tokens.multipleEnqueue(serverPackets.sendMessage(username, packetData["to"], packetData["message"]), who, False) glob.tokens.multipleEnqueue(serverPackets.sendMessage(username, packetData["to"], packetData["message"]), who, False)
@ -98,6 +108,9 @@ def handle(userToken, packetData):
glob.tokens.multipleEnqueue(serverPackets.sendMessage("FokaBot", packetData["to"], fokaMessage), who, False) glob.tokens.multipleEnqueue(serverPackets.sendMessage("FokaBot", packetData["to"], fokaMessage), who, False)
log.chat("FokaBot @ {}: {}".format(packetData["to"], str(fokaMessage.encode("UTF-8")))) log.chat("FokaBot @ {}: {}".format(packetData["to"], str(fokaMessage.encode("UTF-8"))))
# Spam protection
userToken.spamProtection()
# Console and file log # Console and file log
log.chat("{fro} @ {to}: {message}".format(fro=username, to=packetData["to"], message=str(packetData["message"].encode("utf-8")))) log.chat("{fro} @ {to}: {message}".format(fro=username, to=packetData["to"], message=str(packetData["message"].encode("utf-8"))))
@ -109,3 +122,10 @@ def handle(userToken, packetData):
log.warning("{} tried to send a message to an unknown channel ({})".format(username, packetData["to"])) log.warning("{} tried to send a message to an unknown channel ({})".format(username, packetData["to"]))
except exceptions.channelNoPermissionsException: except exceptions.channelNoPermissionsException:
log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, packetData["to"])) log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, packetData["to"]))
except exceptions.messageTooLongWarnException:
# Message > 256 warn
userToken.longMessageWarning = True
userToken.enqueue(serverPackets.sendMessage("FokaBot", username, "Your message was too long and has not been sent. Please keep your messages under 256 characters. This is your last warning."))
except exceptions.messageTooLongException:
# Message > 256 silence
userToken.silence(2*3600, "Sending messages longer than 256 characters")

View File

@ -1,12 +1,12 @@
import uuid
from constants import actions from constants import actions
from constants import gameModes from constants import gameModes
from helpers import userHelper from helpers import userHelper
import time
from helpers import consoleHelper
from constants import bcolors
from constants import serverPackets from constants import serverPackets
from events import logoutEvent from events import logoutEvent
from helpers import logHelper as log
from objects import glob
import uuid
import time
import threading import threading
class token: class token:
@ -95,6 +95,12 @@ class token:
self.tillerino = [0,0,-1.0] # beatmap, mods, acc self.tillerino = [0,0,-1.0] # beatmap, mods, acc
self.queue = bytes() self.queue = bytes()
self.longMessageWarning = False
# Spam protection
self.spamRate = 0
self.lastMessagetime = 0
# Generate/set token # Generate/set token
if __token != None: if __token != None:
self.token = __token self.token = __token
@ -229,9 +235,47 @@ class token:
def kick(self): def kick(self):
"""Kick this user from the server""" """Kick this user from the server"""
# Send packet to target # Send packet to target
consoleHelper.printColored("> {} has been disconnected. (kick)".format(self.username), bcolors.YELLOW) log.info("{} has been disconnected. (kick)".format(self.username))
self.enqueue(serverPackets.notification("You have been kicked from the server. Please login again.")) self.enqueue(serverPackets.notification("You have been kicked from the server. Please login again."))
self.enqueue(serverPackets.loginFailed()) self.enqueue(serverPackets.loginFailed())
# Logout event # Logout event
logoutEvent.handle(self, None) logoutEvent.handle(self, None)
def silence(self, seconds, reason):
"""
Silences this user (both db and packet)
seconds -- silence length in seconds
reason -- silence reason
"""
# Silence user in db
userHelper.silence(self.userID, int(time.time())+seconds, reason)
# Send silence packet to target
self.enqueue(serverPackets.silenceEndTime(seconds))
# Send silenced packet to everyone else
glob.tokens.enqueueAll(serverPackets.userSilenced(self.userID))
# Log
log.info("{} has been silenced for {} seconds for the following reason: {}".format(self.username, seconds, reason), True)
def spamProtection(self, increaseSpamRate = True):
"""
Silences the user if is spamming.
increaseSpamRate -- pass True if the user has sent a new message. Optional. Default: True
"""
if increaseSpamRate == True:
# Reset spam rate every 10 seconds
if int(time.time())-self.lastMessagetime >= 10:
self.spamRate = 0
else:
self.spamRate += 1
# Update last message time
self.lastMessagetime = time.time()
# Silence the user if needed
if self.spamRate > 10:
self.silence(1800, "Spamming (auto spam protection)")

View File

@ -2,6 +2,9 @@ from objects import osuToken
import time import time
import threading import threading
from events import logoutEvent from events import logoutEvent
from helpers import logHelper as log
from constants import serverPackets
from helpers import userHelper
class tokenList: class tokenList:
""" """