Track the last time users were sent chat messages
このコミットが含まれているのは:
コミット
1d5b446291
|
@ -44,6 +44,7 @@ def generate_user(timestamp, token, broadcaster, presence):
|
||||||
'seen': timestamp,
|
'seen': timestamp,
|
||||||
'watching': -inf,
|
'watching': -inf,
|
||||||
'eyes': -inf,
|
'eyes': -inf,
|
||||||
|
'reading': -inf,
|
||||||
},
|
},
|
||||||
'presence': presence,
|
'presence': presence,
|
||||||
'linespan': deque(),
|
'linespan': deque(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ from werkzeug.exceptions import TooManyRequests
|
||||||
from anonstream.captcha import get_captcha_image
|
from anonstream.captcha import get_captcha_image
|
||||||
from anonstream.segments import segments, StopSendingSegments
|
from anonstream.segments import segments, StopSendingSegments
|
||||||
from anonstream.stream import is_online, get_stream_uptime
|
from anonstream.stream import is_online, get_stream_uptime
|
||||||
from anonstream.user import watched, create_eyes, renew_eyes, EyesException, RatelimitedEyes
|
from anonstream.user import watching, create_eyes, renew_eyes, EyesException, RatelimitedEyes
|
||||||
from anonstream.routes.wrappers import with_user_from, auth_required
|
from anonstream.routes.wrappers import with_user_from, auth_required
|
||||||
from anonstream.utils.security import generate_csp
|
from anonstream.utils.security import generate_csp
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ async def stream(user):
|
||||||
except EyesException as e:
|
except EyesException as e:
|
||||||
raise StopSendingSegments(f'eyes {eyes_id} not allowed: {e!r}') from e
|
raise StopSendingSegments(f'eyes {eyes_id} not allowed: {e!r}') from e
|
||||||
print(f'{uri}: {eyes_id}~{user["token"]}')
|
print(f'{uri}: {eyes_id}~{user["token"]}')
|
||||||
watched(user)
|
watching(user)
|
||||||
|
|
||||||
generator = segments(segment_read_hook, token=user['token'])
|
generator = segments(segment_read_hook, token=user['token'])
|
||||||
response = await make_response(generator)
|
response = await make_response(generator)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from quart import current_app, request, render_template, redirect, url_for, esca
|
||||||
from anonstream.captcha import get_random_captcha_digest_for
|
from anonstream.captcha import get_random_captcha_digest_for
|
||||||
from anonstream.chat import add_chat_message, Rejected
|
from anonstream.chat import add_chat_message, Rejected
|
||||||
from anonstream.stream import is_online, get_stream_title, get_stream_uptime_and_viewership
|
from anonstream.stream import is_online, get_stream_title, get_stream_uptime_and_viewership
|
||||||
from anonstream.user import add_state, pop_state, try_change_appearance, update_presence, get_users_by_presence, Presence, verify, deverify, BadCaptcha
|
from anonstream.user import add_state, pop_state, try_change_appearance, update_presence, get_users_by_presence, Presence, verify, deverify, BadCaptcha, reading
|
||||||
from anonstream.routes.wrappers import with_user_from, render_template_with_etag
|
from anonstream.routes.wrappers import with_user_from, render_template_with_etag
|
||||||
from anonstream.helpers.chat import get_scrollback
|
from anonstream.helpers.chat import get_scrollback
|
||||||
from anonstream.helpers.user import get_default_name
|
from anonstream.helpers.user import get_default_name
|
||||||
|
@ -46,6 +46,7 @@ async def nojs_info(user):
|
||||||
@current_app.route('/chat/messages.html')
|
@current_app.route('/chat/messages.html')
|
||||||
@with_user_from(request)
|
@with_user_from(request)
|
||||||
async def nojs_chat_messages(user):
|
async def nojs_chat_messages(user):
|
||||||
|
reading(user)
|
||||||
return await render_template_with_etag(
|
return await render_template_with_etag(
|
||||||
'nojs_chat_messages.html',
|
'nojs_chat_messages.html',
|
||||||
{'csp': generate_csp()},
|
{'csp': generate_csp()},
|
||||||
|
|
|
@ -7,15 +7,16 @@ from math import inf
|
||||||
|
|
||||||
from quart import current_app, websocket
|
from quart import current_app, websocket
|
||||||
|
|
||||||
from anonstream.user import see
|
from anonstream.user import see, reading
|
||||||
from anonstream.websocket import websocket_outbound, websocket_inbound
|
from anonstream.websocket import websocket_outbound, websocket_inbound
|
||||||
from anonstream.routes.wrappers import with_user_from
|
from anonstream.routes.wrappers import with_user_from
|
||||||
|
|
||||||
@current_app.websocket('/live')
|
@current_app.websocket('/live')
|
||||||
@with_user_from(websocket)
|
@with_user_from(websocket)
|
||||||
async def live(user):
|
async def live(user):
|
||||||
queue = asyncio.Queue(maxsize=0)
|
queue = asyncio.Queue()
|
||||||
user['websockets'][queue] = -inf
|
user['websockets'][queue] = -inf
|
||||||
|
reading(user)
|
||||||
|
|
||||||
producer = websocket_outbound(queue, user)
|
producer = websocket_outbound(queue, user)
|
||||||
consumer = websocket_inbound(queue, user)
|
consumer = websocket_inbound(queue, user)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
import time
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from quart import current_app, request, abort, make_response, render_template, request
|
from quart import current_app, request, abort, make_response, render_template, request
|
||||||
|
@ -15,6 +14,7 @@ from anonstream.broadcast import broadcast
|
||||||
from anonstream.user import see
|
from anonstream.user import see
|
||||||
from anonstream.helpers.user import generate_user
|
from anonstream.helpers.user import generate_user
|
||||||
from anonstream.utils.user import generate_token, Presence
|
from anonstream.utils.user import generate_token, Presence
|
||||||
|
from anonstream.wrappers import get_timestamp
|
||||||
|
|
||||||
CONFIG = current_app.config
|
CONFIG = current_app.config
|
||||||
MESSAGES = current_app.messages
|
MESSAGES = current_app.messages
|
||||||
|
@ -68,7 +68,7 @@ def with_user_from(context):
|
||||||
def with_user_from_context(f):
|
def with_user_from_context(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
timestamp = int(time.time())
|
timestamp = get_timestamp()
|
||||||
|
|
||||||
# Check if broadcaster
|
# Check if broadcaster
|
||||||
broadcaster = check_auth(context)
|
broadcaster = check_auth(context)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from math import inf
|
||||||
|
|
||||||
from quart import current_app
|
from quart import current_app
|
||||||
|
|
||||||
from anonstream.wrappers import try_except_log, with_timestamp
|
from anonstream.wrappers import try_except_log, with_timestamp, get_timestamp
|
||||||
from anonstream.helpers.user import get_default_name, get_presence, Presence
|
from anonstream.helpers.user import get_default_name, get_presence, Presence
|
||||||
from anonstream.helpers.captcha import check_captcha_digest, Answer
|
from anonstream.helpers.captcha import check_captcha_digest, Answer
|
||||||
from anonstream.helpers.tripcode import generate_tripcode
|
from anonstream.helpers.tripcode import generate_tripcode
|
||||||
|
@ -136,11 +136,18 @@ def delete_tripcode(user):
|
||||||
def see(timestamp, user):
|
def see(timestamp, user):
|
||||||
user['last']['seen'] = timestamp
|
user['last']['seen'] = timestamp
|
||||||
|
|
||||||
@with_timestamp()
|
def watching(user, timestamp=None):
|
||||||
def watched(timestamp, user):
|
if timestamp is None:
|
||||||
|
timestamp = get_timestamp()
|
||||||
user['last']['seen'] = timestamp
|
user['last']['seen'] = timestamp
|
||||||
user['last']['watching'] = timestamp
|
user['last']['watching'] = timestamp
|
||||||
|
|
||||||
|
def reading(user, timestamp=None):
|
||||||
|
if timestamp is None:
|
||||||
|
timestamp = get_timestamp()
|
||||||
|
user['last']['seen'] = timestamp
|
||||||
|
user['last']['reading'] = timestamp
|
||||||
|
|
||||||
@with_timestamp()
|
@with_timestamp()
|
||||||
def get_all_users_for_websocket(timestamp):
|
def get_all_users_for_websocket(timestamp):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -9,7 +9,7 @@ from quart import current_app, websocket
|
||||||
from anonstream.stream import get_stream_title, get_stream_uptime_and_viewership
|
from anonstream.stream import get_stream_title, get_stream_uptime_and_viewership
|
||||||
from anonstream.captcha import get_random_captcha_digest_for
|
from anonstream.captcha import get_random_captcha_digest_for
|
||||||
from anonstream.chat import get_all_messages_for_websocket, add_chat_message, Rejected
|
from anonstream.chat import get_all_messages_for_websocket, add_chat_message, Rejected
|
||||||
from anonstream.user import get_all_users_for_websocket, see, verify, deverify, BadCaptcha, try_change_appearance
|
from anonstream.user import get_all_users_for_websocket, see, reading, verify, deverify, BadCaptcha, try_change_appearance
|
||||||
from anonstream.wrappers import with_timestamp
|
from anonstream.wrappers import with_timestamp
|
||||||
from anonstream.utils.chat import generate_nonce
|
from anonstream.utils.chat import generate_nonce
|
||||||
from anonstream.utils.websocket import parse_websocket_data, Malformed, WS
|
from anonstream.utils.websocket import parse_websocket_data, Malformed, WS
|
||||||
|
@ -75,6 +75,7 @@ async def websocket_inbound(queue, user):
|
||||||
@with_timestamp()
|
@with_timestamp()
|
||||||
def handle_inbound_pong(timestamp, queue, user):
|
def handle_inbound_pong(timestamp, queue, user):
|
||||||
print(f'[pong] {user["token"]}')
|
print(f'[pong] {user["token"]}')
|
||||||
|
reading(user, timestamp=timestamp)
|
||||||
user['websockets'][queue] = timestamp
|
user['websockets'][queue] = timestamp
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,18 @@ def with_function_call(fn, *fn_args, **fn_kwargs):
|
||||||
def with_constant(x):
|
def with_constant(x):
|
||||||
return with_function_call(lambda: x)
|
return with_function_call(lambda: x)
|
||||||
|
|
||||||
def with_timestamp(monotonic=False, precise=False):
|
def get_timestamp(monotonic=False, precise=False):
|
||||||
n = 1_000_000_000
|
n = 1_000_000_000
|
||||||
if monotonic:
|
if monotonic:
|
||||||
fn = precise and time.monotonic or (lambda: time.monotonic_ns() // n)
|
timestamp = precise and time.monotonic() or time.monotonic_ns() // n
|
||||||
else:
|
else:
|
||||||
fn = precise and time.time or (lambda: time.time_ns() // n)
|
timestamp = precise and time.time() or time.time_ns() // n
|
||||||
return with_function_call(fn)
|
return timestamp
|
||||||
|
|
||||||
|
def with_timestamp(monotonic=False, precise=False):
|
||||||
|
def get_timestamp_specific():
|
||||||
|
return get_timestamp(monotonic=monotonic, precise=precise)
|
||||||
|
return with_function_call(get_timestamp_specific)
|
||||||
|
|
||||||
def try_except_log(errors, exception_class):
|
def try_except_log(errors, exception_class):
|
||||||
def try_except_log_specific(f):
|
def try_except_log_specific(f):
|
||||||
|
|
読み込み中…
新しいイシューから参照