Formalize/tidy user presence logic
このコミットが含まれているのは:
コミット
e9a4b511a3
|
@ -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
|
||||
|
|
読み込み中…
新しいイシューから参照