Detect chat flooding by counting lines

Reject comments by line count. Ratelimit users by number of lines sent
in chat.
このコミットが含まれているのは:
n9k 2022-05-28 05:37:41 +00:00
コミット 542d6c9ae5
6個のファイルの変更52行の追加9行の削除

ファイルの表示

@ -66,8 +66,10 @@ def create_app(config_file):
'CHAT_NAME_MIN_CONTRAST': config['chat']['min_name_contrast'],
'CHAT_BACKGROUND_COLOUR': color_to_colour(config['chat']['background_color']),
'CHAT_LEGACY_TRIPCODE_ALGORITHM': config['chat']['legacy_tripcode_algorithm'],
'FLOOD_DURATION': config['flood']['duration'],
'FLOOD_THRESHOLD': config['flood']['threshold'],
'FLOOD_MESSAGE_DURATION': config['flood']['messages']['duration'],
'FLOOD_MESSAGE_THRESHOLD': config['flood']['messages']['threshold'],
'FLOOD_LINE_DURATION': config['flood']['lines']['duration'],
'FLOOD_LINE_THRESHOLD': config['flood']['lines']['threshold'],
'CAPTCHA_LIFETIME': config['captcha']['lifetime'],
'CAPTCHA_FONTS': config['captcha']['fonts'],
'CAPTCHA_ALPHABET': config['captcha']['alphabet'],

ファイルの表示

@ -8,7 +8,7 @@ from quart import current_app, escape
from anonstream.broadcast import broadcast, broadcast_users_update
from anonstream.helpers.chat import generate_nonce_hash, get_scrollback
from anonstream.utils.chat import get_message_for_websocket
from anonstream.utils.chat import get_message_for_websocket, get_approx_linespan
CONFIG = current_app.config
MESSAGES_BY_ID = current_app.messages_by_id
@ -33,6 +33,26 @@ def add_chat_message(user, nonce, comment, ignore_empty=False):
if ignore_empty and len(comment) == 0:
return False
timestamp_ms = time.time_ns() // 1_000_000
timestamp = timestamp_ms // 1000
# Check user
while user['linespan']:
linespan_timestamp, _ = user['linespan'][0]
if timestamp - linespan_timestamp >= CONFIG['FLOOD_LINE_DURATION']:
user['linespan'].popleft()
else:
break
total_recent_linespan = sum(map(
lambda linespan_tuple: linespan_tuple[1],
user['linespan'],
))
if total_recent_linespan > CONFIG['FLOOD_LINE_THRESHOLD']:
raise Rejected(
f'Chat overuse in the last '
f'{CONFIG["FLOOD_LINE_DURATION"]:.0f} seconds'
)
# Check message
message_id = generate_nonce_hash(nonce)
if message_id in MESSAGES_BY_ID:
@ -41,10 +61,18 @@ def add_chat_message(user, nonce, comment, ignore_empty=False):
raise Rejected('Message was empty')
if len(comment) > 512:
raise Rejected('Message exceeded 512 chars')
if comment.count('\n') + 1 > 12:
raise Rejected('Message exceeded 12 lines')
linespan = get_approx_linespan(comment)
if linespan > 12:
raise Rejected('Message would span too many lines')
# Record linespan
linespan_tuple = (timestamp, linespan)
user['linespan'].append(linespan_tuple)
# Create and add message
timestamp_ms = time.time_ns() // 1_000_000
timestamp = timestamp_ms // 1000
try:
last_message = next(reversed(MESSAGES))
except StopIteration:

ファイルの表示

@ -3,7 +3,7 @@
import hashlib
import base64
from collections import OrderedDict
from collections import deque, OrderedDict
from math import inf
from quart import current_app
@ -45,6 +45,7 @@ def generate_user(timestamp, token, broadcaster, presence):
'watching': -inf,
},
'presence': presence,
'linespan': deque(),
}
def get_default_name(user):

ファイルの表示

@ -152,12 +152,12 @@ def deverify(timestamp, user):
n_user_messages = 0
for message in reversed(MESSAGES):
message_sent_ago = timestamp - message['timestamp']
if message_sent_ago >= CONFIG['FLOOD_DURATION']:
if message_sent_ago >= CONFIG['FLOOD_MESSAGE_DURATION']:
break
elif message['token'] == user['token']:
n_user_messages += 1
if n_user_messages >= CONFIG['FLOOD_THRESHOLD']:
if n_user_messages >= CONFIG['FLOOD_MESSAGE_THRESHOLD']:
user['verified'] = False
def _update_presence(timestamp, user):

ファイルの表示

@ -3,6 +3,7 @@
import base64
import hashlib
import math
import secrets
class NonceReuse(Exception):
@ -18,3 +19,10 @@ def get_message_for_websocket(user, message):
**{key: message[key] for key in message_keys},
**{key: user[key] for key in user_keys},
}
def get_approx_linespan(text):
def height(line):
return math.ceil(len(line) / 48)
linespan = sum(map(height, text.splitlines()))
linespan = linespan if linespan > 0 else 1
return linespan

ファイルの表示

@ -49,10 +49,14 @@ min_name_contrast = 3.0
background_color = "#232327"
legacy_tripcode_algorithm = false
[flood]
[flood.messages]
duration = 20.0
threshold = 4
[flood.lines]
duration = 20.0
threshold = 20
[thresholds]
user_notwatching = 8.0
user_tentative = 20.0