Merge branch 'dev'

このコミットが含まれているのは:
n9k 2022-06-16 03:18:46 +00:00
コミット e9e0862445
44個のファイルの変更287行の追加74行の削除

74
HACKING.md ノーマルファイル
ファイルの表示

@ -0,0 +1,74 @@
## Hacking
By default anonstream has two APIs it exposes through two UNIX sockets:
the control socket `control.sock` and the event socket `event.sock`. If
the platform you are using does not support UNIX sockets, they can be
disabled in the config.
### Control socket
The control socket allows reading and modifying internal state, e.g.
setting the title or changing a user's name. Currently the control
socket has checks to see if what you're doing is sane, but they're not
comprehensive; you could craft commands that lead to undefined
behaviour. If you have `socat`, you can use the control socket
interactively like this:
```sh
rlwrap socat STDIN UNIX-CONNECT:control.sock
```
`rlwrap` only adds line editing and is optional. If you don't have it
you can still get (inferior) line editing by doing:
```sh
socat READLINE UNIX-CONNECT:control.sock
```
Once connected, type "help" and press enter to get a list of commands.
### Event socket
The event socket is a read-only socket that sends out internal events as
they happen. Currently the only supported event is a chat message being
added. The intended use is to hook into other applications that depend
on chat, e.g. text-to-speech or Twitch Plays Pokémon.
View events like this:
```sh
socat UNIX-CONNECT:event.sock STDOUT
```
Sidenote, this will still read from stdin, and if you send anything on
stdin the event socket will close itself. If you want to ignore stdin,
I couldn't figure out how to get `socat` to do it so you can do it like
this:
```sh
cat > /dev/null | socat UNIX-CONNECT:event.sock STDOUT
```
If you do this `cat` will not exit when the connection is closed so you
will probably have to interrupt it with `^C`.
#### Examples
If you have `jq` you can view prettified events like this:
```sh
socat UNIX-CONNECT:event.sock STDOUT | jq
```
(On older versions of `jq` you have to say `jq .` when reading from
stdin.)
Use this to get each new chat message on a new line:
```sh
socat UNIX-CONNECT:event.sock STDOUT | jq 'select(.type == "message") | .event.nomarkup'
```
##### Text-to-speech
This command will take each new chat message with the prefix "!say ",
strip the prefix, and synthesize the rest of the message as speech using
`espeak`:
```sh
socat UNIX-CONNECT:event.sock STDOUT \
| jq --unbuffered 'select(.type == "message") | .event.nomarkup' \
| grep -E --line-buffered '^"!say ' \
| sed -Eu 's/^"!say /"/' \
| jq -r --unbuffered \
| espeak
```

ファイルの表示

