Formalize/tidy user presence logic

このコミットが含まれているのは:
n9k 2022-02-19 00:35:42 +00:00
コミット e9a4b511a3
6個のファイルの変更54行の追加28行の削除

ファイルの表示

@ -31,7 +31,8 @@ async def create_app():
'MAX_CHAT_SCROLLBACK': config['memory']['chat_scrollback'],
'CHECKUP_PERIOD_USER': config['ratelimits']['user_absence'],
'CHECKUP_PERIOD_CAPTCHA': config['ratelimits']['captcha_expiry'],
'THRESHOLD_USER_IDLE': config['thresholds']['user_idle'],
'THRESHOLD_USER_NOTWATCHING': config['thresholds']['user_notwatching'],
'THRESHOLD_USER_TENTATIVE': config['thresholds']['user_tentative'],
'THRESHOLD_USER_ABSENT': config['thresholds']['user_absent'],
'THRESHOLD_NOJS_CHAT_TIMEOUT': config['thresholds']['nojs_chat_timeout'],
'CHAT_COMMENT_MAX_LENGTH': config['chat']['max_name_length'],
@ -42,8 +43,14 @@ async def create_app():
assert app.config['MAX_NOTICES'] >= 0
assert app.config['MAX_CHAT_SCROLLBACK'] >= 0
assert app.config['MAX_CHAT_MESSAGES'] >= app.config['MAX_CHAT_SCROLLBACK']
assert app.config['THRESHOLD_USER_ABSENT'] >= app.config['THRESHOLD_USER_IDLE']
assert (
app.config['MAX_CHAT_MESSAGES'] >= app.config['MAX_CHAT_SCROLLBACK']
)
assert (
app.config['THRESHOLD_USER_ABSENT']
>= app.config['THRESHOLD_USER_TENTATIVE']
>= app.config['THRESHOLD_USER_NOTWATCHING']
)
app.messages_by_id = OrderedDict()
app.users_by_token = {}

ファイルの表示

@ -1,6 +1,7 @@
import hashlib
import base64
from collections import OrderedDict
from enum import Enum
from math import inf
from quart import current_app
@ -9,6 +10,16 @@ from anonstream.utils.colour import generate_colour, colour_to_color
CONFIG = current_app.config
Presence = Enum(
'Presence',
names=(
'WATCHING',
'NOTWATCHING',
'TENTATIVE',
'ABSENT',
)
)
def generate_token_hash(token):
parts = CONFIG['SECRET_KEY'] + b'token-hash\0' + token.encode()
digest = hashlib.sha256(parts).digest()
@ -29,11 +40,10 @@ def generate_user(timestamp, token, broadcaster):
'color': colour_to_color(colour),
'tripcode': None,
'notices': OrderedDict(),
'seen': {
'first': timestamp,
'last': timestamp,
'last': {
'seen': timestamp,
'watching': -inf,
},
'watching_last': -inf,
}
def get_default_name(user):
@ -43,23 +53,32 @@ def get_default_name(user):
CONFIG['DEFAULT_ANON_NAME']
)
def is_watching(timestamp, user):
return user['watching_last'] >= timestamp - CONFIG['THRESHOLD_USER_IDLE']
def get_presence(timestamp, user):
last_watching_ago = timestamp - user['last']['watching']
if last_watching_ago < CONFIG['THRESHOLD_USER_NOTWATCHING']:
return Presence.WATCHING
def is_idle(timestamp, user):
return is_present(timestamp, user) and not is_watching(timestamp, user)
last_seen_ago = timestamp - user['last']['seen']
if last_seen_ago < CONFIG['THRESHOLD_USER_TENTATIVE']:
return Presence.NOTWATCHING
if user['websockets']:
return Presence.NOTWATCHING
def is_present(timestamp, user):
if last_seen_ago < CONFIG['THRESHOLD_USER_ABSENT']:
return Presence.TENTATIVE
return Presence.ABSENT
def is_listed(timestamp, user):
return (
user['seen']['last'] >= timestamp - CONFIG['THRESHOLD_USER_ABSENT']
or len(user['websockets']) > 0
get_presence(timestamp, user)
in {Presence.WATCHING, Presence.NOTWATCHING}
)
def is_absent(timestamp, user):
return not is_present(timestamp, user)
def is_visible(timestamp, messages, user):
has_visible_messages = any(
message['token'] == user['token'] for message in messages
)
return has_visible_messages or is_present(timestamp, user)
def user_left_messages():
return any(
message['token'] == user['token']
for message in messages
)
return is_listed(timestamp, user) or user_left_messages()

ファイルの表示

@ -5,7 +5,7 @@ from functools import wraps
from quart import current_app, request, abort, make_response, render_template, request
from werkzeug.security import check_password_hash
from anonstream.user import sunset, user_for_websocket
from anonstream.user import sunset, see, user_for_websocket
from anonstream.chat import broadcast
from anonstream.helpers.user import generate_user
from anonstream.utils.user import generate_token
@ -71,7 +71,7 @@ def with_user_from(context):
# Update / create user
user = USERS_BY_TOKEN.get(token)
if user is not None:
user['seen']['last'] = timestamp
see(user)
else:
user = generate_user(
timestamp=timestamp,

ファイルの表示

@ -105,7 +105,7 @@ def delete_tripcode(user):
user['tripcode'] = None
def see(user):
user['seen']['last'] = int(time.time())
user['last']['seen'] = int(time.time())
@with_timestamp
def users_for_websocket(timestamp):

ファイルの表示

@ -6,7 +6,6 @@ from anonstream.stream import get_stream_title, get_stream_uptime
from anonstream.chat import messages_for_websocket, add_chat_message, Rejected
from anonstream.user import users_for_websocket, see
from anonstream.wrappers import with_first_argument
from anonstream.helpers.user import is_present
from anonstream.utils.chat import generate_nonce
from anonstream.utils.websocket import parse_websocket_data, Malformed

ファイルの表示

@ -26,6 +26,7 @@ user_absence = 8
captcha_expiry = 8
[thresholds]
user_idle = 12
user_absent = 1800
nojs_chat_timeout = 24
user_notwatching = 8
user_tentative = 20
user_absent = 360
nojs_chat_timeout = 30