From ba7f8bd92a0bc27b576aba28b959b79078692575 Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:00:23 +0300 Subject: [PATCH 1/6] black --- docs/assets/code/first_bot/002.py | 8 ++++---- docs/assets/code/first_bot/003.py | 6 +++--- docs/assets/code/first_bot/004.py | 8 +++++--- docs/assets/code/first_bot/005.py | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/assets/code/first_bot/002.py b/docs/assets/code/first_bot/002.py index 8dc5d80..ea9c21a 100644 --- a/docs/assets/code/first_bot/002.py +++ b/docs/assets/code/first_bot/002.py @@ -1,9 +1,9 @@ -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") bot = Bot( - apikey='algAJW9512kMWGALZIkAMWG', - src_name='test_client18215', - phone_number='79189998877' + apikey="algAJW9512kMWGALZIkAMWG", + src_name="test_client18215", + phone_number="79189998877", ) dp = Dispatcher(bot=bot) diff --git a/docs/assets/code/first_bot/003.py b/docs/assets/code/first_bot/003.py index c5e5ef6..80c6f33 100644 --- a/docs/assets/code/first_bot/003.py +++ b/docs/assets/code/first_bot/003.py @@ -1,7 +1,7 @@ -@dp.message_handler(commands=['start', 'echo']) -async def start_command(message: Message): +@dp.message_handler(commands=["start", "echo"]) +async def start_command(event: Event): """ This handler will be called when user sends `/start` or `/echo` command """ - await message.answer(f'Hello, {message.message.payload.sender.name}') \ No newline at end of file + await event.answer(f"Hello, {event.message.payload.sender.name}") diff --git a/docs/assets/code/first_bot/004.py b/docs/assets/code/first_bot/004.py index ae8ebb3..b4f591f 100644 --- a/docs/assets/code/first_bot/004.py +++ b/docs/assets/code/first_bot/004.py @@ -1,4 +1,6 @@ @dp.message_handler() -async def start_switch(message: Message): - await message.answer(f'Hello, {message.message.payload.sender.name},' - f' text: {message.message.payload.text}') \ No newline at end of file +async def start_switch(event: Event): + await event.answer( + f"Hello, {event.message.payload.sender.name}," + f" text: {event.message.payload.text}" + ) diff --git a/docs/assets/code/first_bot/005.py b/docs/assets/code/first_bot/005.py index 755753f..0ed6e2e 100644 --- a/docs/assets/code/first_bot/005.py +++ b/docs/assets/code/first_bot/005.py @@ -6,5 +6,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) - web.run_app(webhook, port=8017) \ No newline at end of file + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) + web.run_app(webhook, port=8017) From 8da1c8452046b40c31760d225682866eaa4ef4ec Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:05:39 +0300 Subject: [PATCH 2/6] up python 3.7, added magic-filter --- poetry.lock | 285 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 3 +- 2 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..de5681d --- /dev/null +++ b/poetry.lock @@ -0,0 +1,285 @@ +[[package]] +name = "aiohttp" +version = "3.8.3" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +asynctest = {version = "0.13.0", markers = "python_version < \"3.8\""} +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["aiodns", "brotli", "cchardet"] + +[[package]] +name = "aioredis" +version = "2.0.0" +description = "asyncio (PEP 3156) Redis support" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +async-timeout = "*" +typing-extensions = "*" + +[package.extras] +hiredis = ["hiredis (>=1.0)"] + +[[package]] +name = "aiosignal" +version = "1.2.0" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""} + +[[package]] +name = "asynctest" +version = "0.13.0" +description = "Enhance the standard unittest package with features for testing asyncio libraries" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" + +[[package]] +name = "dataclass-factory" +version = "2.16" +description = "An utility class for creating instances of dataclasses" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "frozenlist" +version = "1.3.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "loguru" +version = "0.5.3" +description = "Python logging made (stupidly) simple" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"] + +[[package]] +name = "magic-filter" +version = "1.0.9" +description = "This package provides magic filter based on dynamic attribute getter" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[[package]] +name = "multidict" +version = "6.0.2" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "ujson" +version = "5.4.0" +description = "Ultra fast JSON encoder and decoder for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] + +[[package]] +name = "yarl" +version = "1.8.1" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "caac2ad80771b22b68b083a6559cc06b9293e60892248eb8408c6c6adddb59b7" + +[metadata.files] +aiohttp = [] +aioredis = [] +aiosignal = [ + {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"}, + {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"}, +] +async-timeout = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, +] +asynctest = [] +attrs = [] +charset-normalizer = [] +colorama = [] +dataclass-factory = [] +frozenlist = [] +idna = [] +loguru = [] +magic-filter = [] +multidict = [ + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, + {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, + {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, + {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, + {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, + {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, + {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, + {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, + {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, + {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, + {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, + {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, + {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, + {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, + {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, + {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, + {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, + {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, + {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, + {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, + {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, + {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, +] +typing-extensions = [] +ujson = [] +win32-setctime = [] +yarl = [] diff --git a/pyproject.toml b/pyproject.toml index cc08b0d..26eb5d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,13 @@ include = [ Documentation = "https://waio.readthedocs.io/" [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" aiohttp = "^3.7.4.post0" ujson = "5.4.0" dataclass-factory = "^2.11" loguru = "^0.5.3" aioredis = "2.0.0" +magic-filter = "^1.0.9" [build-system] requires = ["poetry-core>=1.0.0", "wheel>=0.36,<1.0", "poetry>=1.1,<2", "virtualenv==20.0.33"] From 7ed5f26535999b432ecf432236dff45fe35a96d0 Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:06:47 +0300 Subject: [PATCH 3/6] update aiohttp --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index de5681d..e45f1ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -196,7 +196,7 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "caac2ad80771b22b68b083a6559cc06b9293e60892248eb8408c6c6adddb59b7" +content-hash = "a0625a2e32c6f4344219edd134bb1258a8efffe52f3aea8e835b7ecc458e8c96" [metadata.files] aiohttp = [] diff --git a/pyproject.toml b/pyproject.toml index 26eb5d0..b8c409a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ Documentation = "https://waio.readthedocs.io/" [tool.poetry.dependencies] python = "^3.7" -aiohttp = "^3.7.4.post0" +aiohttp = "^3.8.3" ujson = "5.4.0" dataclass-factory = "^2.11" loguru = "^0.5.3" From 8fd769b2578d6a5d985f4cae4fe114dddf3e9c35 Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:13:32 +0300 Subject: [PATCH 4/6] Big update * Rename Message -> Event * Added Router * Added magic filter from aiogram (F) * Added notify_success_handler and EventSubscribe. * Multiple handlers no longer respond to the same event. --- docs/assets/code/fsm/email.py | 7 +- docs/assets/code/fsm/register.py | 6 +- docs/assets/code/fsm/states.py | 1 + docs/assets/code/rules/default_rules.py | 4 +- docs/assets/code/rules/example_rule_001.py | 5 +- docs/assets/code/rules/example_rule_002.py | 6 +- docs/assets/code/rules/full_code_rule_003.py | 22 ++-- docs/assets/code/rules/named_rule.py | 12 +- docs/assets/code/rules/nammed_unnamed.py | 9 +- docs/assets/code/rules/register_rule.py | 2 +- docs/assets/code/rules/rule_with_args.py | 8 +- docs/assets/code/rules/unnamed_rule.py | 9 +- .../assets/code/rules/use_example_rule_001.py | 4 +- .../assets/code/rules/use_example_rule_002.py | 2 +- .../assets/code/rules/use_example_rule_003.py | 4 +- docs/assets/code/rules/use_rule_with_args.py | 7 +- docs/assets/code/split_code/handlers_foo.py | 35 +++-- docs/assets/code/split_code/main.py | 26 +--- docs/assets/code/split_code/misc.py | 10 +- docs/tutorial/middleware.md | 10 +- examples/filters/main.py | 114 +++++++++------- examples/filters/rule_long_message.py | 6 +- examples/filters/rule_number.py | 8 +- examples/first_bot/main.py | 31 ++--- examples/fsm/main.py | 41 +++--- examples/list_keyboard/button.py | 40 ++++-- examples/list_keyboard/callbacks.py | 7 +- examples/list_keyboard/main.py | 39 +++--- examples/middleware/bot.py | 21 +-- examples/middleware/database.py | 8 +- examples/middleware/example_middlewares.py | 22 +++- examples/middleware/misc.py | 10 +- examples/reply_keyboard/button.py | 45 +++++-- examples/reply_keyboard/callback.py | 3 +- examples/reply_keyboard/main.py | 66 ++++++---- examples/routers/__init__.py | 0 examples/routers/binding.py | 7 + examples/routers/filters.py | 11 ++ examples/routers/handlers_1.py | 12 ++ examples/routers/handlers_2.py | 11 ++ examples/routers/main.py | 32 +++++ examples/routers/middlewares.py | 15 +++ examples/send_audio/main.py | 22 ++-- examples/send_file/main.py | 26 ++-- examples/send_image/main.py | 24 ++-- examples/send_sticker/main.py | 20 ++- examples/send_video/main.py | 22 ++-- main.py | 75 ----------- tests/test_filters.py | 30 ++--- tests/test_fsm.py | 35 +++-- tests/test_reply_keyboard.py | 43 +++--- waio/__init__.py | 6 +- waio/bot.py | 123 +++++------------- waio/client/exceptions.py | 2 +- waio/client/http.py | 13 +- waio/dispatcher/__init__.py | 0 waio/dispatcher/dispatcher.py | 69 ++++++++++ waio/dispatcher/event.py | 83 ++++++++++++ waio/dispatcher/router.py | 106 +++++++++++++++ waio/factory/base.py | 6 - waio/factory/factory.py | 19 ++- waio/factory/models/basic.py | 1 - waio/factory/models/main.py | 2 +- waio/factory/models/response.py | 24 ++-- waio/factory/schemas.py | 56 ++++---- waio/gupshup/api.py | 13 +- waio/gupshup/form.py | 10 +- waio/handlers/__init__.py | 1 - waio/handlers/abc.py | 7 +- waio/handlers/base_handlers.py | 61 ++------- waio/handlers/executor.py | 29 +++-- waio/handlers/func_handler.py | 34 +++-- waio/keyboard/list.py | 41 +++--- waio/keyboard/reply.py | 38 +++--- waio/labeler.py | 19 +-- waio/logs/__init__.py | 2 +- waio/logs/log_level.py | 6 +- waio/middleware/base.py | 34 ++--- waio/models/audio.py | 12 +- waio/models/enums.py | 27 ++-- waio/models/file.py | 15 +-- waio/models/image.py | 10 +- waio/models/sticker.py | 12 +- waio/models/system/__init__.py | 0 waio/models/system/handlers.py | 13 ++ waio/models/text.py | 8 +- waio/models/video.py | 15 +-- waio/protocols/bot.py | 41 +++--- waio/rules/abc.py | 5 +- waio/rules/default.py | 64 ++++----- waio/states/context.py | 6 +- waio/states/fsm.py | 17 +-- waio/storage/connector.py | 13 +- waio/storage/redis.py | 7 +- waio/types/__init__.py | 2 +- waio/types/content_types.py | 1 - waio/types/message.py | 67 +++++++--- waio/utils/callback/base_callback.py | 20 +-- waio/utils/callback/base_filter.py | 7 +- waio/utils/callback/callbacks.py | 10 +- waio/utils/callback/filters.py | 10 +- waio/utils/dicts.py | 8 +- waio/utils/form_data.py | 21 ++- 103 files changed, 1246 insertions(+), 1005 deletions(-) create mode 100644 examples/routers/__init__.py create mode 100644 examples/routers/binding.py create mode 100644 examples/routers/filters.py create mode 100644 examples/routers/handlers_1.py create mode 100644 examples/routers/handlers_2.py create mode 100644 examples/routers/main.py create mode 100644 examples/routers/middlewares.py create mode 100644 waio/dispatcher/__init__.py create mode 100644 waio/dispatcher/dispatcher.py create mode 100644 waio/dispatcher/event.py create mode 100644 waio/dispatcher/router.py create mode 100644 waio/models/system/__init__.py create mode 100644 waio/models/system/handlers.py diff --git a/docs/assets/code/fsm/email.py b/docs/assets/code/fsm/email.py index 729b007..38c1836 100644 --- a/docs/assets/code/fsm/email.py +++ b/docs/assets/code/fsm/email.py @@ -1,6 +1,5 @@ @dp.message_handler(state=RegisterStates.birthday) -async def register_age(message: Message, state: FSMContext): - await state.set_data(birthday=message.text) - await message.answer(f'Thanks for sending you birthday!\n' - f'Send you email address') +async def register_age(event: Event, state: FSMContext): + await state.set_data(birthday=event.text) + await event.answer(f"Thanks for sending you birthday!\n" f"Send you email address") await state.set_state(RegisterStates.email) diff --git a/docs/assets/code/fsm/register.py b/docs/assets/code/fsm/register.py index 54f1f05..0ab8acf 100644 --- a/docs/assets/code/fsm/register.py +++ b/docs/assets/code/fsm/register.py @@ -1,4 +1,4 @@ -@dp.message_handler(commands=['register'], state='*') -async def register_name(message: Message, state: FSMContext): - await message.answer(f'Hi, {message.sender_name}! send your date of birth') +@dp.message_handler(commands=["register"], state="*") +async def register_name(event: Event, state: FSMContext): + await event.answer(f"Hi, {event.sender_name}! send your date of birth") await state.set_state(RegisterStates.birthday) diff --git a/docs/assets/code/fsm/states.py b/docs/assets/code/fsm/states.py index c3df168..6cfcca3 100644 --- a/docs/assets/code/fsm/states.py +++ b/docs/assets/code/fsm/states.py @@ -1,5 +1,6 @@ from waio.states import State, StatesGroup + class RegisterStates(StatesGroup): birthday = State() email = State() diff --git a/docs/assets/code/rules/default_rules.py b/docs/assets/code/rules/default_rules.py index e81785b..2a538c4 100644 --- a/docs/assets/code/rules/default_rules.py +++ b/docs/assets/code/rules/default_rules.py @@ -6,5 +6,5 @@ "text_contains": TextRuleContains, "text_startswith": TextRuleStartswith, "text_endswith": TextRuleEndswith, - "content_type": ContentTypeRule -} \ No newline at end of file + "content_type": ContentTypeRule, +} diff --git a/docs/assets/code/rules/example_rule_001.py b/docs/assets/code/rules/example_rule_001.py index 3830d79..51f30b5 100644 --- a/docs/assets/code/rules/example_rule_001.py +++ b/docs/assets/code/rules/example_rule_001.py @@ -1,6 +1,7 @@ from waio.rules import ABCRule +from waio.types import Event class StaticLongMessageRule(ABCRule): - async def check(self, message: Message) -> bool: - return len(message.text) > 200 + async def check(self, event: Event) -> bool: + return len(event.text) > 200 diff --git a/docs/assets/code/rules/example_rule_002.py b/docs/assets/code/rules/example_rule_002.py index 0b849b4..364f653 100644 --- a/docs/assets/code/rules/example_rule_002.py +++ b/docs/assets/code/rules/example_rule_002.py @@ -1,10 +1,10 @@ from waio.rules import ABCRule +from waio.types import Event class DynamicLongMessageRule(ABCRule): def __init__(self, len_message: int): self.len_message = len_message - async def check(self, message: Message) -> bool: - return len(message.text) > self.len_message - + async def check(self, event: Event) -> bool: + return len(event.text) > self.len_message diff --git a/docs/assets/code/rules/full_code_rule_003.py b/docs/assets/code/rules/full_code_rule_003.py index 9dd22b0..23cb014 100644 --- a/docs/assets/code/rules/full_code_rule_003.py +++ b/docs/assets/code/rules/full_code_rule_003.py @@ -2,16 +2,12 @@ from waio import Bot, Dispatcher from waio.rules.abc import ABCRule -from waio.types import Message +from waio.types import Event from waio.logs import loguru_filter -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=79281112233 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=79281112233) dp = Dispatcher(bot=bot) @@ -20,16 +16,16 @@ class DynamicLongMessageRule(ABCRule): def __init__(self, len_message: int): self.len_message = len_message - async def check(self, message: Message) -> bool: - return len(message.text) > self.len_message + async def check(self, event: Event) -> bool: + return len(event.text) > self.len_message -dp.labeler.bind_rule('len_more', DynamicLongMessageRule) +dp.labeler.bind_rule("len_more", DynamicLongMessageRule) @dp.message_handler(len_more=20) -async def text_len(message: Message): - await message.answer(f'msg len: {len(message.text)}') +async def text_len(event: Event): + await event.answer(f"msg len: {len(event.text)}") async def handler_gupshup(request): @@ -40,5 +36,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/docs/assets/code/rules/named_rule.py b/docs/assets/code/rules/named_rule.py index 1b828c7..2670c82 100644 --- a/docs/assets/code/rules/named_rule.py +++ b/docs/assets/code/rules/named_rule.py @@ -1,8 +1,8 @@ -@dp.message_handler(text_equals=['foo', 'bar']) -async def start_text_equals(message: Message): - await message.answer(f'Filter used: [text_equals], msg: {message.text}') +@dp.message_handler(text_equals=["foo", "bar"]) +async def start_text_equals(event: Event): + await event.answer(f"Filter used: [text_equals], msg: {event.text}") -@dp.message_handler(text_contains=['ru', 'com']) -async def start_text_contains(message: Message): - await message.answer(f'Filter used: [text_contains], msg: {message.text}') \ No newline at end of file +@dp.message_handler(text_contains=["ru", "com"]) +async def start_text_contains(event: Event): + await event.answer(f"Filter used: [text_contains], msg: {event.text}") diff --git a/docs/assets/code/rules/nammed_unnamed.py b/docs/assets/code/rules/nammed_unnamed.py index b97d535..58bcdd2 100644 --- a/docs/assets/code/rules/nammed_unnamed.py +++ b/docs/assets/code/rules/nammed_unnamed.py @@ -1,9 +1,10 @@ from waio.rules import TextRule, ContentType +from waio.types import Event @dp.message_handler( - TextRule(startswith=['1111', '2222'], endswith=['x', 'y', 'z']), - content_type=[ContentType.TEXT] + TextRule(startswith=["1111", "2222"], endswith=["x", "y", "z"]), + content_type=[ContentType.TEXT], ) -async def text_start_switch_without_labeler(message: Message): - await message.answer(f'Filter used: [TextRule], msg: {message.text}') \ No newline at end of file +async def text_start_switch_without_labeler(event: Event): + await event.answer(f"Filter used: [TextRule], msg: {event.text}") diff --git a/docs/assets/code/rules/register_rule.py b/docs/assets/code/rules/register_rule.py index e16eb0c..5e75f42 100644 --- a/docs/assets/code/rules/register_rule.py +++ b/docs/assets/code/rules/register_rule.py @@ -1 +1 @@ -dp.labeler.bind_rule('len_more', DynamicLongMessageRule) \ No newline at end of file +dp.labeler.bind_rule("len_more", DynamicLongMessageRule) diff --git a/docs/assets/code/rules/rule_with_args.py b/docs/assets/code/rules/rule_with_args.py index 0c1bd9f..d8d6a80 100644 --- a/docs/assets/code/rules/rule_with_args.py +++ b/docs/assets/code/rules/rule_with_args.py @@ -2,14 +2,14 @@ from phonenumbers import timezone, parse, geocoder from waio.rules import ABCRule -from waio.types import Message +from waio.types import Event G_T = Dict[str, Union[int, str, Tuple[str]]] class RussianNumberRule(ABCRule): - async def check(self, message: Message) -> Union[bool, Dict[str, G_T]]: - phone_number_data = self.get_phone_number_data(message.sender_number) + async def check(self, event: Event) -> Union[bool, Dict[str, G_T]]: + phone_number_data = self.get_phone_number_data(event.sender_number) if phone_number_data["country"] == "Russia": return {"number_data": phone_number_data} return False @@ -25,5 +25,5 @@ def get_phone_number_data(number: str) -> G_T: "country_code": phone_number.country_code, "national_number": phone_number.national_number, "country": country_name, - "time_zone": time_zones_number + "time_zone": time_zones_number, } diff --git a/docs/assets/code/rules/unnamed_rule.py b/docs/assets/code/rules/unnamed_rule.py index 4202fe2..a81d2b4 100644 --- a/docs/assets/code/rules/unnamed_rule.py +++ b/docs/assets/code/rules/unnamed_rule.py @@ -1,7 +1,6 @@ from waio.rules import MessageCommandsRule -@dp.message_handler(MessageCommandsRule(commands=['start', 'echo'])) -async def commands_rule_without_labeler(message: Message): - await message.answer( - f'Filter used: [MessageCommandsRule], msg: {message.text}' - ) + +@dp.message_handler(MessageCommandsRule(commands=["start", "echo"])) +async def commands_rule_without_labeler(event: Event): + await event.answer(f"Filter used: [MessageCommandsRule], msg: {event.text}") diff --git a/docs/assets/code/rules/use_example_rule_001.py b/docs/assets/code/rules/use_example_rule_001.py index 577f5fc..3231406 100644 --- a/docs/assets/code/rules/use_example_rule_001.py +++ b/docs/assets/code/rules/use_example_rule_001.py @@ -1,3 +1,3 @@ @dp.message_handler(StaticLongMessageRule()) -async def foo(message: Message): - ... \ No newline at end of file +async def foo(event: Event): + ... diff --git a/docs/assets/code/rules/use_example_rule_002.py b/docs/assets/code/rules/use_example_rule_002.py index 182c1f6..e5f218f 100644 --- a/docs/assets/code/rules/use_example_rule_002.py +++ b/docs/assets/code/rules/use_example_rule_002.py @@ -1,3 +1,3 @@ @dp.message_handler(DynamicLongMessageRule(len_message=120)) -async def foo(message: Message): +async def foo(event: Event): ... diff --git a/docs/assets/code/rules/use_example_rule_003.py b/docs/assets/code/rules/use_example_rule_003.py index b5923eb..8f2c6c0 100644 --- a/docs/assets/code/rules/use_example_rule_003.py +++ b/docs/assets/code/rules/use_example_rule_003.py @@ -1,3 +1,3 @@ @dp.message_handler(len_more=12) -async def text_len(message: Message): - await message.answer(f'msg len: {len(message.text)}') \ No newline at end of file +async def text_len(event: Event): + await event.answer(f"msg len: {len(event.text)}") diff --git a/docs/assets/code/rules/use_rule_with_args.py b/docs/assets/code/rules/use_rule_with_args.py index 5818839..57d397e 100644 --- a/docs/assets/code/rules/use_rule_with_args.py +++ b/docs/assets/code/rules/use_rule_with_args.py @@ -1,6 +1,3 @@ @dp.message_handler(RussianNumberRule(), commands=["check_number"]) -async def register_rule_check_number(message: Message, number_data: G_T): - await message.answer( - f'You are from Russia! Number data:\n' - f'```{number_data}```' - ) +async def register_rule_check_number(event: Event, number_data: G_T): + await event.answer(f"You are from Russia! Number data:\n" f"```{number_data}```") diff --git a/docs/assets/code/split_code/handlers_foo.py b/docs/assets/code/split_code/handlers_foo.py index a16ce4a..36f5bde 100644 --- a/docs/assets/code/split_code/handlers_foo.py +++ b/docs/assets/code/split_code/handlers_foo.py @@ -1,33 +1,28 @@ from re import Match -from waio.types import Message +from waio.types import Event -async def start_commands(message: Message): - await message.answer( - f'Filter used: [commands and content_type:TEXT], ' - f'msg: {message.message.payload.text}' +async def start_commands(event: Event): + await event.answer( + f"Filter used: [commands and content_type:TEXT], " + f"msg: {event.message.payload.text}" ) -async def start_photo(message: Message): - await message.answer( - f'Filter used: [content_type:PHOTO], ' - f'url_photo: {message.message.payload.url}' +async def start_photo(event: Event): + await event.answer( + f"Filter used: [content_type:PHOTO], " f"url_photo: {event.message.payload.url}" ) -async def start_regex(message: Message, regex: Match): - cart_id = regex.group('cart_id') - item_id = regex.group('item_id') - await message.answer( - f'Filter used: [regex], ' - f'cart_id: {cart_id}, item_id: {item_id}' +async def start_regex(event: Event, regex: Match): + cart_id = regex.group("cart_id") + item_id = regex.group("item_id") + await event.answer( + f"Filter used: [regex], " f"cart_id: {cart_id}, item_id: {item_id}" ) -async def start_text_equals(message: Message): - await message.answer( - f'Filter used: [text_equals], ' - f'msg: {message.text}' - ) +async def start_text_equals(event: Event): + await event.answer(f"Filter used: [text_equals], " f"msg: {event.text}") diff --git a/docs/assets/code/split_code/main.py b/docs/assets/code/split_code/main.py index ddb3cfb..9694de9 100644 --- a/docs/assets/code/split_code/main.py +++ b/docs/assets/code/split_code/main.py @@ -2,31 +2,17 @@ from waio.types import ContentType from misc import dp, webhook -from handlers_foo import ( - start_commands, - start_photo, - start_regex, - start_text_equals -) +from handlers_foo import start_commands, start_photo, start_regex, start_text_equals dp.register_message_handler( - handler=start_commands, - commands=['start', 'echo'], - content_type=[ContentType.TEXT] + handler=start_commands, commands=["start", "echo"], content_type=[ContentType.TEXT] ) +dp.register_message_handler(handler=start_photo, content_type=[ContentType.PHOTO]) dp.register_message_handler( - handler=start_photo, - content_type=[ContentType.PHOTO] -) -dp.register_message_handler( - handler=start_regex, - regex=r'cart_id_(?P\d+)_item_id_(?P\d+)$' -) -dp.register_message_handler( - handler=start_text_equals, - text_equals=['foo', 'bar'] + handler=start_regex, regex=r"cart_id_(?P\d+)_item_id_(?P\d+)$" ) +dp.register_message_handler(handler=start_text_equals, text_equals=["foo", "bar"]) async def handler_gupshup(request): @@ -36,5 +22,5 @@ async def handler_gupshup(request): if __name__ == "__main__": - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/docs/assets/code/split_code/misc.py b/docs/assets/code/split_code/misc.py index d1eb95d..0410949 100644 --- a/docs/assets/code/split_code/misc.py +++ b/docs/assets/code/split_code/misc.py @@ -4,15 +4,11 @@ from waio.logs import loguru_filter from waio.storage import RedisStorage -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -storage = RedisStorage(prefix_fsm='fsm', redis_url="redis://localhost:6379") +storage = RedisStorage(prefix_fsm="fsm", redis_url="redis://localhost:6379") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=7928994433 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=7928994433) dp = Dispatcher(bot=bot, storage=storage) diff --git a/docs/tutorial/middleware.md b/docs/tutorial/middleware.md index 28b86b1..e5a1090 100644 --- a/docs/tutorial/middleware.md +++ b/docs/tutorial/middleware.md @@ -127,7 +127,7 @@ dp.labeler.register_middleware(DatabaseMiddleware()) main.py ```python from aiohttp import web -from waio.types import Message +from waio.types import Event from examples.middleware.database import ExampleDatabase from examples.middleware.misc import * @@ -136,15 +136,15 @@ webhook = web.Application() @dp.message_handler(text_startswith='ch') -async def check_ban(message: Message): - await message.answer('The message has been processed, you are not blocked.') +async def check_ban(event: Event): + await event.answer('The message has been processed, you are not blocked.') @dp.message_handler(commands=['s', 'session']) -async def session_check(message: Message, session: ExampleDatabase): +async def session_check(event: Event, session: ExampleDatabase): session.set('name', 'Marina') session.set('age', '21') - await message.answer('Hello man!') + await event.answer('Hello man!') async def handler_gupshup(request): diff --git a/examples/filters/main.py b/examples/filters/main.py index f642ebc..ec165e4 100644 --- a/examples/filters/main.py +++ b/examples/filters/main.py @@ -1,88 +1,110 @@ import re from aiohttp import web -from waio import Bot, Dispatcher -from waio.types import Message, ContentType -from waio.logs import loguru_filter -from waio.rules import TextRule, MessageCommandsRule from examples.filters.rule_long_message import LongMessageRule from examples.filters.rule_number import RussianNumberRule +from waio.bot import Bot +from waio.dispatcher.dispatcher import Dispatcher +from waio.logs import loguru_filter +from waio.rules import MessageCommandsRule +from waio.rules import TextRule +from waio.types import ContentType +from waio.types import Event +from waio import F -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=79289998877 + apikey="", + src_name="", + phone_number=917834811114, ) dp = Dispatcher(bot=bot) - -dp.labeler.bind_rule('len', LongMessageRule) +dp.labeler.bind_rule("len", LongMessageRule) @dp.message_handler(RussianNumberRule(), commands=["check_number"]) -async def text_rule_check_number(message: Message, number_data): - await message.answer(f'You are from Russia! Number data:\n```{number_data}```') +async def text_rule_check_number(event: Event, number_data): + await event.answer(f"You are from Russia! Number data:\n```{number_data}```") -@dp.message_handler(commands=['start', 'echo'], content_type=[ContentType.TEXT]) -async def start_commands(message: Message): - await message.answer(f'Filter used: [commands and content_type:TEXT], msg: {message.message.payload.text}') +@dp.message_handler(commands=["start", "echo"], content_type=[ContentType.TEXT]) +async def start_commands(event): + await event.answer( + f"Filter used: [commands and content_type:TEXT], msg: {event.message.payload.text}" + ) @dp.message_handler(content_type=[ContentType.PHOTO]) -async def start_photo(message: Message): - await message.answer(f'Filter used: [content_type:PHOTO], url_photo: {message.message.payload.url}') +async def start_photo(event: Event): + await event.answer( + f"Filter used: [content_type:PHOTO], url_photo: {event.message.payload.url}" + ) -@dp.message_handler(regex=r'cart_id_(?P\d+)_item_id_(?P\d+)$') -async def start_regex(message: Message, regex: re.Match): - cart_id = regex.group('cart_id') - item_id = regex.group('item_id') - await message.answer(f'Filter used: [regex], cart_id: {cart_id}, item_id: {item_id}') +@dp.message_handler(regex=r"cart_id_(?P\d+)_item_id_(?P\d+)$") +async def start_regex(event: Event, regex: re.Match): + cart_id = regex.group("cart_id") + item_id = regex.group("item_id") + await event.answer(f"Filter used: [regex], cart_id: {cart_id}, item_id: {item_id}") -@dp.message_handler(text_equals=['foo', 'bar']) -async def start_text_equals(message: Message): - await message.answer(f'Filter used: [text_equals], msg: {message.text}') +@dp.message_handler(text_equals=["foo", "bar"]) +async def start_text_equals(event: Event): + await event.answer(f"Filter used: [text_equals], msg: {event.text}") -@dp.message_handler(text_contains=['ru', 'com']) -async def start_text_contains(message: Message): - await message.answer(f'Filter used: [text_contains], msg: {message.text}') +@dp.message_handler(text_contains=["ru", "com"]) +async def start_text_contains(event: Event): + await event.answer(f"Filter used: [text_contains], msg: {event.text}") -@dp.message_handler(text_startswith=['ftp', 'sftp']) -async def text_startswith(message: Message): - await message.answer(f'Filter used: [text_startswith], msg: {message.text}') +@dp.message_handler(text_startswith=["ftp", "sftp"]) +async def text_startswith(event: Event): + await event.answer(f"Filter used: [text_startswith], msg: {event.text}") -@dp.message_handler(text_endswith=['.png', '.jpg']) -async def text_endswith(message: Message): - await message.answer(f'Filter used: [text_endswith], msg: {message.text}') +@dp.message_handler(text_endswith=[".png", ".jpg"]) +async def text_endswith(event: Event): + await event.answer(f"Filter used: [text_endswith], msg: {event.text}") @dp.message_handler(LongMessageRule(len_message=100)) -async def text_len(message: Message): - await message.answer(f'Filter used: [LongMessageRule], msg_len: {len(message.text)}') +async def text_len(event: Event): + await event.answer(f"Filter used: [LongMessageRule], msg_len: {len(event.text)}") @dp.message_handler(len=12) -async def text_len(message: Message): - await message.answer(f'Filter used: [labeler: len = LongMessageRule], msg_len: {len(message.text)}') +async def text_len(event: Event): + await event.answer( + f"Filter used: [labeler: len = LongMessageRule], msg_len: {len(event.text)}" + ) + +@dp.message_handler(MessageCommandsRule(commands=["start", "echo"])) +async def commands_rule_without_labeler(event: Event): + await event.answer(f"Filter used: [MessageCommandsRule], msg: {event.text}") -@dp.message_handler(MessageCommandsRule(commands=['start', 'echo'])) -async def commands_rule_without_labeler(message: Message): - await message.answer(f'Filter used: [MessageCommandsRule], msg: {message.text}') + +@dp.message_handler( + TextRule(startswith=["1111", "2222"], endswith=["x", "y", "z"]), + content_type=[ContentType.TEXT], +) +async def text_start_switch_without_labeler(event: Event): + await event.answer(f"Filter used: [TextRule], msg: {event.text}") @dp.message_handler( - TextRule(startswith=['1111', '2222'], endswith=['x', 'y', 'z']), content_type=[ContentType.TEXT]) -async def text_start_switch_without_labeler(message: Message): - await message.answer(f'Filter used: [TextRule], msg: {message.text}') + (F.message.payload.content_type == "image/gif") + & (F.message.payload.sender.name == "Alex") + & ((F.message.payload.sender.phone.cast(str)[:4]) == '7928') +) +async def test_magic_filter(event: Event): + await event.answer( + f"Test Filter" + ) async def handler_gupshup(request): @@ -93,5 +115,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) - web.run_app(webhook, port=8017) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) + web.run_app(webhook, port=8005) diff --git a/examples/filters/rule_long_message.py b/examples/filters/rule_long_message.py index 936c587..3656272 100644 --- a/examples/filters/rule_long_message.py +++ b/examples/filters/rule_long_message.py @@ -1,10 +1,10 @@ from waio.rules import ABCRule -from waio.types import Message +from waio.types import Event class LongMessageRule(ABCRule): def __init__(self, len_message: int): self.len_message = len_message - async def check(self, message: Message) -> bool: - return len(message.text) > self.len_message + async def check(self, event: Event) -> bool: + return len(event.text) > self.len_message diff --git a/examples/filters/rule_number.py b/examples/filters/rule_number.py index 470e63e..c4df891 100644 --- a/examples/filters/rule_number.py +++ b/examples/filters/rule_number.py @@ -2,14 +2,14 @@ from phonenumbers import timezone, parse, geocoder from waio.rules import ABCRule -from waio.types import Message +from waio.types import Event G_T = Dict[str, Union[int, str, Tuple[str]]] class RussianNumberRule(ABCRule): - async def check(self, message: Message) -> Union[bool, Dict[str, G_T]]: - phone_number_data = self.get_phone_number_data(message.sender_number) + async def check(self, event: Event) -> Union[bool, Dict[str, G_T]]: + phone_number_data = self.get_phone_number_data(event.sender_number) if phone_number_data["country"] == "Russia": return {"number_data": phone_number_data} return False @@ -25,5 +25,5 @@ def get_phone_number_data(number: str) -> G_T: "country_code": phone_number.country_code, "national_number": phone_number.national_number, "country": country_name, - "time_zone": time_zones_number + "time_zone": time_zones_number, } diff --git a/examples/first_bot/main.py b/examples/first_bot/main.py index ebbaec6..059811c 100644 --- a/examples/first_bot/main.py +++ b/examples/first_bot/main.py @@ -1,33 +1,34 @@ from aiohttp import web from waio import Bot, Dispatcher -from waio.types import Message +from waio.types import Event from waio.logs import loguru_filter -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=0000000000 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=0000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start', 'echo']) -async def start_command(message: Message): +# router = Router() + + +@dp.message_handler(commands=["start", "echo"]) +async def start_command(event: Event): """ This handler will be called when user sends `/start` or `/echo` command """ - await message.answer(f'Hello, {message.message.payload.sender.name}') + await event.answer(f"Hello, {event.message.payload.sender.name}") -@dp.message_handler(text_startswitch=['!!', '##']) -async def start_switch(message: Message): - await message.answer(f'Hello, {message.message.payload.sender.name},' - f' text: {message.message.payload.text}') +@dp.message_handler(text_startswitch=["!!", "##"]) +async def start_switch(event: Event): + await event.answer( + f"Hello, {event.message.payload.sender.name}," + f" text: {event.message.payload.text}" + ) async def handler_gupshup(request): @@ -38,5 +39,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/examples/fsm/main.py b/examples/fsm/main.py index 911ee33..3ede5b6 100644 --- a/examples/fsm/main.py +++ b/examples/fsm/main.py @@ -1,19 +1,15 @@ from aiohttp import web from waio import Bot, Dispatcher from waio.states import StatesGroup, BaseState, FSMContext -from waio.types import Message +from waio.types import Event from waio.logs import loguru_filter from waio.storage import RedisStorage -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=79281112233 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=79281112233) -storage = RedisStorage(prefix_fsm='fsm', redis_url="redis://localhost:6379") +storage = RedisStorage(prefix_fsm="fsm", redis_url="redis://localhost:6379") dp = Dispatcher(bot=bot, storage=storage) @@ -22,31 +18,32 @@ class RegisterStates(StatesGroup): email = BaseState() -@dp.message_handler(commands=['register'], state='*') -async def register_name(message: Message, state: FSMContext): - await message.answer(f'Hi, {message.sender_name}! send your date of birth') +@dp.message_handler(commands=["register"], state="*") +async def register_name(event: Event, state: FSMContext): + await event.answer(f"Hi, {event.sender_name}! send your date of birth") await state.set_state(RegisterStates.birthday) @dp.message_handler(state=RegisterStates.birthday) -async def register_age(message: Message, state: FSMContext): - await state.set_data(birthday=message.text) - await message.answer(f'Thanks for sending you birthday!\n' - f'Send you email address') +async def register_age(event: Event, state: FSMContext): + await state.set_data(birthday=event.text) + await event.answer(f"Thanks for sending you birthday!\n" f"Send you email address") await state.set_state(RegisterStates.email) @dp.message_handler(state=RegisterStates.email) -async def register_age(message: Message, state: FSMContext): - await state.set_data(email=message.text) +async def register_age(event: Event, state: FSMContext): + await state.set_data(email=event.text) state_data_certain = await state.get_data("email", "birthday") # Alternative for get all data: # await state.get_data() - await message.answer(f'Register completed...\n\n' - f'Your name: {message.sender_name}\n' - f'Your email: {state_data_certain["email"]}\n' - f'Your birthday: {state_data_certain["birthday"]}\n') + await event.answer( + f"Register completed...\n\n" + f"Your name: {event.sender_name}\n" + f'Your email: {state_data_certain["email"]}\n' + f'Your birthday: {state_data_certain["birthday"]}\n' + ) await state.finish(clear_data=True) @@ -59,5 +56,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/examples/list_keyboard/button.py b/examples/list_keyboard/button.py index d3ed23b..bdc3725 100644 --- a/examples/list_keyboard/button.py +++ b/examples/list_keyboard/button.py @@ -1,19 +1,33 @@ from waio.keyboard.list import ListGroup, ListGroupItem, ListMessage -from examples.list_keyboard.callbacks import callback_list_dish, callback_element_potato, callback_element_rice +from examples.list_keyboard.callbacks import ( + callback_list_dish, + callback_element_potato, + callback_element_rice, +) def generate_button(): - list_group_mashed_potatoes = ListGroup(title='Картофельное пюре', subtitle='Not used in docs.') - mashed_potatoes_cutlets = ListGroupItem(title='Пюре с котлетками', - callback_data=callback_element_potato.new(name="cutlets", id="1")) - mashed_potatoes_chicken = ListGroupItem(title='Пюре с курочкой', - callback_data=callback_element_potato.new(name="chicken", id="2")) + list_group_mashed_potatoes = ListGroup( + title="Картофельное пюре", subtitle="Not used in docs." + ) + mashed_potatoes_cutlets = ListGroupItem( + title="Пюре с котлетками", + callback_data=callback_element_potato.new(name="cutlets", id="1"), + ) + mashed_potatoes_chicken = ListGroupItem( + title="Пюре с курочкой", + callback_data=callback_element_potato.new(name="chicken", id="2"), + ) - list_group_rice = ListGroup(title='Рис', subtitle='Not used in docs.') - rice_cutlets = ListGroupItem(title='Рис с котлетками', - callback_data=callback_element_rice.new(name="cutlets", id="3")) - rice_chicken = ListGroupItem(title='Рис с курочкой', - callback_data=callback_element_rice.new(name="chicken", id="4")) + list_group_rice = ListGroup(title="Рис", subtitle="Not used in docs.") + rice_cutlets = ListGroupItem( + title="Рис с котлетками", + callback_data=callback_element_rice.new(name="cutlets", id="3"), + ) + rice_chicken = ListGroupItem( + title="Рис с курочкой", + callback_data=callback_element_rice.new(name="chicken", id="4"), + ) list_group_mashed_potatoes.add(mashed_potatoes_cutlets).add(mashed_potatoes_chicken) list_group_rice.add(rice_cutlets).add(rice_chicken) @@ -21,8 +35,8 @@ def generate_button(): data = ListMessage( title="Что Вы сегодня хотите на ужин?", body="Выберите из предложенного списка", - callback_data=callback_list_dish.new(id='1234'), + callback_data=callback_list_dish.new(id="1234"), button_title="Нажмите для выбора", - items=[list_group_mashed_potatoes, list_group_rice] + items=[list_group_mashed_potatoes, list_group_rice], ) return data diff --git a/examples/list_keyboard/callbacks.py b/examples/list_keyboard/callbacks.py index d95c931..6c57fa0 100644 --- a/examples/list_keyboard/callbacks.py +++ b/examples/list_keyboard/callbacks.py @@ -1,6 +1,5 @@ from waio.utils.callback import CallbackDataItem, CallbackDataGroup -callback_element_potato = CallbackDataItem('potato', 'name', 'id') -callback_element_rice = CallbackDataItem('rice', 'name', 'id') -callback_list_dish = CallbackDataGroup('list', 'id') - +callback_element_potato = CallbackDataItem("potato", "name", "id") +callback_element_rice = CallbackDataItem("rice", "name", "id") +callback_list_dish = CallbackDataGroup("list", "id") diff --git a/examples/list_keyboard/main.py b/examples/list_keyboard/main.py index 879a815..a5a88cb 100644 --- a/examples/list_keyboard/main.py +++ b/examples/list_keyboard/main.py @@ -4,35 +4,28 @@ from examples.list_keyboard.callbacks import callback_element_rice from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event from examples.list_keyboard.button import generate_button from examples.list_keyboard.callbacks import callback_element_potato -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='', - src_name='', - phone_number=1337 -) +bot = Bot(apikey="", src_name="", phone_number=1337) dp = Dispatcher(bot=bot) @dp.message_handler(commands=["dinner"]) -async def start(message: Message): - await bot.send_list( - receiver=message.sender_number, - keyboard=generate_button() - ) +async def start(event: Event): + await bot.send_list(receiver=event.sender_number, keyboard=generate_button()) @dp.message_handler(callback_element_potato.filter(id=["1", "2"])) -async def mashed_potatoes(message: Message): - if message.callback_data_item.endswith("1"): - await message.answer("Отличный выбор! Сегодня на ужин у Вас пюре с котлетками") - if message.callback_data_item.endswith("2"): - await message.answer("Отличный выбор! Сегодня на ужин у Вас пюре с курочкой") +async def mashed_potatoes(event: Event): + if event.callback_data_item.endswith("1"): + await event.answer("Отличный выбор! Сегодня на ужин у Вас пюре с котлетками") + if event.callback_data_item.endswith("2"): + await event.answer("Отличный выбор! Сегодня на ужин у Вас пюре с курочкой") # OR @@ -43,11 +36,11 @@ async def mashed_potatoes(message: Message): # Пример отлова колбеков с фильтром и обработка по аргументам @dp.message_handler(callback_element_rice.filter()) -async def rice(message: Message): - if message.callback_data_item.split(sep=":")[1] == "cutlets": # Обработка по name - await message.answer(f"Отличный выбор! Сегодня на ужин у Вас рис с котлетками") - if message.callback_data_item.split(sep=":")[1] == "chicken": - await message.answer("Отличный выбор! Сегодня на ужин у Вас рис с курочкой") +async def rice(event: Event): + if event.callback_data_item.split(sep=":")[1] == "cutlets": # Обработка по name + await event.answer(f"Отличный выбор! Сегодня на ужин у Вас рис с котлетками") + if event.callback_data_item.split(sep=":")[1] == "chicken": + await event.answer("Отличный выбор! Сегодня на ужин у Вас рис с курочкой") async def handler_gupshup(request): @@ -60,5 +53,5 @@ async def handler_gupshup(request): loop = asyncio.get_event_loop() webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/examples/middleware/bot.py b/examples/middleware/bot.py index 7119c44..c37438d 100644 --- a/examples/middleware/bot.py +++ b/examples/middleware/bot.py @@ -1,5 +1,6 @@ from aiohttp import web -from waio.types import Message + +from waio.types import Event from examples.middleware.database import ExampleDatabase from examples.middleware.misc import * @@ -7,16 +8,16 @@ webhook = web.Application() -@dp.message_handler(text_startswith='ch') -async def check_ban(message: Message): - await message.answer('The message has been processed, you are not blocked.') +@dp.message_handler(text_startswith="ch") +async def check_ban(event: Event): + await event.answer("The message has been processed, you are not blocked.") -@dp.message_handler(commands=['s', 'session']) -async def session_check(message: Message, session: ExampleDatabase): - session.set('name', 'Marina') - session.set('age', '21') - await message.answer('Hello man!') +@dp.message_handler(commands=["s", "session"]) +async def session_check(event: Event, session: ExampleDatabase): + session.set("name", "Marina") + session.set("age", "21") + await event.answer("Hello man!") async def handler_gupshup(request): @@ -26,5 +27,5 @@ async def handler_gupshup(request): if __name__ == "__main__": - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/examples/middleware/database.py b/examples/middleware/database.py index 2bc72eb..4b83f34 100644 --- a/examples/middleware/database.py +++ b/examples/middleware/database.py @@ -7,15 +7,15 @@ def __init__(self): self.data: Dict[str, Any] = {} def open_session(self) -> None: - self.session = 'OPEN' + self.session = "OPEN" def close_session(self) -> None: - self.session = 'CLOSED' + self.session = "CLOSED" @property def connection(self) -> bool: - if self.session == 'CLOSED': - raise Exception('Database is closed...') + if self.session == "CLOSED": + raise Exception("Database is closed...") return True def set(self, key, value) -> None: diff --git a/examples/middleware/example_middlewares.py b/examples/middleware/example_middlewares.py index 5bf7b54..7e2468b 100644 --- a/examples/middleware/example_middlewares.py +++ b/examples/middleware/example_middlewares.py @@ -1,5 +1,10 @@ from waio.logs import logger -from waio.middleware import BaseMiddleware, MiddlewareResponse, CancelHandler, ContinueHandler +from waio.middleware import ( + BaseMiddleware, + MiddlewareResponse, + CancelHandler, + ContinueHandler, +) from examples.middleware.database import ExampleDatabase @@ -9,7 +14,7 @@ def __init__(self): async def pre(self): if self.event.message.payload.sender.phone in self.BAN_PHONES: - await self.event.answer('Sorry, you are blocked.') + await self.event.answer("Sorry, you are blocked.") return MiddlewareResponse(CancelHandler) return MiddlewareResponse(ContinueHandler) @@ -20,14 +25,17 @@ def __init__(self): async def pre(self): self.session.open_session() - self.session.set('key_pre', 'value') + self.session.set("key_pre", "value") - logger.info(f'[PRE]-DatabaseMiddleware conn: {self.session.session} values: {self.session.data}') + logger.info( + f"[PRE]-DatabaseMiddleware conn: {self.session.session} values: {self.session.data}" + ) return {"session": self.session} async def post(self): - self.session.set('key_post', 'value') + self.session.set("key_post", "value") self.session.close_session() - logger.info(f'[POST]-DatabaseMiddleware conn: {self.session.session} values: {self.session.data}') - + logger.info( + f"[POST]-DatabaseMiddleware conn: {self.session.session} values: {self.session.data}" + ) diff --git a/examples/middleware/misc.py b/examples/middleware/misc.py index 7f7eb4d..c1e6f62 100644 --- a/examples/middleware/misc.py +++ b/examples/middleware/misc.py @@ -1,15 +1,11 @@ -from waio.bot import Bot, Dispatcher +from waio import Bot, Dispatcher from waio.logs import loguru_filter from examples.middleware.example_middlewares import BanMiddleware, DatabaseMiddleware -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=7928994433 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=7928994433) dp = Dispatcher(bot=bot) diff --git a/examples/reply_keyboard/button.py b/examples/reply_keyboard/button.py index c85c723..2d3c196 100644 --- a/examples/reply_keyboard/button.py +++ b/examples/reply_keyboard/button.py @@ -1,4 +1,9 @@ -from waio.keyboard.reply import QuickReplyContentText, QuickReply, KeyboardButton, QuickReplyContentImage +from waio.keyboard.reply import ( + QuickReplyContentText, + QuickReply, + KeyboardButton, + QuickReplyContentImage, +) from callback import callback_reply_keyboard @@ -6,22 +11,26 @@ def generate_keyboard_place(): kb_content = QuickReplyContentText( header="Куда вы сегодня хотите сходить?", text="Выберите из предложенного списка", - caption="" + caption="", + ) + kb = QuickReply( + callback_data=callback_reply_keyboard.new(name="place", id="1"), + content=kb_content, ) - kb = QuickReply(callback_data=callback_reply_keyboard.new(name="place", id="1"), content=kb_content) - kb.add(KeyboardButton(title='Кинотеатр')).add(KeyboardButton(title='Ресторан')) + kb.add(KeyboardButton(title="Кинотеатр")).add(KeyboardButton(title="Ресторан")) return kb def generate_keyboard_cinema_time(): kb_content = QuickReplyContentText( - header="Кинотеатр", - text="Выберите удобное для Вас время", - caption="" + header="Кинотеатр", text="Выберите удобное для Вас время", caption="" + ) + kb = QuickReply( + callback_data=callback_reply_keyboard.new(name="cinema_time", id="2"), + content=kb_content, ) - kb = QuickReply(callback_data=callback_reply_keyboard.new(name="cinema_time", id="2"), content=kb_content) - kb.add(KeyboardButton(title='18:00')).add(KeyboardButton(title='20:00')) + kb.add(KeyboardButton(title="18:00")).add(KeyboardButton(title="20:00")) return kb @@ -32,8 +41,11 @@ def generate_keyboard_restaurant_time(): text="Выберите удобное для Вас время", caption="", ) - kb = QuickReply(callback_data=callback_reply_keyboard.new(name="restaurant_time", id="2"), content=kb_content) - kb.add(KeyboardButton(title='18:30')).add(KeyboardButton(title='21:00')) + kb = QuickReply( + callback_data=callback_reply_keyboard.new(name="restaurant_time", id="2"), + content=kb_content, + ) + kb.add(KeyboardButton(title="18:30")).add(KeyboardButton(title="21:00")) return kb @@ -42,9 +54,14 @@ def generate_keyboard_image(): # Можно отправить клавиату kb_content = QuickReplyContentImage( url="https://www.buildquickbots.com/whatsapp/media/sample/jpg/sample01.jpg", text="this is the body", - caption="this is the footer" + caption="this is the footer", + ) + kb = QuickReply( + callback_data=callback_reply_keyboard.new(type="start", id="1"), + content=kb_content, + ) + kb.add(KeyboardButton(title="Сменить ресторан")).add( + KeyboardButton(title="Новый ресторан") ) - kb = QuickReply(callback_data=callback_reply_keyboard.new(type="start", id="1"), content=kb_content) - kb.add(KeyboardButton(title='Сменить ресторан')).add(KeyboardButton(title='Новый ресторан')) return kb diff --git a/examples/reply_keyboard/callback.py b/examples/reply_keyboard/callback.py index 67b4c1b..d445a20 100644 --- a/examples/reply_keyboard/callback.py +++ b/examples/reply_keyboard/callback.py @@ -1,4 +1,3 @@ from waio.utils.callback import CallbackDataGroup -callback_reply_keyboard = CallbackDataGroup('reply', 'name', 'id') - +callback_reply_keyboard = CallbackDataGroup("reply", "name", "id") diff --git a/examples/reply_keyboard/main.py b/examples/reply_keyboard/main.py index 75ab0ab..04005c3 100644 --- a/examples/reply_keyboard/main.py +++ b/examples/reply_keyboard/main.py @@ -2,47 +2,63 @@ from aiohttp import web from aiohttp.web_request import Request -from examples.reply_keyboard.button import generate_keyboard_place, generate_keyboard_image, \ - generate_keyboard_restaurant_time, generate_keyboard_cinema_time +from examples.reply_keyboard.button import ( + generate_keyboard_place, + generate_keyboard_image, + generate_keyboard_restaurant_time, + generate_keyboard_cinema_time, +) from examples.reply_keyboard.callback import callback_reply_keyboard from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event from settings import apikey, src_name, phone_number -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey=apikey, - src_name=src_name, - phone_number=phone_number -) +bot = Bot(apikey=apikey, src_name=src_name, phone_number=phone_number) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['place']) # Обработчик команды /place -async def place(message: Message): - await message.bot.send_reply(receiver=message.sender_number, keyboard=generate_keyboard_place()) +@dp.message_handler(commands=["place"]) # Обработчик команды /place +async def place(event: Event): + await event.bot.send_reply( + receiver=event.sender_number, keyboard=generate_keyboard_place() + ) -@dp.message_handler(callback_reply_keyboard.filter(name="place")) # Обработчик колбэка выбранного места -async def place_choose(message: Message, callback_data: Dict): - if message.message.payload.title == "Кинотеатр": # Обработка по тексту сообщения - await message.bot.send_reply(receiver=message.sender_number, keyboard=generate_keyboard_cinema_time()) - if message.message.payload.title == "Ресторан": - await message.bot.send_reply(receiver=message.sender_number, keyboard=generate_keyboard_restaurant_time()) +@dp.message_handler( + callback_reply_keyboard.filter(name="place") +) # Обработчик колбэка выбранного места +async def place_choose(event: Event, callback_data: Dict): + if event.message.payload.title == "Кинотеатр": # Обработка по тексту сообщения + await event.bot.send_reply( + receiver=event.sender_number, keyboard=generate_keyboard_cinema_time() + ) + if event.message.payload.title == "Ресторан": + await event.bot.send_reply( + receiver=event.sender_number, keyboard=generate_keyboard_restaurant_time() + ) -@dp.message_handler(callback_reply_keyboard.filter(name="cinema_time")) # Обработчик колбэка выбора времени для кино -async def cinema_time(message: Message, callback_data: Dict): - await message.answer(f"Отлично! Вы записаны в кинотеатр на {message.message.payload.title}") +@dp.message_handler( + callback_reply_keyboard.filter(name="cinema_time") +) # Обработчик колбэка выбора времени для кино +async def cinema_time(event: Event, callback_data: Dict): + await event.answer( + f"Отлично! Вы записаны в кинотеатр на {event.message.payload.title}" + ) -@dp.message_handler(callback_reply_keyboard.filter(name="restaurant_time")) # Обработчик колбэка выбора времени для ресторана -async def rest_time(message: Message, callback_data: Dict): - await message.answer(f"Отлично! Вы записаны в ресторан на {message.message.payload.title}") +@dp.message_handler( + callback_reply_keyboard.filter(name="restaurant_time") +) # Обработчик колбэка выбора времени для ресторана +async def rest_time(event: Event, callback_data: Dict): + await event.answer( + f"Отлично! Вы записаны в ресторан на {event.message.payload.title}" + ) async def handler_gupshup(request: Request): @@ -53,5 +69,5 @@ async def handler_gupshup(request: Request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8017) diff --git a/examples/routers/__init__.py b/examples/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/routers/binding.py b/examples/routers/binding.py new file mode 100644 index 0000000..48823ea --- /dev/null +++ b/examples/routers/binding.py @@ -0,0 +1,7 @@ +from waio import Router +from .handlers_1 import router1 +from .handlers_2 import router2 + +own_router = Router(name='Own Router') +own_router.include_router(router1) +own_router.include_router(router2) diff --git a/examples/routers/filters.py b/examples/routers/filters.py new file mode 100644 index 0000000..c72cea0 --- /dev/null +++ b/examples/routers/filters.py @@ -0,0 +1,11 @@ +from waio.rules import ABCRule +from waio.types import Event + + +class DynamicLongMessageRule(ABCRule): + + def __init__(self, len_message: int): + self.len_message = len_message + + async def check(self, event: Event) -> bool: + return len(event.text) > self.len_message diff --git a/examples/routers/handlers_1.py b/examples/routers/handlers_1.py new file mode 100644 index 0000000..f206a54 --- /dev/null +++ b/examples/routers/handlers_1.py @@ -0,0 +1,12 @@ +from examples.routers.filters import DynamicLongMessageRule +from waio import Router +from waio.types import Event + +router1 = Router(name='Router 1') +router1.labeler.bind_rule("len_more", DynamicLongMessageRule) + + +@router1.message_handler(len_more=20) +async def text_len(event: Event): + await event.answer(f"msg len: {len(event.text)}") + diff --git a/examples/routers/handlers_2.py b/examples/routers/handlers_2.py new file mode 100644 index 0000000..5ba78ee --- /dev/null +++ b/examples/routers/handlers_2.py @@ -0,0 +1,11 @@ +from waio import Router +from waio.types import Event + + +router2 = Router(name='Router2') + + +@router2.message_handler(commands=['start']) +async def text_start(event: Event): + await event.answer("started...") + diff --git a/examples/routers/main.py b/examples/routers/main.py new file mode 100644 index 0000000..718387e --- /dev/null +++ b/examples/routers/main.py @@ -0,0 +1,32 @@ +from aiohttp import web + +from examples.routers.binding import own_router +from examples.routers.middlewares import BanMiddleware +from waio import Bot +from waio import Dispatcher +from waio.logs import loguru_filter + +loguru_filter.set_level("DEBUG") + +bot = Bot( + apikey="", + src_name="", + phone_number=917834811114, +) + + +dp = Dispatcher(bot=bot, name="Dispatcher") +dp.labeler.register_middleware(BanMiddleware()) +dp.include_router(own_router) + + +async def handler_gupshup(request): + event = await request.json() + await dp.handle_event(event) + return web.Response(status=200) + + +if __name__ == "__main__": + webhook = web.Application() + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) + web.run_app(webhook, port=8005) diff --git a/examples/routers/middlewares.py b/examples/routers/middlewares.py new file mode 100644 index 0000000..7935e0a --- /dev/null +++ b/examples/routers/middlewares.py @@ -0,0 +1,15 @@ +from waio.middleware import BaseMiddleware +from waio.middleware import CancelHandler +from waio.middleware import ContinueHandler +from waio.middleware import MiddlewareResponse + + +class BanMiddleware(BaseMiddleware): + def __init__(self): + self.BAN_PHONES = [79283334455, 79283334477, 79185554433] + + async def pre(self): + if self.event.message.payload.sender.phone in self.BAN_PHONES: + await self.event.answer("Sorry, you are blocked.") + return MiddlewareResponse(CancelHandler) + return MiddlewareResponse(ContinueHandler) diff --git a/examples/send_audio/main.py b/examples/send_audio/main.py index 42e8b88..5eb3139 100644 --- a/examples/send_audio/main.py +++ b/examples/send_audio/main.py @@ -2,24 +2,20 @@ from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=0000000000 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=0000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start']) -async def start_command(message: Message): - await message.bot.send_audio( - receiver=message.sender_number, - url="https://www.buildquickbots.com/whatsapp/media/sample/audio/sample01.mp3" +@dp.message_handler(commands=["start"]) +async def start_command(event: Event): + await event.bot.send_audio( + receiver=event.sender_number, + url="https://www.buildquickbots.com/whatsapp/media/sample/audio/sample01.mp3", ) @@ -31,5 +27,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8008) diff --git a/examples/send_file/main.py b/examples/send_file/main.py index 05f0073..fc951e3 100644 --- a/examples/send_file/main.py +++ b/examples/send_file/main.py @@ -2,26 +2,22 @@ from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=0000000000 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=0000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start']) -async def start_command(message: Message): - await message.bot.send_file( - receiver=message.sender_number, - url='https://www.buildquickbots.com/whatsapp/media/sample/pdf/sample01.pdf', - filename='test_file.pdf', - caption='Test Caption' +@dp.message_handler(commands=["start"]) +async def start_command(event: Event): + await event.bot.send_file( + receiver=event.sender_number, + url="https://www.buildquickbots.com/whatsapp/media/sample/pdf/sample01.pdf", + filename="test_file.pdf", + caption="Test Caption", ) @@ -33,5 +29,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8008) diff --git a/examples/send_image/main.py b/examples/send_image/main.py index d5a8d73..2711d76 100644 --- a/examples/send_image/main.py +++ b/examples/send_image/main.py @@ -1,27 +1,23 @@ from aiohttp import web from waio import Bot, Dispatcher -from waio.types import Message +from waio.types import Event from waio.logs import loguru_filter -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='test', - src_name='test', - phone_number=0000000000000 -) +bot = Bot(apikey="test", src_name="test", phone_number=0000000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start']) -async def start_command(message: Message): - await message.bot.send_image( - receiver=message.sender_number, +@dp.message_handler(commands=["start"]) +async def start_command(event: Event): + await event.bot.send_image( + receiver=event.sender_number, original_url="https://www.buildquickbots.com/whatsapp/media" - "/sample/jpg/sample01.jpg", - caption="Test caption" + "/sample/jpg/sample01.jpg", + caption="Test caption", ) @@ -33,5 +29,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8008) diff --git a/examples/send_sticker/main.py b/examples/send_sticker/main.py index 92d0106..9c600d0 100644 --- a/examples/send_sticker/main.py +++ b/examples/send_sticker/main.py @@ -2,23 +2,19 @@ from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=0000000000 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=0000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start']) -async def start_command(message: Message): - await message.bot.send_sticker( - receiver=message.sender_number, +@dp.message_handler(commands=["start"]) +async def start_command(event: Event): + await event.bot.send_sticker( + receiver=event.sender_number, url="http://www.buildquickbots.com/whatsapp/stickers/SampleSticker01.webp", ) @@ -31,5 +27,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8008) diff --git a/examples/send_video/main.py b/examples/send_video/main.py index 690ef94..2e2d2b0 100644 --- a/examples/send_video/main.py +++ b/examples/send_video/main.py @@ -2,25 +2,21 @@ from waio import Bot, Dispatcher from waio.logs import loguru_filter -from waio.types import Message +from waio.types import Event -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='API_KEY', - src_name='SRC_NAME', - phone_number=0000000000 -) +bot = Bot(apikey="API_KEY", src_name="SRC_NAME", phone_number=0000000000) dp = Dispatcher(bot=bot) -@dp.message_handler(commands=['start']) -async def start_command(message: Message): - await message.bot.send_video( - receiver=message.sender_number, +@dp.message_handler(commands=["start"]) +async def start_command(event: Event): + await event.bot.send_video( + receiver=event.sender_number, url="https://www.buildquickbots.com/whatsapp/media/sample/video/sample01.mp4", - caption="Test video caption" + caption="Test video caption", ) @@ -32,5 +28,5 @@ async def handler_gupshup(request): if __name__ == "__main__": webhook = web.Application() - webhook.add_routes([web.post('/api/v1/gupshup/hook', handler_gupshup)]) + webhook.add_routes([web.post("/api/v1/gupshup/hook", handler_gupshup)]) web.run_app(webhook, port=8008) diff --git a/main.py b/main.py index 535d5f2..e69de29 100644 --- a/main.py +++ b/main.py @@ -1,75 +0,0 @@ -import asyncio -import re - -import uvicorn -from fastapi import FastAPI -from starlette.requests import Request -from starlette.responses import Response - -from waio.bot import Bot, Dispatcher -from waio.middleware import BaseMiddleware -from waio.middlewares.type import MiddlewareResponse -from waio.rules.abc import ABCMessageRule -from waio.rules.default import TextRule, TextRuleEquals -from waio.types.content_types import ContentType -from waio.types.message import Message -from waio.states.context import FSMContext -from waio.states.fsm import StatesGroup, BaseState -from waio.storage.redis import RedisStorage -from env import PHONE_NUMBER, SRC_NAME, API_KEY - -app = FastAPI() -bot = Bot( - apikey=API_KEY, - src_name=SRC_NAME, - phone_number=PHONE_NUMBER) - -storage = RedisStorage(prefix_fsm='fsm', redis_url="redis://localhost:6379") -dp = Dispatcher(bot=bot, storage=storage) - - -class BanMiddleware(BaseMiddleware): - def __init__(self): - self.BAN_PHONES = [79189781010, 79189781009] - - async def pre(self, message: Message): - if message.message.payload.sender.phone in self.BAN_PHONES: - return {"example_key_middleware": "example_value_middleware"} - return True - - -dp.labeler.register_middleware(BanMiddleware) - - -class FooState(StatesGroup): - name = BaseState() - age = BaseState() - email = BaseState() - end = BaseState() - - -def rate_limit(limit: int, key=None): - def decorator(func): - setattr(func, 'throttling_rate_limit', limit) - if key: - setattr(func, 'throttling_key', key) - return func - return decorator - - -@dp.message_handler(text_startswitch='121') -@rate_limit(10, 'foos') -async def foo(message: Message, state: FSMContext): - await message.bot.send_message(79189781008, 'start_swith 121') - - -@app.post("/api/v1/gupshup/hook") -async def foo(request: Request): - event = await request.json() - # print(event) - await dp.handle_event(event) - return Response(status_code=200) - - -if __name__ == "__main__": - uvicorn.run("main:app", port=8017, debug=True, reload=True) diff --git a/tests/test_filters.py b/tests/test_filters.py index d024940..5d4e707 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -8,13 +8,9 @@ from waio.storage import RedisStorage from waio.types import Message -bot = Bot( - apikey='FAKE_API_KEY', - src_name='FAKE_SRC_NAME', - phone_number=79289998877 -) +bot = Bot(apikey="FAKE_API_KEY", src_name="FAKE_SRC_NAME", phone_number=79289998877) -storage = RedisStorage(prefix_fsm='fsm', redis_url="redis://localhost:6379") +storage = RedisStorage(prefix_fsm="fsm", redis_url="redis://localhost:6379") dp = Dispatcher(bot=bot, storage=storage) @@ -31,16 +27,14 @@ async def test_check_state_filter_true(): "id": "ABEGkYaYVSEEAhAL3SLAWwHKeKrt6s3FKB0c", "source": "79289998877", "type": "text", - "payload": { - "text": "Hi" - }, + "payload": {"text": "Hi"}, "sender": { "phone": "79990000000", "name": "Smit", "country_code": "7", - "dial_code": "9990000000" - } - } + "dial_code": "9990000000", + }, + }, } data_load = factory_gupshup.load(message_json, ResponseModel) message_model = Message(bot=dp.bot, message=data_load, state_func=dp.state) @@ -66,16 +60,14 @@ async def test_check_state_filter_false(): "id": "ABEGkYaYVSEEAhAL3SLAWwHKeKrt6s3FKB0c", "source": "79289998877", "type": "text", - "payload": { - "text": "Hi" - }, + "payload": {"text": "Hi"}, "sender": { "phone": "79287776655", "name": "Anna", "country_code": "7", - "dial_code": "9287776655" - } - } + "dial_code": "9287776655", + }, + }, } data_load = factory_gupshup.load(message_json, ResponseModel) message_model = Message(bot=dp.bot, message=data_load, state_func=dp.state) @@ -84,4 +76,4 @@ async def test_check_state_filter_false(): check_filter = await rule.check(message=message_model) assert check_filter is False - await _state.finish(clear_data=True) \ No newline at end of file + await _state.finish(clear_data=True) diff --git a/tests/test_fsm.py b/tests/test_fsm.py index 5b0aeaa..5139bb0 100644 --- a/tests/test_fsm.py +++ b/tests/test_fsm.py @@ -4,15 +4,11 @@ from waio.logs import loguru_filter from waio.storage import RedisStorage -loguru_filter.set_level('DEBUG') +loguru_filter.set_level("DEBUG") -bot = Bot( - apikey='FAKE_API_KEY', - src_name='FAKE_SRC_NAME', - phone_number=79289998877 -) +bot = Bot(apikey="FAKE_API_KEY", src_name="FAKE_SRC_NAME", phone_number=79289998877) -storage = RedisStorage(prefix_fsm='fsm', redis_url="redis://localhost:6379") +storage = RedisStorage(prefix_fsm="fsm", redis_url="redis://localhost:6379") dp = Dispatcher(bot=bot, storage=storage) @@ -52,22 +48,22 @@ async def test_check_set_state(): @pytest.mark.asyncio async def test_check_get_set_data(): _state = dp.state(user_phone=79990000000) - await _state.set_data(foo='bar', baz='baq') + await _state.set_data(foo="bar", baz="baq") current_data = await _state.get_data() - assert current_data == {'baz': 'baq', 'foo': 'bar'} + assert current_data == {"baz": "baq", "foo": "bar"} await _state.finish(clear_data=True) @pytest.mark.asyncio async def test_check_get_set_data_keys(): _state = dp.state(user_phone=79990000000) - await _state.set_data(foo='foo_value', bar='bar_value') + await _state.set_data(foo="foo_value", bar="bar_value") - current_data = await _state.get_data('foo') - assert current_data == {'foo': 'foo_value'} + current_data = await _state.get_data("foo") + assert current_data == {"foo": "foo_value"} - current_data = await _state.get_data('foo', 'bar') - assert current_data == {'foo': 'foo_value', 'bar': 'bar_value'} + current_data = await _state.get_data("foo", "bar") + assert current_data == {"foo": "foo_value", "bar": "bar_value"} await _state.finish(clear_data=True) @@ -75,13 +71,12 @@ async def test_check_get_set_data_keys(): @pytest.mark.asyncio async def test_check_get_set_data_keys(): _state = dp.state(user_phone=79990000000) - await _state.set_data(foo='foo_value', bar='bar_value') + await _state.set_data(foo="foo_value", bar="bar_value") - current_data = await _state.get_data('foo') - assert current_data == {'foo': 'foo_value'} + current_data = await _state.get_data("foo") + assert current_data == {"foo": "foo_value"} - current_data = await _state.get_data('foo', 'bar') - assert current_data == {'foo': 'foo_value', 'bar': 'bar_value'} + current_data = await _state.get_data("foo", "bar") + assert current_data == {"foo": "foo_value", "bar": "bar_value"} await _state.finish(clear_data=True) - diff --git a/tests/test_reply_keyboard.py b/tests/test_reply_keyboard.py index d01ac02..4120c87 100644 --- a/tests/test_reply_keyboard.py +++ b/tests/test_reply_keyboard.py @@ -1,16 +1,21 @@ import pytest -from waio.keyboard.reply import QuickReply, QuickReplyContentText, KeyboardButton, QuickReplyContentImage +from waio.keyboard.reply import ( + QuickReply, + QuickReplyContentText, + KeyboardButton, + QuickReplyContentImage, +) def test_reply_keyboard_text(): kb_content = QuickReplyContentText( header="this is the header", text="this is the body", - caption="this is the footer" + caption="this is the footer", ) kb = QuickReply(callback_data="callback_keyboard_123", content=kb_content) - kb.add(KeyboardButton(title='keyboard1')).add(KeyboardButton(title='keyboard2')) + kb.add(KeyboardButton(title="keyboard1")).add(KeyboardButton(title="keyboard2")) assert kb.dict() == { "type": "quick_reply", @@ -19,18 +24,12 @@ def test_reply_keyboard_text(): "type": "text", "header": "this is the header", "text": "this is the body", - "caption": "this is the footer" + "caption": "this is the footer", }, "options": [ - { - "type": "text", - "title": "keyboard1" - }, - { - "type": "text", - "title": "keyboard2" - } - ] + {"type": "text", "title": "keyboard1"}, + {"type": "text", "title": "keyboard2"}, + ], } @@ -38,11 +37,11 @@ def test_reply_keyboard_image(): kb_content = QuickReplyContentImage( text="this is the body", caption="this is the footer", - url="https://www.buildquickbots.com/whatsapp/media/sample/jpg/sample01.jpg" + url="https://www.buildquickbots.com/whatsapp/media/sample/jpg/sample01.jpg", ) kb = QuickReply(callback_data="callback_keyboard_123_img", content=kb_content) - kb.add(KeyboardButton(title='keyboard1')).add(KeyboardButton(title='keyboard2')) + kb.add(KeyboardButton(title="keyboard1")).add(KeyboardButton(title="keyboard2")) assert kb.dict() == { "type": "quick_reply", @@ -51,16 +50,10 @@ def test_reply_keyboard_image(): "type": "image", "url": "https://www.buildquickbots.com/whatsapp/media/sample/jpg/sample01.jpg", "text": "this is the body", - "caption": "this is the footer" + "caption": "this is the footer", }, "options": [ - { - "type": "text", - "title": "keyboard1" - }, - { - "type": "text", - "title": "keyboard2" - } - ] + {"type": "text", "title": "keyboard1"}, + {"type": "text", "title": "keyboard2"}, + ], } diff --git a/waio/__init__.py b/waio/__init__.py index 39de060..c5af7bc 100644 --- a/waio/__init__.py +++ b/waio/__init__.py @@ -1 +1,5 @@ -from .bot import Bot, Dispatcher +from .bot import Bot +from .dispatcher.dispatcher import Dispatcher +from .dispatcher.router import Router +from magic_filter import F + diff --git a/waio/bot.py b/waio/bot.py index ef5e7f6..b38c168 100644 --- a/waio/bot.py +++ b/waio/bot.py @@ -1,17 +1,11 @@ -from typing import Any, Dict, Optional +from typing import Any +from typing import Optional from waio.client.http import HTTPClient -from waio.factory.base import ResponseModel -from waio.factory.factory import factory_gupshup from waio.gupshup.api import GupshupSettings from waio.gupshup.form import generate_message_form -from waio.handlers.base_handlers import Handler, BaseHandlers -from waio.handlers.executor import HandlerExecutor from waio.keyboard.list import ListMessage from waio.keyboard.reply import QuickReply -from waio.labeler import BotLabeler -from waio.logs.logger import logger -from waio.middleware import MiddlewareResponse from waio.models.audio import AudioModel from waio.models.enums import GupshupMethods from waio.models.file import FileModel @@ -19,13 +13,10 @@ from waio.models.sticker import StickerModel from waio.models.text import MessageText from waio.models.video import VideoModel -from waio.states.context import FSMContext -from waio.storage.redis import RedisStorage -from waio.types.message import Message +from waio.utils.form_data import CustomFormData class Bot(GupshupSettings, HTTPClient): - def __init__(self, apikey: str, src_name: str, phone_number: int): super().__init__(apikey=apikey, src_name=src_name, phone_number=phone_number) @@ -33,18 +24,18 @@ async def _base_request(self, receiver: int, data: Any): form = self._generate_form(receiver=receiver, data=data) response = await self.request( headers=self._headers(), - method='POST', - url=GupshupMethods.message.value, + method="POST", + url=str(GupshupMethods.message.value), data=form, ) return response - def _generate_form(self, receiver, data): + def _generate_form(self, receiver, data) -> CustomFormData: return generate_message_form( source=self.phone_number, receiver=receiver, app_name=self.src_name, - message=data.json() + message=data.json(), ) async def send_message(self, receiver: int, message: str): @@ -52,46 +43,48 @@ async def send_message(self, receiver: int, message: str): return await self._base_request(receiver=receiver, data=msg) async def send_image( - self, - receiver: int, - original_url: str, - preview_url: Optional[str] = None, - caption: Optional[str] = None, + self, + receiver: int, + original_url: str, + preview_url: Optional[str] = None, + caption: Optional[str] = None, ): - image = ImageModel(original_url=original_url, preview_url=preview_url, caption=caption) + image = ImageModel( + original_url=original_url, preview_url=preview_url, caption=caption + ) return await self._base_request(receiver=receiver, data=image) async def send_file( - self, - receiver: int, - url: str, - filename: str, - caption: Optional[str] = None, + self, + receiver: int, + url: str, + filename: str, + caption: Optional[str] = None, ): file = FileModel(url=url, filename=filename, caption=caption) return await self._base_request(receiver=receiver, data=file) async def send_video( - self, - receiver: int, - url: str, - caption: Optional[str] = None, + self, + receiver: int, + url: str, + caption: Optional[str] = None, ): video = VideoModel(url=url, caption=caption) return await self._base_request(receiver=receiver, data=video) async def send_audio( - self, - receiver: int, - url: str, + self, + receiver: int, + url: str, ): audio = AudioModel(url=url) return await self._base_request(receiver=receiver, data=audio) async def send_sticker( - self, - receiver: int, - url: str, + self, + receiver: int, + url: str, ): sticker = StickerModel(url=url) return await self._base_request(receiver=receiver, data=sticker) @@ -101,59 +94,3 @@ async def send_list(self, receiver: int, keyboard: ListMessage): async def send_reply(self, receiver: int, keyboard: QuickReply): return await self._base_request(receiver=receiver, data=keyboard) - - -class Dispatcher(Handler, BaseHandlers): - def __init__(self, bot: Bot, storage: Optional[RedisStorage] = None): - self.bot = bot - self.storage = storage - self.labeler = BotLabeler() - super().__init__(self.labeler) - - def state(self, user_phone: int) -> FSMContext: - return FSMContext(storage=self.storage, user_phone=user_phone) - - async def handle_event(self, event: Dict[str, Any]) -> None: - try: - logger.debug(f'Event: - {event}') - if event.get("type") == "message": - context_variables = {} - - data_load = factory_gupshup.load(event, ResponseModel) - message = Message(bot=self.bot, message=data_load, state_func=self.state) - - for middleware in self.labeler.MIDDLEWARES: - middleware.fill(event=message) - response = await middleware.pre() - - logger.debug(f'[PRE]-Middleware: {middleware}') - - if response == MiddlewareResponse(False): - return - elif isinstance(response, dict): - context_variables.update(response) - - logger.debug(f'[PRE]-Middleware values: {context_variables}') - - handle_responses = [] - current_handlers = [] - - for handler in self.handlers: - resp = await HandlerExecutor.execute(handler=handler, message=message, **context_variables) - - if resp: - handle_responses.append(resp) - current_handlers.append(handler) - await handler.handle(**resp) - break - - logger.debug(f'Handlers: {current_handlers}, Return: {handle_responses}') - - for middleware in reversed(self.labeler.MIDDLEWARES): - middleware.fill(event=message, handlers=current_handlers, handle_responses=handle_responses) - logger.debug(f'[POST]-Middleware - {middleware}') - - await middleware.post() - except Exception as exc: - logger.exception('Exception', exc) - diff --git a/waio/client/exceptions.py b/waio/client/exceptions.py index ac2c018..39ba126 100644 --- a/waio/client/exceptions.py +++ b/waio/client/exceptions.py @@ -1,2 +1,2 @@ -class FailedDecodeJson(Exception): +class FailedDecodeJsonError(Exception): pass diff --git a/waio/client/http.py b/waio/client/http.py index 7b9b76b..7848d1e 100644 --- a/waio/client/http.py +++ b/waio/client/http.py @@ -5,7 +5,7 @@ from typing import Optional, Union, Dict, Any, List, Tuple from aiohttp import ContentTypeError -from waio.client.exceptions import FailedDecodeJson +from waio.client.exceptions import FailedDecodeJsonError class HTTPClient: @@ -24,22 +24,21 @@ async def request( return await self.generate_json_from_response(resp) async def generate_json_from_response( - self, - resp: aiohttp.ClientResponse + self, resp: aiohttp.ClientResponse ) -> Tuple[Dict[str, Any], int]: - content_type = resp.headers.get('Content-Type') + content_type = resp.headers.get("Content-Type") try: - if content_type == 'text/plain': + if content_type == "text/plain": resp_text = await resp.text() resp_json = ujson.loads(resp_text) return self.decode_json(resp_json), resp.status - elif content_type == 'application/json': + elif content_type == "application/json": resp_json = await resp.json() return resp_json, resp.status except ContentTypeError as e: - raise FailedDecodeJson(f"Check args, URL is invalid - {e}") + raise FailedDecodeJsonError(f"Check args, URL is invalid - {e}") @staticmethod def decode_json(data: Union[List, Dict[str, Any]]): diff --git a/waio/dispatcher/__init__.py b/waio/dispatcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/waio/dispatcher/dispatcher.py b/waio/dispatcher/dispatcher.py new file mode 100644 index 0000000..92509f9 --- /dev/null +++ b/waio/dispatcher/dispatcher.py @@ -0,0 +1,69 @@ +from typing import Any +from typing import Dict +from typing import Optional + +from waio import Bot +from waio.dispatcher.router import Router +from waio.dispatcher.event import WhatsAppEventObserver +from waio.factory.factory import factory_gupshup +from waio.factory.models.main import BaseResponse +from waio.factory.models.response import ResponseUserEvent +from waio.labeler import BotLabeler +from waio.logs.logger import logger +from waio.models.enums import EventPayloadType +from waio.models.enums import ResponseGupshupMessageType +from waio.states.context import FSMContext +from waio.storage.redis import RedisStorage +from waio.types.message import Event +from waio.types.message import EventSubscribe + + +class Dispatcher(Router): + def __init__( + self, + bot: Bot, + storage: Optional[RedisStorage] = None, + name: Optional[str] = None + ): + self.bot = bot + self.storage = storage + self.labeler = BotLabeler() + self.message_handler = WhatsAppEventObserver(router=self, event_name="message") + self.notify_success_handler = WhatsAppEventObserver(router=self, event_name="opted-in") + self.notify_denied_handler = WhatsAppEventObserver(router=self, event_name="opted-out") + + self.observers: Dict[str, WhatsAppEventObserver] = { + "message": self.message_handler, + "opted-in": self.notify_success_handler, + "opted-out": self.notify_denied_handler, + } + super().__init__(name=name, labeler=self.labeler) + + def state(self, user_phone: int) -> FSMContext: + return FSMContext(storage=self.storage, user_phone=user_phone) + + async def handle_event(self, event: Dict[str, Any]) -> None: + try: + # TODO: Refactoring + logger.debug(f"Event: - {event}") + if event.get("type", "") == "message": + data_load = factory_gupshup.load(event, BaseResponse) + event_obj = Event( + bot=self.bot, + message=data_load, + state_func=self.state + ) + await self.observers["message"].notify(event=event_obj) + + if event.get("type", "") == ResponseGupshupMessageType.message_event: + if event["payload"]["type"] == EventPayloadType.start_dialog: + data_load = factory_gupshup.load(event, ResponseUserEvent) + event_obj = EventSubscribe( + bot=self.bot, + message=data_load, + state_func=self.state + ) + await self.observers["opted-in"].notify(event=event_obj) + + except Exception as exc: + logger.exception("Exception", exc) diff --git a/waio/dispatcher/event.py b/waio/dispatcher/event.py new file mode 100644 index 0000000..d49ce35 --- /dev/null +++ b/waio/dispatcher/event.py @@ -0,0 +1,83 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Union +from waio.handlers import FromFuncHandler +from waio.handlers import HandlerExecutor +from waio.middleware import MiddlewareResponse +from waio.models.system.handlers import ExecutedHandlerData + +from waio.logs.logger import logger +from waio.types import Event +from waio.types.message import EventSubscribe + +if TYPE_CHECKING: + from waio.dispatcher.router import Router + + +class WhatsAppEventObserver: + def __init__(self, router: Router, event_name: str): + self.router = router + self.event_name = event_name + self._handlers = [] + + def register(self, handler, *rules, **custom_rules): + handler_object = FromFuncHandler( + handler, + *rules, + *self.router.base_rules(**custom_rules), + *self.router.custom_rules(**custom_rules), + ) + self._handlers.append(handler_object) + + def __call__(self, *rules, **custom_rules): + def wrapper(handler) -> None: + self.register(handler=handler, *rules, **custom_rules) + return handler + + return wrapper + + async def notify(self, event: Union[Event, EventSubscribe]): + context_variables = {} + for middleware in self.router.labeler.MIDDLEWARES: + middleware.fill(event=event) + response = await middleware.pre() + + logger.debug(f"[PRE]-Middleware: {middleware}") + + if response == MiddlewareResponse(False): + logger.debug(f"[PRE]-Middleware: Exited...") + return + elif isinstance(response, dict): + context_variables.update(response) + + logger.debug(f"[PRE]-Middleware values: {context_variables}") + executed_handler_data = await self._resolve_handler( + event=event, **context_variables + ) + + for middleware in reversed(self.router.labeler.MIDDLEWARES): + middleware.fill( + event=event, + handler=executed_handler_data.handler, + response=executed_handler_data.response, + ) + await middleware.post() + logger.debug(f"[POST]-Middleware - {middleware}") + + async def _resolve_handler( + self, event: Event, **context_variables + ) -> ExecutedHandlerData: + for router in self.router.chain_tail: + for handler in router.observers[self.event_name]._handlers: + logger.debug(f'[Resolver]: Router: {router} Handler: {handler}') + resp = await HandlerExecutor.execute( + handler=handler, event=event, **context_variables + ) + if resp: + await handler.handle(**resp) + logger.debug(f"[Result] Router: {router} Handler: {handler} Return {resp}") + return ExecutedHandlerData( + handler=handler, + response=resp, + ) + logger.debug(f"[Result] Router: None Handler: None Return None") + return ExecutedHandlerData() diff --git a/waio/dispatcher/router.py b/waio/dispatcher/router.py new file mode 100644 index 0000000..2b7c7fe --- /dev/null +++ b/waio/dispatcher/router.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +from typing import Dict +from typing import Generator +from typing import List +from typing import Optional +from typing import Union + +from waio.dispatcher.event import WhatsAppEventObserver +from waio.handlers.base_handlers import HandlerRule +from waio.labeler import BotLabeler + + +class Router(HandlerRule): + def __init__( + self, + *, + name: Optional[str] = None, + labeler: Optional[BotLabeler] = None + ): + self.labeler = labeler or BotLabeler() + + self.name = name or hex(id(self)) + self._parent_router: Optional[Router] = None + self.sub_routers: List[Router] = [] + + self.message_handler = WhatsAppEventObserver(router=self, event_name="message") + self.notify_success_handler = WhatsAppEventObserver(router=self, event_name="opted-in") + self.notify_denied_handler = WhatsAppEventObserver(router=self, event_name="opted-out") + + self.observers: Dict[str, WhatsAppEventObserver] = { + "message": self.message_handler, + "opted-in": self.notify_success_handler, + "opted-out": self.notify_denied_handler, + } + super().__init__(self.labeler) + + def __str__(self) -> str: + return f"{type(self).__name__} {self.name!r}" + + def __repr__(self) -> str: + return f"<{self}>" + + @property + def chain_head(self) -> Generator[Router, None, None]: + router: Optional[Router] = self + while router: + yield router + router = router.parent_router + + @property + def chain_tail(self) -> Generator[Router, None, None]: + yield self + for router in self.sub_routers: + yield from router.chain_tail + + @property + def parent_router(self) -> Optional[Router]: + return self._parent_router + + @parent_router.setter + def parent_router(self, router: Router) -> None: + """ + Internal property setter of parent router fot this router. + Do not use this method in own code. + All routers should be included via `include_router` method. + + Self- and circular- referencing are not allowed here + + :param router: + """ + if not isinstance(router, Router): + raise ValueError( + f"router should be instance of Router not {type(router).__name__!r}" + ) + if self._parent_router: + raise RuntimeError(f"Router is already attached to {self._parent_router!r}") + if self == router: + raise RuntimeError("Self-referencing routers is not allowed") + + parent: Optional[Router] = router + while parent is not None: + if parent == self: + raise RuntimeError("Circular referencing of Router is not allowed") + + parent = parent.parent_router + + self._parent_router = router + router.sub_routers.append(self) + + def include_router(self, router: Union[Router, str]) -> Router: + """ + Attach another router. + + Can be attached directly or by import string in format ":" + + :param router: + :return: + """ + if not isinstance(router, Router): + raise ValueError( + f"router should be instance of Router not {type(router).__class__.__name__}" + ) + router.parent_router = self + return router + diff --git a/waio/factory/base.py b/waio/factory/base.py index 54ad920..e69de29 100644 --- a/waio/factory/base.py +++ b/waio/factory/base.py @@ -1,6 +0,0 @@ -from typing import Union - -from waio.factory.models.main import BaseResponse -from waio.factory.models.response import ResponseMessage - -ResponseModel = Union[ResponseMessage, BaseResponse] diff --git a/waio/factory/factory.py b/waio/factory/factory.py index 2025831..a3c0437 100644 --- a/waio/factory/factory.py +++ b/waio/factory/factory.py @@ -8,7 +8,7 @@ from waio.factory.models.response import ( ResponseMessagePayload, ResponseMessagePayloadPayload, - ResponseMessage + ResponseUserEvent, ) from waio.factory.models.contact import ( PayloadContact, @@ -19,7 +19,7 @@ PayloadContactOrganization, PayloadContacts, PayloadContactUrl, - PayloadSender + PayloadSender, ) from waio.factory.schemas import ( BASE_MODEL, @@ -39,7 +39,7 @@ RESPONSE_MESSAGE, PAYLOAD_CONTACT_EMAIL, RESPONSE_MESSAGE_PAYLOAD, - RESPONSE_MESSAGE_PAYLOAD_PAYLOAD + RESPONSE_MESSAGE_PAYLOAD_PAYLOAD, ) factory_gupshup = Factory( @@ -59,12 +59,11 @@ PayloadContactInformation: PAYLOAD_CONTACT_INFORMATION, PayloadContactEmail: PAYLOAD_CONTACT_EMAIL, PayloadContactAddress: PAYLOAD_CONTACT_ADDRESS, - ResponseMessage: RESPONSE_MESSAGE, + ResponseUserEvent: RESPONSE_MESSAGE, ResponseMessagePayload: RESPONSE_MESSAGE_PAYLOAD, - ResponseMessagePayloadPayload: RESPONSE_MESSAGE_PAYLOAD_PAYLOAD - }, debug_path=True) - -factory_default = Factory( - default_schema=Schema(omit_default=True), - debug_path=True + ResponseMessagePayloadPayload: RESPONSE_MESSAGE_PAYLOAD_PAYLOAD, + }, + debug_path=True, ) + +factory_default = Factory(default_schema=Schema(omit_default=True), debug_path=True) diff --git a/waio/factory/models/basic.py b/waio/factory/models/basic.py index 7ceacff..14e5f42 100644 --- a/waio/factory/models/basic.py +++ b/waio/factory/models/basic.py @@ -11,4 +11,3 @@ class PayloadSender: class PayloadBaseModel: sender: PayloadSender payload_id: str - diff --git a/waio/factory/models/main.py b/waio/factory/models/main.py index d641b49..15d1e64 100644 --- a/waio/factory/models/main.py +++ b/waio/factory/models/main.py @@ -19,5 +19,5 @@ class BaseResponse: PayloadButtonReply, PayloadImage, PayloadFile, - PayloadContacts + PayloadContacts, ] diff --git a/waio/factory/models/response.py b/waio/factory/models/response.py index 345057f..811670f 100644 --- a/waio/factory/models/response.py +++ b/waio/factory/models/response.py @@ -1,29 +1,35 @@ from dataclasses import dataclass +from dataclasses import field +from typing import Optional from uuid import uuid4 -from waio.models.enums import ResponseGupshupMessageType, ResponseGupshupPayloadType, EventPayloadType +from waio.models.enums import ( + ResponseGupshupMessageType, + ResponseGupshupPayloadType, + EventPayloadType, +) @dataclass class ResponseMessagePayloadPayload: - whatsapp_Message_id: str type: EventPayloadType + whatsapp_Message_id: Optional[str] = field(default=None) + phone: Optional[int] = field(default=None) @dataclass class ResponseMessagePayload: - id: uuid4 - type: ResponseGupshupPayloadType - destination: str - payload: ResponseMessagePayloadPayload + # id: uuid4 + type: EventPayloadType + phone: int + # destination: str + # payload: ResponseMessagePayloadPayload @dataclass -class ResponseMessage: +class ResponseUserEvent: app: str timestamp: int version: int type: ResponseGupshupMessageType payload: ResponseMessagePayload - - diff --git a/waio/factory/schemas.py b/waio/factory/schemas.py index 32e947e..cfa71db 100644 --- a/waio/factory/schemas.py +++ b/waio/factory/schemas.py @@ -8,7 +8,7 @@ "type": "type", "text": ("payload", "text"), }, - skip_internal=True + skip_internal=True, ) PAYLOAD_SENDER = Schema( @@ -16,7 +16,7 @@ "phone": "phone", "name": "name", }, - skip_internal=True + skip_internal=True, ) PAYLOAD_LIST = Schema( @@ -30,7 +30,7 @@ "description": ("payload", "description"), }, pre_parse=type_checker("list_reply", field="type"), - skip_internal=True + skip_internal=True, ) PAYLOAD_REPLY_KEYBOARD = Schema( @@ -42,7 +42,7 @@ "reply": ("payload", "reply"), }, pre_parse=type_checker(value="button_reply", field="type"), - skip_internal=True + skip_internal=True, ) PAYLOAD_IMAGE = Schema( @@ -52,10 +52,10 @@ "url": ("payload", "url"), "content_type": ("payload", "contentType"), "url_expiry": ("payload", "urlExpiry"), - "caption": ("payload", "caption") + "caption": ("payload", "caption"), }, pre_parse=type_checker(value="image", field="type"), - skip_internal=True + skip_internal=True, ) PAYLOAD_FILE = Schema( @@ -69,7 +69,7 @@ "name": ("payload", "name"), }, pre_parse=type_checker(value="file", field="type"), - skip_internal=True + skip_internal=True, ) BASE_MODEL = Schema( @@ -77,7 +77,7 @@ "payload_id": "id", "type": "type", }, - skip_internal=True + skip_internal=True, ) CONTACTS_PAYLOAD = Schema( @@ -85,7 +85,7 @@ "payload_id": "id", "contacts": ("payload", "contacts"), }, - skip_internal=True + skip_internal=True, ) PAYLOAD_CONTACT_PHONE = Schema( @@ -93,30 +93,22 @@ "phone": "phone", "type": "type", }, - skip_internal=True + skip_internal=True, ) PAYLOAD_CONTACT_ORGANIZATION = Schema( name_mapping={ "company": "company", }, - skip_internal=True + skip_internal=True, ) PAYLOAD_CONTACT_URL = Schema( - name_mapping={ - "url": "url", - "type": "type" - }, - skip_internal=True + name_mapping={"url": "url", "type": "type"}, skip_internal=True ) PAYLOAD_CONTACT_INFORMATION = Schema( - name_mapping={ - "service": "service", - "user_id": "user_id" - }, - skip_internal=True + name_mapping={"service": "service", "user_id": "user_id"}, skip_internal=True ) PAYLOAD_CONTACT = Schema( @@ -129,15 +121,11 @@ "phones": "phones", "urls": "urls", }, - skip_internal=True + skip_internal=True, ) PAYLOAD_CONTACT_EMAIL = Schema( - name_mapping={ - "email": "email", - "type": "type" - }, - skip_internal=True + name_mapping={"email": "email", "type": "type"}, skip_internal=True ) PAYLOAD_CONTACT_ADDRESS = Schema( name_mapping={ @@ -147,9 +135,9 @@ "state": "state", "street": "street", "type": "type", - "zip": "zip" + "zip": "zip", }, - skip_internal=True + skip_internal=True, ) RESPONSE_MESSAGE_PAYLOAD_PAYLOAD = Schema( @@ -157,7 +145,7 @@ "whatsapp_Message_id": "whatsappMessageId", "type": "type", }, - skip_internal=True + skip_internal=True, ) RESPONSE_MESSAGE_PAYLOAD = Schema( @@ -165,9 +153,9 @@ "id": "id", "type": "type", "destination": "destination", - "payload": "payload" + "payload": "payload", }, - skip_internal=True + skip_internal=True, ) RESPONSE_MESSAGE = Schema( @@ -176,7 +164,7 @@ "timestamp": "timestamp", "version": "version", "type": "type", - "payload": "payload" + "payload": "payload", }, - skip_internal=True + skip_internal=True, ) diff --git a/waio/gupshup/api.py b/waio/gupshup/api.py index 05123dd..1929bd4 100644 --- a/waio/gupshup/api.py +++ b/waio/gupshup/api.py @@ -1,14 +1,17 @@ +from typing import Dict + + class GupshupSettings: def __init__(self, apikey: str, src_name: str, phone_number: int): self.apikey = apikey self.src_name = src_name self.phone_number = phone_number - def _headers(self): + def _headers(self) -> Dict[str, str]: headers = { - 'Cache-Control': 'no-cache', - 'cache-control': 'no-cache', - 'Content-Type': 'application/x-www-form-urlencoded', - 'apikey': self.apikey + "Cache-Control": "no-cache", + "cache-control": "no-cache", + "Content-Type": "application/x-www-form-urlencoded", + "apikey": self.apikey, } return headers diff --git a/waio/gupshup/form.py b/waio/gupshup/form.py index 80d2dbe..41293cf 100644 --- a/waio/gupshup/form.py +++ b/waio/gupshup/form.py @@ -5,10 +5,10 @@ def generate_message_form(source, receiver, app_name, message: json) -> CustomFormData: form = CustomFormData() - form.add_www_form(name='channel', value='whatsapp') - form.add_www_form(name='source', value=source) - form.add_www_form(name='destination', value=receiver) - form.add_www_form(name='src.name', value=app_name) - form.add_www_form(name='message', value=message) + form.add_www_form(name="channel", value="whatsapp") + form.add_www_form(name="source", value=source) + form.add_www_form(name="destination", value=receiver) + form.add_www_form(name="src.name", value=app_name) + form.add_www_form(name="message", value=message) return form diff --git a/waio/handlers/__init__.py b/waio/handlers/__init__.py index add99ee..ac5f9cd 100644 --- a/waio/handlers/__init__.py +++ b/waio/handlers/__init__.py @@ -1,4 +1,3 @@ from .abc import ABCHandler from .executor import HandlerExecutor from .func_handler import FromFuncHandler -from .base_handlers import BaseHandlers, Handler diff --git a/waio/handlers/abc.py b/waio/handlers/abc.py index ca5012f..921770a 100644 --- a/waio/handlers/abc.py +++ b/waio/handlers/abc.py @@ -2,20 +2,19 @@ from typing import Any, Union, Callable from waio.rules.abc import ABCRule -from waio.types import Message +from waio.types import Event class ABCHandler(ABC): - @abstractmethod def __init__(self, handler: Callable, *rules: ABCRule): self.handler = handler self.rules = rules @abstractmethod - async def filter(self, event: "Message") -> Union[dict, bool]: + async def filter(self, event: Event) -> Union[dict, bool]: pass @abstractmethod - async def handle(self, event: "Message", **context) -> Any: + async def handle(self, event: Event, **context) -> Any: pass diff --git a/waio/handlers/base_handlers.py b/waio/handlers/base_handlers.py index 169864d..06f9557 100644 --- a/waio/handlers/base_handlers.py +++ b/waio/handlers/base_handlers.py @@ -1,67 +1,24 @@ -from typing import List, Callable - -from waio.handlers import ABCHandler, FromFuncHandler +from typing import List from waio.labeler import BotLabeler +from waio.rules import ABCRule -class BaseHandlers: - +class HandlerRule: def __init__(self, labeler: BotLabeler): - self.handlers: List[ABCHandler] = [] self.labeler = labeler - def add_message_handler(self, handler): - self.handlers.append(handler) - - def register_message_handler( - self, - handler: Callable, - *rules, - **custom_rules, - ): - handler_object = FromFuncHandler( - handler, - *rules, - *self.base_rules(**custom_rules), - *self.custom_rules(**custom_rules) - ) - self.add_message_handler(handler=handler_object) - - def base_rules(self, **rules): - default_rules = [ - self.labeler.default_rules[k](v) + def base_rules(self, **rules) -> List[ABCRule]: + return [ + self.labeler.default_rules[k](v) # type: ignore for k, v in rules.items() if k in self.labeler.default_rules.keys() ] - return default_rules - - def custom_rules(self, **rules): - custom = [ - self.labeler.custom_rules[k](v) + def custom_rules(self, **rules) -> List[ABCRule]: + return [ + self.labeler.custom_rules[k](v) # type: ignore for k, v in rules.items() if k in self.labeler.custom_rules.keys() ] - return custom - - -class Handler(BaseHandlers): - - def message_handler( - self, - *rules, - **custom_rules - ): - def decorator(handler) -> None: - - handler_object = FromFuncHandler( - handler, - *rules, - *self.base_rules(**custom_rules), - *self.custom_rules(**custom_rules) - ) - self.add_message_handler(handler=handler_object) - return handler - return decorator diff --git a/waio/handlers/executor.py b/waio/handlers/executor.py index bbcb5ca..f2d3a17 100644 --- a/waio/handlers/executor.py +++ b/waio/handlers/executor.py @@ -1,19 +1,30 @@ +from __future__ import annotations import inspect -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, TYPE_CHECKING +from waio.types.message import Event -from waio.handlers import ABCHandler -from waio.types.message import Message +if TYPE_CHECKING: + from waio.handlers import FromFuncHandler class HandlerExecutor: - @classmethod - async def execute(cls, handler: ABCHandler, message: Message, **middleware_kwargs) -> Optional[Dict[str, Any]]: - handler_filter = await handler.filter(message) - + async def execute( + cls, handler: FromFuncHandler, event: Event, **middleware_kwargs + ) -> Optional[Dict[str, Any]]: + handler_filter = await handler.filter(event=event) if isinstance(handler_filter, dict): - values_handler = {"message": message, "state": message.state, **handler_filter, **middleware_kwargs} + values_handler = { + "event": event, + "state": event.state, + **handler_filter, + **middleware_kwargs, + } func_info = inspect.getfullargspec(handler.handler) - values_to_func = {key: value for key, value in values_handler.items() if key in func_info.args} + values_to_func = { + key: value + for key, value in values_handler.items() + if key in func_info.args + } return values_to_func return None diff --git a/waio/handlers/func_handler.py b/waio/handlers/func_handler.py index a757b46..e5b322b 100644 --- a/waio/handlers/func_handler.py +++ b/waio/handlers/func_handler.py @@ -1,5 +1,10 @@ from typing import Callable, Union, Any +from typing import Dict + +from magic_filter import MagicFilter + from waio.rules.abc import ABCRule +from waio.types import Event class FromFuncHandler: @@ -7,22 +12,31 @@ def __init__(self, handler: Callable, *rules: ABCRule): self.handler = handler self.rules = rules - async def filter(self, event: Any) -> Union[dict, bool]: + @classmethod + async def _f_rule_event( + cls, + rule: Union[ABCRule, MagicFilter], + event: Event + ) -> Union[bool, Dict[Any, Any]]: + if isinstance(rule, MagicFilter): + result = rule.resolve(event) + return result + return await rule.check(event) + + async def filter(self, event: Event) -> Union[Dict[str, Any], bool]: rule_context = {} for rule in self.rules: - result = await rule.check(event) - if result is False or result is None: + filter_result = await self._f_rule_event(rule=rule, event=event) + if filter_result is False or filter_result is None: return False - elif result is True: - continue - rule_context.update(result) + else: + if not isinstance(filter_result, bool): + rule_context.update(filter_result) + return rule_context async def handle(self, **context) -> Any: return await self.handler(**context) def __repr__(self): - return ( - f"" - ) + return f"" diff --git a/waio/keyboard/list.py b/waio/keyboard/list.py index 91e6d17..ef5a6d6 100644 --- a/waio/keyboard/list.py +++ b/waio/keyboard/list.py @@ -1,3 +1,4 @@ +from typing import Dict from typing import Optional, Union, List import ujson @@ -6,31 +7,31 @@ class ListMainButton: def __init__(self, title: str): - self.type = 'text' + self.type = "text" self.title = title - def json(self): + def json(self) -> Dict[str, str]: return {"type": self.type, "title": self.title} class ListGroupItem: def __init__( - self, - title: str, - callback_data: Optional[str] = None, - description: Optional[str] = None, + self, + title: str, + callback_data: Optional[str] = None, + description: Optional[str] = None, ): - self.type = 'text' + self.type = "text" self.title = title # Maximum length: 24 characters self.description = description # Maximum length: 72 characters self.callback_data = callback_data - def json(self): + def json(self) -> Dict[str, Union[str, None]]: return { "type": self.type, "title": self.title, "description": self.description, - "postbackText": self.callback_data + "postbackText": self.callback_data, } @@ -40,7 +41,7 @@ def __init__(self, title: str, subtitle: str): self.subtitle = subtitle self.options: List[ListGroupItem] = [] - def add(self, list_item: ListGroupItem) -> 'ListGroup': + def add(self, list_item: ListGroupItem) -> "ListGroup": self.options.append(list_item) return self @@ -56,14 +57,14 @@ def json(self): class ListMessage: def __init__( - self, - title: str, - body: str, - button_title: str, - items: Optional[List[ListGroup]] = None, - callback_data: Optional[Union[int, str]] = None, + self, + title: str, + body: str, + button_title: str, + items: Optional[List[ListGroup]] = None, + callback_data: Optional[Union[int, str]] = None, ): - self.type = 'list' + self.type = "list" self.title = title self.body = body self.button_title = button_title @@ -75,16 +76,16 @@ def __init__( self.items = items @property - def main_button(self): + def main_button(self) -> ListMainButton: return ListMainButton(title=self.button_title) - def json(self): + def json(self) -> str: d = { "type": self.type, "title": self.title, "body": self.body, "msgid": self.callback_data, "globalButtons": [self.main_button.json()], - "items": [item.json() for item in self.items] + "items": [item.json() for item in self.items], } return ujson.dumps(d, indent=2) diff --git a/waio/keyboard/reply.py b/waio/keyboard/reply.py index f3678ba..a0cf23d 100644 --- a/waio/keyboard/reply.py +++ b/waio/keyboard/reply.py @@ -1,3 +1,5 @@ +from typing import Dict + import ujson from typing import Union, List, Optional from waio.keyboard.list import ListMainButton @@ -21,12 +23,12 @@ def __init__(self, header: str, text: str, caption: str): super().__init__(text, caption) - def json(self): + def json(self) -> Dict[str, str]: return { "type": self.type, "header": self.header, "text": self.text, - "caption": self.caption + "caption": self.caption, } @@ -37,12 +39,12 @@ def __init__(self, url: str, text: str, caption: str): super().__init__(text, caption) - def json(self): + def json(self) -> Dict[str, str]: return { "type": self.type, "url": self.url, "text": self.text, - "caption": self.caption + "caption": self.caption, } @@ -54,13 +56,13 @@ def __init__(self, url: str, filename: str, text: str, caption: str): super().__init__(text, caption) - def json(self): + def json(self) -> Dict[str, str]: return { "type": self.type, "url": self.url, "text": self.text, "caption": self.caption, - "filename": self.filename + "filename": self.filename, } @@ -71,7 +73,7 @@ def __init__(self, url: str, text: str, caption: str): super().__init__(text, caption) - def json(self): + def json(self) -> Dict[str, str]: return { "type": self.type, "url": self.url, @@ -82,15 +84,15 @@ def json(self): class QuickReply: def __init__( - self, - callback_data: str, - content: Union[ - QuickReplyContentText, - QuickReplyContentImage, - QuickReplyContentDocument, - QuickReplyContentVideo - ], - options: Optional[List[KeyboardButton]] = None + self, + callback_data: str, + content: Union[ + QuickReplyContentText, + QuickReplyContentImage, + QuickReplyContentDocument, + QuickReplyContentVideo, + ], + options: Optional[List[KeyboardButton]] = None, ): self.type = "quick_reply" self.callback_data = callback_data @@ -101,7 +103,7 @@ def __init__( else: self.options = options - def add(self, element: KeyboardButton) -> 'QuickReply': + def add(self, element: KeyboardButton) -> "QuickReply": self.options.append(element) return self @@ -110,7 +112,7 @@ def dict(self): "type": self.type, "msgid": self.callback_data, "content": self.content.json(), - "options": [element.json() for element in self.options] + "options": [element.json() for element in self.options], } def json(self): diff --git a/waio/labeler.py b/waio/labeler.py index 6039c62..4160603 100644 --- a/waio/labeler.py +++ b/waio/labeler.py @@ -25,23 +25,24 @@ class BotLabeler: "text_startswith": TextRuleStartswith, "text_endswith": TextRuleEndswith, "content_type": ContentTypeRule, - "callback": CallbackFilter, + "callback": CallbackFilter, } MIDDLEWARES: List[BaseMiddleware] = [] + CUSTOM_RULES: Dict[str, Type[ABCRule]] = {} - def __init__(self): - self._custom_rules = {} + def __str__(self): + return f"BotLabeler custom_rules({self.CUSTOM_RULES}) middlewares ({self.MIDDLEWARES})" - def bind_rule(self, name: str, value: ABCRule): - self._custom_rules[name] = value + def bind_rule(self, name: str, value: Type[ABCRule]) -> None: + self.CUSTOM_RULES[name] = value @property - def custom_rules(self): - return self._custom_rules + def custom_rules(self) -> Dict[str, Type[ABCRule]]: + return self.CUSTOM_RULES @property - def default_rules(self): + def default_rules(self) -> Dict[str, Type[ABCRule]]: return self.DEFAULT_RULES - def register_middleware(self, middleware): + def register_middleware(self, middleware: BaseMiddleware) -> None: self.MIDDLEWARES.append(middleware) diff --git a/waio/logs/__init__.py b/waio/logs/__init__.py index 2fd4f46..356d375 100644 --- a/waio/logs/__init__.py +++ b/waio/logs/__init__.py @@ -1 +1 @@ -from .logger import logger, loguru_filter \ No newline at end of file +from .logger import logger, loguru_filter diff --git a/waio/logs/log_level.py b/waio/logs/log_level.py index 6e9edf2..17fd760 100644 --- a/waio/logs/log_level.py +++ b/waio/logs/log_level.py @@ -2,11 +2,10 @@ class LogFilterLevel: - def __init__(self, lvl): self.level = lvl - def __call__(self, record): + def __call__(self, record) -> bool: level_no = logger.level(self.level).no return record["level"].no >= level_no @@ -14,5 +13,4 @@ def set_level(self, lvl): self.level = lvl -loguru_filter = LogFilterLevel('WARNING') - +loguru_filter = LogFilterLevel("WARNING") diff --git a/waio/middleware/base.py b/waio/middleware/base.py index 2b4c279..96e3a3c 100644 --- a/waio/middleware/base.py +++ b/waio/middleware/base.py @@ -1,25 +1,27 @@ from abc import ABC -from typing import List, Any, Dict, Optional +from typing import Any +from typing import Dict +from typing import Optional from waio.handlers import ABCHandler -from waio.types import Message +from waio.types import Event class BaseMiddleware(ABC): - event: Message - handle_responses: Optional[List[Dict[str, Any]]] - handlers: Optional[List[ABCHandler]] + event: Event + handler: Optional[ABCHandler] + response: Optional[Dict[str, Any]] def fill( - self, - event: Message, - handle_responses: Optional[List[Dict[str, Any]]] = None, - handlers: Optional[List[ABCHandler]] = None, + self, + event: Event, + response: Optional[Dict[str, Any]] = None, + handler: Optional[ABCHandler] = None, ): self.event = event - self.handle_responses = handle_responses - self.handlers = handlers + self.handler = handler + self.response = response async def pre(self): pass @@ -28,7 +30,9 @@ async def post(self): pass def __repr__(self) -> str: - return (f"<{self.__class__.__name__} " - f"event: {self.event}, " - f"handlers: {self.handlers}, " - f"returns: {self.handle_responses}>") + return ( + f"<{self.__class__.__name__} " + f"event: {self.event}, " + f"handler: {self.handler}, " + f"return: {self.response}>" + ) diff --git a/waio/models/audio.py b/waio/models/audio.py index 1d85c6e..d344dba 100644 --- a/waio/models/audio.py +++ b/waio/models/audio.py @@ -1,19 +1,21 @@ +from typing import Dict + import ujson class AudioModel: def __init__( - self, - url: str, + self, + url: str, ): - self.type = 'audio' + self.type = "audio" self.url = url - def dict(self): + def dict(self) -> Dict[str, str]: return { "type": self.type, "url": self.url, } - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/models/enums.py b/waio/models/enums.py index c03dc0d..b87de58 100644 --- a/waio/models/enums.py +++ b/waio/models/enums.py @@ -10,26 +10,25 @@ class RequestMethods(str, Enum): class ResponseGupshupMessageType(str, Enum): - request = 'message' - message_event = 'message-event' - event = 'user-event' + request = "message" + message_event = "message-event" + event = "user-event" class ResponseGupshupPayloadType(str, Enum): - enqueued = 'enqueued' - text = 'text' - file = 'file' - contact = 'contact' - image = 'image' + enqueued = "enqueued" + text = "text" + file = "file" + contact = "contact" + image = "image" class EventPayloadType(str, Enum): - start_dialog = 'opted-in' - session = 'session' - sandbox_start = 'SANDBOX_START' - failed = 'failed' + start_dialog = "opted-in" + session = "session" + sandbox_start = "SANDBOX_START" + failed = "failed" class GupshupMethods(str, Enum): - message = 'https://api.gupshup.io/sm/api/v1/msg' - + message = "https://api.gupshup.io/sm/api/v1/msg" diff --git a/waio/models/file.py b/waio/models/file.py index a46e4ba..ce44049 100644 --- a/waio/models/file.py +++ b/waio/models/file.py @@ -1,21 +1,18 @@ +from typing import Dict from typing import Optional +from typing import Union import ujson class FileModel: - def __init__( - self, - url: str, - filename: str, - caption: Optional[str] = None - ): - self.type = 'file' + def __init__(self, url: str, filename: str, caption: Optional[str] = None): + self.type = "file" self.url = url self.filename = filename self.caption = caption - def dict(self): + def dict(self) -> Dict[str, Union[str, None]]: return { "type": self.type, "url": self.url, @@ -23,5 +20,5 @@ def dict(self): "caption": self.caption, } - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/models/image.py b/waio/models/image.py index e0c3db9..aa57ba1 100644 --- a/waio/models/image.py +++ b/waio/models/image.py @@ -1,4 +1,6 @@ +from typing import Dict from typing import Optional +from typing import Union import ujson @@ -8,14 +10,14 @@ def __init__( self, original_url: str, preview_url: Optional[str] = None, - caption: Optional[str] = None + caption: Optional[str] = None, ): - self.type = 'image' + self.type = "image" self.original_url = original_url self.preview_url = preview_url or original_url self.caption = caption - def dict(self): + def dict(self) -> Dict[str, Union[str, None]]: return { "type": self.type, "originalUrl": self.original_url, @@ -23,5 +25,5 @@ def dict(self): "caption": self.caption, } - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/models/sticker.py b/waio/models/sticker.py index 19a22ab..a31c369 100644 --- a/waio/models/sticker.py +++ b/waio/models/sticker.py @@ -1,19 +1,21 @@ +from typing import Dict + import ujson class StickerModel: def __init__( - self, - url: str, + self, + url: str, ): - self.type = 'sticker' + self.type = "sticker" self.url = url - def dict(self): + def dict(self) -> Dict[str, str]: return { "type": self.type, "url": self.url, } - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/models/system/__init__.py b/waio/models/system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/waio/models/system/handlers.py b/waio/models/system/handlers.py new file mode 100644 index 0000000..bcbf404 --- /dev/null +++ b/waio/models/system/handlers.py @@ -0,0 +1,13 @@ +from dataclasses import dataclass +from dataclasses import field +from typing import Any +from typing import Dict +from typing import Optional + +from waio.handlers import FromFuncHandler + + +@dataclass +class ExecutedHandlerData: + handler: Optional[FromFuncHandler] = field(default=None) + response: Optional[Dict[str, Any]] = field(default=None) diff --git a/waio/models/text.py b/waio/models/text.py index 4261b66..a2b4c79 100644 --- a/waio/models/text.py +++ b/waio/models/text.py @@ -1,13 +1,15 @@ +from typing import Dict + import ujson class MessageText: def __init__(self, text: str): - self.type = 'text' + self.type = "text" self.text = text - def dict(self): + def dict(self) -> Dict[str, str]: return {"type": self.type, "text": self.text} - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/models/video.py b/waio/models/video.py index 9cdd87f..a069ff2 100644 --- a/waio/models/video.py +++ b/waio/models/video.py @@ -1,25 +1,22 @@ +from typing import Dict from typing import Optional +from typing import Union import ujson class VideoModel: - def __init__( - self, - url: str, - caption: Optional[str] = None - - ): - self.type = 'video' + def __init__(self, url: str, caption: Optional[str] = None): + self.type = "video" self.url = url self.caption = caption - def dict(self): + def dict(self) -> Dict[str, Union[str, None]]: return { "type": self.type, "url": self.url, "caption": self.caption, } - def json(self): + def json(self) -> str: return ujson.dumps(self.dict(), indent=2) diff --git a/waio/protocols/bot.py b/waio/protocols/bot.py index 7164108..211c261 100644 --- a/waio/protocols/bot.py +++ b/waio/protocols/bot.py @@ -6,7 +6,6 @@ class Bot(Protocol): - async def send_message(self, receiver: int, message: str): """Send message""" @@ -17,41 +16,41 @@ async def send_reply(self, receiver: int, keyboard: QuickReply): """Send reply message""" async def send_image( - self, - receiver: int, - original_url: str, - preview_url: Optional[str] = None, - caption: Optional[str] = None, + self, + receiver: int, + original_url: str, + preview_url: Optional[str] = None, + caption: Optional[str] = None, ): """Send image message""" async def send_file( - self, - receiver: int, - url: str, - filename: str, - caption: Optional[str] = None, + self, + receiver: int, + url: str, + filename: str, + caption: Optional[str] = None, ): """Send file message""" async def send_video( - self, - receiver: int, - url: str, - caption: Optional[str] = None, + self, + receiver: int, + url: str, + caption: Optional[str] = None, ): """Send video message""" async def send_audio( - self, - receiver: int, - url: str, + self, + receiver: int, + url: str, ): """Send audio message""" async def send_sticker( - self, - receiver: int, - url: str, + self, + receiver: int, + url: str, ): """Send sticker message""" diff --git a/waio/rules/abc.py b/waio/rules/abc.py index fe14335..6fa63b7 100644 --- a/waio/rules/abc.py +++ b/waio/rules/abc.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod from typing import Union +from waio.types import Event class ABCRule(ABC): @abstractmethod - async def check(self, message) -> Union[dict, bool]: - pass + async def check(self, event: Event) -> Union[dict, bool]: + raise NotImplemented diff --git a/waio/rules/default.py b/waio/rules/default.py index ac7b8a6..958c089 100644 --- a/waio/rules/default.py +++ b/waio/rules/default.py @@ -4,7 +4,7 @@ from waio.rules.abc import ABCRule from waio.states import BaseState from waio.types.content_types import ContentType -from waio.types.message import Message +from waio.types.message import Event from waio.utils.callback.filters import CallbackDataFilterItem @@ -13,22 +13,22 @@ def __init__(self, commands: List[str], prefix: str = "/"): self.prefix = prefix self.commands = commands - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for command in self.commands: command_and_prefix = f"{self.prefix}{command}" - if message.text.startswith(self.prefix): - if command_and_prefix == message.text: + if event.text.startswith(self.prefix): + if command_and_prefix == event.text: return True return False class StateRule(ABCRule): - def __init__(self, state: Optional[Union[BaseState, Literal['*']]] = None): + def __init__(self, state: Optional[Union[BaseState, Literal["*"]]] = None): self.state = state - async def check(self, message: Message) -> Union[dict, bool]: - user_state = await message.current_state() - if str(self.state) == user_state or not self.state or self.state == '*': + async def check(self, event: Event) -> Union[dict, bool]: + user_state = await event.current_state() + if str(self.state) == user_state or not self.state or self.state == "*": return True return False @@ -38,15 +38,15 @@ def __init__(self, regexp: Union[str, List[str], re.Pattern, List[re.Pattern]]): if isinstance(regexp, re.Pattern): regexp = [regexp] elif isinstance(regexp, str): - regexp = [re.compile(regexp)] + regexp = [re.compile(pattern=regexp)] elif isinstance(regexp, list): - regexp = [re.compile(exp) for exp in regexp] + regexp = [re.compile(pattern=exp) for exp in regexp] self.regexp = regexp - async def check(self, message: Message) -> Union[Dict[str, re.Match], bool]: + async def check(self, event: Event) -> Union[Dict[str, re.Match], bool]: for regexp in self.regexp: - match = re.match(regexp, message.text) + match = re.match(pattern=regexp, string=event.text) if match: return {"regex": match} return False @@ -56,9 +56,9 @@ class TextRuleEquals(ABCRule): def __init__(self, equals: List[str]): self.equals = equals - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for elem in self.equals: - if elem == message.text: + if elem == event.text: return True return False @@ -67,9 +67,9 @@ class TextRuleContains(ABCRule): def __init__(self, contains: List[str]): self.contains = contains - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for elem in self.contains: - if elem in message.text: + if elem in event.text: return True return False @@ -78,9 +78,9 @@ class TextRuleStartswith(ABCRule): def __init__(self, startswith: List[str]): self.startswith = startswith - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for elem in self.startswith: - if message.text.startswith(elem): + if event.text.startswith(elem): return True return False @@ -89,9 +89,9 @@ class TextRuleEndswith(ABCRule): def __init__(self, endswith: List[str]): self.endswith = endswith - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for elem in self.endswith: - if message.text.endswith(elem): + if event.text.endswith(elem): return True return False @@ -102,38 +102,38 @@ def __init__( equals: Optional[List[str]] = None, contains: Optional[List[str]] = None, startswith: Optional[List[str]] = None, - endswith: Optional[List[str]] = None + endswith: Optional[List[str]] = None, ): self.equals = equals self.contains = contains self.startswith = startswith self.endswith = endswith - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: if self.equals: eq = TextRuleEquals(equals=self.equals) - return await eq.check(message) + return await eq.check(event) if self.contains: ct = TextRuleContains(contains=self.equals) - return await ct.check(message) + return await ct.check(event) if self.startswith: ss = TextRuleStartswith(startswith=self.startswith) - return await ss.check(message) + return await ss.check(event) if self.endswith: es = TextRuleEndswith(endswith=self.endswith) - return await es.check(message) + return await es.check(event) class ContentTypeRule(ABCRule): def __init__(self, content_types: List[ContentType]): self.content_types = content_types - async def check(self, message: Message) -> Union[Dict[str, re.Match], bool]: + async def check(self, event: Event) -> Union[Dict[str, re.Match], bool]: for content_type in self.content_types: - if isinstance(message.message.payload, content_type.value): + if isinstance(event.message.payload, content_type.value): return True return False @@ -142,9 +142,9 @@ class PhoneNumberRule(ABCRule): def __init__(self, phones: List[int]): self.phones = phones - async def check(self, message: Message) -> bool: + async def check(self, event: Event) -> bool: for phone in self.phones: - if phone == message.message.payload.sender.phone: + if phone == event.message.payload.sender.phone: return True return False @@ -153,5 +153,5 @@ class CallbackFilter(ABCRule): def __init__(self, item: CallbackDataFilterItem): self.item = item - async def check(self, message: Message): - return await self.item.check(message=message) + async def check(self, event: Event) -> Union[bool, Dict[str, Dict[str, str]]]: + return await self.item.check(event=event) diff --git a/waio/states/context.py b/waio/states/context.py index 5d19099..762ff80 100644 --- a/waio/states/context.py +++ b/waio/states/context.py @@ -1,4 +1,4 @@ -from typing import Union, Optional, Dict, Any +from typing import Union, Optional, Dict from waio.states.fsm import BaseState from waio.storage.redis import RedisStorage @@ -26,5 +26,7 @@ async def set_data(self, **kwargs: Union[str, int]) -> bool: async def get_data(self, *data_keys) -> Dict[str, Union[str, int]]: data_values = await self.storage.get_data(key=self.user) if data_keys: - return {key: value for key, value in data_values.items() if key in data_keys} + return { + key: value for key, value in data_values.items() if key in data_keys + } return data_values diff --git a/waio/states/fsm.py b/waio/states/fsm.py index ce68250..e268ea7 100644 --- a/waio/states/fsm.py +++ b/waio/states/fsm.py @@ -2,18 +2,14 @@ class BaseState: - def __init__( - self, - state: Optional[str] = None, - group: Optional[object] = None - ): + def __init__(self, state: Optional[str] = None, group: Optional[object] = None): self._group = group self._state = state - def __repr__(self): + def __repr__(self) -> str: return f" {self.group.__name__}:{self.state}" - def __str__(self): + def __str__(self) -> str: return f"{self.group.__name__}:{self.state}" def __set_name__(self, owner: "StatesGroup", name: str) -> None: @@ -40,15 +36,16 @@ def group(self, value): class StatesGroupMeta(type): __states__: Dict[str, Any] - def __new__(mcs, cls_name, bases, attrs: Dict[str, Union[Any, BaseState]]): + def __new__( + mcs, cls_name, bases, attrs: Dict[str, Union[Any, BaseState]] + ) -> "StatesGroupMeta": mcs.__states__ = { state_attr: state_value for state_attr, state_value in attrs.items() if not state_attr.startswith("__") } - return super(StatesGroupMeta, mcs).__new__( - mcs, cls_name, bases, attrs) + return super(StatesGroupMeta, mcs).__new__(mcs, cls_name, bases, attrs) class StatesGroup(metaclass=StatesGroupMeta): diff --git a/waio/storage/connector.py b/waio/storage/connector.py index de9c28b..e8e75d3 100644 --- a/waio/storage/connector.py +++ b/waio/storage/connector.py @@ -5,7 +5,9 @@ class RedisConnector: - def __init__(self, redis_url: str, db_number: int = 1, encoding: Optional[str] = "utf-8"): + def __init__( + self, redis_url: str, db_number: int = 1, encoding: Optional[str] = "utf-8" + ): self._redis: Optional[aioredis.Redis] = None self.redis_url = redis_url self.db = db_number @@ -19,14 +21,19 @@ def redis(self) -> aioredis.Redis: @property def check_connection(self) -> bool: - conn_attr = hasattr(self._redis, 'connection') + conn_attr = hasattr(self._redis, "connection") if conn_attr: if self._redis.connection: return True return False def create_session(self) -> None: - self._redis = aioredis.from_url(url=self.redis_url, encoding=self.encoding, db=self.db, decode_responses=True) + self._redis = aioredis.from_url( + url=self.redis_url, + encoding=self.encoding, + db=self.db, + decode_responses=True, + ) async def close_session(self) -> None: await self._redis.close() diff --git a/waio/storage/redis.py b/waio/storage/redis.py index dc453b7..e5cbf30 100644 --- a/waio/storage/redis.py +++ b/waio/storage/redis.py @@ -1,14 +1,11 @@ import ujson -from typing import ( - Optional, - Any, Dict, Union -) +from typing import Optional, Any, Dict, Union from waio.storage.connector import RedisConnector class RedisStorage(RedisConnector): - def __init__(self, redis_url: str, prefix_fsm: str = 'fsm', db: int = 1): + def __init__(self, redis_url: str, prefix_fsm: str = "fsm", db: int = 1): super().__init__(redis_url=redis_url, db_number=db) self.prefix_fsm = prefix_fsm diff --git a/waio/types/__init__.py b/waio/types/__init__.py index 3d413b8..d7c1dd5 100644 --- a/waio/types/__init__.py +++ b/waio/types/__init__.py @@ -1,2 +1,2 @@ from .content_types import ContentType -from .message import Message \ No newline at end of file +from .message import Event diff --git a/waio/types/content_types.py b/waio/types/content_types.py index 7a53cf4..55841d4 100644 --- a/waio/types/content_types.py +++ b/waio/types/content_types.py @@ -15,4 +15,3 @@ class ContentType(Enum): CONTACT = PayloadContacts BUTTON = PayloadButtonReply LIST = PayloadList - diff --git a/waio/types/message.py b/waio/types/message.py index c5d626a..f330f46 100644 --- a/waio/types/message.py +++ b/waio/types/message.py @@ -1,45 +1,52 @@ -from typing import Callable, Any +from typing import Callable +from typing import Optional -from waio.factory.base import ResponseModel +from waio.factory.models.main import BaseResponse +from waio.factory.models.response import ResponseUserEvent from waio.protocols.bot import Bot from waio.states.context import FSMContext class MessagePropertyEvent: - def __init__(self, message: ResponseModel): + def __init__(self, message: BaseResponse): self.message = message @property def text(self) -> str: - if hasattr(self.message.payload, 'caption') and self.message.payload.caption: + if hasattr(self.message.payload, "caption") and self.message.payload.caption: return self.message.payload.caption - if hasattr(self.message.payload, 'text') and self.message.payload.text: + if hasattr(self.message.payload, "text") and self.message.payload.text: return self.message.payload.text - return '' + return "" @property - def sender_number(self): + def sender_number(self) -> int: return self.message.payload.sender.phone @property - def sender_name(self): + def sender_name(self) -> str: return self.message.payload.sender.name @property - def callback_data_item(self): - if hasattr(self.message.payload, 'postback_text') and self.message.payload.postback_text: + def callback_data_item(self) -> Optional[str]: + if ( + hasattr(self.message.payload, "postback_text") + and self.message.payload.postback_text + ): return self.message.payload.postback_text - return '' + return "" @property - def callback_data_list(self): - if hasattr(self.message.payload, 'id') and self.message.payload.id: + def callback_data_list(self) -> Optional[str]: + if hasattr(self.message.payload, "id") and self.message.payload.id: return self.message.payload.id - return '' + return "" -class Message(MessagePropertyEvent): - def __init__(self, bot: Bot, message: ResponseModel, state_func: Callable[[int], FSMContext]): +class Event(MessagePropertyEvent): + def __init__( + self, bot: Bot, message: BaseResponse, state_func: Callable[[int], FSMContext] + ): self.bot = bot self.message = message self._state_func = state_func @@ -47,7 +54,7 @@ def __init__(self, bot: Bot, message: ResponseModel, state_func: Callable[[int], super().__init__(message) @property - def state(self): + def state(self) -> FSMContext: return self._state_func(self.message.payload.sender.phone) async def current_state(self) -> str: @@ -55,4 +62,28 @@ async def current_state(self) -> str: return str(current_state) async def answer(self, message: str): - await self.bot.send_message(receiver=self.message.payload.sender.phone, message=message) + await self.bot.send_message( + receiver=self.message.payload.sender.phone, message=message + ) + + +class EventSubscribe: + def __init__( + self, bot: Bot, message: ResponseUserEvent, state_func: Callable[[int], FSMContext] + ): + self.bot = bot + self.message = message + self._state_func = state_func + + @property + def state(self) -> FSMContext: + return self._state_func(self.message.payload.phone) + + async def current_state(self) -> str: + current_state = await self.state.get_state() + return str(current_state) + + async def answer(self, message: str): + await self.bot.send_message( + receiver=self.message.payload.phone, message=message + ) diff --git a/waio/utils/callback/base_callback.py b/waio/utils/callback/base_callback.py index c3d75ec..202798b 100644 --- a/waio/utils/callback/base_callback.py +++ b/waio/utils/callback/base_callback.py @@ -7,9 +7,11 @@ class CallbackDataBase(ABC): Callback data factory """ - def __init__(self, prefix, *parts, sep=':'): + def __init__(self, prefix, *parts, sep=":"): if not isinstance(prefix, str): - raise TypeError(f'Prefix must be instance of str not {type(prefix).__name__}') + raise TypeError( + f"Prefix must be instance of str not {type(prefix).__name__}" + ) if not prefix: raise ValueError("Prefix can't be empty") if sep in prefix: @@ -37,22 +39,24 @@ def new(self, *args, **kwargs) -> str: if args: value = args.pop(0) else: - raise ValueError(f'Value for {part!r} was not passed!') + raise ValueError(f"Value for {part!r} was not passed!") if value is not None and not isinstance(value, str): value = str(value) if self.sep in value: - raise ValueError(f"Symbol {self.sep!r} is defined as the separator and can't be used in parts' values") + raise ValueError( + f"Symbol {self.sep!r} is defined as the separator and can't be used in parts' values" + ) data.append(value) if args or kwargs: - raise TypeError('Too many arguments were passed!') + raise TypeError("Too many arguments were passed!") callback_data = self.sep.join(data) if len(callback_data.encode()) > 64: - raise ValueError('Resulted callback data is too long!') + raise ValueError("Resulted callback data is too long!") return callback_data @@ -66,9 +70,9 @@ def parse(self, callback_data: str) -> Dict[str, str]: if prefix != self.prefix: raise ValueError("Passed callback data can't be parsed with that prefix.") elif len(parts) != len(self._part_names): - raise ValueError('Invalid parts count!') + raise ValueError("Invalid parts count!") - result = {'@': prefix} + result = {"@": prefix} result.update(zip(self._part_names, parts)) return result diff --git a/waio/utils/callback/base_filter.py b/waio/utils/callback/base_filter.py index e7e267b..ffaa138 100644 --- a/waio/utils/callback/base_filter.py +++ b/waio/utils/callback/base_filter.py @@ -1,12 +1,11 @@ from abc import ABC, abstractmethod from typing import Dict, Any -from waio.types import Message +from waio.types import Event from waio.utils.callback.base_callback import CallbackDataBase class CallbackDataFilterBase(ABC): - def __init__(self, factory: CallbackDataBase, config: Dict[str, str]): self.config = config self.factory = factory @@ -26,8 +25,8 @@ async def base_check(self, data): return False elif data.get(key) != value: return False - return {'callback_data': data} + return {"callback_data": data} @abstractmethod - async def check(self, message: Message): + async def check(self, event: Event): pass diff --git a/waio/utils/callback/callbacks.py b/waio/utils/callback/callbacks.py index 23853f5..ad0ffae 100644 --- a/waio/utils/callback/callbacks.py +++ b/waio/utils/callback/callbacks.py @@ -3,18 +3,16 @@ class CallbackDataItem(CallbackDataBase): - - def filter(self, **config) -> 'CallbackDataFilterItem': + def filter(self, **config) -> "CallbackDataFilterItem": for key in config.keys(): if key not in self._part_names: - raise ValueError(f'Invalid field name {key!r}') + raise ValueError(f"Invalid field name {key!r}") return CallbackDataFilterItem(self, config) class CallbackDataGroup(CallbackDataBase): - - def filter(self, **config) -> 'CallbackDataFilterGroup': + def filter(self, **config) -> "CallbackDataFilterGroup": for key in config.keys(): if key not in self._part_names: - raise ValueError(f'Invalid field name {key!r}') + raise ValueError(f"Invalid field name {key!r}") return CallbackDataFilterGroup(self, config) diff --git a/waio/utils/callback/filters.py b/waio/utils/callback/filters.py index a3e96db..3180971 100644 --- a/waio/utils/callback/filters.py +++ b/waio/utils/callback/filters.py @@ -1,12 +1,12 @@ -from waio.types import Message +from waio.types import Event from waio.utils.callback.base_filter import CallbackDataFilterBase class CallbackDataFilterGroup(CallbackDataFilterBase): - async def check(self, message: Message): - return await self.base_check(message.callback_data_list) + async def check(self, event: Event): + return await self.base_check(event.callback_data_list) class CallbackDataFilterItem(CallbackDataFilterBase): - async def check(self, message: Message): - return await self.base_check(message.callback_data_item) + async def check(self, event: Event): + return await self.base_check(event.callback_data_item) diff --git a/waio/utils/dicts.py b/waio/utils/dicts.py index fe99a1a..9cef56f 100644 --- a/waio/utils/dicts.py +++ b/waio/utils/dicts.py @@ -10,9 +10,11 @@ def clear_none_values(d: Union[List, Dict]): return d else: r = dict( - filter(lambda x: x[1] is not None, - map(lambda x: (x[0], clear_none_values(x[1])), - d.items()))) + filter( + lambda x: x[1] is not None, + map(lambda x: (x[0], clear_none_values(x[1])), d.items()), + ) + ) if not bool(r): return None return r diff --git a/waio/utils/form_data.py b/waio/utils/form_data.py index e88736c..d7e45fb 100644 --- a/waio/utils/form_data.py +++ b/waio/utils/form_data.py @@ -1,3 +1,6 @@ +from typing import Generator +from typing import Tuple + from aiohttp import FormData from typing import Any, Optional, Dict @@ -6,8 +9,16 @@ class CustomFormDataStorage(FormData): def add_www_form(self, name: str, value: Any): self.add_field(name=name, value=value) - def add_multipart_form(self, name: str, filename: Optional[str], value: Any, content_type: Optional[str] = None): - self.add_field(name=name, filename=filename, value=value, content_type=content_type) + def add_multipart_form( + self, + name: str, + filename: Optional[str], + value: Any, + content_type: Optional[str] = None, + ): + self.add_field( + name=name, filename=filename, value=value, content_type=content_type + ) class CustomFormData(CustomFormDataStorage): @@ -20,7 +31,7 @@ async def uploads(self, elements: Dict[str, Any]): for key, value in elements.items(): await self.upload(key=key, value=value) - def __iter__(self): - data = self.__dict__.get('_fields') + def __iter__(self) -> Generator[Tuple, Any, None]: + data = self.__dict__.get("_fields") for element in data: - yield element[0]['name'], element[2] + yield element[0]["name"], element[2] From 10cfa38409a34588d972d2485520fd2f5bbb5f1c Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:20:19 +0300 Subject: [PATCH 5/6] Fix gitguardian --- docs/assets/code/first_bot/002.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/assets/code/first_bot/002.py b/docs/assets/code/first_bot/002.py index ea9c21a..ce78bbc 100644 --- a/docs/assets/code/first_bot/002.py +++ b/docs/assets/code/first_bot/002.py @@ -1,9 +1,9 @@ loguru_filter.set_level("DEBUG") bot = Bot( - apikey="algAJW9512kMWGALZIkAMWG", - src_name="test_client18215", - phone_number="79189998877", + apikey="XXX", + src_name="YYY", + phone_number="PHONE", ) dp = Dispatcher(bot=bot) From 924284eba67cacb9b08e07f920301ddb63e51ad7 Mon Sep 17 00:00:00 2001 From: dotX12 Date: Sun, 6 Nov 2022 20:23:27 +0300 Subject: [PATCH 6/6] Black code --- examples/filters/main.py | 6 ++---- examples/routers/binding.py | 2 +- examples/routers/filters.py | 1 - examples/routers/handlers_1.py | 3 +-- examples/routers/handlers_2.py | 5 ++--- waio/__init__.py | 1 - waio/dispatcher/dispatcher.py | 18 +++++++++--------- waio/dispatcher/event.py | 8 +++++--- waio/dispatcher/router.py | 14 +++++++------- waio/handlers/base_handlers.py | 2 -- waio/handlers/func_handler.py | 4 +--- waio/rules/abc.py | 1 - waio/types/message.py | 5 ++++- 13 files changed, 32 insertions(+), 38 deletions(-) diff --git a/examples/filters/main.py b/examples/filters/main.py index ec165e4..bb87ea9 100644 --- a/examples/filters/main.py +++ b/examples/filters/main.py @@ -99,12 +99,10 @@ async def text_start_switch_without_labeler(event: Event): @dp.message_handler( (F.message.payload.content_type == "image/gif") & (F.message.payload.sender.name == "Alex") - & ((F.message.payload.sender.phone.cast(str)[:4]) == '7928') + & ((F.message.payload.sender.phone.cast(str)[:4]) == "7928") ) async def test_magic_filter(event: Event): - await event.answer( - f"Test Filter" - ) + await event.answer(f"Test Filter") async def handler_gupshup(request): diff --git a/examples/routers/binding.py b/examples/routers/binding.py index 48823ea..fc6914a 100644 --- a/examples/routers/binding.py +++ b/examples/routers/binding.py @@ -2,6 +2,6 @@ from .handlers_1 import router1 from .handlers_2 import router2 -own_router = Router(name='Own Router') +own_router = Router(name="Own Router") own_router.include_router(router1) own_router.include_router(router2) diff --git a/examples/routers/filters.py b/examples/routers/filters.py index c72cea0..364f653 100644 --- a/examples/routers/filters.py +++ b/examples/routers/filters.py @@ -3,7 +3,6 @@ class DynamicLongMessageRule(ABCRule): - def __init__(self, len_message: int): self.len_message = len_message diff --git a/examples/routers/handlers_1.py b/examples/routers/handlers_1.py index f206a54..a9f8f18 100644 --- a/examples/routers/handlers_1.py +++ b/examples/routers/handlers_1.py @@ -2,11 +2,10 @@ from waio import Router from waio.types import Event -router1 = Router(name='Router 1') +router1 = Router(name="Router 1") router1.labeler.bind_rule("len_more", DynamicLongMessageRule) @router1.message_handler(len_more=20) async def text_len(event: Event): await event.answer(f"msg len: {len(event.text)}") - diff --git a/examples/routers/handlers_2.py b/examples/routers/handlers_2.py index 5ba78ee..075d143 100644 --- a/examples/routers/handlers_2.py +++ b/examples/routers/handlers_2.py @@ -2,10 +2,9 @@ from waio.types import Event -router2 = Router(name='Router2') +router2 = Router(name="Router2") -@router2.message_handler(commands=['start']) +@router2.message_handler(commands=["start"]) async def text_start(event: Event): await event.answer("started...") - diff --git a/waio/__init__.py b/waio/__init__.py index c5af7bc..7d80fbf 100644 --- a/waio/__init__.py +++ b/waio/__init__.py @@ -2,4 +2,3 @@ from .dispatcher.dispatcher import Dispatcher from .dispatcher.router import Router from magic_filter import F - diff --git a/waio/dispatcher/dispatcher.py b/waio/dispatcher/dispatcher.py index 92509f9..0a13271 100644 --- a/waio/dispatcher/dispatcher.py +++ b/waio/dispatcher/dispatcher.py @@ -23,14 +23,18 @@ def __init__( self, bot: Bot, storage: Optional[RedisStorage] = None, - name: Optional[str] = None + name: Optional[str] = None, ): self.bot = bot self.storage = storage self.labeler = BotLabeler() self.message_handler = WhatsAppEventObserver(router=self, event_name="message") - self.notify_success_handler = WhatsAppEventObserver(router=self, event_name="opted-in") - self.notify_denied_handler = WhatsAppEventObserver(router=self, event_name="opted-out") + self.notify_success_handler = WhatsAppEventObserver( + router=self, event_name="opted-in" + ) + self.notify_denied_handler = WhatsAppEventObserver( + router=self, event_name="opted-out" + ) self.observers: Dict[str, WhatsAppEventObserver] = { "message": self.message_handler, @@ -49,9 +53,7 @@ async def handle_event(self, event: Dict[str, Any]) -> None: if event.get("type", "") == "message": data_load = factory_gupshup.load(event, BaseResponse) event_obj = Event( - bot=self.bot, - message=data_load, - state_func=self.state + bot=self.bot, message=data_load, state_func=self.state ) await self.observers["message"].notify(event=event_obj) @@ -59,9 +61,7 @@ async def handle_event(self, event: Dict[str, Any]) -> None: if event["payload"]["type"] == EventPayloadType.start_dialog: data_load = factory_gupshup.load(event, ResponseUserEvent) event_obj = EventSubscribe( - bot=self.bot, - message=data_load, - state_func=self.state + bot=self.bot, message=data_load, state_func=self.state ) await self.observers["opted-in"].notify(event=event_obj) diff --git a/waio/dispatcher/event.py b/waio/dispatcher/event.py index d49ce35..83f11c9 100644 --- a/waio/dispatcher/event.py +++ b/waio/dispatcher/event.py @@ -19,7 +19,7 @@ def __init__(self, router: Router, event_name: str): self.event_name = event_name self._handlers = [] - def register(self, handler, *rules, **custom_rules): + def register(self, handler, *rules, **custom_rules): handler_object = FromFuncHandler( handler, *rules, @@ -68,13 +68,15 @@ async def _resolve_handler( ) -> ExecutedHandlerData: for router in self.router.chain_tail: for handler in router.observers[self.event_name]._handlers: - logger.debug(f'[Resolver]: Router: {router} Handler: {handler}') + logger.debug(f"[Resolver]: Router: {router} Handler: {handler}") resp = await HandlerExecutor.execute( handler=handler, event=event, **context_variables ) if resp: await handler.handle(**resp) - logger.debug(f"[Result] Router: {router} Handler: {handler} Return {resp}") + logger.debug( + f"[Result] Router: {router} Handler: {handler} Return {resp}" + ) return ExecutedHandlerData( handler=handler, response=resp, diff --git a/waio/dispatcher/router.py b/waio/dispatcher/router.py index 2b7c7fe..4eaa2f2 100644 --- a/waio/dispatcher/router.py +++ b/waio/dispatcher/router.py @@ -13,10 +13,7 @@ class Router(HandlerRule): def __init__( - self, - *, - name: Optional[str] = None, - labeler: Optional[BotLabeler] = None + self, *, name: Optional[str] = None, labeler: Optional[BotLabeler] = None ): self.labeler = labeler or BotLabeler() @@ -25,8 +22,12 @@ def __init__( self.sub_routers: List[Router] = [] self.message_handler = WhatsAppEventObserver(router=self, event_name="message") - self.notify_success_handler = WhatsAppEventObserver(router=self, event_name="opted-in") - self.notify_denied_handler = WhatsAppEventObserver(router=self, event_name="opted-out") + self.notify_success_handler = WhatsAppEventObserver( + router=self, event_name="opted-in" + ) + self.notify_denied_handler = WhatsAppEventObserver( + router=self, event_name="opted-out" + ) self.observers: Dict[str, WhatsAppEventObserver] = { "message": self.message_handler, @@ -103,4 +104,3 @@ def include_router(self, router: Union[Router, str]) -> Router: ) router.parent_router = self return router - diff --git a/waio/handlers/base_handlers.py b/waio/handlers/base_handlers.py index 06f9557..8972bea 100644 --- a/waio/handlers/base_handlers.py +++ b/waio/handlers/base_handlers.py @@ -20,5 +20,3 @@ def custom_rules(self, **rules) -> List[ABCRule]: for k, v in rules.items() if k in self.labeler.custom_rules.keys() ] - - diff --git a/waio/handlers/func_handler.py b/waio/handlers/func_handler.py index e5b322b..cc7a305 100644 --- a/waio/handlers/func_handler.py +++ b/waio/handlers/func_handler.py @@ -14,9 +14,7 @@ def __init__(self, handler: Callable, *rules: ABCRule): @classmethod async def _f_rule_event( - cls, - rule: Union[ABCRule, MagicFilter], - event: Event + cls, rule: Union[ABCRule, MagicFilter], event: Event ) -> Union[bool, Dict[Any, Any]]: if isinstance(rule, MagicFilter): result = rule.resolve(event) diff --git a/waio/rules/abc.py b/waio/rules/abc.py index 6fa63b7..215491c 100644 --- a/waio/rules/abc.py +++ b/waio/rules/abc.py @@ -4,7 +4,6 @@ class ABCRule(ABC): - @abstractmethod async def check(self, event: Event) -> Union[dict, bool]: raise NotImplemented diff --git a/waio/types/message.py b/waio/types/message.py index f330f46..a04ccc3 100644 --- a/waio/types/message.py +++ b/waio/types/message.py @@ -69,7 +69,10 @@ async def answer(self, message: str): class EventSubscribe: def __init__( - self, bot: Bot, message: ResponseUserEvent, state_func: Callable[[int], FSMContext] + self, + bot: Bot, + message: ResponseUserEvent, + state_func: Callable[[int], FSMContext], ): self.bot = bot self.message = message