@ -13,7 +13,8 @@ These mirrors also exist:
## Setup ## Setup
You must have Python 3.10 at a minimum. You must have Python 3.10 at a minimum. You can check your version of Python
with `python --version`.
Clone the repo: Clone the repo:
```sh ```sh
@ -28,12 +29,14 @@ source venv/bin/activate
python -m pip install -r requirements.txt python -m pip install -r requirements.txt
``` ```
Before you run it you should edit [/config.toml][config], e.g. these Before you run it you may want to edit the config ([/config.toml][config]).
options: Most of the defaults are probably okay, but here are some that you might want
to know what they do:
* `secret_key`: * `secret_key`:
used for cryptography, make it any long random string used for cryptography, make it any long random string (e.g.
(e.g. `$ dd if=/dev/urandom bs=16 count=1 | base64`) `$ dd if=/dev/urandom bs=16 count=1 | base64`), definitely set this
yourself before running in "production" (whatever that is for you)
* `segments/directory`: * `segments/directory`:
directory containing stream segments, the default is `stream/` in directory containing stream segments, the default is `stream/` in
@ -49,15 +52,16 @@ options:
Run it: Run it:
```sh ```sh
python -m uvicorn app:app --port 5051 python -m anonstream
``` ```
This will start a webserver listening on localhost port 5051. This will start a webserver listening on the local host at port 5051 (use
`--port PORT` to override).
If you go to `http://localhost:5051` in a web browser now you should see If you go to `http://localhost:5051` in a web browser now you should see
the site. When you started the webserver some credentials were the site. When you started the webserver some credentials were printed
printed in the terminal; you can log in with those at in the terminal; you can log in with those at
`http://localhost:5051/login` (requires cookies). `http://localhost:5051/login`.
The only things left are (1) streaming, and (2) letting other people The only things left are (1) streaming, and (2) letting other people
access your stream. [/STREAMING.md][streaming] has instructions for access your stream. [/STREAMING.md][streaming] has instructions for
@ -65,6 +69,30 @@ setting up OBS Studio and a Tor onion service. If you want to use
different streaming software and put your stream on the Internet some different streaming software and put your stream on the Internet some
other way, read those instructions and copy the gist. other way, read those instructions and copy the gist.
## Running
Start anonstream like this:
```sh
python -m anonstream
```
The default port is 5051. Append `--help` to see options.
If you want to use a different ASGI server, point it to the app factory
at `asgi:create_app()`. For example with `uvicorn`:
```sh
python -m uvicorn asgi:create_app --factory --port 5051
```
In either case you can explicitly set the location of the config file
using the `ANONSTREAM_CONFIG` environment variable.
## Hacking
anonstream has APIs for accessing internal state and hooking into
internal events. They can be used by humans and other programs. See
[/HACKING.md][hacking].
## Copying ## Copying
anonstream is AGPL 3.0 or later, see anonstream is AGPL 3.0 or later, see
@ -104,6 +132,7 @@ anonstream is AGPL 3.0 or later, see
([BSD 3-Clause][werkzeug]) ([BSD 3-Clause][werkzeug])
[config]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/config.toml [config]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/config.toml
[hacking]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/HACKING.md
[licence]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/LICENSES/AGPL-3.0-or-later.md [licence]: https://git.076.ne.jp/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://git.076.ne.jp/ninya9k/anonstream/src/branch/master/anonstream/static/settings.svg
[streaming]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/STREAMING.md [streaming]: https://git.076.ne.jp/ninya9k/anonstream/src/branch/master/STREAMING.md

ファイルの表示

@ -1,9 +1,8 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k] # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from collections import OrderedDict from collections import OrderedDict
import toml
from quart_compress import Compress from quart_compress import Compress
from anonstream.config import update_flask_from_toml from anonstream.config import update_flask_from_toml
@ -12,15 +11,12 @@ from anonstream.quart import Quart
compress = Compress() compress = Compress()
def create_app(config_file): def create_app(toml_config):
app = Quart('anonstream') app = Quart('anonstream')
app.jinja_options['trim_blocks'] = True app.jinja_options['trim_blocks'] = True
app.jinja_options['lstrip_blocks'] = True app.jinja_options['lstrip_blocks'] = True
with open(config_file) as fp:
toml_config = toml.load(fp)
auth_password = update_flask_from_toml(toml_config, app.config) auth_password = update_flask_from_toml(toml_config, app.config)
print('Broadcaster username:', app.config['AUTH_USERNAME']) print('Broadcaster username:', app.config['AUTH_USERNAME'])
print('Broadcaster password:', auth_password) print('Broadcaster password:', auth_password)

51
anonstream/__main__.py ノーマルファイル
ファイルの表示

@ -0,0 +1,51 @@
import argparse
import os
import toml
import uvicorn
from anonstream import create_app
DEFAULT_PORT = 5051
DEFAULT_CONFIG = 'config.toml'
def want_rel(path):
'''
Prepend './' to relative paths.
>>> want_rel('/some/abs/path')
'/some/abs/path'
>>> want_rel('config.toml')
'./config.toml'
'''
if os.path.isabs(path):
return path
else:
return os.path.join('.', path)
formatter = lambda prog: argparse.HelpFormatter(prog, max_help_position=26)
parser = argparse.ArgumentParser(
'python -m anonstream',
description='Start the anonstream webserver locally.',
formatter_class=formatter,
)
parser.add_argument(
'--config', '-c',
metavar='FILE',
default=os.environ.get('ANONSTREAM_CONFIG', 'config.toml'),
help=(
'location of config.toml '
f'(default: $ANONSTREAM_CONFIG or {want_rel(DEFAULT_CONFIG)})'
),
)
parser.add_argument(
'--port', '-p',
type=int,
default=DEFAULT_PORT,
help=f'bind webserver to this port (default: {DEFAULT_PORT})',
)
args = parser.parse_args()
with open(args.config) as fp:
config = toml.load(fp)
app = create_app(config)
uvicorn.run(app, port=args.port)

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k] # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import time import time

