Merge branch 'dev'
このコミットが含まれているのは:
コミット
e9e0862445
|
@ -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
|
||||
```
|
49
README.md
49
README.md
|
@ -13,7 +13,8 @@ These mirrors also exist:
|
|||
|
||||
## 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:
|
||||
```sh
|
||||
|
@ -28,12 +29,14 @@ source venv/bin/activate
|
|||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Before you run it you should edit [/config.toml][config], e.g. these
|
||||
options:
|
||||
Before you run it you may want to edit the config ([/config.toml][config]).
|
||||
Most of the defaults are probably okay, but here are some that you might want
|
||||
to know what they do:
|
||||
|
||||
* `secret_key`:
|
||||
used for cryptography, make it any long random string
|
||||
(e.g. `$ dd if=/dev/urandom bs=16 count=1 | base64`)
|
||||
used for cryptography, make it any long random string (e.g.
|
||||
`$ dd if=/dev/urandom bs=16 count=1 | base64`), definitely set this
|
||||
yourself before running in "production" (whatever that is for you)
|
||||
|
||||
* `segments/directory`:
|
||||
directory containing stream segments, the default is `stream/` in
|
||||
|
@ -49,15 +52,16 @@ options:
|
|||
|
||||
Run it:
|
||||
```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
|
||||
the site. When you started the webserver some credentials were
|
||||
printed in the terminal; you can log in with those at
|
||||
`http://localhost:5051/login` (requires cookies).
|
||||
the site. When you started the webserver some credentials were printed
|
||||
in the terminal; you can log in with those at
|
||||
`http://localhost:5051/login`.
|
||||
|
||||
The only things left are (1) streaming, and (2) letting other people
|
||||
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
|
||||
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
|
||||
|
||||
anonstream is AGPL 3.0 or later, see
|
||||
|
@ -104,6 +132,7 @@ anonstream is AGPL 3.0 or later, see
|
|||
([BSD 3-Clause][werkzeug])
|
||||
|
||||
[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
|
||||
[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
|
||||
|
|
|
@ -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
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import toml
|
||||
from quart_compress import Compress
|
||||
|
||||
from anonstream.config import update_flask_from_toml
|
||||
|
@ -12,15 +11,12 @@ from anonstream.quart import Quart
|
|||
|
||||
compress = Compress()
|
||||
|
||||
def create_app(config_file):
|
||||
def create_app(toml_config):
|
||||
app = Quart('anonstream')
|
||||
app.jinja_options['trim_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)
|
||||
|
||||
print('Broadcaster username:', app.config['AUTH_USERNAME'])
|
||||
print('Broadcaster password:', auth_password)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 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
|
||||
|
||||
class Fail(Exception):
|
||||
class CommandFailed(Exception):
|
||||
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.methods.chat import SPEC as SPEC_CHAT
|
||||
from anonstream.control.spec.methods.exit import SPEC as SPEC_EXIT
|
||||
|
@ -25,10 +28,7 @@ async def parse(request):
|
|||
while True:
|
||||
try:
|
||||
spec, n_consumed, more_args = spec.consume(words, index)
|
||||
except NoParse as e:
|
||||
normal, response = None, e.args[0] + '\n'
|
||||
break
|
||||
except Ambiguous as e:
|
||||
except ParseException as e:
|
||||
normal, response = None, e.args[0] + '\n'
|
||||
break
|
||||
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
|
||||
|
||||
from anonstream.control.exceptions import Exit, Fail
|
||||
from anonstream.control.exceptions import ControlSocketExit, CommandFailed
|
||||
from anonstream.control.parse import parse
|
||||
|
||||
def start_control_server_at(address):
|
||||
|
@ -15,9 +18,9 @@ async def serve_control_client(reader, writer):
|
|||
else:
|
||||
try:
|
||||
normal, response = await parse(request)
|
||||
except Fail as e:
|
||||
except CommandFailed as e:
|
||||
normal, response = None, e.args[0] + '\n'
|
||||
except Exit:
|
||||
except ControlSocketExit:
|
||||
writer.close()
|
||||
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
|
||||
|
||||
class Ambiguous(Exception):
|
||||
class NoParse(ParseException):
|
||||
pass
|
||||
|
||||
class Ambiguous(ParseException):
|
||||
pass
|
||||
|
||||
class BadArgument(ParseException):
|
||||
pass
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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.exceptions import Exit
|
||||
from anonstream.control.exceptions import ControlSocketExit
|
||||
|
||||
async def cmd_exit():
|
||||
raise Exit
|
||||
raise ControlSocketExit
|
||||
|
||||
async def cmd_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
|
||||
|
||||
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
|
||||
|
||||
from anonstream.control.exceptions import Fail
|
||||
from anonstream.control.exceptions import CommandFailed
|
||||
from anonstream.control.spec import Spec, NoParse
|
||||
from anonstream.control.spec.common import Str, End, ArgsJsonString
|
||||
from anonstream.control.spec.utils import get_item, json_dumps_contiguous
|
||||
|
@ -27,7 +30,7 @@ async def cmd_title_set(title):
|
|||
try:
|
||||
await set_stream_title(title)
|
||||
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)]
|
||||
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
|
||||
|
||||
from quart import current_app
|
||||
|
||||
from anonstream.control.exceptions import Fail
|
||||
from anonstream.control.spec import NoParse
|
||||
from anonstream.control.exceptions import CommandFailed
|
||||
from anonstream.control.spec import BadArgument
|
||||
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.utils.user import USER_WEBSOCKET_ATTRS
|
||||
|
@ -17,7 +20,7 @@ class ArgsJsonTokenUser(ArgsJsonString):
|
|||
try:
|
||||
user = USERS_BY_TOKEN[token]
|
||||
except KeyError:
|
||||
raise NoParse(f'no user with token {token!r}')
|
||||
raise BadArgument(f'no user with token {token!r}')
|
||||
return user
|
||||
|
||||
class ArgsJsonHashUser(ArgsString):
|
||||
|
@ -26,7 +29,7 @@ class ArgsJsonHashUser(ArgsString):
|
|||
if user['token_hash'] == token_hash:
|
||||
break
|
||||
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
|
||||
|
||||
def ArgsUser(spec):
|
||||
|
@ -69,11 +72,11 @@ async def cmd_user_get(user, attr):
|
|||
try:
|
||||
value = user[attr]
|
||||
except KeyError as e:
|
||||
raise Fail('user has no such attribute') from e
|
||||
raise CommandFailed('user has no such attribute') from e
|
||||
try:
|
||||
value_json = json.dumps(value)
|
||||
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 = [
|
||||
'user',
|
||||
'get',
|
||||
|
@ -86,7 +89,7 @@ async def cmd_user_get(user, attr):
|
|||
|
||||
async def cmd_user_set(user, attr, value):
|
||||
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
|
||||
if attr in USER_WEBSOCKET_ATTRS:
|
||||
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
|
||||
|
||||
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 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
import time
|
||||
|
|
11
app.py
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)
|
|
@ -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)
|
読み込み中…
新しいイシューから参照