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
|
|
|
|
|
2022-02-17 21:51:09 +09:00
|
|
|
from quart import current_app, request, render_template, redirect, url_for, escape, Markup
|
2022-02-16 18:55:30 +09:00
|
|
|
|
2022-02-21 07:07:32 +09:00
|
|
|
from anonstream.captcha import get_random_captcha_digest_for
|
2022-02-16 18:55:30 +09:00
|
|
|
from anonstream.chat import add_chat_message, Rejected
|
2022-07-28 19:48:33 +09:00
|
|
|
from anonstream.locale import get_locale_from
|
2022-03-30 17:41:42 +09:00
|
|
|
from anonstream.stream import is_online, get_stream_title, get_stream_uptime_and_viewership
|
2022-06-19 17:10:23 +09:00
|
|
|
from anonstream.user import add_state, pop_state, try_change_appearance, update_presence, get_users_by_presence, Presence, verify, deverify, BadCaptcha, reading
|
2022-02-18 21:24:19 +09:00
|
|
|
from anonstream.routes.wrappers import with_user_from, render_template_with_etag
|
|
|
|
from anonstream.helpers.chat import get_scrollback
|
2022-02-27 10:01:32 +09:00
|
|
|
from anonstream.helpers.user import get_default_name
|
2022-07-20 15:04:55 +09:00
|
|
|
from anonstream.utils.chat import generate_nonce
|
2022-03-07 16:11:49 +09:00
|
|
|
from anonstream.utils.security import generate_csp
|
2022-02-16 18:55:30 +09:00
|
|
|
|
2022-02-20 13:23:32 +09:00
|
|
|
CONFIG = current_app.config
|
|
|
|
USERS_BY_TOKEN = current_app.users_by_token
|
|
|
|
|
2022-03-02 19:13:07 +09:00
|
|
|
@current_app.route('/stream.html')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_stream(timestamp, user):
|
2022-03-02 19:13:07 +09:00
|
|
|
return await render_template(
|
|
|
|
'nojs_stream.html',
|
2022-03-07 16:11:49 +09:00
|
|
|
csp=generate_csp(),
|
2022-03-02 19:13:07 +09:00
|
|
|
user=user,
|
2022-03-30 17:41:42 +09:00
|
|
|
online=is_online(),
|
2022-07-28 19:48:33 +09:00
|
|
|
locale=get_locale_from(request)['anonstream']['stream'],
|
2022-03-02 19:13:07 +09:00
|
|
|
)
|
|
|
|
|
2022-02-16 18:55:30 +09:00
|
|
|
@current_app.route('/info.html')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_info(timestamp, user):
|
2022-03-02 19:13:07 +09:00
|
|
|
update_presence(user)
|
2022-02-28 20:01:24 +09:00
|
|
|
uptime, viewership = get_stream_uptime_and_viewership()
|
2022-07-24 11:00:49 +09:00
|
|
|
return await render_template_with_etag(
|
2022-02-16 18:55:30 +09:00
|
|
|
'nojs_info.html',
|
2022-07-24 11:00:49 +09:00
|
|
|
{'csp': generate_csp()},
|
2022-06-17 08:39:38 +09:00
|
|
|
refresh=CONFIG['NOJS_REFRESH_INFO'],
|
2022-02-16 18:55:30 +09:00
|
|
|
user=user,
|
2022-07-28 19:48:33 +09:00
|
|
|
locale=get_locale_from(request)['anonstream']['info'],
|
2022-02-28 20:01:24 +09:00
|
|
|
viewership=viewership,
|
|
|
|
uptime=uptime,
|
2022-02-26 08:06:36 +09:00
|
|
|
title=await get_stream_title(),
|
2022-03-02 19:13:07 +09:00
|
|
|
Presence=Presence,
|
2022-02-16 18:55:30 +09:00
|
|
|
)
|
|
|
|
|
|
|
|
@current_app.route('/chat/messages.html')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_chat_messages(timestamp, user):
|
2022-06-19 17:10:23 +09:00
|
|
|
reading(user)
|
2022-02-18 21:24:19 +09:00
|
|
|
return await render_template_with_etag(
|
2022-03-04 15:15:56 +09:00
|
|
|
'nojs_chat_messages.html',
|
2022-03-07 16:11:49 +09:00
|
|
|
{'csp': generate_csp()},
|
2022-06-17 08:39:38 +09:00
|
|
|
refresh=CONFIG['NOJS_REFRESH_MESSAGES'],
|
2022-02-16 18:55:30 +09:00
|
|
|
user=user,
|
2022-02-20 13:23:32 +09:00
|
|
|
users_by_token=USERS_BY_TOKEN,
|
2022-07-28 19:48:33 +09:00
|
|
|
locale=get_locale_from(request)['anonstream']['chat'],
|
2022-02-18 21:24:19 +09:00
|
|
|
messages=get_scrollback(current_app.messages),
|
2022-06-17 08:39:38 +09:00
|
|
|
timeout=CONFIG['NOJS_TIMEOUT_CHAT'],
|
2022-02-16 18:55:30 +09:00
|
|
|
get_default_name=get_default_name,
|
|
|
|
)
|
|
|
|
|
2022-02-20 13:23:32 +09:00
|
|
|
@current_app.route('/chat/messages')
|
2022-02-18 21:24:19 +09:00
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_chat_messages_redirect(timestamp, user):
|
2022-06-22 13:45:32 +09:00
|
|
|
url = url_for('nojs_chat_messages', token=user['token'], _anchor='end')
|
|
|
|
return redirect(url, 303)
|
2022-02-18 21:24:19 +09:00
|
|
|
|
2022-02-26 19:34:14 +09:00
|
|
|
@current_app.route('/chat/users.html')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_chat_users(timestamp, user):
|
2022-02-26 19:34:14 +09:00
|
|
|
users_by_presence = get_users_by_presence()
|
2022-02-27 14:01:49 +09:00
|
|
|
return await render_template_with_etag(
|
2022-03-04 15:15:56 +09:00
|
|
|
'nojs_chat_users.html',
|
2022-03-07 16:11:49 +09:00
|
|
|
{'csp': generate_csp()},
|
2022-06-17 08:39:38 +09:00
|
|
|
refresh=CONFIG['NOJS_REFRESH_USERS'],
|
2022-02-26 19:34:14 +09:00
|
|
|
user=user,
|
2022-07-28 19:48:33 +09:00
|
|
|
locale=get_locale_from(request)['anonstream']['chat'],
|
2022-02-26 19:34:14 +09:00
|
|
|
get_default_name=get_default_name,
|
|
|
|
users_watching=users_by_presence[Presence.WATCHING],
|
|
|
|
users_notwatching=users_by_presence[Presence.NOTWATCHING],
|
2022-06-17 08:39:38 +09:00
|
|
|
timeout=CONFIG['NOJS_TIMEOUT_CHAT'],
|
2022-02-26 19:34:14 +09:00
|
|
|
)
|
|
|
|
|
2022-02-16 18:55:30 +09:00
|
|
|
@current_app.route('/chat/form.html')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_chat_form(timestamp, user):
|
2022-02-20 13:23:32 +09:00
|
|
|
state_id = request.args.get('state', type=int)
|
|
|
|
state = pop_state(user, state_id)
|
2022-02-16 18:55:30 +09:00
|
|
|
prefer_chat_form = request.args.get('landing') != 'appearance'
|
2022-07-28 19:48:33 +09:00
|
|
|
print(state)
|
2022-02-16 18:55:30 +09:00
|
|
|
return await render_template(
|
2022-03-04 15:15:56 +09:00
|
|
|
'nojs_chat_form.html',
|
2022-03-07 16:11:49 +09:00
|
|
|
csp=generate_csp(),
|
2022-02-16 18:55:30 +09:00
|
|
|
user=user,
|
|
|
|
prefer_chat_form=prefer_chat_form,
|
2022-07-28 19:48:33 +09:00
|
|
|
state=state,
|
|
|
|
locale=get_locale_from(request)['anonstream'],
|
2022-02-16 18:55:30 +09:00
|
|
|
nonce=generate_nonce(),
|
2022-02-21 07:07:32 +09:00
|
|
|
digest=get_random_captcha_digest_for(user),
|
2022-02-16 18:55:30 +09:00
|
|
|
default_name=get_default_name(user),
|
2022-06-17 09:06:45 +09:00
|
|
|
max_comment_length=CONFIG['CHAT_COMMENT_MAX_LENGTH'],
|
|
|
|
max_name_length=CONFIG['CHAT_NAME_MAX_LENGTH'],
|
|
|
|
max_password_length=CONFIG['CHAT_TRIPCODE_PASSWORD_MAX_LENGTH'],
|
2022-02-16 18:55:30 +09:00
|
|
|
)
|
|
|
|
|
2022-02-20 13:23:32 +09:00
|
|
|
@current_app.post('/chat/form')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_chat_form_redirect(timestamp, user):
|
2022-02-20 13:23:32 +09:00
|
|
|
comment = (await request.form).get('comment', '')
|
|
|
|
if comment:
|
2022-06-17 09:36:13 +09:00
|
|
|
state_id = add_state(
|
|
|
|
user,
|
|
|
|
comment=comment[:CONFIG['CHAT_COMMENT_MAX_LENGTH']],
|
|
|
|
)
|
2022-02-20 13:23:32 +09:00
|
|
|
else:
|
|
|
|
state_id = None
|
2022-06-22 13:45:32 +09:00
|
|
|
url = url_for('nojs_chat_form', token=user['token'], state=state_id)
|
|
|
|
return redirect(url, 303)
|
2022-02-20 13:23:32 +09:00
|
|
|
|
2022-02-16 18:55:30 +09:00
|
|
|
@current_app.post('/chat/message')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_submit_message(timestamp, user):
|
2022-02-16 18:55:30 +09:00
|
|
|
form = await request.form
|
|
|
|
|
2022-02-20 13:23:32 +09:00
|
|
|
comment = form.get('comment', '')
|
|
|
|
digest = form.get('captcha-digest', '')
|
|
|
|
answer = form.get('captcha-answer', '')
|
2022-02-16 18:55:30 +09:00
|
|
|
try:
|
2022-02-20 13:23:32 +09:00
|
|
|
verification_happened = verify(user, digest, answer)
|
|
|
|
except BadCaptcha as e:
|
2022-07-28 19:48:33 +09:00
|
|
|
string, *args = e.args
|
2022-06-17 09:36:13 +09:00
|
|
|
state_id = add_state(
|
|
|
|
user,
|
2022-07-28 19:48:33 +09:00
|
|
|
notice=[(string, args)],
|
2022-06-17 09:36:13 +09:00
|
|
|
comment=comment[:CONFIG['CHAT_COMMENT_MAX_LENGTH']],
|
|
|
|
)
|
2022-02-16 18:55:30 +09:00
|
|
|
else:
|
2022-02-20 13:23:32 +09:00
|
|
|
nonce = form.get('nonce', '')
|
|
|
|
try:
|
2022-02-21 07:07:32 +09:00
|
|
|
# If the comment is empty but the captcha was just solved,
|
2022-02-20 13:23:32 +09:00
|
|
|
# be lenient: don't raise an exception and don't create a notice
|
2022-08-02 13:46:38 +09:00
|
|
|
seq = add_chat_message(
|
2022-02-20 13:23:32 +09:00
|
|
|
user,
|
|
|
|
nonce,
|
|
|
|
comment,
|
|
|
|
ignore_empty=verification_happened,
|
|
|
|
)
|
2022-08-02 13:46:38 +09:00
|
|
|
message_was_added = seq is not None
|
2022-02-20 13:23:32 +09:00
|
|
|
except Rejected as e:
|
2022-07-28 19:48:33 +09:00
|
|
|
string, *args = e.args
|
2022-06-17 09:36:13 +09:00
|
|
|
state_id = add_state(
|
|
|
|
user,
|
2022-07-28 19:48:33 +09:00
|
|
|
notice=[(string, args)],
|
2022-06-17 09:36:13 +09:00
|
|
|
comment=comment[:CONFIG['CHAT_COMMENT_MAX_LENGTH']],
|
|
|
|
)
|
2022-02-20 13:23:32 +09:00
|
|
|
else:
|
|
|
|
state_id = None
|
2022-03-02 18:16:10 +09:00
|
|
|
if message_was_added:
|
2022-06-29 11:35:24 +09:00
|
|
|
deverify(user, timestamp=timestamp)
|
2022-02-16 18:55:30 +09:00
|
|
|
|
2022-06-22 13:45:32 +09:00
|
|
|
url = url_for(
|
2022-03-04 15:15:56 +09:00
|
|
|
'nojs_chat_form',
|
2022-02-20 13:23:32 +09:00
|
|
|
token=user['token'],
|
|
|
|
landing='chat',
|
|
|
|
state=state_id,
|
2022-06-22 13:45:32 +09:00
|
|
|
)
|
|
|
|
return redirect(url, 303)
|
2022-02-16 18:55:30 +09:00
|
|
|
|
|
|
|
@current_app.post('/chat/appearance')
|
|
|
|
@with_user_from(request)
|
2022-06-19 17:51:42 +09:00
|
|
|
async def nojs_submit_appearance(timestamp, user):
|
2022-02-17 21:51:09 +09:00
|
|
|
form = await request.form
|
2022-02-26 19:34:14 +09:00
|
|
|
|
|
|
|
# Collect form data
|
2022-02-22 18:30:23 +09:00
|
|
|
name = form.get('name', '').strip()
|
2022-03-07 14:39:06 +09:00
|
|
|
if len(name) == 0:
|
2022-02-26 19:34:14 +09:00
|
|
|
name = None
|
|
|
|
|
2022-02-17 21:51:09 +09:00
|
|
|
color = form.get('color', '')
|
|
|
|
password = form.get('password', '')
|
|
|
|
|
2022-02-26 19:34:14 +09:00
|
|
|
if form.get('clear-tripcode', type=bool):
|
|
|
|
want_tripcode = False
|
|
|
|
elif form.get('set-tripcode', type=bool):
|
|
|
|
want_tripcode = True
|
|
|
|
else:
|
|
|
|
want_tripcode = None
|
2022-02-22 18:30:23 +09:00
|
|
|
|
2022-02-26 19:34:14 +09:00
|
|
|
# Change appearance (iff form data was good)
|
|
|
|
errors = try_change_appearance(user, name, color, password, want_tripcode)
|
2022-02-17 21:51:09 +09:00
|
|
|
if errors:
|
2022-07-28 19:48:33 +09:00
|
|
|
notice = []
|
|
|
|
for string, *args in (error.args for error in errors):
|
|
|
|
notice.append((string, args))
|
2022-02-17 21:51:09 +09:00
|
|
|
else:
|
2022-07-28 19:48:33 +09:00
|
|
|
notice = [('appearance_changed', ())]
|
2022-02-17 21:51:09 +09:00
|
|
|
|
2022-07-28 19:48:33 +09:00
|
|
|
state_id = add_state(user, notice=notice)
|
2022-06-22 13:45:32 +09:00
|
|
|
url = url_for(
|
2022-03-04 15:15:56 +09:00
|
|
|
'nojs_chat_form',
|
2022-02-17 21:51:09 +09:00
|
|
|
token=user['token'],
|
|
|
|
landing='appearance' if errors else 'chat',
|
2022-02-20 13:23:32 +09:00
|
|
|
state=state_id,
|
2022-06-22 13:45:32 +09:00
|
|
|
)
|
|
|
|
return redirect(url, 303)
|