ファイルの表示

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

ファイルの表示

@ -1,5 +1,8 @@
class Exit(Exception): # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
class ControlSocketExit(Exception):
pass pass
class Fail(Exception): class CommandFailed(Exception):
pass pass

ファイルの表示

@ -1,4 +1,7 @@
from anonstream.control.spec import NoParse, Ambiguous, Parsed # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
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.chat import SPEC as SPEC_CHAT from anonstream.control.spec.methods.chat import SPEC as SPEC_CHAT
from anonstream.control.spec.methods.exit import SPEC as SPEC_EXIT from anonstream.control.spec.methods.exit import SPEC as SPEC_EXIT
@ -25,10 +28,7 @@ async def parse(request):
while True: while True:
try: try:
spec, n_consumed, more_args = spec.consume(words, index) spec, n_consumed, more_args = spec.consume(words, index)
except NoParse as e: except ParseException as e:
normal, response = None, e.args[0] + '\n'
break
except Ambiguous as e:
normal, response = None, e.args[0] + '\n' normal, response = None, e.args[0] + '\n'
break break
except Parsed as e: except Parsed as e:

ファイルの表示

@ -1,6 +1,9 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import asyncio import asyncio
from anonstream.control.exceptions import Exit, Fail from anonstream.control.exceptions import ControlSocketExit, CommandFailed
from anonstream.control.parse import parse from anonstream.control.parse import parse
def start_control_server_at(address): def start_control_server_at(address):
@ -15,9 +18,9 @@ async def serve_control_client(reader, writer):
else: else:
try: try:
normal, response = await parse(request) normal, response = await parse(request)
except Fail as e: except CommandFailed as e:
normal, response = None, e.args[0] + '\n' normal, response = None, e.args[0] + '\n'
except Exit: except ControlSocketExit:
writer.close() writer.close()
break break

ファイルの表示

@ -1,7 +1,16 @@
class NoParse(Exception): # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
class ParseException(Exception):
pass pass
class Ambiguous(Exception): class NoParse(ParseException):
pass
class Ambiguous(ParseException):
pass
class BadArgument(ParseException):
pass pass
class Parsed(Exception): class Parsed(Exception):

ファイルの表示

@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
from anonstream.control.spec import Spec, NoParse, Ambiguous, Parsed from anonstream.control.spec import Spec, NoParse, Ambiguous, Parsed

ファイルの表示

@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import itertools import itertools
from anonstream.chat import delete_chat_messages from anonstream.chat import delete_chat_messages

ファイルの表示

@ -1,8 +1,11 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
from anonstream.control.spec.common import Str, End from anonstream.control.spec.common import Str, End
from anonstream.control.exceptions import Exit from anonstream.control.exceptions import ControlSocketExit
async def cmd_exit(): async def cmd_exit():
raise Exit raise ControlSocketExit
async def cmd_exit_help(): async def cmd_exit_help():
normal = ['exit', 'help'] normal = ['exit', 'help']

ファイルの表示

@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
from anonstream.control.spec.common import Str, End from anonstream.control.spec.common import Str, End
async def cmd_help(): async def cmd_help():

ファイルの表示

@ -1,6 +1,9 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
from anonstream.control.exceptions import Fail from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec import Spec, NoParse from anonstream.control.spec import Spec, NoParse
from anonstream.control.spec.common import Str, End, ArgsJsonString from anonstream.control.spec.common import Str, End, ArgsJsonString
from anonstream.control.spec.utils import get_item, json_dumps_contiguous from anonstream.control.spec.utils import get_item, json_dumps_contiguous
@ -27,7 +30,7 @@ async def cmd_title_set(title):
try: try:
await set_stream_title(title) await set_stream_title(title)
except OSError as e: except OSError as e:
raise Fail(f'could not set title: {e}') from e raise CommandFailed(f'could not set title: {e}') from e
normal = ['title', 'set', json_dumps_contiguous(title)] normal = ['title', 'set', json_dumps_contiguous(title)]
response = '' response = ''
return normal, response return normal, response

