anonstream/anonstream/chat.py

153 行
4.7 KiB
Python
Raw 通常表示 履歴

2022-06-16 10:12:37 +09:00
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
2022-03-07 23:51:59 +09:00
# SPDX-License-Identifier: AGPL-3.0-or-later
import time
from datetime import datetime
2022-02-18 10:32:34 +09:00
from quart import current_app, escape
from anonstream.broadcast import broadcast, broadcast_users_update
from anonstream.events import notify_event_sockets
2022-08-01 07:56:34 +09:00
from anonstream.helpers.chat import generate_nonce_hash, get_scrollback
from anonstream.helpers.emote import insert_emotes
from anonstream.utils.chat import get_message_for_websocket, get_approx_linespan
2022-02-18 10:32:34 +09:00
CONFIG = current_app.config
2022-02-18 10:32:34 +09:00
MESSAGES_BY_ID = current_app.messages_by_id
MESSAGES = current_app.messages
USERS_BY_TOKEN = current_app.users_by_token
USERS = current_app.users
class Rejected(ValueError):
pass
def get_all_messages_for_websocket():
2022-02-18 10:32:34 +09:00
return list(map(
lambda message: get_message_for_websocket(
2022-02-18 10:32:34 +09:00
user=USERS_BY_TOKEN[message['token']],
message=message,
),
get_scrollback(MESSAGES),
2022-02-18 10:32:34 +09:00
))
def add_chat_message(user, nonce, comment, ignore_empty=False):
# Special case: if the comment is empty, do nothing and return None
if ignore_empty and len(comment) == 0:
return None
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']:
2022-07-28 19:48:33 +09:00
raise Rejected('message_ratelimited', CONFIG['FLOOD_LINE_THRESHOLD'])
# Check message
2022-02-18 10:32:34 +09:00
message_id = generate_nonce_hash(nonce)
if message_id in MESSAGES_BY_ID:
2022-07-28 19:48:33 +09:00
raise Rejected('message_suspected_duplicate')
if len(comment) == 0:
2022-07-28 19:48:33 +09:00
raise Rejected('message_empty')
2022-05-28 14:40:53 +09:00
if len(comment.strip()) == 0:
2022-07-28 19:48:33 +09:00
raise Rejected('message_practically_empty')
if len(comment) > CONFIG['CHAT_COMMENT_MAX_LENGTH']:
2022-07-28 19:48:33 +09:00
raise Rejected('message_too_long', CONFIG['CHAT_COMMENT_MAX_LENGTH'])
if comment.count('\n') + 1 > CONFIG['CHAT_COMMENT_MAX_LINES']:
2022-07-28 19:48:33 +09:00
raise Rejected('message_too_many_lines', CONFIG['CHAT_COMMENT_MAX_LINES'])
linespan = get_approx_linespan(comment)
if linespan > CONFIG['CHAT_COMMENT_MAX_LINES']:
2022-07-28 19:48:33 +09:00
raise Rejected('message_too_many_apparent_lines', CONFIG['CHAT_COMMENT_MAX_LINES'])
# Record linespan
linespan_tuple = (timestamp, linespan)
user['linespan'].append(linespan_tuple)
# Create and add message
try:
2022-02-18 10:32:34 +09:00
last_message = next(reversed(MESSAGES))
except StopIteration:
2022-02-18 10:32:34 +09:00
seq = timestamp_ms
else:
2022-02-18 10:32:34 +09:00
if timestamp_ms > last_message['seq']:
seq = timestamp_ms
else:
seq = last_message['seq'] + 1
dt = datetime.utcfromtimestamp(timestamp)
markup = insert_emotes(escape(comment))
message = {
'id': message_id,
2022-02-18 10:32:34 +09:00
'seq': seq,
'token': user['token'],
'timestamp': timestamp,
'date': dt.strftime('%Y-%m-%d'),
'time_minutes': dt.strftime('%H:%M'),
'time_seconds': dt.strftime('%H:%M:%S'),
'nomarkup': comment,
'markup': markup,
}
MESSAGES_BY_ID[message_id] = message
# Limit number of stored messages
while len(MESSAGES_BY_ID) > CONFIG['MAX_CHAT_MESSAGES']:
MESSAGES_BY_ID.popitem(last=False)
# Deverify user every n messages
if CONFIG['CHAT_DEVERIFY_CLOCK'] is not None:
user['clock'] = (user['clock'] + 1) % CONFIG['CHAT_DEVERIFY_CLOCK']
if user['clock'] == 0 and not user['broadcaster']:
user['verified'] = False
# Notify event sockets that a chat message was added
notify_event_sockets({
'type': 'message',
'event': message,
})
# Broadcast a users update to all websockets,
# in case this message is from a new user
broadcast_users_update()
# Broadcast message to websockets
broadcast(
2022-02-18 10:32:34 +09:00
USERS,
payload={
'type': 'message',
'message': get_message_for_websocket(user, message),
2022-02-18 14:16:54 +09:00
},
)
return seq
2022-06-14 05:43:51 +09:00
def delete_chat_messages(seqs):
seq_set = set(seqs)
message_ids = set()
for message_id, message in MESSAGES_BY_ID.items():
if len(seq_set) == 0:
break
try:
seq_set.remove(message['seq'])
except KeyError:
pass
else:
message_ids.add(message_id)
for message_id in message_ids:
MESSAGES_BY_ID.pop(message_id)
broadcast(
USERS,
payload={
'type': 'delete',
'seqs': seqs,
},
)