mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-27 13:34:28 +00:00
Make the bot runnable
This commit is contained in:
parent
ef645ab143
commit
192b4cf3d6
29 changed files with 247 additions and 315 deletions
|
@ -3,6 +3,7 @@
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/royalnet" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/royalnet" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/royalnet.egg-info" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.8 (royalnet-1MWM6-kd-py3.8)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.8 (royalnet-1MWM6-kd-py3.8)" jdkType="Python SDK" />
|
||||||
|
|
39
poetry.lock
generated
39
poetry.lock
generated
|
@ -82,7 +82,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "7.0"
|
version = "7.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "main"
|
||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
marker = "sys_platform == \"win32\""
|
marker = "sys_platform == \"win32\""
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
|
@ -90,6 +90,17 @@ optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Log formatting with colors!"
|
||||||
|
name = "colorlog"
|
||||||
|
optional = true
|
||||||
|
python-versions = "*"
|
||||||
|
version = "4.0.2"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||||
|
@ -142,7 +153,7 @@ voice = ["PyNaCl (1.3.0)"]
|
||||||
[package.source]
|
[package.source]
|
||||||
reference = "09a08f9a9f126aa1f55c2444eb70508d1d52f8d9"
|
reference = "09a08f9a9f126aa1f55c2444eb70508d1d52f8d9"
|
||||||
type = "git"
|
type = "git"
|
||||||
url = "https://github.com/Steffo99/discord.py.git"
|
url = "https://github.com/Steffo99/discord.py"
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Discover and load entry points from installed packages."
|
description = "Discover and load entry points from installed packages."
|
||||||
|
@ -328,7 +339,7 @@ description = "pytest: simple powerful testing with Python"
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "5.2.2"
|
version = "5.2.3"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
atomicwrites = ">=1.0"
|
atomicwrites = ">=1.0"
|
||||||
|
@ -562,6 +573,7 @@ version = "2019.11.5"
|
||||||
alchemy_easy = ["sqlalchemy", "psycopg2_binary"]
|
alchemy_easy = ["sqlalchemy", "psycopg2_binary"]
|
||||||
alchemy_hard = ["sqlalchemy", "psycopg2"]
|
alchemy_hard = ["sqlalchemy", "psycopg2"]
|
||||||
bard = ["ffmpeg_python", "youtube_dl"]
|
bard = ["ffmpeg_python", "youtube_dl"]
|
||||||
|
colorlog = ["colorlog"]
|
||||||
constellation = ["starlette", "uvicorn"]
|
constellation = ["starlette", "uvicorn"]
|
||||||
discord = ["discord.py", "pynacl"]
|
discord = ["discord.py", "pynacl"]
|
||||||
herald = ["websockets"]
|
herald = ["websockets"]
|
||||||
|
@ -569,7 +581,7 @@ sentry = ["sentry_sdk"]
|
||||||
telegram = ["python_telegram_bot"]
|
telegram = ["python_telegram_bot"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "a51bc903341dd7fb6c2923e4f0a45654ccc9c011182ad9a67c2c80d24f62fb26"
|
content-hash = "48fd4f6a0a25ffaf89999db4a999774b8e22794e07488b032ac0f244049caa5f"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
|
@ -660,6 +672,10 @@ colorama = [
|
||||||
{file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"},
|
{file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"},
|
||||||
{file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"},
|
{file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"},
|
||||||
]
|
]
|
||||||
|
colorlog = [
|
||||||
|
{file = "colorlog-4.0.2-py2.py3-none-any.whl", hash = "sha256:450f52ea2a2b6ebb308f034ea9a9b15cea51e65650593dca1da3eb792e4e4981"},
|
||||||
|
{file = "colorlog-4.0.2.tar.gz", hash = "sha256:3cf31b25cbc8f86ec01fef582ef3b840950dea414084ed19ab922c8b493f9b42"},
|
||||||
|
]
|
||||||
cryptography = [
|
cryptography = [
|
||||||
{file = "cryptography-2.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"},
|
{file = "cryptography-2.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"},
|
||||||
{file = "cryptography-2.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2"},
|
{file = "cryptography-2.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2"},
|
||||||
|
@ -845,8 +861,8 @@ pyparsing = [
|
||||||
{file = "pyparsing-2.4.5.tar.gz", hash = "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"},
|
{file = "pyparsing-2.4.5.tar.gz", hash = "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"},
|
||||||
]
|
]
|
||||||
pytest = [
|
pytest = [
|
||||||
{file = "pytest-5.2.2-py3-none-any.whl", hash = "sha256:58cee9e09242937e136dbb3dab466116ba20d6b7828c7620f23947f37eb4dae4"},
|
{file = "pytest-5.2.3-py3-none-any.whl", hash = "sha256:b6cf7ad9064049ee486586b3a0ddd70dc5136c40e1147e7d286efd77ba66c5eb"},
|
||||||
{file = "pytest-5.2.2.tar.gz", hash = "sha256:27abc3fef618a01bebb1f0d6d303d2816a99aa87a5968ebc32fe971be91eb1e6"},
|
{file = "pytest-5.2.3.tar.gz", hash = "sha256:15837d2880cb94821087bc07476892ea740696b20e90288fd6c19e44b435abdb"},
|
||||||
]
|
]
|
||||||
python-dateutil = [
|
python-dateutil = [
|
||||||
{file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
|
{file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
|
||||||
|
@ -936,24 +952,13 @@ websockets = [
|
||||||
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
|
{file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
|
{file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"},
|
{file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"},
|
|
||||||
{file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"},
|
|
||||||
{file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"},
|
{file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"},
|
||||||
{file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"},
|
{file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"},
|
{file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"},
|
{file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"},
|
{file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"},
|
|
||||||
{file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"},
|
|
||||||
{file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"},
|
{file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"},
|
||||||
{file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"},
|
{file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"},
|
||||||
{file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"},
|
|
||||||
{file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"},
|
|
||||||
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
|
||||||
]
|
]
|
||||||
yarl = [
|
yarl = [
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
# telegram
|
# telegram
|
||||||
python_telegram_bot = {version="^12.2.0", optional=true}
|
python_telegram_bot = {version="^12.2.0", optional=true}
|
||||||
# discord
|
# discord
|
||||||
"discord.py" = {git="https://github.com/Steffo99/discord.py.git", optional=true} # discord.py 1.2.4 is missing Go Live related methods
|
"discord.py" = {git="https://github.com/Steffo99/discord.py", optional=true} # discord.py 1.2.4 is missing Go Live related methods
|
||||||
pynacl = {version="^1.3.0", optional=true} # This requires libffi-dev and python3.*-dev to be installed on Linux systems
|
pynacl = {version="^1.3.0", optional=true} # This requires libffi-dev and python3.*-dev to be installed on Linux systems
|
||||||
# bard
|
# bard
|
||||||
ffmpeg_python = {version="~0.2.0", optional=true}
|
ffmpeg_python = {version="~0.2.0", optional=true}
|
||||||
|
@ -40,6 +40,8 @@
|
||||||
sentry_sdk = {version="~0.13.2", optional=true}
|
sentry_sdk = {version="~0.13.2", optional=true}
|
||||||
# herald
|
# herald
|
||||||
websockets = {version="^8.1", optional=true}
|
websockets = {version="^8.1", optional=true}
|
||||||
|
# colorlog
|
||||||
|
colorlog = {version="^4.0.2", optional=true}
|
||||||
|
|
||||||
# Development dependencies
|
# Development dependencies
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
@ -55,7 +57,7 @@
|
||||||
constellation = ["starlette", "uvicorn"]
|
constellation = ["starlette", "uvicorn"]
|
||||||
sentry = ["sentry_sdk"]
|
sentry = ["sentry_sdk"]
|
||||||
herald = ["websockets"]
|
herald = ["websockets"]
|
||||||
|
colorlog = ["colorlog"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry>=0.12"]
|
requires = ["poetry>=0.12"]
|
||||||
|
|
|
@ -1 +1,14 @@
|
||||||
__version__ = "5.1a1"
|
__version__ = "5.1a1"
|
||||||
|
|
||||||
|
from . import alchemy, bard, commands, constellation, herald, backpack, serf, utils
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"alchemy",
|
||||||
|
"bard",
|
||||||
|
"commands",
|
||||||
|
"constellation",
|
||||||
|
"herald",
|
||||||
|
"serf",
|
||||||
|
"utils",
|
||||||
|
"backpack",
|
||||||
|
]
|
||||||
|
|
|
@ -2,10 +2,9 @@ import click
|
||||||
import typing
|
import typing
|
||||||
import importlib
|
import importlib
|
||||||
import royalnet as r
|
import royalnet as r
|
||||||
import royalherald as rh
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import keyring
|
import keyring
|
||||||
import logging
|
from logging import Formatter, StreamHandler, getLogger, Logger
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
|
@ -13,55 +12,53 @@ import logging
|
||||||
help="Enable/disable the Telegram bot.")
|
help="Enable/disable the Telegram bot.")
|
||||||
@click.option("--discord/--no-discord", default=None,
|
@click.option("--discord/--no-discord", default=None,
|
||||||
help="Enable/disable the Discord bot.")
|
help="Enable/disable the Discord bot.")
|
||||||
@click.option("--webserver/--no-webserver", default=None,
|
@click.option("--constellation/--no-constellation", default=None,
|
||||||
help="Enable/disable the Web server.")
|
help="Enable/disable the Constellation web server.")
|
||||||
@click.option("--webserver-port", default=8001,
|
@click.option("--herald/--no-herald", default=None,
|
||||||
help="The port on which the web server will listen on.")
|
help="Enable/disable the integrated Herald server."
|
||||||
@click.option("-d", "--database", type=str, default=None,
|
" If turned off, Royalnet will try to connect to another server.")
|
||||||
help="The PostgreSQL database path.")
|
@click.option("--remote-herald-address", type=str, default=None,
|
||||||
@click.option("-p", "--packs", type=str, multiple=True, default=[],
|
help="If --no-herald is specified, connect to the Herald server at this URL instead.")
|
||||||
help="The names of the Packs that should be used.")
|
@click.option("-c", "--constellation-port", default=44445,
|
||||||
@click.option("-n", "--network-address", type=str, default=None,
|
help="The port on which the Constellation will serve webpages on.")
|
||||||
help="The Network server URL to connect to.")
|
@click.option("-a", "--alchemy-url", type=str, default=None,
|
||||||
@click.option("-l", "--local-network-server", is_flag=True, default=False,
|
help="The Alchemy database path.")
|
||||||
help="Locally run a Network server and bind it to port 44444. Overrides -n.")
|
@click.option("-h", "--herald-port", type=int, default=44444,
|
||||||
@click.option("--local-network-server-port", type=int, default=44444,
|
help="The port on which the Herald should be running.")
|
||||||
help="The port on which the local network will be ran.")
|
@click.option("-p", "--pack", type=str, multiple=True, default=tuple(),
|
||||||
|
help="Import the pack with the specified name and use it in the Royalnet instance.")
|
||||||
@click.option("-s", "--secrets-name", type=str, default="__default__",
|
@click.option("-s", "--secrets-name", type=str, default="__default__",
|
||||||
help="The name in the keyring that the secrets are stored with.")
|
help="The name in the keyring that the secrets are stored with.")
|
||||||
@click.option("-v", "--verbose", is_flag=True, default=False,
|
@click.option("-l", "--log-level", type=str, default="INFO",
|
||||||
help="Print all possible debug information.")
|
help="Select how much information you want to be printed on the console."
|
||||||
|
" Valid log levels are: FATAL/ERROR/WARNING/INFO/DEBUG")
|
||||||
def run(telegram: typing.Optional[bool],
|
def run(telegram: typing.Optional[bool],
|
||||||
discord: typing.Optional[bool],
|
discord: typing.Optional[bool],
|
||||||
webserver: typing.Optional[bool],
|
constellation: typing.Optional[bool],
|
||||||
webserver_port: typing.Optional[int],
|
herald: typing.Optional[bool],
|
||||||
database: typing.Optional[str],
|
remote_herald_address: typing.Optional[str],
|
||||||
packs: typing.Tuple[str],
|
constellation_port: int,
|
||||||
network_address: typing.Optional[str],
|
alchemy_url: typing.Optional[str],
|
||||||
local_network_server: bool,
|
herald_port: int,
|
||||||
local_network_server_port: int,
|
pack: typing.Tuple[str],
|
||||||
secrets_name: str,
|
secrets_name: str,
|
||||||
verbose: bool):
|
log_level: str):
|
||||||
# Setup logging
|
# Initialize logging
|
||||||
if verbose:
|
royalnet_log: Logger = getLogger("royalnet")
|
||||||
core_logger = logging.root
|
royalnet_log.setLevel(log_level)
|
||||||
core_logger.setLevel(logging.DEBUG)
|
stream_handler = StreamHandler()
|
||||||
stream_handler = logging.StreamHandler()
|
stream_handler.formatter = Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{")
|
||||||
stream_handler.formatter = logging.Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{")
|
royalnet_log.addHandler(stream_handler)
|
||||||
core_logger.addHandler(stream_handler)
|
|
||||||
core_logger.debug("Logging setup complete.")
|
|
||||||
|
|
||||||
# Get the network password
|
def get_secret(username: str):
|
||||||
network_password = keyring.get_password(f"Royalnet/{secrets_name}", "network")
|
return keyring.get_password(f"Royalnet/{secrets_name}", username)
|
||||||
|
|
||||||
# Get the sentry dsn
|
|
||||||
sentry_dsn = keyring.get_password(f"Royalnet/{secrets_name}", "sentry")
|
|
||||||
|
|
||||||
# Enable / Disable interfaces
|
# Enable / Disable interfaces
|
||||||
interfaces = {
|
interfaces = {
|
||||||
"telegram": telegram,
|
"telegram": telegram,
|
||||||
"discord": discord,
|
"discord": discord,
|
||||||
"webserver": webserver
|
"herald": herald,
|
||||||
|
"constellation": constellation,
|
||||||
}
|
}
|
||||||
# If any interface is True, then the undefined ones should be False
|
# If any interface is True, then the undefined ones should be False
|
||||||
if any(interfaces[name] is True for name in interfaces):
|
if any(interfaces[name] is True for name in interfaces):
|
||||||
|
@ -79,36 +76,30 @@ def run(telegram: typing.Optional[bool],
|
||||||
for name in interfaces:
|
for name in interfaces:
|
||||||
interfaces[name] = True
|
interfaces[name] = True
|
||||||
|
|
||||||
server_process: typing.Optional[multiprocessing.Process] = None
|
herald_process: typing.Optional[multiprocessing.Process] = None
|
||||||
# Start the network server
|
# Start the Herald server
|
||||||
if local_network_server:
|
if interfaces["herald"]:
|
||||||
server_process = multiprocessing.Process(name="Network Server",
|
herald_config = r.herald.Config(name="<server>",
|
||||||
target=rh.Server("0.0.0.0", local_network_server_port, network_password).run_blocking,
|
address="127.0.0.1",
|
||||||
|
port=herald_port,
|
||||||
|
secret=get_secret("herald"),
|
||||||
|
secure=False,
|
||||||
|
path="/")
|
||||||
|
herald_process = multiprocessing.Process(name="Herald",
|
||||||
|
target=r.herald.Server(config=herald_config).run_blocking,
|
||||||
daemon=True)
|
daemon=True)
|
||||||
server_process.start()
|
herald_process.start()
|
||||||
network_address = f"ws://127.0.0.1:{local_network_server_port}/"
|
else:
|
||||||
|
herald_config = r.herald.Config(name=...,
|
||||||
# Create a Royalnet configuration
|
address=remote_herald_address,
|
||||||
network_config: typing.Optional[rh.Config] = None
|
port=herald_port,
|
||||||
if network_address is not None:
|
secret=get_secret("herald"),
|
||||||
network_config = rh.Config(network_address, network_password)
|
secure=False,
|
||||||
|
path="/")
|
||||||
# Create a Alchemy configuration
|
|
||||||
telegram_db_config: typing.Optional[r.alchemy.DatabaseConfig] = None
|
|
||||||
discord_db_config: typing.Optional[r.alchemy.DatabaseConfig] = None
|
|
||||||
if database is not None:
|
|
||||||
telegram_db_config = r.alchemy.DatabaseConfig(database,
|
|
||||||
r.packs.common.tables.User,
|
|
||||||
r.packs.common.tables.Telegram,
|
|
||||||
"tg_id")
|
|
||||||
discord_db_config = r.alchemy.DatabaseConfig(database,
|
|
||||||
r.packs.common.tables.User,
|
|
||||||
r.packs.common.tables.Discord,
|
|
||||||
"discord_id")
|
|
||||||
|
|
||||||
# Import command and star packs
|
# Import command and star packs
|
||||||
packs: typing.List[str] = list(packs)
|
packs: typing.List[str] = list(pack)
|
||||||
packs.append("royalnet.packs.common") # common pack is always imported
|
packs.append("royalnet.backpack") # backpack is always imported
|
||||||
enabled_commands = []
|
enabled_commands = []
|
||||||
enabled_page_stars = []
|
enabled_page_stars = []
|
||||||
enabled_exception_stars = []
|
enabled_exception_stars = []
|
||||||
|
@ -132,62 +123,66 @@ def run(telegram: typing.Optional[bool],
|
||||||
|
|
||||||
telegram_process: typing.Optional[multiprocessing.Process] = None
|
telegram_process: typing.Optional[multiprocessing.Process] = None
|
||||||
if interfaces["telegram"]:
|
if interfaces["telegram"]:
|
||||||
click.echo("\n@BotFather Commands String")
|
telegram_db_config = r.serf.AlchemyConfig(database_url=alchemy_url,
|
||||||
for command in enabled_commands:
|
master_table=r.backpack.tables.User,
|
||||||
click.echo(f"{command.name} - {command.description}")
|
identity_table=r.backpack.tables.Telegram,
|
||||||
click.echo("")
|
identity_column="tg_id")
|
||||||
telegram_bot = r.interfaces.TelegramBot(network_config=network_config,
|
telegram_serf_kwargs = {
|
||||||
database_config=telegram_db_config,
|
'alchemy_config': telegram_db_config,
|
||||||
sentry_dsn=sentry_dsn,
|
'commands': enabled_commands,
|
||||||
commands=enabled_commands,
|
'network_config': herald_config.copy(name="telegram"),
|
||||||
secrets_name=secrets_name)
|
'secrets_name': secrets_name
|
||||||
telegram_process = multiprocessing.Process(name="Telegram Interface",
|
}
|
||||||
target=telegram_bot.run_blocking,
|
telegram_process = multiprocessing.Process(name="Telegram Serf",
|
||||||
args=(verbose,),
|
target=r.serf.telegram.TelegramSerf.run_process,
|
||||||
|
kwargs=telegram_serf_kwargs,
|
||||||
daemon=True)
|
daemon=True)
|
||||||
telegram_process.start()
|
telegram_process.start()
|
||||||
|
|
||||||
discord_process: typing.Optional[multiprocessing.Process] = None
|
discord_process: typing.Optional[multiprocessing.Process] = None
|
||||||
if interfaces["discord"]:
|
if interfaces["discord"]:
|
||||||
discord_bot = r.interfaces.DiscordBot(network_config=network_config,
|
discord_db_config = r.serf.AlchemyConfig(database_url=alchemy_url,
|
||||||
database_config=discord_db_config,
|
master_table=r.backpack.tables.User,
|
||||||
sentry_dsn=sentry_dsn,
|
identity_table=r.backpack.tables.Discord,
|
||||||
commands=enabled_commands,
|
identity_column="discord_id")
|
||||||
secrets_name=secrets_name)
|
discord_serf_kwargs = {
|
||||||
discord_process = multiprocessing.Process(name="Discord Interface",
|
'alchemy_config': discord_db_config,
|
||||||
target=discord_bot.run_blocking,
|
'commands': enabled_commands,
|
||||||
args=(verbose,),
|
'network_config': herald_config.copy(name="discord"),
|
||||||
|
'secrets_name': secrets_name
|
||||||
|
}
|
||||||
|
discord_process = multiprocessing.Process(name="Discord Serf",
|
||||||
|
target=r.serf.discord.DiscordSerf.run_process,
|
||||||
|
kwargs=discord_serf_kwargs,
|
||||||
daemon=True)
|
daemon=True)
|
||||||
discord_process.start()
|
discord_process.start()
|
||||||
|
|
||||||
webserver_process: typing.Optional[multiprocessing.Process] = None
|
constellation_process: typing.Optional[multiprocessing.Process] = None
|
||||||
if interfaces["webserver"]:
|
if interfaces["constellation"]:
|
||||||
# Common tables are always included
|
|
||||||
constellation_tables = set(r.packs.common.available_tables)
|
|
||||||
# Find the required tables
|
|
||||||
for star in [*enabled_page_stars, *enabled_exception_stars]:
|
|
||||||
constellation_tables = constellation_tables.union(star.tables)
|
|
||||||
# Create the Constellation
|
# Create the Constellation
|
||||||
constellation = r.web.Constellation(page_stars=enabled_page_stars,
|
constellation_kwargs = {
|
||||||
exc_stars=enabled_exception_stars,
|
'address': "127.0.0.1",
|
||||||
secrets_name=secrets_name,
|
'port': constellation_port,
|
||||||
database_uri=database,
|
'secrets_name': secrets_name,
|
||||||
tables=constellation_tables)
|
'database_uri': alchemy_url,
|
||||||
webserver_process = multiprocessing.Process(name="Constellation Webserver",
|
'page_stars': enabled_page_stars,
|
||||||
target=constellation.run_blocking,
|
'exc_stars': enabled_exception_stars,
|
||||||
args=("0.0.0.0", webserver_port, verbose,),
|
}
|
||||||
daemon=True)
|
constellation_process = multiprocessing.Process(name="Constellation",
|
||||||
webserver_process.start()
|
target=r.constellation.Constellation.run_process,
|
||||||
|
kwargs=constellation_kwargs,
|
||||||
|
daemon=True)
|
||||||
|
constellation_process.start()
|
||||||
|
|
||||||
click.echo("Royalnet processes have been started. You can force-quit by pressing Ctrl+C.")
|
click.echo("Royalnet is now running! You can stop its execution by pressing Ctrl+C at any time.")
|
||||||
if server_process is not None:
|
if herald_process is not None:
|
||||||
server_process.join()
|
herald_process.join()
|
||||||
if telegram_process is not None:
|
if telegram_process is not None:
|
||||||
telegram_process.join()
|
telegram_process.join()
|
||||||
if discord_process is not None:
|
if discord_process is not None:
|
||||||
discord_process.join()
|
discord_process.join()
|
||||||
if webserver_process is not None:
|
if constellation_process is not None:
|
||||||
webserver_process.join()
|
constellation_process.join()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
3
royalnet/backpack/README.md
Normal file
3
royalnet/backpack/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# `backpack`
|
||||||
|
|
||||||
|
A Pack that is imported by default by all `royalnet` instances.
|
|
@ -1,4 +1,4 @@
|
||||||
# This is a template Pack __init__. You can use this without changing anything in other packages too!
|
"""A Pack that is imported by default by all :mod:`royalnet` instances."""
|
||||||
|
|
||||||
from . import commands, tables, stars
|
from . import commands, tables, stars
|
||||||
from .commands import available_commands
|
from .commands import available_commands
|
|
@ -1,5 +1,5 @@
|
||||||
|
import royalnet
|
||||||
from royalnet.commands import *
|
from royalnet.commands import *
|
||||||
from royalnet.version import semantic
|
|
||||||
|
|
||||||
|
|
||||||
class VersionCommand(Command):
|
class VersionCommand(Command):
|
||||||
|
@ -8,7 +8,7 @@ class VersionCommand(Command):
|
||||||
description: str = "Get the current Royalnet version."
|
description: str = "Get the current Royalnet version."
|
||||||
|
|
||||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||||
message = f"ℹ️ Royalnet {semantic}\n"
|
message = f"ℹ️ Royalnet {royalnet.__version__}\n"
|
||||||
if "69" in message:
|
if "69" in message:
|
||||||
message += "(Nice.)"
|
message += "(Nice.)"
|
||||||
await data.reply(message)
|
await data.reply(message)
|
|
@ -1,15 +1,18 @@
|
||||||
import royalnet
|
import royalnet
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import *
|
from starlette.responses import *
|
||||||
from royalnet.web import PageStar
|
from royalnet.constellation import PageStar
|
||||||
|
from ..tables import available_tables
|
||||||
|
|
||||||
|
|
||||||
class ApiRoyalnetVersionStar(PageStar):
|
class ApiRoyalnetVersionStar(PageStar):
|
||||||
path = "/api/royalnet/version"
|
path = "/api/royalnet/version"
|
||||||
|
|
||||||
|
tables = set(available_tables)
|
||||||
|
|
||||||
async def page(self, request: Request) -> JSONResponse:
|
async def page(self, request: Request) -> JSONResponse:
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
"version": {
|
"version": {
|
||||||
"semantic": royalnet.version.semantic
|
"semantic": royalnet.__version__,
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -34,7 +34,7 @@ class YtdlMp3:
|
||||||
)
|
)
|
||||||
self.mp3_filename = destination_filename
|
self.mp3_filename = destination_filename
|
||||||
|
|
||||||
def delete_asap(self) -> None:
|
async def delete_asap(self) -> None:
|
||||||
"""Delete the mp3 file."""
|
"""Delete the mp3 file."""
|
||||||
if self.is_converted:
|
if self.is_converted:
|
||||||
async with self.lock.exclusive():
|
async with self.lock.exclusive():
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from asyncio import AbstractEventLoop
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
from .errors import UnsupportedError
|
from .errors import UnsupportedError
|
||||||
from .commandinterface import CommandInterface
|
from .commandinterface import CommandInterface
|
||||||
|
@ -8,9 +9,10 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class CommandData:
|
class CommandData:
|
||||||
def __init__(self, interface: CommandInterface, session: Optional["Session"]):
|
def __init__(self, interface: CommandInterface, session: Optional["Session"], loop: AbstractEventLoop):
|
||||||
self._interface: CommandInterface = interface
|
self._interface: CommandInterface = interface
|
||||||
self._session: Optional["Session"] = session
|
self._session: Optional["Session"] = session
|
||||||
|
self.loop: AbstractEventLoop = loop
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session(self) -> "Session":
|
def session(self) -> "Session":
|
||||||
|
|
|
@ -6,9 +6,9 @@ import keyring
|
||||||
def run():
|
def run():
|
||||||
click.echo("Welcome to the Royalnet configuration creator!")
|
click.echo("Welcome to the Royalnet configuration creator!")
|
||||||
secrets_name = click.prompt("Desired secrets name", default="__default__")
|
secrets_name = click.prompt("Desired secrets name", default="__default__")
|
||||||
network = click.prompt("Network password", default="")
|
network = click.prompt("Herald password", default="")
|
||||||
if network:
|
if network:
|
||||||
keyring.set_password(f"Royalnet/{secrets_name}", "network", network)
|
keyring.set_password(f"Royalnet/{secrets_name}", "herald", network)
|
||||||
telegram = click.prompt("Telegram Bot API token", default="")
|
telegram = click.prompt("Telegram Bot API token", default="")
|
||||||
if telegram:
|
if telegram:
|
||||||
keyring.set_password(f"Royalnet/{secrets_name}", "telegram", telegram)
|
keyring.set_password(f"Royalnet/{secrets_name}", "telegram", telegram)
|
||||||
|
@ -21,9 +21,6 @@ def run():
|
||||||
sentry = click.prompt("Sentry DSN", default="")
|
sentry = click.prompt("Sentry DSN", default="")
|
||||||
if sentry:
|
if sentry:
|
||||||
keyring.set_password(f"Royalnet/{secrets_name}", "sentry", sentry)
|
keyring.set_password(f"Royalnet/{secrets_name}", "sentry", sentry)
|
||||||
leagueoflegends = click.prompt("League of Legends API Token", default="")
|
|
||||||
if leagueoflegends:
|
|
||||||
keyring.set_password(f"Royalnet/{secrets_name}", "leagueoflegends", leagueoflegends)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -2,7 +2,6 @@ import typing
|
||||||
import logging
|
import logging
|
||||||
import royalnet
|
import royalnet
|
||||||
import keyring
|
import keyring
|
||||||
from royalnet.alchemy import Alchemy
|
|
||||||
from .star import PageStar, ExceptionStar
|
from .star import PageStar, ExceptionStar
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -60,7 +59,7 @@ class Constellation:
|
||||||
"""The :class:`Starlette` app."""
|
"""The :class:`Starlette` app."""
|
||||||
|
|
||||||
log.debug("Finding required Tables...")
|
log.debug("Finding required Tables...")
|
||||||
tables = set()
|
tables = set(royalnet.backpack.available_tables)
|
||||||
for SelectedPageStar in page_stars:
|
for SelectedPageStar in page_stars:
|
||||||
tables = tables.union(SelectedPageStar.tables)
|
tables = tables.union(SelectedPageStar.tables)
|
||||||
for SelectedExcStar in exc_stars:
|
for SelectedExcStar in exc_stars:
|
||||||
|
@ -68,7 +67,7 @@ class Constellation:
|
||||||
log.debug(f"Found Tables: {' '.join([table.__name__ for table in tables])}")
|
log.debug(f"Found Tables: {' '.join([table.__name__ for table in tables])}")
|
||||||
|
|
||||||
log.info(f"Creating Alchemy...")
|
log.info(f"Creating Alchemy...")
|
||||||
self.alchemy: Alchemy = Alchemy(database_uri=database_uri, tables=tables)
|
self.alchemy: royalnet.alchemy.Alchemy = royalnet.alchemy.Alchemy(database_uri=database_uri, tables=tables)
|
||||||
"""The :class:`Alchemy: of this Constellation."""
|
"""The :class:`Alchemy: of this Constellation."""
|
||||||
|
|
||||||
log.info("Registering PageStars...")
|
log.info("Registering PageStars...")
|
||||||
|
@ -98,19 +97,34 @@ class Constellation:
|
||||||
username: the name of the secret that should be retrieved."""
|
username: the name of the secret that should be retrieved."""
|
||||||
return keyring.get_password(f"Royalnet/{self.secrets_name}", username)
|
return keyring.get_password(f"Royalnet/{self.secrets_name}", username)
|
||||||
|
|
||||||
def run_blocking(self, address: str, port: int):
|
@classmethod
|
||||||
"""Blockingly run the Constellation.
|
def run_process(cls,
|
||||||
|
address: str,
|
||||||
|
port: int,
|
||||||
|
secrets_name: str,
|
||||||
|
database_uri: str,
|
||||||
|
page_stars: typing.List[typing.Type[PageStar]] = None,
|
||||||
|
exc_stars: typing.List[typing.Type[ExceptionStar]] = None,
|
||||||
|
*,
|
||||||
|
debug: bool = __debug__,):
|
||||||
|
"""Blockingly create and run the Constellation.
|
||||||
|
|
||||||
This should be used as the target of a :class:`multiprocessing.Process`.
|
This should be used as the target of a :class:`multiprocessing.Process`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
address: The IP address this Constellation should bind to.
|
address: The IP address this Constellation should bind to.
|
||||||
port: The port this Constellation should listen for requests on."""
|
port: The port this Constellation should listen for requests on."""
|
||||||
|
constellation = cls(secrets_name=secrets_name,
|
||||||
|
database_uri=database_uri,
|
||||||
|
page_stars=page_stars,
|
||||||
|
exc_stars=exc_stars,
|
||||||
|
debug=debug)
|
||||||
|
|
||||||
# Initialize Sentry on the process
|
# Initialize Sentry on the process
|
||||||
if sentry_sdk is None:
|
if sentry_sdk is None:
|
||||||
log.info("Sentry: not installed")
|
log.info("Sentry: not installed")
|
||||||
else:
|
else:
|
||||||
sentry_dsn = self.get_secret("sentry")
|
sentry_dsn = constellation.get_secret("sentry")
|
||||||
if not sentry_dsn:
|
if not sentry_dsn:
|
||||||
log.info("Sentry: disabled")
|
log.info("Sentry: disabled")
|
||||||
else:
|
else:
|
||||||
|
@ -128,11 +142,11 @@ class Constellation:
|
||||||
log.info(f"Sentry: enabled (Royalnet {release})")
|
log.info(f"Sentry: enabled (Royalnet {release})")
|
||||||
# Run the server
|
# Run the server
|
||||||
log.info(f"Running Constellation on {address}:{port}...")
|
log.info(f"Running Constellation on {address}:{port}...")
|
||||||
self.running = True
|
constellation.running = True
|
||||||
try:
|
try:
|
||||||
uvicorn.run(self.starlette, host=address, port=port)
|
uvicorn.run(constellation.starlette, host=address, port=port)
|
||||||
finally:
|
finally:
|
||||||
self.running = False
|
constellation.running = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__qualname__}: {'running' if self.running else 'inactive'}>"
|
return f"<{self.__class__.__qualname__}: {'running' if self.running else 'inactive'}>"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
name: str,
|
name: str,
|
||||||
|
@ -30,5 +33,20 @@ class Config:
|
||||||
def url(self):
|
def url(self):
|
||||||
return f"ws{'s' if self.secure else ''}://{self.address}:{self.port}{self.path}"
|
return f"ws{'s' if self.secure else ''}://{self.address}:{self.port}{self.path}"
|
||||||
|
|
||||||
|
def copy(self,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
address: Optional[str] = None,
|
||||||
|
port: Optional[int] = None,
|
||||||
|
secret: Optional[str] = None,
|
||||||
|
secure: Optional[bool] = None,
|
||||||
|
path: Optional[str] = None):
|
||||||
|
"""Create an exact copy of this configuration, but with different parameters."""
|
||||||
|
return self.__class__(name=name if name else self.name,
|
||||||
|
address=address if address else self.address,
|
||||||
|
port=port if port else self.port,
|
||||||
|
secret=secret if secret else self.secret,
|
||||||
|
secure=secure if secure else self.secure,
|
||||||
|
path=path if path else self.path)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<HeraldConfig for {self.url}>"
|
return f"<HeraldConfig for {self.url}>"
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from .serf import Serf
|
from .serf import Serf
|
||||||
from .alchemyconfig import AlchemyConfig
|
from .alchemyconfig import AlchemyConfig
|
||||||
|
from . import telegram, discord
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Serf",
|
"Serf",
|
||||||
"AlchemyConfig"
|
"AlchemyConfig",
|
||||||
|
"telegram",
|
||||||
|
"discord",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
from typing import Type, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
if TYPE_CHECKING:
|
|
||||||
from sqlalchemy.schema import Table
|
|
||||||
|
|
||||||
|
|
||||||
class AlchemyConfig:
|
class AlchemyConfig:
|
||||||
"""A helper class to configure :class:`Alchemy` in a :class:`Serf`."""
|
"""A helper class to configure :class:`Alchemy` in a :class:`Serf`."""
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
database_url: str,
|
database_url: str,
|
||||||
master_table: Type[Table],
|
master_table: type,
|
||||||
identity_table: Type[Table],
|
identity_table: type,
|
||||||
identity_column: str):
|
identity_column: str):
|
||||||
self.database_url: str = database_url
|
self.database_url: str = database_url
|
||||||
self.master_table: Type[Table] = master_table
|
self.master_table: type = master_table
|
||||||
self.identity_table: Type[Table] = identity_table
|
self.identity_table: type = identity_table
|
||||||
self.identity_column: str = identity_column
|
self.identity_column: str = identity_column
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Type, Optional, List, Union
|
from typing import Type, Optional, List, Union
|
||||||
from royalnet.commands import Command, CommandInterface, CommandData, CommandArgs, CommandError, InvalidInputError, \
|
from royalnet.commands import Command, CommandInterface, CommandData, CommandArgs, CommandError, InvalidInputError, \
|
||||||
|
@ -63,8 +64,12 @@ class DiscordSerf(Serf):
|
||||||
def data_factory(self) -> Type[CommandData]:
|
def data_factory(self) -> Type[CommandData]:
|
||||||
# noinspection PyMethodParameters,PyAbstractClass
|
# noinspection PyMethodParameters,PyAbstractClass
|
||||||
class DiscordData(CommandData):
|
class DiscordData(CommandData):
|
||||||
def __init__(data, interface: CommandInterface, session, message: discord.Message):
|
def __init__(data,
|
||||||
super().__init__(interface=interface, session=session)
|
interface: CommandInterface,
|
||||||
|
session,
|
||||||
|
loop: asyncio.AbstractEventLoop,
|
||||||
|
message: discord.Message):
|
||||||
|
super().__init__(interface=interface, session=session, loop=loop)
|
||||||
data.message = message
|
data.message = message
|
||||||
|
|
||||||
async def reply(data, text: str):
|
async def reply(data, text: str):
|
||||||
|
@ -118,7 +123,7 @@ class DiscordSerf(Serf):
|
||||||
else:
|
else:
|
||||||
session = None
|
session = None
|
||||||
# Prepare data
|
# Prepare data
|
||||||
data = self.Data(interface=command.interface, session=session, message=message)
|
data = self.Data(interface=command.interface, session=session, loop=self.loop, message=message)
|
||||||
try:
|
try:
|
||||||
# Run the command
|
# Run the command
|
||||||
await command.run(CommandArgs(parameters), data)
|
await command.run(CommandArgs(parameters), data)
|
||||||
|
@ -196,6 +201,7 @@ class DiscordSerf(Serf):
|
||||||
return DiscordClient
|
return DiscordClient
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
|
await super().run()
|
||||||
token = self.get_secret("discord")
|
token = self.get_secret("discord")
|
||||||
await self.client.login(token)
|
await self.client.login(token)
|
||||||
await self.client.connect()
|
await self.client.connect()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from asyncio import Task, AbstractEventLoop
|
from asyncio import Task, AbstractEventLoop, get_event_loop
|
||||||
from typing import Type, Optional, Awaitable, Dict, List, Any, Callable, Union, Set
|
from typing import Type, Optional, Awaitable, Dict, List, Any, Callable, Union, Set
|
||||||
from keyring import get_password
|
from keyring import get_password
|
||||||
from sqlalchemy.schema import Table
|
from sqlalchemy.schema import Table
|
||||||
|
@ -130,10 +130,10 @@ class Serf:
|
||||||
self.alchemy = Alchemy(alchemy_config.database_url, tables)
|
self.alchemy = Alchemy(alchemy_config.database_url, tables)
|
||||||
self._master_table = self.alchemy.get(alchemy_config.master_table)
|
self._master_table = self.alchemy.get(alchemy_config.master_table)
|
||||||
self._identity_table = self.alchemy.get(alchemy_config.identity_table)
|
self._identity_table = self.alchemy.get(alchemy_config.identity_table)
|
||||||
# FIXME: this MAY break
|
# This is fine, as Pycharm doesn't know that identity_table is a class and not an object
|
||||||
self._identity_column = self._identity_table.__getattribute__(alchemy_config.identity_column)
|
# noinspection PyArgumentList
|
||||||
# self._identity_column = self._identity_table.__getattribute__(self._identity_table,
|
self._identity_column = self._identity_table.__getattribute__(self._identity_table,
|
||||||
# alchemy_config.identity_column)
|
alchemy_config.identity_column)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _identity_chain(self) -> tuple:
|
def _identity_chain(self) -> tuple:
|
||||||
|
@ -147,7 +147,6 @@ class Serf:
|
||||||
class GenericInterface(CommandInterface):
|
class GenericInterface(CommandInterface):
|
||||||
alchemy: Alchemy = self.alchemy
|
alchemy: Alchemy = self.alchemy
|
||||||
bot: "Serf" = self
|
bot: "Serf" = self
|
||||||
loop: AbstractEventLoop = self.loop
|
|
||||||
|
|
||||||
def register_herald_action(ci,
|
def register_herald_action(ci,
|
||||||
event_name: str,
|
event_name: str,
|
||||||
|
@ -232,10 +231,9 @@ class Serf:
|
||||||
def init_network(self, config: HeraldConfig):
|
def init_network(self, config: HeraldConfig):
|
||||||
"""Create a :py:class:`Link`, and run it as a :py:class:`asyncio.Task`."""
|
"""Create a :py:class:`Link`, and run it as a :py:class:`asyncio.Task`."""
|
||||||
log.debug(f"Initializing herald...")
|
log.debug(f"Initializing herald...")
|
||||||
self.herald: Link = Link(config, self._network_handler)
|
self.herald: Link = Link(config, self.network_handler)
|
||||||
self.herald_task = self.loop.create_task(self.herald.run())
|
|
||||||
|
|
||||||
async def _network_handler(self, message: Union[Request, Broadcast]) -> Response:
|
async def network_handler(self, message: Union[Request, Broadcast]) -> Response:
|
||||||
try:
|
try:
|
||||||
network_handler = self.herald_handlers[message.handler]
|
network_handler = self.herald_handlers[message.handler]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -288,19 +286,24 @@ class Serf:
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
"""A coroutine that starts the event loop and handles command calls."""
|
"""A coroutine that starts the event loop and handles command calls."""
|
||||||
raise NotImplementedError()
|
self.herald_task = self.loop.create_task(self.herald.run())
|
||||||
|
# OVERRIDE THIS METHOD!
|
||||||
|
|
||||||
def run_blocking(self):
|
@classmethod
|
||||||
"""Blockingly run the Serf.
|
def run_process(cls, *args, **kwargs):
|
||||||
|
"""Blockingly create and run the Serf.
|
||||||
|
|
||||||
This should be used as the target of a :class:`multiprocessing.Process`."""
|
This should be used as the target of a :class:`multiprocessing.Process`."""
|
||||||
|
serf = cls(*args, **kwargs)
|
||||||
|
|
||||||
if sentry_sdk is None:
|
if sentry_sdk is None:
|
||||||
log.info("Sentry: not installed")
|
log.info("Sentry: not installed")
|
||||||
else:
|
else:
|
||||||
sentry_dsn = self.get_secret("sentry")
|
sentry_dsn = serf.get_secret("sentry")
|
||||||
if sentry_dsn is None:
|
if sentry_dsn is None:
|
||||||
log.info("Sentry: disabled")
|
log.info("Sentry: disabled")
|
||||||
else:
|
else:
|
||||||
self.init_sentry(sentry_dsn)
|
serf.init_sentry(sentry_dsn)
|
||||||
|
|
||||||
self.loop.run_until_complete(self.run())
|
serf.loop = get_event_loop()
|
||||||
|
serf.loop.run_until_complete(serf.run())
|
||||||
|
|
|
@ -99,8 +99,12 @@ class TelegramSerf(Serf):
|
||||||
def data_factory(self) -> Type[CommandData]:
|
def data_factory(self) -> Type[CommandData]:
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
class TelegramData(CommandData):
|
class TelegramData(CommandData):
|
||||||
def __init__(data, interface: CommandInterface, session, update: telegram.Update):
|
def __init__(data,
|
||||||
super().__init__(interface=interface, session=session)
|
interface: CommandInterface,
|
||||||
|
session,
|
||||||
|
loop: asyncio.AbstractEventLoop,
|
||||||
|
update: telegram.Update):
|
||||||
|
super().__init__(interface=interface, session=session, loop=loop)
|
||||||
data.update = update
|
data.update = update
|
||||||
|
|
||||||
async def reply(data, text: str):
|
async def reply(data, text: str):
|
||||||
|
@ -192,7 +196,7 @@ class TelegramSerf(Serf):
|
||||||
session = None
|
session = None
|
||||||
try:
|
try:
|
||||||
# Create the command data
|
# Create the command data
|
||||||
data = self.Data(interface=command.interface, session=session, update=update)
|
data = self.Data(interface=command.interface, session=session, loop=self.loop, update=update)
|
||||||
try:
|
try:
|
||||||
# Run the command
|
# Run the command
|
||||||
await command.run(CommandArgs(parameters), data)
|
await command.run(CommandArgs(parameters), data)
|
||||||
|
@ -240,6 +244,7 @@ class TelegramSerf(Serf):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
|
await super().run()
|
||||||
while True:
|
while True:
|
||||||
# Get the latest 100 updates
|
# Get the latest 100 updates
|
||||||
last_updates: List[telegram.Update] = await self.api_call(self.client.get_updates,
|
last_updates: List[telegram.Update] = await self.api_call(self.client.get_updates,
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
"""Miscellaneous useful functions and classes."""
|
|
||||||
|
|
||||||
from .asyncify import asyncify
|
from .asyncify import asyncify
|
||||||
from .escaping import telegram_escape, discord_escape
|
|
||||||
from .safeformat import safeformat
|
from .safeformat import safeformat
|
||||||
from .classdictjanitor import cdj
|
|
||||||
from .sleep_until import sleep_until
|
from .sleep_until import sleep_until
|
||||||
from .formatters import andformat, plusformat, underscorize, ytdldateformat, numberemojiformat, splitstring, ordinalformat
|
from .formatters import andformat, underscorize, ytdldateformat, numberemojiformat, splitstring, ordinalformat
|
||||||
from .urluuid import to_urluuid, from_urluuid
|
from .urluuid import to_urluuid, from_urluuid
|
||||||
from .multilock import MultiLock
|
from .multilock import MultiLock
|
||||||
|
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
import typing
|
|
||||||
import uvicorn
|
|
||||||
import logging
|
|
||||||
import sentry_sdk
|
|
||||||
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
|
|
||||||
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
|
|
||||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
||||||
import royalnet
|
|
||||||
import keyring
|
|
||||||
from starlette.applications import Starlette
|
|
||||||
from .star import PageStar, ExceptionStar
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Constellation:
|
|
||||||
def __init__(self,
|
|
||||||
secrets_name: str,
|
|
||||||
database_uri: str,
|
|
||||||
tables: set,
|
|
||||||
page_stars: typing.List[typing.Type[PageStar]] = None,
|
|
||||||
exc_stars: typing.List[typing.Type[ExceptionStar]] = None,
|
|
||||||
*,
|
|
||||||
debug: bool = __debug__,):
|
|
||||||
if page_stars is None:
|
|
||||||
page_stars = []
|
|
||||||
|
|
||||||
if exc_stars is None:
|
|
||||||
exc_stars = []
|
|
||||||
|
|
||||||
self.secrets_name: str = secrets_name
|
|
||||||
|
|
||||||
log.info("Creating starlette app...")
|
|
||||||
self.starlette = Starlette(debug=debug)
|
|
||||||
|
|
||||||
log.info(f"Creating alchemy with tables: {' '.join([table.__name__ for table in tables])}")
|
|
||||||
self.alchemy: royalnet.alchemy.Alchemy = royalnet.alchemy.Alchemy(database_uri=database_uri, tables=tables)
|
|
||||||
|
|
||||||
log.info("Registering page_stars...")
|
|
||||||
for SelectedPageStar in page_stars:
|
|
||||||
try:
|
|
||||||
page_star_instance = SelectedPageStar(constellation=self)
|
|
||||||
except Exception as e:
|
|
||||||
log.error(f"{e.__class__.__qualname__} during the registration of {SelectedPageStar.__qualname__}")
|
|
||||||
sentry_sdk.capture_exception(e)
|
|
||||||
continue
|
|
||||||
log.info(f"Registering: {page_star_instance.path} -> {page_star_instance.__class__.__name__}")
|
|
||||||
self.starlette.add_route(page_star_instance.path, page_star_instance.page, page_star_instance.methods)
|
|
||||||
|
|
||||||
log.info("Registering exc_stars...")
|
|
||||||
for SelectedExcStar in exc_stars:
|
|
||||||
try:
|
|
||||||
exc_star_instance = SelectedExcStar(constellation=self)
|
|
||||||
except Exception as e:
|
|
||||||
log.error(f"{e.__class__.__qualname__} during the registration of {SelectedExcStar.__qualname__}")
|
|
||||||
sentry_sdk.capture_exception(e)
|
|
||||||
continue
|
|
||||||
log.info(f"Registering: {exc_star_instance.error} -> {exc_star_instance.__class__.__name__}")
|
|
||||||
self.starlette.add_exception_handler(exc_star_instance.error, exc_star_instance.page)
|
|
||||||
|
|
||||||
def _init_sentry(self):
|
|
||||||
sentry_dsn = self.get_secret("sentry")
|
|
||||||
if sentry_dsn:
|
|
||||||
# noinspection PyUnreachableCode
|
|
||||||
if __debug__:
|
|
||||||
release = "DEV"
|
|
||||||
else:
|
|
||||||
release = royalnet.version.semantic
|
|
||||||
log.info(f"Sentry: enabled (Royalnet {release})")
|
|
||||||
self.sentry = sentry_sdk.init(sentry_dsn,
|
|
||||||
integrations=[AioHttpIntegration(),
|
|
||||||
SqlalchemyIntegration(),
|
|
||||||
LoggingIntegration(event_level=None)],
|
|
||||||
release=release)
|
|
||||||
else:
|
|
||||||
log.info("Sentry: disabled")
|
|
||||||
|
|
||||||
def get_secret(self, username: str):
|
|
||||||
return keyring.get_password(f"Royalnet/{self.secrets_name}", username)
|
|
||||||
|
|
||||||
def set_secret(self, username: str, password: str):
|
|
||||||
return keyring.set_password(f"Royalnet/{self.secrets_name}", username, password)
|
|
||||||
|
|
||||||
def run_blocking(self, address: str, port: int, verbose: bool):
|
|
||||||
if verbose:
|
|
||||||
core_logger = logging.root
|
|
||||||
core_logger.setLevel(logging.DEBUG)
|
|
||||||
stream_handler = logging.StreamHandler()
|
|
||||||
stream_handler.formatter = logging.Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{")
|
|
||||||
core_logger.addHandler(stream_handler)
|
|
||||||
core_logger.debug("Logging setup complete.")
|
|
||||||
self._init_sentry()
|
|
||||||
log.info(f"Running constellation server on {address}:{port}...")
|
|
||||||
uvicorn.run(self.starlette, host=address, port=port)
|
|
|
@ -1,37 +0,0 @@
|
||||||
import typing
|
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
if typing.TYPE_CHECKING:
|
|
||||||
from .constellation import Constellation
|
|
||||||
|
|
||||||
|
|
||||||
class Star:
|
|
||||||
tables: set = {}
|
|
||||||
|
|
||||||
def __init__(self, constellation: "Constellation"):
|
|
||||||
self.constellation: "Constellation" = constellation
|
|
||||||
|
|
||||||
async def page(self, request: Request) -> Response:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def alchemy(self):
|
|
||||||
return self.constellation.alchemy
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Session(self):
|
|
||||||
return self.constellation.alchemy._Session
|
|
||||||
|
|
||||||
@property
|
|
||||||
def session_acm(self):
|
|
||||||
return self.constellation.alchemy.session_acm
|
|
||||||
|
|
||||||
|
|
||||||
class PageStar(Star):
|
|
||||||
path: str = NotImplemented
|
|
||||||
|
|
||||||
methods: typing.List[str] = ["GET"]
|
|
||||||
|
|
||||||
|
|
||||||
class ExceptionStar(Star):
|
|
||||||
error: typing.Union[typing.Type[Exception], int]
|
|
Loading…
Reference in a new issue