ファイルの表示

@ -1,9 +1,12 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
from quart import current_app from quart import current_app
from anonstream.control.exceptions import Fail from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec import NoParse from anonstream.control.spec import BadArgument
from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonString from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonString
from anonstream.control.spec.utils import get_item, json_dumps_contiguous from anonstream.control.spec.utils import get_item, json_dumps_contiguous
from anonstream.utils.user import USER_WEBSOCKET_ATTRS from anonstream.utils.user import USER_WEBSOCKET_ATTRS
@ -17,7 +20,7 @@ class ArgsJsonTokenUser(ArgsJsonString):
try: try:
user = USERS_BY_TOKEN[token] user = USERS_BY_TOKEN[token]
except KeyError: except KeyError:
raise NoParse(f'no user with token {token!r}') raise BadArgument(f'no user with token {token!r}')
return user return user
class ArgsJsonHashUser(ArgsString): class ArgsJsonHashUser(ArgsString):
@ -26,7 +29,7 @@ class ArgsJsonHashUser(ArgsString):
if user['token_hash'] == token_hash: if user['token_hash'] == token_hash:
break break
else: else:
raise NoParse(f'no user with token_hash {token_hash!r}') raise BadArgument(f'no user with token_hash {token_hash!r}')
return user return user
def ArgsUser(spec): def ArgsUser(spec):
@ -69,11 +72,11 @@ async def cmd_user_get(user, attr):
try: try:
value = user[attr] value = user[attr]
except KeyError as e: except KeyError as e:
raise Fail('user has no such attribute') from e raise CommandFailed('user has no such attribute') from e
try: try:
value_json = json.dumps(value) value_json = json.dumps(value)
except (TypeError, ValueError) as e: except (TypeError, ValueError) as e:
raise Fail('value is not representable in json') from e raise CommandFailed('value is not representable in json') from e
normal = [ normal = [
'user', 'user',
'get', 'get',
@ -86,7 +89,7 @@ async def cmd_user_get(user, attr):
async def cmd_user_set(user, attr, value): async def cmd_user_set(user, attr, value):
if attr not in user: if attr not in user:
raise Fail(f'user has no attribute {attr!r}') raise CommandFailed(f'user has no attribute {attr!r}')
user[attr] = value 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'])

ファイルの表示

@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
import json import json
def get_item(index, words): def get_item(index, words):

ファイルの表示

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

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k] # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import hashlib import hashlib

ファイルの表示

@ -1,3 +1,9 @@
# 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
# 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
# version.
import asyncio import asyncio
from werkzeug.wrappers import Response as WerkzeugResponse from werkzeug.wrappers import Response as WerkzeugResponse

ファイルの表示

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

ファイルの表示

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

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k] # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import operator import operator

ファイルの表示

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k] # SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/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://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import time import time

11
app.py
ファイルの表示

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k]
# SPDX-License-Identifier: AGPL-3.0-or-later
import os
import anonstream
config_file = os.path.join(os.path.dirname(__file__), 'config.toml')
app = anonstream.create_app(config_file)
if __name__ == '__main__':
app.run(port=5051, debug=True)

26
asgi.py ノーマルファイル
ファイルの表示

@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2022 n9k <https://git.076.ne.jp/ninya9k>
# SPDX-License-Identifier: AGPL-3.0-or-later
if __name__ == '__main__':
import sys
message = (
'To start anonstream, run one of:\n'
' $ python -m anonstream\n'
' $ python -m uvicorn asgi:create_app --factory --port 5051\n'
)
print(message, file=sys.stderr, end='')
exit(1)
import os
import toml
import anonstream
config_file = os.environ.get(
'ANONSTREAM_CONFIG',
os.path.join(os.path.dirname(__file__), 'config.toml'),
)
def create_app():
with open(config_file) as fp:
config = toml.load(fp)
return anonstream.create_app(config)