diff --git a/poetry.lock b/poetry.lock index 78aab6d5..0ee3134d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -143,7 +143,7 @@ humanfriendly = ">=4.7" [[package]] name = "cryptography" -version = "3.1" +version = "3.1.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = true @@ -184,7 +184,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*" [[package]] name = "discord.py" -version = "1.4.1" +version = "1.5.0" description = "A Python wrapper for the Discord API" category = "main" optional = true @@ -344,7 +344,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.7.0" +version = "2.7.1" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -453,9 +453,20 @@ category = "main" optional = false 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]] name = "regex" -version = "2020.7.14" +version = "2020.9.27" description = "Alternative regular expression module, to replace re." category = "main" optional = false @@ -747,7 +758,7 @@ python-versions = ">=3.6.1" [[package]] name = "yarl" -version = "1.5.1" +version = "1.6.0" description = "Yet another URL library" category = "main" optional = true @@ -758,19 +769,19 @@ idna = ">=2.0" multidict = ">=4.0" [extras] -telegram = ["python_telegram_bot"] +telegram = ["python_telegram_bot", "urllib3"] discord = ["discord.py", "pynacl"] alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"] alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"] constellation = ["starlette", "uvicorn", "python-multipart"] sentry = ["sentry_sdk"] -herald = ["websockets"] coloredlogs = ["coloredlogs"] +baron = ["redis"] [metadata] lock-version = "1.0" python-versions = "^3.8" -content-hash = "88bf2cfd1fc7d44eadf8222999146e8ba8846200bcc37899bb0ce268d441a313" +content-hash = "9151cde5dbb6b2f35316ed0b3674b37b9ac7ab919216f56646bb023b502c01f4" [metadata.files] aiohttp = [ @@ -875,28 +886,28 @@ coloredlogs = [ {file = "coloredlogs-10.0.tar.gz", hash = "sha256:b869a2dda3fa88154b9dd850e27828d8755bfab5a838a1c97fbc850c6e377c36"}, ] cryptography = [ - {file = "cryptography-3.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f"}, - {file = "cryptography-3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0"}, - {file = "cryptography-3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36"}, - {file = "cryptography-3.1-cp27-cp27m-win32.whl", hash = "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a"}, - {file = "cryptography-3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791"}, - {file = "cryptography-3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761"}, - {file = "cryptography-3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e"}, - {file = "cryptography-3.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8"}, - {file = "cryptography-3.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c"}, - {file = "cryptography-3.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f"}, - {file = "cryptography-3.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237"}, - {file = "cryptography-3.1-cp35-cp35m-win32.whl", hash = "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716"}, - {file = "cryptography-3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695"}, - {file = "cryptography-3.1-cp36-abi3-win32.whl", hash = "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af"}, - {file = "cryptography-3.1-cp36-abi3-win_amd64.whl", hash = "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618"}, - {file = "cryptography-3.1-cp36-cp36m-win32.whl", hash = "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1"}, - {file = "cryptography-3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c"}, - {file = "cryptography-3.1-cp37-cp37m-win32.whl", hash = "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32"}, - {file = "cryptography-3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed"}, - {file = "cryptography-3.1-cp38-cp38-win32.whl", hash = "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67"}, - {file = "cryptography-3.1-cp38-cp38-win_amd64.whl", hash = "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10"}, - {file = "cryptography-3.1.tar.gz", hash = "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08"}, + {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"}, + {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"}, + {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"}, + {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"}, + {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"}, + {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"}, + {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"}, + {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"}, + {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"}, + {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"}, + {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"}, + {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"}, + {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"}, + {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"}, + {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"}, + {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"}, + {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"}, + {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"}, + {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"}, ] dateparser = [ {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"}, ] "discord.py" = [ - {file = "discord.py-1.4.1-py3-none-any.whl", hash = "sha256:98ea3096a3585c9c379209926f530808f5fcf4930928d8cfb579d2562d119570"}, - {file = "discord.py-1.4.1.tar.gz", hash = "sha256:f9decb3bfa94613d922376288617e6a6f969260923643e2897f4540c34793442"}, + {file = "discord.py-1.5.0-py3-none-any.whl", hash = "sha256:3acb61fde0d862ed346a191d69c46021e6063673f63963bc984ae09a685ab211"}, + {file = "discord.py-1.5.0.tar.gz", hash = "sha256:e71089886aa157341644bdecad63a72ff56b44406b1a6467b66db31c8e5a5a15"}, ] docutils = [ {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"}, ] pygments = [ - {file = "Pygments-2.7.0-py3-none-any.whl", hash = "sha256:2df50d16b45b977217e02cba6c8422aaddb859f3d0570a88e09b00eafae89c6e"}, - {file = "Pygments-2.7.0.tar.gz", hash = "sha256:2594e8fdb06fef91552f86f4fd3a244d148ab24b66042036e64f29a291515048"}, + {file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"}, + {file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"}, ] pynacl = [ {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.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 = [ - {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, - {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, - {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, - {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, - {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, - {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, - {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, - {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, - {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, + {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"}, + {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"}, + {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"}, + {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"}, + {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"}, + {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, + {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, + {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, + {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, ] requests = [ {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"}, ] yarl = [ - {file = "yarl-1.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb"}, - {file = "yarl-1.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593"}, - {file = "yarl-1.5.1-cp35-cp35m-win32.whl", hash = "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409"}, - {file = "yarl-1.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317"}, - {file = "yarl-1.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511"}, - {file = "yarl-1.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e"}, - {file = "yarl-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f"}, - {file = "yarl-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2"}, - {file = "yarl-1.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a"}, - {file = "yarl-1.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8"}, - {file = "yarl-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8"}, - {file = "yarl-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d"}, - {file = "yarl-1.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02"}, - {file = "yarl-1.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a"}, - {file = "yarl-1.5.1-cp38-cp38-win32.whl", hash = "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"}, - {file = "yarl-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692"}, - {file = "yarl-1.5.1.tar.gz", hash = "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6"}, + {file = "yarl-1.6.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1"}, + {file = "yarl-1.6.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188"}, + {file = "yarl-1.6.0-cp35-cp35m-win32.whl", hash = "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580"}, + {file = "yarl-1.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc"}, + {file = "yarl-1.6.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a"}, + {file = "yarl-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a"}, + {file = "yarl-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e"}, + {file = "yarl-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2"}, + {file = "yarl-1.6.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e"}, + {file = "yarl-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a"}, + {file = "yarl-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131"}, + {file = "yarl-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d"}, + {file = "yarl-1.6.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921"}, + {file = "yarl-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1"}, + {file = "yarl-1.6.0-cp38-cp38-win32.whl", hash = "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5"}, + {file = "yarl-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020"}, + {file = "yarl-1.6.0.tar.gz", hash = "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b"}, ] diff --git a/pyproject.toml b/pyproject.toml index d72fbb0f..f3df695f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "royalnet" -version = "5.11.13" +version = "6.0.0a1" description = "A multipurpose bot and web framework" authors = ["Stefano Pigozzi "] license = "AGPL-3.0+" @@ -48,12 +48,12 @@ python-multipart = { version = "^0.0.5", optional = true } # sentry sentry_sdk = { version = "~0.13.2", optional = true } -# herald -websockets = { version = "^8.1", optional = true } - # logging coloredlogs = { version = "^10.0", optional = true } +# baron +redis = { version = "^3.5.3", optional = true } + # Development dependencies [tool.poetry.dev-dependencies] pytest = "^5.2.1" @@ -69,8 +69,8 @@ alchemy_easy = ["sqlalchemy", "psycopg2_binary", "bcrypt"] alchemy_hard = ["sqlalchemy", "psycopg2", "bcrypt"] constellation = ["starlette", "uvicorn", "python-multipart"] sentry = ["sentry_sdk"] -herald = ["websockets"] coloredlogs = ["coloredlogs"] +baron = ["redis"] # Executable aliases diff --git a/royalnet/baron/__init__.py b/royalnet/baron/__init__.py new file mode 100644 index 00000000..deebbcae --- /dev/null +++ b/royalnet/baron/__init__.py @@ -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() diff --git a/royalnet/herald/README.md b/royalnet/herald/README.md deleted file mode 100644 index 1f1913f8..00000000 --- a/royalnet/herald/README.md +++ /dev/null @@ -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] -``` diff --git a/royalnet/herald/__init__.py b/royalnet/herald/__init__.py deleted file mode 100644 index 14b9687e..00000000 --- a/royalnet/herald/__init__.py +++ /dev/null @@ -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", -] diff --git a/royalnet/herald/broadcast.py b/royalnet/herald/broadcast.py deleted file mode 100644 index 165a1ed7..00000000 --- a/royalnet/herald/broadcast.py +++ /dev/null @@ -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})" diff --git a/royalnet/herald/config.py b/royalnet/herald/config.py deleted file mode 100644 index a26c3646..00000000 --- a/royalnet/herald/config.py +++ /dev/null @@ -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"" - - @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 - ) diff --git a/royalnet/herald/errors.py b/royalnet/herald/errors.py deleted file mode 100644 index 8c404697..00000000 --- a/royalnet/herald/errors.py +++ /dev/null @@ -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`.""" diff --git a/royalnet/herald/link.py b/royalnet/herald/link.py deleted file mode 100644 index 3565bea5..00000000 --- a/royalnet/herald/link.py +++ /dev/null @@ -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 == "": - 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)) diff --git a/royalnet/herald/package.py b/royalnet/herald/package.py deleted file mode 100644 index 0dafa641..00000000 --- a/royalnet/herald/package.py +++ /dev/null @@ -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") diff --git a/royalnet/herald/request.py b/royalnet/herald/request.py deleted file mode 100644 index 6655b65f..00000000 --- a/royalnet/herald/request.py +++ /dev/null @@ -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})" diff --git a/royalnet/herald/response.py b/royalnet/herald/response.py deleted file mode 100644 index 2b09d403..00000000 --- a/royalnet/herald/response.py +++ /dev/null @@ -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")" diff --git a/royalnet/herald/server.py b/royalnet/herald/server.py deleted file mode 100644 index ed530427..00000000 --- a/royalnet/herald/server.py +++ /dev/null @@ -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="", - 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 == "": - # 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 == "": - 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() diff --git a/sample_config.toml b/sample_config.toml index 0a937142..2bd81391 100644 --- a/sample_config.toml +++ b/sample_config.toml @@ -1,31 +1,8 @@ # ROYALNET CONFIGURATION FILE -[Herald] -# Enable the herald module, allowing different parts of Royalnet to talk to each other -# Requires the `herald` extra to be installed -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 +[Baron] +host = "combo.steffo.eu" +port = [Alchemy] @@ -106,10 +83,5 @@ active = [ ] -# Configuration settings for specific packs -[Packs."royalnet.backpack"] -# Enable exception debug commands and stars -exc_debug = false - -# Add your packs config here! +# Add packs config here! # [Packs."yourpack"]