コミットを比較

...

16 コミット

52個のファイルの変更243行の追加98行の削除

ファイルの表示

@ -5,7 +5,7 @@ Recipe for livestreaming over Tor
## Repo ## Repo
The canonical location of this repo is The canonical location of this repo is
<https://git.076.ne.jp/ninya9k/anonstream>. <https://gitler.moe/ninya9k/anonstream>.
These mirrors also exist: These mirrors also exist:
* <https://gitlab.com/ninya9k/anonstream> * <https://gitlab.com/ninya9k/anonstream>
@ -18,7 +18,7 @@ Python with `python --version`.
Clone the repo: Clone the repo:
```sh ```sh
git clone https://git.076.ne.jp/ninya9k/anonstream.git git clone https://gitler.moe/ninya9k/anonstream.git
cd anonstream cd anonstream
``` ```
@ -136,8 +136,8 @@ anonstream is AGPL 3.0 or later, see
* werkzeug <https://github.com/pallets/werkzeug> * werkzeug <https://github.com/pallets/werkzeug>
([BSD 3-Clause][werkzeug]) ([BSD 3-Clause][werkzeug])
[licence]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/LICENSES/AGPL-3.0-or-later.md [licence]: https://gitler.moe/ninya9k/anonstream/src/branch/master/LICENSES/AGPL-3.0-or-later.md
[settings.svg]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/anonstream/static/settings.svg [settings.svg]: https://gitler.moe/ninya9k/anonstream/src/branch/master/anonstream/static/settings.svg
[aiofiles]: https://github.com/Tinche/aiofiles/blob/master/LICENSE [aiofiles]: https://github.com/Tinche/aiofiles/blob/master/LICENSE
[captcha]: https://github.com/lepture/captcha/blob/master/LICENSE [captcha]: https://github.com/lepture/captcha/blob/master/LICENSE

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio
@ -13,7 +13,7 @@ from anonstream.quart import Quart
from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer
from anonstream.utils.user import generate_blank_allowedness from anonstream.utils.user import generate_blank_allowedness
__version__ = '1.6.7' __version__ = '1.6.9'
def create_app(toml_config): def create_app(toml_config):
app = Quart('anonstream', static_folder=None) app = Quart('anonstream', static_folder=None)

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import argparse import argparse

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import time import time

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from quart import current_app from quart import current_app

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import secrets import secrets

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import time import time

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import os import os

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
class ControlSocketExit(Exception): class ControlSocketExit(Exception):

ファイルの表示

