diff --git a/poetry.lock b/poetry.lock index 85546ac..a85fb59 100644 --- a/poetry.lock +++ b/poetry.lock @@ -209,98 +209,37 @@ files = [ ] [[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +name = "tornado" +version = "6.4.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, ] [[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "be9ba03222aa256e2da6280b5060f06ef7cbfad1078a1b56da260f977418c025" +content-hash = "5a3fcad8e2c25e9251f09a1ca8ddb029b78c67a043a17f652bf13ba40fc5b114" diff --git a/pyproject.toml b/pyproject.toml index 7618639..7233ebe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,10 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" -websockets = "^12.0" injector = "^0.21.0" pydantic = "^2.7.4" envyaml = "^1.10.211231" +tornado = "^6.4.1" [build-system] diff --git a/requirements.txt b/requirements.txt index 7cb7f1e..47afd21 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -websockets==12.0 injector==0.21.0 pydantic==2.7.4 envyaml==1.10.211231 +tornado==6.4.1 diff --git a/slack_server_mock/__main__.py b/slack_server_mock/__main__.py index 1e5cc63..ee8fe1e 100644 --- a/slack_server_mock/__main__.py +++ b/slack_server_mock/__main__.py @@ -1,19 +1,16 @@ """ Application """ -import asyncio +import tornado from slack_server_mock.servers.http.server import start_http_server, stop_http_server -from slack_server_mock.servers.websocket.server import start_websocket_server +from slack_server_mock.servers.websocket.server import start_websocket_server, stop_websocket_server -loop = asyncio.new_event_loop() -websocket_server_task = loop.create_task(start_websocket_server()) -loop.run_in_executor(None, start_http_server) +start_websocket_server() +start_http_server() try: - loop.run_forever() + tornado.ioloop.IOLoop.current().start() except KeyboardInterrupt: - print("Shutting down servers") + print("Shutting down") finally: - websocket_server_task.cancel() - loop.run_until_complete(websocket_server_task) stop_http_server() - loop.close() + stop_websocket_server() diff --git a/slack_server_mock/actor/actor.py b/slack_server_mock/actor/actor.py index d608705..96740c9 100644 --- a/slack_server_mock/actor/actor.py +++ b/slack_server_mock/actor/actor.py @@ -1,9 +1,8 @@ """ Slack Actor """ -import asyncio import json from injector import inject, singleton -from websockets.server import WebSocketServerProtocol +from tornado.websocket import WebSocketHandler from slack_server_mock.settings.settings import Settings @@ -18,10 +17,10 @@ def __init__(self, settings: Settings) -> None: self._counter = 0 self._websocket = None - async def app_connected(self, websocket: WebSocketServerProtocol): + def app_connected(self, websocket: WebSocketHandler): """ Notify the actor that the application connected """ self._websocket = websocket - await self._websocket.send(self._wrap_message_with_envelope(self._conversation[0]['question'])) + self._websocket.write_message(self._wrap_message_with_envelope(self._conversation[0]['question'])) def message_received(self, msg: str): """ Notify the actor that the application sent a message """ @@ -32,19 +31,12 @@ def message_received(self, msg: str): self._conversation[self._counter]['answer'] = msg self._counter += 1 if self._counter < len(self._conversation): - self._send_response() + self._websocket.write_message( + self._wrap_message_with_envelope(self._conversation[self._counter]['question']) + ) else: self._dump_conversation() - def _send_response(self): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - loop.run_until_complete(self._do_send_response()) - loop.close() - - async def _do_send_response(self): - await self._websocket.send(self._wrap_message_with_envelope(self._conversation[self._counter]['question'])) - def _dump_conversation(self): with open(self._output, "+w", encoding="utf-8") as f: json.dump(self._conversation, f) diff --git a/slack_server_mock/servers/http/handler.py b/slack_server_mock/servers/http/handler.py index f98d1ac..27e0cd7 100644 --- a/slack_server_mock/servers/http/handler.py +++ b/slack_server_mock/servers/http/handler.py @@ -1,75 +1,62 @@ """ HTTP Handler """ from datetime import datetime -from http import HTTPStatus -from http.server import SimpleHTTPRequestHandler import json -import logging import re -from urllib.parse import urlparse, parse_qs + +from tornado.web import RequestHandler from slack_server_mock.actor.actor import Actor from slack_server_mock.injector.di import global_injector from slack_server_mock.settings.settings import Settings -class SlackHTTPHandler(SimpleHTTPRequestHandler): - """ Mock Implementation of the handler for the Slack HTTP server """ - protocol_version = "HTTP/1.1" - default_request_version = "HTTP/1.1" - logger = logging.getLogger(__name__) - +class BaseSlackHandler(RequestHandler): + """ Base class for all Slack HTTP Handlers """ pattern_for_language = re.compile("python/(\\S+)", re.IGNORECASE) pattern_for_package_identifier = re.compile("slackclient/(\\S+)") + _invalid_auth = { + "ok": False, + "error": "invalid_auth", + } + def _is_valid_user_agent(self): - user_agent = self.headers["User-Agent"] + user_agent = self.request.headers["User-Agent"] return self.pattern_for_language.search(user_agent) and self.pattern_for_package_identifier.search(user_agent) def _is_valid_token(self): - if self.path.startswith("oauth"): + if self.request.path.startswith("oauth"): return True - auth = self.headers.get("Authorization") + auth = self.request.headers.get("Authorization") return auth and (str(auth).startswith("Bearer xoxb-") or str(auth).startswith("Bearer xapp-")) - def _set_common_headers(self): - self.send_header("content-type", "application/json;charset=utf-8") - self.send_header("connection", "close") - self.end_headers() + def _validate_request(self, func): + def wrapper(*args, **kwargs): + if self._is_valid_token() and self._is_valid_user_agent(): + self.set_header("content-type", "application/json;charset=utf-8") + self.set_header("connection", "close") + func(*args, **kwargs) + else: + self.write(self._invalid_auth) + return wrapper - _invalid_auth = { - "ok": False, - "error": "invalid_auth", - } + def _is_request_valid(self) -> bool: + if self._is_valid_token() and self._is_valid_user_agent(): + self.set_header("content-type", "application/json;charset=utf-8") + self.set_header("connection", "close") + return True + self.write(self._invalid_auth) + return False - _not_found = { - "ok": False, - "error": "test_data_not_found", - } - def _parse_request_body(self): - content_len = int(self.headers.get("Content-Length", 0)) - post_body = self.rfile.read(content_len) - request_body = None - if post_body: - try: - post_body = post_body.decode("utf-8") - if post_body.startswith("{"): - request_body = json.loads(post_body) - else: - request_body = {k: v[0] for k, v in parse_qs(post_body).items()} - except UnicodeDecodeError: - pass - else: - parsed_path = urlparse(self.path) - if parsed_path and parsed_path.query: - request_body = {k: v[0] for k, v in parse_qs(parsed_path.query).items()} - return request_body - - def _handle_authorized_request(self): - request_body = self._parse_request_body() - match self.path: - case "/auth.test": - body = { +class AuthTestHandler(BaseSlackHandler): + """ Handler for auth.test endpoint""" + + def post(self): + """ Handle post request """ + if self._is_request_valid(): + self.write( + { "ok": True, "url": "https://xyz.slack.com/", "team": "Testing Workspace", @@ -80,49 +67,63 @@ def _handle_authorized_request(self): "enterprise_id": "E111", "is_enterprise_install": False, } - case "/apps.connections.open": - port = global_injector.get(Settings).websocket_server.port - body = { + ) + + +class AppsConnectionsOpenHandler(BaseSlackHandler): + """ Handler for apps.connection.open endpoint """ + + def post(self): + """ Handle post request """ + if self._is_request_valid(): + port = global_injector.get(Settings).websocket_server.port + self.write( + { "ok": True, - "url": f"ws://0.0.0.0:{port}/link", + "url": f"ws://{self.request.host_name}:{port}/link", } - case "/api.test": - if request_body: - body = {"ok": True, "args": request_body} - case "/chat.postMessage": - global_injector.get(Actor).message_received(request_body['text']) - ts = datetime.timestamp(datetime.now()) - body = { + ) + + +class ApiTestHandler(BaseSlackHandler): + """ Handler for api.test endpoint """ + + def post(self): + """ Handle post request """ + if self._is_request_valid(): + try: + data = json.loads(self.request.body) + if data: + self.write({"ok": True, "args": data}) + except json.JSONDecodeError: + self.set_status(400) + self.write({"error": "Invalid JSON"}) + + +class ChatPostMessageHandler(BaseSlackHandler): + """ Handler for chat.postMessage endpoint """ + + def post(self): + """ Handle post request """ + if self._is_request_valid(): + try: + data = json.loads(self.request.body) + except json.JSONDecodeError: + self.set_status(400) + self.write({"error": "Invalid JSON"}) + return + + global_injector.get(Actor).message_received(data['text']) + ts = datetime.timestamp(datetime.now()) + self.write( + { "ok": True, - "channel": request_body['channel'], + "channel": data['channel'], "ts": ts, "message": { - "text": request_body['text'], + "text": data['text'], "type": "message", "ts": ts } } - case _: - body = self._not_found - return body - - def _handle(self): - try: - if self._is_valid_token() and self._is_valid_user_agent(): - body = self._handle_authorized_request() - else: - body = self._invalid_auth - self.send_response(HTTPStatus.OK) - self._set_common_headers() - self.wfile.write(json.dumps(body).encode("utf-8")) - except Exception as e: - self.logger.error(str(e), exc_info=True) - raise - - def do_GET(self): - """ Handle GET requests """ - self._handle() - - def do_POST(self): # pylint:disable=C0103 - """ Handle POST requests """ - self._handle() + ) diff --git a/slack_server_mock/servers/http/server.py b/slack_server_mock/servers/http/server.py index 9a73e99..29eef9b 100644 --- a/slack_server_mock/servers/http/server.py +++ b/slack_server_mock/servers/http/server.py @@ -1,11 +1,12 @@ """ HTTP Server """ -from http.server import HTTPServer - from injector import inject, singleton +from tornado.httpserver import HTTPServer +from tornado.web import Application + from slack_server_mock.injector.di import global_injector from slack_server_mock.settings.settings import Settings -from slack_server_mock.servers.http.handler import SlackHTTPHandler +from slack_server_mock.servers.http import handler @singleton @@ -14,17 +15,25 @@ class SlackHTTPServer(): @inject def __init__(self, settings: Settings) -> None: self._port = settings.http_server.port - self._httpd = HTTPServer(('', self._port), SlackHTTPHandler) + app = Application( + [ + (r"/auth.test", handler.AuthTestHandler), + (r"/apps.connections.open", handler.AppsConnectionsOpenHandler), + (r"/api.test", handler.ApiTestHandler), + (r"/chat.postMessage", handler.ChatPostMessageHandler) + ] + ) + self._http_server = HTTPServer(app) def run(self): """ Start the HTTP Server """ - print("HTTP server running on port 8888") - self._httpd.serve_forever() - print("HTTPD server saying goodbye") + print(f"HTTP server running on port {self._port}") + self._http_server.listen(self._port) def stop(self): """ Stop the HTTP Server """ - self._httpd.shutdown() + self._http_server.stop() + print("HTTP server shutdown") def start_http_server(): diff --git a/slack_server_mock/servers/websocket/handler.py b/slack_server_mock/servers/websocket/handler.py new file mode 100644 index 0000000..67a06fc --- /dev/null +++ b/slack_server_mock/servers/websocket/handler.py @@ -0,0 +1,17 @@ +from tornado.websocket import WebSocketHandler + +from slack_server_mock.actor.actor import Actor +from slack_server_mock.injector.di import global_injector + + +# Define WebSocket handler +class SlackWebSocketHandler(WebSocketHandler): + def open(self): + print("WebSocket opened") + global_injector.get(Actor).app_connected(self) + + def on_message(self, message): + print("Received message:", message) + + def on_close(self): + print("WebSocket closed") diff --git a/slack_server_mock/servers/websocket/server.py b/slack_server_mock/servers/websocket/server.py index 9b20363..8d9573a 100644 --- a/slack_server_mock/servers/websocket/server.py +++ b/slack_server_mock/servers/websocket/server.py @@ -1,12 +1,11 @@ """ WebSocket Server """ -import asyncio - from injector import inject, singleton -import websockets -from websockets.server import WebSocketServerProtocol +from tornado.httpserver import HTTPServer +from tornado.web import Application from slack_server_mock.actor.actor import Actor from slack_server_mock.injector.di import global_injector +from slack_server_mock.servers.websocket import handler from slack_server_mock.settings.settings import Settings @@ -17,23 +16,27 @@ class SlackWebSocketServer(): def __init__(self, settings: Settings, actor: Actor) -> None: self._port = settings.websocket_server.port self._actor = actor + ws_app = Application( + [ + (r"/link", handler.SlackWebSocketHandler), + ] + ) + self._ws_server = HTTPServer(ws_app) + + def run(self): + """ Start the HTTP Server """ + print(f"WebSocket server running on port {self._port}") + self._ws_server.listen(self._port) + + def stop(self): + """ Stop the HTTP Server """ + self._ws_server.stop() + print(f"WebSocket server shutdown") + + +def start_websocket_server(): + global_injector.get(SlackWebSocketServer).run() + - async def websocket_handler(self, websocket: WebSocketServerProtocol): - print(f"New connection from {websocket.remote_address}") - await self._actor.app_connected(websocket) - try: - async for message in websocket: - print(f"Received message: {message}") - except websockets.ConnectionClosed as e: - print(f"Connection closed: {e}") - - async def start_websocket_server(self): - async with websockets.serve(self.websocket_handler, 'localhost', self._port): - try: - await asyncio.Future() # run forever - except asyncio.exceptions.CancelledError: - print("Websocket server saying goodbye") - - -async def start_websocket_server(): - await global_injector.get(SlackWebSocketServer).start_websocket_server() +def stop_websocket_server(): + global_injector.get(SlackWebSocketServer).stop()