1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-23 11:34:18 +00:00

small tiny progress

This commit is contained in:
Steffo 2020-10-01 18:44:23 +02:00
parent fdddc70469
commit 18eeb6d487
14 changed files with 126 additions and 825 deletions

159
poetry.lock generated
View file

@ -143,7 +143,7 @@ humanfriendly = ">=4.7"
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "3.1" version = "3.1.1"
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."
category = "main" category = "main"
optional = true optional = true
@ -184,7 +184,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*"
[[package]] [[package]]
name = "discord.py" name = "discord.py"
version = "1.4.1" version = "1.5.0"
description = "A Python wrapper for the Discord API" description = "A Python wrapper for the Discord API"
category = "main" category = "main"
optional = true optional = true
@ -344,7 +344,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.7.0" version = "2.7.1"
description = "Pygments is a syntax highlighting package written in Python." description = "Pygments is a syntax highlighting package written in Python."
category = "dev" category = "dev"
optional = false optional = false
@ -453,9 +453,20 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "redis"
version = "3.5.3"
description = "Python client for Redis key-value store"
category = "main"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
hiredis = ["hiredis (>=0.1.3)"]
[[package]] [[package]]
name = "regex" name = "regex"
version = "2020.7.14" version = "2020.9.27"
description = "Alternative regular expression module, to replace re." description = "Alternative regular expression module, to replace re."
category = "main" category = "main"
optional = false optional = false
@ -747,7 +758,7 @@ python-versions = ">=3.6.1"
[[package]] [[package]]
name = "yarl" name = "yarl"
version = "1.5.1" version = "1.6.0"
description = "Yet another URL library" description = "Yet another URL library"
category = "main" category = "main"
optional = true optional = true
@ -758,19 +769,19 @@ idna = ">=2.0"
multidict = ">=4.0" multidict = ">=4.0"
[extras] [extras]
telegram = ["python_telegram_bot"] telegram = ["python_telegram_bot", "urllib3"]
discord = ["discord.py", "pynacl"] discord = ["discord.py", "pynacl"]
alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"] alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"]
alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"] alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"]
constellation = ["starlette", "uvicorn", "python-multipart"] constellation = ["starlette", "uvicorn", "python-multipart"]
sentry = ["sentry_sdk"] sentry = ["sentry_sdk"]
herald = ["websockets"]
coloredlogs = ["coloredlogs"] coloredlogs = ["coloredlogs"]
baron = ["redis"]
[metadata] [metadata]
lock-version = "1.0" lock-version = "1.0"
python-versions = "^3.8" python-versions = "^3.8"
content-hash = "88bf2cfd1fc7d44eadf8222999146e8ba8846200bcc37899bb0ce268d441a313" content-hash = "9151cde5dbb6b2f35316ed0b3674b37b9ac7ab919216f56646bb023b502c01f4"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@ -875,28 +886,28 @@ coloredlogs = [
{file = "coloredlogs-10.0.tar.gz", hash = "sha256:b869a2dda3fa88154b9dd850e27828d8755bfab5a838a1c97fbc850c6e377c36"}, {file = "coloredlogs-10.0.tar.gz", hash = "sha256:b869a2dda3fa88154b9dd850e27828d8755bfab5a838a1c97fbc850c6e377c36"},
] ]
cryptography = [ cryptography = [
{file = "cryptography-3.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f"}, {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"},
{file = "cryptography-3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0"}, {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"},
{file = "cryptography-3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36"}, {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"},
{file = "cryptography-3.1-cp27-cp27m-win32.whl", hash = "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a"}, {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"},
{file = "cryptography-3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791"}, {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"},
{file = "cryptography-3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761"}, {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"},
{file = "cryptography-3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e"}, {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"},
{file = "cryptography-3.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8"}, {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"},
{file = "cryptography-3.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c"}, {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"},
{file = "cryptography-3.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f"}, {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"},
{file = "cryptography-3.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237"}, {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"},
{file = "cryptography-3.1-cp35-cp35m-win32.whl", hash = "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716"}, {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"},
{file = "cryptography-3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695"}, {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"},
{file = "cryptography-3.1-cp36-abi3-win32.whl", hash = "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af"}, {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"},
{file = "cryptography-3.1-cp36-abi3-win_amd64.whl", hash = "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618"}, {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"},
{file = "cryptography-3.1-cp36-cp36m-win32.whl", hash = "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1"}, {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"},
{file = "cryptography-3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c"}, {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"},
{file = "cryptography-3.1-cp37-cp37m-win32.whl", hash = "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32"}, {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"},
{file = "cryptography-3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed"}, {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"},
{file = "cryptography-3.1-cp38-cp38-win32.whl", hash = "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67"}, {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"},
{file = "cryptography-3.1-cp38-cp38-win_amd64.whl", hash = "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10"}, {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"},
{file = "cryptography-3.1.tar.gz", hash = "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08"}, {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"},
] ]
dateparser = [ dateparser = [
{file = "dateparser-0.7.6-py2.py3-none-any.whl", hash = "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b"}, {file = "dateparser-0.7.6-py2.py3-none-any.whl", hash = "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b"},
@ -907,8 +918,8 @@ decorator = [
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
] ]
"discord.py" = [ "discord.py" = [
{file = "discord.py-1.4.1-py3-none-any.whl", hash = "sha256:98ea3096a3585c9c379209926f530808f5fcf4930928d8cfb579d2562d119570"}, {file = "discord.py-1.5.0-py3-none-any.whl", hash = "sha256:3acb61fde0d862ed346a191d69c46021e6063673f63963bc984ae09a685ab211"},
{file = "discord.py-1.4.1.tar.gz", hash = "sha256:f9decb3bfa94613d922376288617e6a6f969260923643e2897f4540c34793442"}, {file = "discord.py-1.5.0.tar.gz", hash = "sha256:e71089886aa157341644bdecad63a72ff56b44406b1a6467b66db31c8e5a5a15"},
] ]
docutils = [ docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
@ -1061,8 +1072,8 @@ pycparser = [
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
] ]
pygments = [ pygments = [
{file = "Pygments-2.7.0-py3-none-any.whl", hash = "sha256:2df50d16b45b977217e02cba6c8422aaddb859f3d0570a88e09b00eafae89c6e"}, {file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"},
{file = "Pygments-2.7.0.tar.gz", hash = "sha256:2594e8fdb06fef91552f86f4fd3a244d148ab24b66042036e64f29a291515048"}, {file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"},
] ]
pynacl = [ pynacl = [
{file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"},
@ -1112,28 +1123,32 @@ pytz = [
{file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"},
{file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
] ]
redis = [
{file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
{file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
]
regex = [ regex = [
{file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"},
{file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"},
{file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"},
{file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"},
{file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"},
{file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"},
{file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"},
{file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"},
{file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"},
{file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"},
{file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"},
{file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"},
{file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"},
{file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"},
{file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"},
] ]
requests = [ requests = [
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
@ -1287,21 +1302,21 @@ websockets = [
{file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"},
] ]
yarl = [ yarl = [
{file = "yarl-1.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb"}, {file = "yarl-1.6.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1"},
{file = "yarl-1.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593"}, {file = "yarl-1.6.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188"},
{file = "yarl-1.5.1-cp35-cp35m-win32.whl", hash = "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409"}, {file = "yarl-1.6.0-cp35-cp35m-win32.whl", hash = "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580"},
{file = "yarl-1.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317"}, {file = "yarl-1.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc"},
{file = "yarl-1.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511"}, {file = "yarl-1.6.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a"},
{file = "yarl-1.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e"}, {file = "yarl-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a"},
{file = "yarl-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f"}, {file = "yarl-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e"},
{file = "yarl-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2"}, {file = "yarl-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2"},
{file = "yarl-1.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a"}, {file = "yarl-1.6.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e"},
{file = "yarl-1.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8"}, {file = "yarl-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a"},
{file = "yarl-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8"}, {file = "yarl-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131"},
{file = "yarl-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d"}, {file = "yarl-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d"},
{file = "yarl-1.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02"}, {file = "yarl-1.6.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921"},
{file = "yarl-1.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a"}, {file = "yarl-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1"},
{file = "yarl-1.5.1-cp38-cp38-win32.whl", hash = "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"}, {file = "yarl-1.6.0-cp38-cp38-win32.whl", hash = "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5"},
{file = "yarl-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692"}, {file = "yarl-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020"},
{file = "yarl-1.5.1.tar.gz", hash = "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6"}, {file = "yarl-1.6.0.tar.gz", hash = "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b"},
] ]

View file

@ -5,7 +5,7 @@
[tool.poetry] [tool.poetry]
name = "royalnet" name = "royalnet"
version = "5.11.13" version = "6.0.0a1"
description = "A multipurpose bot and web framework" description = "A multipurpose bot and web framework"
authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"] authors = ["Stefano Pigozzi <ste.pigozzi@gmail.com>"]
license = "AGPL-3.0+" license = "AGPL-3.0+"
@ -48,12 +48,12 @@ python-multipart = { version = "^0.0.5", optional = true }
# sentry # sentry
sentry_sdk = { version = "~0.13.2", optional = true } sentry_sdk = { version = "~0.13.2", optional = true }
# herald
websockets = { version = "^8.1", optional = true }
# logging # logging
coloredlogs = { version = "^10.0", optional = true } coloredlogs = { version = "^10.0", optional = true }
# baron
redis = { version = "^3.5.3", optional = true }
# Development dependencies # Development dependencies
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^5.2.1" pytest = "^5.2.1"
@ -69,8 +69,8 @@ alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"]
alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"] alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"]
constellation = ["starlette", "uvicorn", "python-multipart"] constellation = ["starlette", "uvicorn", "python-multipart"]
sentry = ["sentry_sdk"] sentry = ["sentry_sdk"]
herald = ["websockets"]
coloredlogs = ["coloredlogs"] coloredlogs = ["coloredlogs"]
baron = ["redis"]
# Executable aliases # Executable aliases

View file

@ -0,0 +1,30 @@
from typing import *
import redis
import redis.client
import threading
__all__ = [
"Baron"
]
class Baron:
def __init__(self, baron_cfg):
self.publisher: redis.Redis = redis.Redis(
host=baron_cfg["host"],
port=baron_cfg["port"],
db=baron_cfg["db"],
password=baron_cfg["password"]
)
self.listener: redis.client.PubSub = self.publisher.pubsub()
def publish(self, channel: str, message):
self.publisher.publish(channel=channel, message=message)
def subscribe(self, channel: str, callback: Callable):
self.listener.subscribe({
channel: callback
})
def listen(self):
self.listener.listen()

View file

@ -1,12 +0,0 @@
# `royalnet.herald`
The subpackage providing all functions and classes to handle communication between process (even over the Internet).
It is based on [`websockets`](https://github.com/websockets).
It requires the `herald` extra to be installed.
You can install it with:
```
pip install royalnet[herald]
```

View file

@ -1,37 +0,0 @@
"""The subpackage providing all functions and classes to handle communication between process (even over the Internet).
It is based on :mod:`websockets`.
It requires the ``herald`` extra to be installed.
You can install it with: ::
pip install royalnet[herald]
"""
from .broadcast import Broadcast
from .config import Config
from .errors import *
from .link import Link
from .package import Package
from .request import Request
from .response import Response, ResponseSuccess, ResponseFailure
from .server import Server
__all__ = [
"Config",
"HeraldError",
"ConnectionClosedError",
"LinkError",
"InvalidServerResponseError",
"ServerError",
"Link",
"Package",
"Request",
"Response",
"ResponseSuccess",
"ResponseFailure",
"Server",
"Broadcast",
]

View file

@ -1,26 +0,0 @@
from typing import *
class Broadcast:
def __init__(self, handler: str, data: dict, msg_type: Optional[str] = None):
super().__init__()
if msg_type is not None:
assert msg_type == self.__class__.__name__
self.msg_type = self.__class__.__name__
self.handler: str = handler
self.data: dict = data
def to_dict(self):
return self.__dict__
@classmethod
def from_dict(cls, d: dict):
return cls(**d)
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.handler == other.handler and self.data == other.data
return False
def __repr__(self):
return f"{self.__class__.__qualname__}(handler={self.handler}, data={self.data})"

View file

@ -1,73 +0,0 @@
from typing import Optional
class Config:
def __init__(self,
name: str,
address: str,
port: int,
secret: str,
secure: bool = False,
path: str = "/"
):
if ":" in name:
raise ValueError("Herald names cannot contain colons (:)")
self.name = name
self.address = address
if port < 0 or port > 65535:
raise ValueError("No such port")
self.port = port
self.secure = secure
if ":" in secret:
raise ValueError("Herald secrets cannot contain colons (:)")
self.secret = secret
if not path.startswith("/"):
raise ValueError("Herald paths must start with a slash (/)")
self.path = path
@property
def url(self):
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):
return f"<HeraldConfig for {self.url}>"
@classmethod
def from_config(
cls, *,
name: str,
address: str,
port: int,
secret: str,
secure: bool = False,
path: str = "/",
**_,
):
return cls(
name=name,
address=address,
port=port,
secret=secret,
secure=secure,
path=path
)

View file

@ -1,18 +0,0 @@
class HeraldError(Exception):
"""A generic :mod:`royalnet.herald` error."""
class LinkError(HeraldError):
"""An error for something that happened in a :class:`Link`."""
class ServerError(HeraldError):
"""An error for something that happened in a :class:`Server`."""
class ConnectionClosedError(LinkError):
"""The :py:class:`Link`'s connection was closed unexpectedly. The link can't be used anymore."""
class InvalidServerResponseError(LinkError):
"""The :py:class:`Server` sent invalid data to the :class:`Link`."""

View file

@ -1,189 +0,0 @@
import asyncio as aio
import functools
import logging
import uuid
from typing import *
import websockets
from .broadcast import Broadcast
from .config import Config
from .errors import ConnectionClosedError, InvalidServerResponseError
from .package import Package
from .request import Request
from .response import Response, ResponseSuccess, ResponseFailure
log = logging.getLogger(__name__)
class PendingRequest:
def __init__(self, *, loop: aio.AbstractEventLoop = None):
if loop is None:
self.loop = aio.get_event_loop()
else:
self.loop = loop
self.event: aio.Event = aio.Event(loop=loop)
self.data: Optional[dict] = None
def __repr__(self):
if self.event.is_set():
return f"<{self.__class__.__qualname__}: {self.data.__class__.__name__}>"
return f"<{self.__class__.__qualname__}>"
def set(self, data):
self.data = data
self.event.set()
def requires_connection(func):
@functools.wraps(func)
async def new_func(self, *args, **kwargs):
await self.connect_event.wait()
return await func(self, *args, **kwargs)
return new_func
def requires_identification(func):
@functools.wraps(func)
async def new_func(self, *args, **kwargs):
await self.identify_event.wait()
return await func(self, *args, **kwargs)
return new_func
class Link:
def __init__(self, config: Config, request_handler, *,
loop: aio.AbstractEventLoop = None):
self.config: Config = config
self.nid: str = str(uuid.uuid4())
self.websocket: Optional["websockets.WebSocketClientProtocol"] = None
self.request_handler: Callable[[Union[Request, Broadcast]],
Awaitable[Response]] = request_handler
self._pending_requests: Dict[str, PendingRequest] = {}
if loop is None:
self._loop = aio.get_event_loop()
else:
self._loop = loop
self.error_event: aio.Event = aio.Event(loop=self._loop)
self.connect_event: aio.Event = aio.Event(loop=self._loop)
self.identify_event: aio.Event = aio.Event(loop=self._loop)
def __repr__(self):
if self.identify_event.is_set():
return f"<{self.__class__.__qualname__} (identified)>"
elif self.connect_event.is_set():
return f"<{self.__class__.__qualname__} (connected)>"
elif self.error_event.is_set():
return f"<{self.__class__.__qualname__} (error)>"
else:
return f"<{self.__class__.__qualname__} (disconnected)>"
async def connect(self):
"""Connect to the :class:`Server` at :attr:`.config.url`."""
log.debug(f"Connecting to Herald Server at {self.config.url}...")
self.websocket = await websockets.connect(self.config.url, loop=self._loop)
self.connect_event.set()
log.debug(f"Connected!")
@requires_connection
async def receive(self) -> Package:
"""Recieve a :py:class:`Package` from the :py:class:`Server`.
Raises:
:exc:`ConnectionClosedError` if the connection is closed."""
try:
jbytes: bytes = await self.websocket.recv()
package: Package = Package.from_json_bytes(jbytes)
except websockets.ConnectionClosed:
self.error_event.set()
self.connect_event.clear()
self.identify_event.clear()
log.warning(f"Herald Server connection closed: {self.config.url}")
# What to do now? Let's just reraise.
raise ConnectionClosedError()
if self.identify_event.is_set() and package.destination != self.nid:
raise InvalidServerResponseError("Package is not addressed to this NetworkLink.")
log.debug(f"Received package: {package}")
return package
@requires_connection
async def identify(self) -> None:
log.debug(f"Identifying...")
await self.websocket.send(f"Identify {self.nid}:{self.config.name}:{self.config.secret}")
response: Package = await self.receive()
if not response.source == "<server>":
raise InvalidServerResponseError("Received a non-service package before identification.")
if "type" not in response.data:
raise InvalidServerResponseError("Missing 'type' in response data")
if response.data["type"] == "error":
raise ConnectionClosedError(f"Identification error: {response.data['type']}")
assert response.data["type"] == "success"
self.identify_event.set()
log.debug(f"Identified successfully!")
@requires_identification
async def send(self, package: Package):
"""Send a package to the :class:`Server`."""
log.debug(f"Trying to send package: {package}")
try:
jbytes = package.to_json_bytes()
except TypeError as e:
log.fatal(f"Could not send package: {' '.join(e.args)}")
raise
await self.websocket.send(jbytes)
log.debug(f"Sent package: {package}")
@requires_identification
async def broadcast(self, destination: str, broadcast: Broadcast) -> None:
package = Package(broadcast.to_dict(), source=self.nid, destination=destination)
await self.send(package)
log.debug(f"Sent broadcast to {destination}: {broadcast}")
@requires_identification
async def request(self, destination: str, request: Request) -> Response:
if destination.startswith("*"):
raise ValueError("requests cannot have multiple destinations")
package = Package(request.to_dict(), source=self.nid, destination=destination)
request = PendingRequest(loop=self._loop)
self._pending_requests[package.source_conv_id] = request
await self.send(package)
log.debug(f"Sent request to {destination}: {request}")
await request.event.wait()
if request.data["type"] == "ResponseSuccess":
response: Response = ResponseSuccess.from_dict(request.data)
elif request.data["type"] == "ResponseFailure":
response: Response = ResponseFailure.from_dict(request.data)
else:
raise TypeError("Unknown response type")
log.debug(f"Received from {destination}: {request} -> {response}")
return response
async def run(self):
"""Blockingly run the Link."""
log.debug(f"Running link: {self.config.name}")
if self.error_event.is_set():
raise ConnectionClosedError("RoyalnetLinks can't be rerun after an error.")
while True:
if not self.connect_event.is_set():
await self.connect()
if not self.identify_event.is_set():
await self.identify()
package: Package = await self.receive()
# Package is a response
if package.destination_conv_id in self._pending_requests:
request = self._pending_requests[package.destination_conv_id]
request.set(package.data)
continue
# Package is a request
elif package.data["msg_type"] == "Request":
log.debug(f"Received request {package.source_conv_id}: {package}")
response: Response = await self.request_handler(Request.from_dict(package.data))
response_package: Package = package.reply(response.to_dict())
await self.send(response_package)
log.debug(f"Replied to request {response_package.source_conv_id}: {response_package}")
# Package is a broadcast
elif package.data["msg_type"] == "Broadcast":
log.debug(f"Received broadcast {package.source_conv_id}: {package}")
await self.request_handler(Broadcast.from_dict(package.data))

View file

@ -1,114 +0,0 @@
import json
import uuid
from typing import *
class Package:
"""A data type with which a :py:class:`Link` communicates with a :py:class:`Server` or
another Link.
Contains info about the source and the destination."""
def __init__(self,
data: dict,
*,
source: str,
destination: str,
source_conv_id: Optional[str] = None,
destination_conv_id: Optional[str] = None):
"""Create a Package.
Parameters:
data: The data that should be sent.
source: The ``nid`` of the node that created this Package.
destination: The ``link_type`` of the destination node, or alternatively, the ``nid`` of the node.
Can also be the ``NULL`` value to send the message to nobody.
source_conv_id: The conversation id of the node that created this package.
Akin to the sequence number on IP packets.
destination_conv_id: The conversation id of the node that this Package is a reply to."""
self.data: dict = data
self.source: str = source
self.source_conv_id: str = source_conv_id or str(uuid.uuid4())
self.destination: str = destination
self.destination_conv_id: Optional[str] = destination_conv_id
def __repr__(self):
return f"<{self.__class__.__qualname__} {self.source} » {self.destination}>"
def __eq__(self, other):
if isinstance(other, Package):
return (self.data == other.data) and \
(self.source == other.source) and \
(self.destination == other.destination) and \
(self.source_conv_id == other.source_conv_id) and \
(self.destination_conv_id == other.destination_conv_id)
return False
def reply(self, data) -> "Package":
"""Reply to this :class:`Package` with another :class:`Package`.
Parameters:
data: The data that should be sent. Usually a :class:`Request`.
Returns:
The reply :class:`Package`."""
return Package(data,
source=self.destination,
destination=self.source,
source_conv_id=self.destination_conv_id or str(uuid.uuid4()),
destination_conv_id=self.source_conv_id)
@staticmethod
def from_dict(d) -> "Package":
"""Create a :class:`Package` from a dictionary."""
if "source" not in d:
raise ValueError("Missing source field")
if "nid" not in d["source"]:
raise ValueError("Missing source.nid field")
if "conv_id" not in d["source"]:
raise ValueError("Missing source.conv_id field")
if "destination" not in d:
raise ValueError("Missing destination field")
if "nid" not in d["destination"]:
raise ValueError("Missing destination.nid field")
if "conv_id" not in d["destination"]:
raise ValueError("Missing destination.conv_id field")
if "data" not in d:
raise ValueError("Missing data field")
return Package(d["data"],
source=d["source"]["nid"],
destination=d["destination"]["nid"],
source_conv_id=d["source"]["conv_id"],
destination_conv_id=d["destination"]["conv_id"])
def to_dict(self) -> dict:
"""Convert the :class:`Package` into a dictionary."""
return {
"source": {
"nid": self.source,
"conv_id": self.source_conv_id
},
"destination": {
"nid": self.destination,
"conv_id": self.destination_conv_id
},
"data": self.data
}
@staticmethod
def from_json_string(string: str) -> "Package":
"""Create a :class:`Package` from a JSON string."""
return Package.from_dict(json.loads(string))
def to_json_string(self) -> str:
"""Convert the :class:`Package` into a JSON string."""
return json.dumps(self.to_dict())
@staticmethod
def from_json_bytes(b: bytes) -> "Package":
"""Create a :class:`Package` from UTF-8-encoded JSON bytes."""
return Package.from_json_string(str(b, encoding="utf8"))
def to_json_bytes(self) -> bytes:
"""Convert the :class:`Package` into UTF-8-encoded JSON bytes."""
return bytes(self.to_json_string(), encoding="utf8")

View file

@ -1,30 +0,0 @@
from typing import *
class Request:
"""A request sent from a :class:`Link` to another.
It contains the name of the requested handler, in addition to the data."""
def __init__(self, handler: str, data: dict, msg_type: Optional[str] = None):
super().__init__()
if msg_type is not None:
assert msg_type == self.__class__.__name__
self.msg_type = self.__class__.__name__
self.handler: str = handler
self.data: dict = data
def to_dict(self):
return self.__dict__
@classmethod
def from_dict(cls, d: dict):
return cls(**d)
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.handler == other.handler and self.data == other.data
return False
def __repr__(self):
return f"{self.__class__.__qualname__}(handler={self.handler}, data={self.data})"

View file

@ -1,54 +0,0 @@
from typing import *
class Response:
"""A base class to be inherited by all other response types."""
def to_dict(self) -> dict:
"""Prepare the Response to be sent by converting it to a JSONable :py:class:`dict`."""
return {
"type": self.__class__.__name__,
**self.__dict__
}
def __eq__(self, other):
if isinstance(other, Response):
return self.to_dict() == other.to_dict()
return False
@classmethod
def from_dict(cls, d: dict) -> "Response":
"""Recreate the response from a received :py:class:`dict`."""
# Ignore type in dict
del d["type"]
# noinspection PyArgumentList
return cls(**d)
class ResponseSuccess(Response):
"""A response to a successful :py:class:`Request`."""
def __init__(self, data: Optional[dict] = None):
if data is None:
self.data = {}
else:
self.data = data
def __repr__(self):
return f"{self.__class__.__qualname__}(data={self.data})"
class ResponseFailure(Response):
"""A response to a invalid :py:class:`Request`."""
def __init__(self, name: str, description: str, extra_info: Optional[dict] = None):
self.name: str = name
self.description: str = description
self.extra_info: Optional[dict] = extra_info
def __repr__(self):
return f"{self.__class__.__qualname__}(" \
f"name={self.name}, " \
f"description={self.description}, " \
f"extra_info={self.extra_info}" \
f")"

View file

@ -1,163 +0,0 @@
import asyncio as aio
import datetime
import logging
import re
import uuid
from typing import *
import websockets
import royalnet.utils as ru
from .config import Config
from .package import Package
log = logging.getLogger(__name__)
class ConnectedClient:
"""The :py:class:`Server`-side representation of a connected :py:class:`Link`."""
def __init__(self, socket: "websockets.WebSocketServerProtocol"):
self.socket: "websockets.WebSocketServerProtocol" = socket
self.nid: Optional[str] = None
self.link_type: Optional[str] = None
self.connection_datetime: datetime.datetime = datetime.datetime.now()
def __repr__(self):
return f"<{self.__class__.__qualname__} {self.nid}>"
@property
def is_identified(self) -> bool:
"""Has the client sent a valid identification package?"""
return bool(self.nid)
async def send_service(self, msg_type: str, message: str):
await self.send(Package({"type": msg_type, "service": message},
source="<server>",
destination=self.nid))
async def send(self, package: Package):
"""Send a :py:class:`Package` to the :py:class:`Link`."""
await self.socket.send(package.to_json_bytes())
class Server:
def __init__(self, config: Config, *, loop: aio.AbstractEventLoop = None):
self.config: Config = config
self.identified_clients: List[ConnectedClient] = []
self.loop = loop
def __repr__(self):
return f"<{self.__class__.__qualname__}>"
def find_client(self, *, nid: str = None, link_type: str = None) -> List[ConnectedClient]:
assert not (nid and link_type)
if nid:
matching = [client for client in self.identified_clients if client.nid == nid]
assert len(matching) <= 1
return matching
if link_type:
matching = [client for client in self.identified_clients if client.link_type == link_type]
return matching or []
# noinspection PyUnusedLocal
async def listener(self, websocket: "websockets.server.WebSocketServerProtocol", path):
connected_client = ConnectedClient(websocket)
# Wait for identification
identify_msg = await websocket.recv()
log.debug(f"{websocket.remote_address} identified itself with: {identify_msg}.")
if not isinstance(identify_msg, str):
log.warning(f"Failed Herald identification: {websocket.remote_address[0]}:{websocket.remote_address[1]}")
await connected_client.send_service("error", "Invalid identification message (not a str)")
return
identification = re.match(r"Identify ([^:\s]+):([^:\s]+):([^:\s]+)", identify_msg)
if identification is None:
log.warning(f"Failed Herald identification: {websocket.remote_address[0]}:{websocket.remote_address[1]}")
await connected_client.send_service("error", "Invalid identification message (regex failed)")
return
secret = identification.group(3)
if secret != self.config.secret:
log.warning(f"Invalid Herald secret: {websocket.remote_address[0]}:{websocket.remote_address[1]}")
await connected_client.send_service("error", "Invalid secret")
return
# Identification successful
connected_client.nid = identification.group(1)
connected_client.link_type = identification.group(2)
log.info(f"Joined the Herald: {websocket.remote_address[0]}:{websocket.remote_address[1]}"
f" ({connected_client.link_type})")
self.identified_clients.append(connected_client)
await connected_client.send_service("success", "Identification successful!")
log.debug(f"{connected_client.nid}'s identification confirmed.")
# Main loop
while True:
# Receive packages
raw_bytes = await websocket.recv()
package: Package = Package.from_json_bytes(raw_bytes)
log.debug(f"Received package: {package}")
# Check if the package destination is the server itself.
if package.destination == "<server>":
# Do... nothing for now?
pass
# Otherwise, route the package to its destination
# noinspection PyAsyncCall
self.loop.create_task(self.route_package(package))
def find_destination(self, package: Package) -> List[ConnectedClient]:
"""Find a list of destinations for the package.
Parameters:
package: The package to find the destination of.
Returns:
A :class:`list` of :class:`ConnectedClient` to send the package to."""
# Parse destination
# Is it nothing?
if package.destination == "<none>":
return []
# Is it all possible destinations?
if package.destination == "*":
return self.identified_clients
# Is it a valid nid?
try:
destination = str(uuid.UUID(package.destination))
except ValueError:
pass
else:
return self.find_client(nid=destination)
# Is it a link_type?
return self.find_client(link_type=package.destination)
async def route_package(self, package: Package) -> None:
"""Executed every time a :class:`Package` is received and must be routed somewhere."""
destinations = self.find_destination(package)
log.debug(f"Routing package: {package} -> {destinations}")
for destination in destinations:
# This may have some consequences
specific_package = Package(package.data,
source=package.source,
destination=destination.nid,
source_conv_id=package.source_conv_id,
destination_conv_id=package.destination_conv_id)
await destination.send(specific_package)
def serve(self):
if self.config.secure:
raise Exception("Secure servers aren't supported yet")
log.debug(f"Serving on {self.config.url}")
try:
self.loop.run_until_complete(self.run())
except OSError as e:
log.fatal(f"OSError: {e}")
self.loop.run_forever()
async def run(self):
await websockets.serve(self.listener,
host=self.config.address,
port=self.config.port,
loop=self.loop)
def run_blocking(self, logging_cfg: Dict[str, Any]):
ru.init_logging(logging_cfg)
if self.loop is None:
self.loop = aio.get_event_loop()
self.serve()

View file

@ -1,31 +1,8 @@
# ROYALNET CONFIGURATION FILE # ROYALNET CONFIGURATION FILE
[Herald] [Baron]
# Enable the herald module, allowing different parts of Royalnet to talk to each other host = "combo.steffo.eu"
# Requires the `herald` extra to be installed port =
enabled = true
# Herald can run locally or connect to a remote instance
# "local" = run a local server
# "remote" = connect to a remote
mode = "local"
# The address of the network interface on which the Herald server should listen for connections
# If 0.0.0.0, listen for connections on all interfaces
# If 127.0.0.1, listen only for connections coming from the local machine
# OR
# The address of the remote Herald server
address = "127.0.0.1"
# The port on which the Herald server should run
# OR
# The port of the remote Herald server
port = 44444
# A password required to connect to the local Herald server
# OR
# The password required to connect to the remote Herald server
secret = "p4ssw0rd"
# Use HTTPS instead of HTTP for Herald connections
secure = false # Not supported yet!
# Use a different HTTP path for Herald connections
path = "/" # Different values aren't supported yet
[Alchemy] [Alchemy]
@ -106,10 +83,5 @@ active = [
] ]
# Configuration settings for specific packs # Add packs config here!
[Packs."royalnet.backpack"]
# Enable exception debug commands and stars
exc_debug = false
# Add your packs config here!
# [Packs."yourpack"] # [Packs."yourpack"]