@ -1,14 +1,16 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from anonstream.control.spec import ParseException, Parsed from anonstream.control.spec import ParseException, Parsed
from anonstream.control.spec.common import Str from anonstream.control.spec.common import Str
from anonstream.control.spec.methods.allowedness import SPEC as SPEC_ALLOWEDNESS from anonstream.control.spec.methods.allowedness import SPEC as SPEC_ALLOWEDNESS
from anonstream.control.spec.methods.chat import SPEC as SPEC_CHAT from anonstream.control.spec.methods.chat import SPEC as SPEC_CHAT
from anonstream.control.spec.methods.config import SPEC as SPEC_CONFIG
from anonstream.control.spec.methods.emote import SPEC as SPEC_EMOTE from anonstream.control.spec.methods.emote import SPEC as SPEC_EMOTE
from anonstream.control.spec.methods.help import SPEC as SPEC_HELP from anonstream.control.spec.methods.help import SPEC as SPEC_HELP
from anonstream.control.spec.methods.quit import SPEC as SPEC_QUIT from anonstream.control.spec.methods.quit import SPEC as SPEC_QUIT
from anonstream.control.spec.methods.title import SPEC as SPEC_TITLE from anonstream.control.spec.methods.title import SPEC as SPEC_TITLE
from anonstream.control.spec.methods.tripcode import SPEC as SPEC_TRIPCODE
from anonstream.control.spec.methods.user import SPEC as SPEC_USER from anonstream.control.spec.methods.user import SPEC as SPEC_USER
SPEC = Str({ SPEC = Str({
@ -19,6 +21,8 @@ SPEC = Str({
'user': SPEC_USER, 'user': SPEC_USER,
'allowednesss': SPEC_ALLOWEDNESS, 'allowednesss': SPEC_ALLOWEDNESS,
'emote': SPEC_EMOTE, 'emote': SPEC_EMOTE,
'config': SPEC_CONFIG,
'tripcode': SPEC_TRIPCODE,
}) })
async def parse(request): async def parse(request):

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
class ParseException(Exception): class ParseException(Exception):

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json

ファイルの表示

@ -1,7 +1,10 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import itertools import itertools
import json
from quart import current_app
from anonstream.chat import delete_chat_messages from anonstream.chat import delete_chat_messages
from anonstream.control.exceptions import CommandFailed from anonstream.control.exceptions import CommandFailed
@ -10,6 +13,8 @@ from anonstream.control.spec.common import Str, End, Args, ArgsJsonString, ArgsU
from anonstream.control.spec.utils import get_item, json_dumps_contiguous from anonstream.control.spec.utils import get_item, json_dumps_contiguous
from anonstream.chat import add_chat_message, Rejected from anonstream.chat import add_chat_message, Rejected
MESSAGES = current_app.messages
class ArgsSeqs(Args): class ArgsSeqs(Args):
def consume(self, words, index): def consume(self, words, index):
seqs = [] seqs = []
@ -33,13 +38,12 @@ class ArgsSeqs(Args):
async def cmd_chat_help(): async def cmd_chat_help():
normal = ['chat', 'help'] normal = ['chat', 'help']
response = ( response = (
'Usage: chat delete SEQS\n' 'Usage: chat {show | delete SEQS | add USER NONCE COMMENT}\n'
'Commands:\n' 'Commands:\n'
#' chat show [MESSAGES]...........show chat messages\n' ' chat show......................show all chat messages\n'
' chat delete SEQS...............delete chat messages\n' ' chat delete SEQS...............delete chat messages\n'
' chat add USER NONCE COMMENT....add chat message\n' ' chat add USER NONCE COMMENT....add chat message\n'
'Definitions:\n' 'Definitions:\n'
#' MESSAGES...................undefined\n'
' SEQS.......................=SEQ [SEQ...]\n' ' SEQS.......................=SEQ [SEQ...]\n'
' SEQ........................a chat message\'s seq, base-10 integer\n' ' SEQ........................a chat message\'s seq, base-10 integer\n'
' USER.......................={token TOKEN | hash HASH}\n' ' USER.......................={token TOKEN | hash HASH}\n'
@ -50,6 +54,11 @@ async def cmd_chat_help():
) )
return normal, response return normal, response
async def cmd_chat_show():
normal = ['chat', 'show']
response = json.dumps(tuple(MESSAGES), separators=(',', ':')) + '\n'
return normal, response
async def cmd_chat_delete(*seqs): async def cmd_chat_delete(*seqs):
delete_chat_messages(seqs) delete_chat_messages(seqs)
normal = ['chat', 'delete', *map(str, seqs)] normal = ['chat', 'delete', *map(str, seqs)]
@ -74,6 +83,7 @@ async def cmd_chat_add(user, nonce, comment):
SPEC = Str({ SPEC = Str({
None: End(cmd_chat_help), None: End(cmd_chat_help),
'help': End(cmd_chat_help), 'help': End(cmd_chat_help),
'show': End(cmd_chat_show),
'delete': ArgsSeqs(End(cmd_chat_delete)), 'delete': ArgsSeqs(End(cmd_chat_delete)),
'add': ArgsUser(ArgsJsonString(ArgsJsonString(End(cmd_chat_add)))), 'add': ArgsUser(ArgsJsonString(ArgsJsonString(End(cmd_chat_add)))),
}) })

43
anonstream/control/spec/methods/config.py ノーマルファイル
ファイルの表示

@ -0,0 +1,43 @@
# SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json
from quart import current_app
from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec.common import Str, End, ArgsString
CONFIG = current_app.config
async def cmd_config_help():
normal = ['config', 'help']
response = (
'Usage: config show OPTION\n'
'Commands:\n'
' config show OPTION....show entry in app.config\n'
'Definitions:\n'
' OPTION................app.config key, re:[A-Z0-9_]+\n'
)
return normal, response
async def cmd_config_show(option):
if option in {'SECRET_KEY', 'SECRET_KEY_STRING'}:
raise CommandFailed('not going to show our secret key')
try:
value = CONFIG[option]
except KeyError:
raise CommandFailed(f'no config option with key {option!r}')
try:
value_json = json.dumps(value)
except (TypeError, ValueError):
raise CommandFailed(f'value is not json serializable')
normal = ['config', 'show']
response = value_json + '\n'
return normal, response
SPEC = Str({
None: End(cmd_config_help),
'help': End(cmd_config_help),
'show': ArgsString(End(cmd_config_show)),
})

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
@ -16,7 +16,7 @@ EMOTES = current_app.emotes
async def cmd_emote_help(): async def cmd_emote_help():
normal = ['emote', 'help'] normal = ['emote', 'help']
response = ( response = (
'Usage: emote [show | reload]\n' 'Usage: emote {show | reload}\n'
'Commands:\n' 'Commands:\n'
' emote show........show all current emotes\n' ' emote show........show all current emotes\n'
' emote reload......try to reload the emote schema (existing messages are not modified)\n' ' emote reload......try to reload the emote schema (existing messages are not modified)\n'

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from anonstream.control.spec.common import Str, End from anonstream.control.spec.common import Str, End
@ -19,15 +19,17 @@ async def cmd_help():
' user eyes USER [show]..........show a list of active video responses\n' ' user eyes USER [show]..........show a list of active video responses\n'
' user eyes USER delete EYES_ID..end a video response\n' ' user eyes USER delete EYES_ID..end a video response\n'
' user add VERIFIED TOKEN........add new user\n' ' user add VERIFIED TOKEN........add new user\n'
#' chat show MESSAGES.............show a list of messages\n' ' chat show......................show a list of all chat messages\n'
' chat delete SEQS...............delete a set of messages\n' ' chat delete SEQS...............delete a set of chat messages\n'
' chat add USER NONCE COMMENT....add chat message\n' ' chat add USER NONCE COMMENT....add a chat message\n'
' allowedness [show].............show the current allowedness\n' ' allowedness [show].............show the current allowedness\n'
' allowedness setdefault BOOLEAN.set the default allowedness\n' ' allowedness setdefault BOOLEAN.set the default allowedness\n'
' allowedness add SET STRING.....add to the blacklist/whitelist\n' ' allowedness add SET STRING.....add to the blacklist/whitelist\n'
' allowedness remove SET STRING..remove from the blacklist/whitelist\n' ' allowedness remove SET STRING..remove from the blacklist/whitelist\n'
' emote show.....................show all current emotes\n' ' emote show.....................show all current emotes\n'
' emote reload...................try reloading the emote schema\n' ' emote reload...................try reloading the emote schema\n'
' config show OPTION.............show app config option\n'
' tripcode generate PASSWORD.....show tripcode for given password\n'
) )
return normal, response return normal, response

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from anonstream.control.spec.common import Str, End from anonstream.control.spec.common import Str, End

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
@ -12,7 +12,7 @@ from anonstream.stream import get_stream_title, set_stream_title
async def cmd_title_help(): async def cmd_title_help():
normal = ['title', 'help'] normal = ['title', 'help']
response = ( response = (
'Usage: title [show | set TITLE]\n' 'Usage: title {show | set TITLE}\n'
'Commands:\n' 'Commands:\n'
' title [show].......show the stream title\n' ' title [show].......show the stream title\n'
' title set TITLE....set the stream title to TITLE\n' ' title set TITLE....set the stream title to TITLE\n'

42
anonstream/control/spec/methods/tripcode.py ノーマルファイル
ファイルの表示

@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json
from quart import current_app
from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec.common import Str, End, ArgsJsonString
from anonstream.control.spec.utils import json_dumps_contiguous
from anonstream.helpers.tripcode import generate_tripcode
CONFIG = current_app.config
async def cmd_tripcode_help():
normal = ['tripcode', 'help']
response = (
'Usage: tripcode generate PASSWORD\n'
'Commands:\n'
' tripcode generate PASSWORD....show tripcode for given password\n'
'Definitions:\n'
' PASSWORD................json string, max length in config.toml (`chat.max_tripcode_password_length`)\n'
)
return normal, response
async def cmd_tripcode_generate(password):
if len(password) > CONFIG['CHAT_TRIPCODE_PASSWORD_MAX_LENGTH']:
raise CommandFailed(
f'password exceeded maximum configured length of '
f'{CONFIG["CHAT_TRIPCODE_PASSWORD_MAX_LENGTH"]} '
f'characters'
)
tripcode = generate_tripcode(password)
normal = ['tripcode', 'generate', json_dumps_contiguous(password)]
response = json.dumps(tripcode) + '\n'
return normal, response
SPEC = Str({
None: End(cmd_tripcode_help),
'help': End(cmd_tripcode_help),
'generate': ArgsJsonString(End(cmd_tripcode_generate)),
})

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
@ -58,10 +58,8 @@ async def cmd_user_get(user, attr):
except (TypeError, ValueError) as e: except (TypeError, ValueError) as e:
raise CommandFailed('value is not representable in json') from e raise CommandFailed('value is not representable in json') from e
normal = [ normal = [
'user', 'user', 'get',
'get', 'token', json_dumps_contiguous(user['token']),
'token',
json_dumps_contiguous(user['token']),
attr, attr,
] ]
response = value_json + '\n' response = value_json + '\n'
@ -74,10 +72,8 @@ async def cmd_user_set(user, attr, value):
if attr in USER_WEBSOCKET_ATTRS: if attr in USER_WEBSOCKET_ATTRS:
USERS_UPDATE_BUFFER.add(user['token']) USERS_UPDATE_BUFFER.add(user['token'])
normal = [ normal = [
'user', 'user', 'set',
'set', 'token', json_dumps_contiguous(user['token']),
'token',
json_dumps_contiguous(user['token']),
attr, attr,
json_dumps_contiguous(value), json_dumps_contiguous(value),
] ]
@ -86,11 +82,9 @@ async def cmd_user_set(user, attr, value):
async def cmd_user_eyes_show(user): async def cmd_user_eyes_show(user):
normal = [ normal = [
'user', 'user', 'eyes',
'eyes', 'token', json_dumps_contiguous(user['token']),
'token', 'show',
json_dumps_contiguous(user['token']),
'show'
] ]
response = json.dumps(user['eyes']['current']) + '\n' response = json.dumps(user['eyes']['current']) + '\n'
return normal, response return normal, response
@ -101,12 +95,9 @@ async def cmd_user_eyes_delete(user, eyes_id):
except KeyError: except KeyError:
pass pass
normal = [ normal = [
'user', 'user', 'eyes',
'eyes', 'token', json_dumps_contiguous(user['token']),
'token', 'delete', str(eyes_id),
json_dumps_contiguous(user['token']),
'delete',
str(eyes_id),
] ]
response = '' response = ''
return normal, response return normal, response

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import json import json

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import base64 import base64

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import hashlib import hashlib

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import base64 import base64

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import hashlib import hashlib

ファイルの表示

@ -1,7 +1,7 @@
# This file is pretty much entirely based on a snippet from asgi.py in # This file is pretty much entirely based on a snippet from asgi.py in
# the Quart repository (MIT, see README.md). That means it takes on the # the Quart repository (MIT, see README.md). That means it takes on the
# MIT licence I guess(???) If not then it's the same as every other file # MIT licence I guess(???) If not then it's the same as every other file
# by me: 2022 n9k <https://git.076.ne.jp/ninya9k>, AGPL 3.0 or any later # by me: 2022 n9k <https://gitler.moe/ninya9k>, AGPL 3.0 or any later
# version. # version.
import asyncio import asyncio

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import anonstream.routes.error import anonstream.routes.error

ファイルの表示

@ -1,8 +1,9 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import math import math
import re import re
from urllib.parse import quote
from quart import current_app, request, render_template, abort, make_response, redirect, url_for, send_from_directory from quart import current_app, request, render_template, abort, make_response, redirect, url_for, send_from_directory
from werkzeug.exceptions import Forbidden, NotFound, TooManyRequests from werkzeug.exceptions import Forbidden, NotFound, TooManyRequests
@ -22,7 +23,12 @@ CAPTCHA_SIGNER = current_app.captcha_signer
STATIC_DIRECTORY = current_app.root_path / 'static' STATIC_DIRECTORY = current_app.root_path / 'static'
@current_app.route('/') @current_app.route('/')
@with_user_from(request, fallback_to_token=True, ignore_allowedness=True) @with_user_from(
request,
fallback_to_token=True,
ignore_allowedness=True,
redundant_token_redirect=True,
)
async def home(timestamp, user_or_token): async def home(timestamp, user_or_token):
match user_or_token: match user_or_token:
case str() | None as token: case str() | None as token:
@ -128,12 +134,13 @@ async def access(timestamp, user_or_token):
failure_id = None failure_id = None
user = generate_and_add_user(timestamp, token, verified=True) user = generate_and_add_user(timestamp, token, verified=True)
if failure_id is not None: if failure_id is not None:
url = url_for('home', token=token, failure=failure_id) response = redirect(url_for('home', token=token, failure=failure_id), 303)
raise abort(redirect(url, 303)) else:
response = redirect(url_for('home', token=user['token']), 303)
response.headers['Set-Cookie'] = f'token={quote(user["token"])}; path=/'
case dict() as user: case dict() as user:
pass response = redirect(url_for('home', token=user['token']), 303)
url = url_for('home', token=user['token']) return response
return redirect(url, 303)
@current_app.route('/static/<path:filename>') @current_app.route('/static/<path:filename>')
@with_user_from(request) @with_user_from(request)

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from quart import current_app, request, render_template, redirect, url_for, escape, Markup from quart import current_app, request, render_template, redirect, url_for, escape, Markup

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import hashlib import hashlib
@ -8,7 +8,7 @@ import string
from functools import wraps from functools import wraps
from urllib.parse import quote, unquote from urllib.parse import quote, unquote
from quart import current_app, request, make_response, render_template, request, url_for, Markup from quart import current_app, request, make_response, render_template, redirect, url_for, Markup
from werkzeug.exceptions import BadRequest, Unauthorized, Forbidden from werkzeug.exceptions import BadRequest, Unauthorized, Forbidden
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
@ -87,7 +87,12 @@ def generate_and_add_user(
USERS_UPDATE_BUFFER.add(token) USERS_UPDATE_BUFFER.add(token)
return user return user
def with_user_from(context, fallback_to_token=False, ignore_allowedness=False): def with_user_from(
context,
fallback_to_token=False,
ignore_allowedness=False,
redundant_token_redirect=False,
):
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):
@ -129,6 +134,18 @@ def with_user_from(context, fallback_to_token=False, ignore_allowedness=False):
f"terminal when they started anonstream." f"terminal when they started anonstream."
)) ))
# If token from the client's cookie is same as the token in the URL
# query string, the client supports cookies. If we want, we can
# redirect the client to this same URL path but with the token
# parameter removed, since we'll pick up their token from their
# cookie anyway.
if (
redundant_token_redirect
and token_from_context is not None
and token_from_args == token_from_cookie
):
return redirect(context.path, 303)
# Create response # Create response
user = USERS_BY_TOKEN.get(token) user = USERS_BY_TOKEN.get(token)
if CONFIG['ACCESS_CAPTCHA'] and not broadcaster: if CONFIG['ACCESS_CAPTCHA'] and not broadcaster:

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio

ファイルの表示

@ -665,6 +665,11 @@ const on_websocket_message = async (event) => {
info_button.dataset.visible = ""; info_button.dataset.visible = "";
} }
// form input maxlengths
chat_form_comment.maxLength = receipt.maxlength.comment;
chat_appearance_form_name.maxLength = receipt.maxlength.name;
chat_appearance_form_password.maxLength = receipt.maxlength.password;
// chat form nonce // chat form nonce
chat_form_nonce.value = receipt.nonce; chat_form_nonce.value = receipt.nonce;
@ -936,7 +941,7 @@ info_button.addEventListener("click", (event) => {
info_button.removeAttribute("data-visible"); info_button.removeAttribute("data-visible");
}); });
video.addEventListener("error", (event) => { video.addEventListener("error", (event) => {
if (video.error !== null && video.error.message === "404: Not Found") { if (video.error !== null && video.networkState === video.NETWORK_NO_SOURCE) {
show_offline_screen(); show_offline_screen();
} }
if (stats !== null) { if (stats !== null) {

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import itertools import itertools

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio

ファイルの表示

@ -15,7 +15,7 @@
font-family: sans-serif; font-family: sans-serif;
font-size: 14pt; font-size: 14pt;
display: grid; display: grid;
grid-template-rows: calc(50% - 4rem) 1fr; grid-template-rows: calc(50% - 10vh + 2rem) 1fr;
height: 100vh; height: 100vh;
margin: 0; margin: 0;
padding: 1rem; padding: 1rem;
@ -37,20 +37,23 @@
border-radius: 2px; border-radius: 2px;
color: #ddd; color: #ddd;
font-size: 14pt; font-size: 14pt;
padding: 4px 5px; padding: 5px 6px;
width: 10ch; width: 10ch;
}
input[name="answer"]:hover {
background-color: #37373a;
transition: 0.25s; transition: 0.25s;
} }
input[type="submit"] { input[name="answer"]:focus {
background-color: black;
border-color: #3584e4;
}
input[type="submit"] {
font-size: 14pt; font-size: 14pt;
} padding-left: 8px;
p { padding-right: 8px;
}
p {
grid-column: 1 / span 2; grid-column: 1 / span 2;
text-align: center; text-align: center;
} }
</style> </style>
</head> </head>
<body> <body>

ファイルの表示

@ -41,7 +41,7 @@
<a href="#chat">chat</a> <a href="#chat">chat</a>
<a href="#both">both</a> <a href="#both">both</a>
</nav> </nav>
<footer>anonstream {{ version }} &mdash; <a href="https://git.076.ne.jp/ninya9k/anonstream" target="_blank">source</a></footer> <footer>anonstream {{ version }} &mdash; <a href="https://gitler.moe/ninya9k/anonstream" target="_blank">source</a></footer>
<script src="{{ url_for('static', filename='anonstream.js') }}" type="text/javascript"></script> <script src="{{ url_for('static', filename='anonstream.js') }}" type="text/javascript"></script>
</body> </body>
</html> </html>

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import operator import operator
@ -8,6 +8,7 @@ from math import inf
from quart import current_app from quart import current_app
from anonstream.events import notify_event_sockets
from anonstream.wrappers import try_except_log, with_timestamp, get_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
@ -98,6 +99,21 @@ def try_change_appearance(user, name, color, password, want_tripcode):
# Add to the users update buffer # Add to the users update buffer
USERS_UPDATE_BUFFER.add(user['token']) USERS_UPDATE_BUFFER.add(user['token'])
# Notify event sockets that a user's appearance was set
# NOTE: Changing appearance is currently NOT ratelimited.
# Applications using the event socket API should buffer these
# events or do something else to a prevent a potential denial of
# service.
notify_event_sockets({
'type': 'appearance',
'event': {
'token': user['token'],
'name': user['name'],
'color': user['color'],
'tripcode': user['tripcode'],
}
})
return errors return errors
def change_name(user, name, dry_run=False): def change_name(user, name, dry_run=False):

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import hashlib import hashlib

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import base64 import base64

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import re import re

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import secrets import secrets

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import base64 import base64

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from enum import Enum from enum import Enum

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio
@ -36,6 +36,11 @@ async def websocket_outbound(queue, user):
'scrollback': CONFIG['MAX_CHAT_SCROLLBACK'], 'scrollback': CONFIG['MAX_CHAT_SCROLLBACK'],
'digest': get_random_captcha_digest_for(user), 'digest': get_random_captcha_digest_for(user),
'pingpong': CONFIG['TASK_BROADCAST_PING'], 'pingpong': CONFIG['TASK_BROADCAST_PING'],
'maxlength': {
'comment': CONFIG['CHAT_COMMENT_MAX_LENGTH'],
'name': CONFIG['CHAT_NAME_MAX_LENGTH'],
'password': CONFIG['CHAT_TRIPCODE_PASSWORD_MAX_LENGTH'],
},
}) })
while True: while True:
payload = await queue.get() payload = await queue.get()

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import time import time

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k> # SPDX-FileCopyrightText: 2022 n9k <https://gitler.moe/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
if __name__ == '__main__': if __name__ == '__main__':

ファイルの表示

@ -91,4 +91,4 @@ stream/stream.m3u8
``` ```
[hwaccel]: https://trac.ffmpeg.org/wiki/HWAccelIntro [hwaccel]: https://trac.ffmpeg.org/wiki/HWAccelIntro
[plaintext]: https://git.076.ne.jp/ninya9k/anonstream/raw/branch/master/doc/guide/OBS.md [plaintext]: https://gitler.moe/ninya9k/anonstream/raw/branch/master/doc/guide/OBS.md

ファイルの表示

@ -128,6 +128,6 @@ systemd you can alternatively do `# systemctl reload tor`. If
everything went well, the directory will have been created and your everything went well, the directory will have been created and your
onion address will be in `$HIDDEN_SERVICE_DIR/hostname`. onion address will be in `$HIDDEN_SERVICE_DIR/hostname`.
[readme]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/README.md#setup [readme]: https://gitler.moe/ninya9k/anonstream/src/branch/master/README.md#setup
[tor]: https://gitlab.torproject.org/tpo/core/tor [tor]: https://gitlab.torproject.org/tpo/core/tor
[torrc]: https://support.torproject.org/#tbb-editing-torrc [torrc]: https://support.torproject.org/#tbb-editing-torrc