From a368de757122363038ee2d71732c5ee568953f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro?= Date: Thu, 4 Apr 2024 16:40:26 -0300 Subject: [PATCH] add inital config --- botgen/adapters/web_adapter.py | 63 +++++++++++++++-- botgen/core.py | 124 ++++++++++++++++++++++++++++++++- poetry.lock | 85 +++++++++++++++++++++- pyproject.toml | 1 + 4 files changed, 266 insertions(+), 7 deletions(-) diff --git a/botgen/adapters/web_adapter.py b/botgen/adapters/web_adapter.py index 78972c6..5999bc4 100644 --- a/botgen/adapters/web_adapter.py +++ b/botgen/adapters/web_adapter.py @@ -1,8 +1,11 @@ import dataclasses +import json from datetime import datetime from typing import Awaitable from typing import Callable +from typing import Dict +import websockets from aiohttp.web import Request from botbuilder.core import BotAdapter from botbuilder.core import TurnContext @@ -12,17 +15,69 @@ from botbuilder.schema import ResourceResponse from loguru import logger +from botgen.core import Bot from botgen.core import BotMessage +clients = dict() + + class WebAdapter(BotAdapter): - """ Connects PyBot to websocket or webhook """ + """Connects PyBot to websocket or webhook""" + + name = "webadapter" def __init__(self, on_turn_error: Callable[[TurnContext, Exception], Awaitable] = None): super().__init__(on_turn_error) + def init(self, bot: Bot): + def _init_adapter(): + self.create_conversation(bot.handle_turn) + + bot.ready(_init_adapter) + + def create_socket_server(self, logic: callable): + """Create a websocket server""" + logger.debug("Initing websocket server") + self.wss = websockets.serve(on_message, "localhost", 8765) + + async def on_message(payload: str, ws) -> None: + try: + message: Dict = json.loads(payload) + + # Note the websocket connection for this user + ws.user = message["user"] + clients[message["user"]] = ws + + # This stuff normally lives inside Botkit.congfigureWebhookEndpoint + activity = Activity( + timestamp=datetime.now(), + channelId="websocket", + conversation={"id": message["user"]}, + from_property={"id": message["user"]}, + recipient={"id": "bot"}, + channelData=message, + text=message.get("text", ""), + type=ActivityTypes.message + if message["type"] == "message" + else ActivityTypes.event, + ) + + # Set botkit's event type + if activity["type"] != ActivityTypes.message: + activity["channelData"]["botkitEventType"] = message["type"] + + context = TurnContext(self, activity) + await self.run_pipeline(context, logic) + except json.JSONDecodeError as e: + alert = ["Error parsing incoming message from websocket."] + logger.warning("\n".join(alert)) + logger.warning(e) + except Exception as ex: + logger.warning("An error occurred:", ex) + def activity_to_message(self, activity: Activity) -> BotMessage: - """ Caste a message to the simple format used by the websocket client """ + """Caste a message to the simple format used by the websocket client""" message = BotMessage( type=activity.type, text=activity.text, @@ -36,7 +91,7 @@ def activity_to_message(self, activity: Activity) -> BotMessage: async def send_activities( self, context: TurnContext, activities: list[Activity] ) -> ResourceResponse: - """ Standard BotBuilder adapter method to send a message from the bot to the messaging API """ + """Standard BotBuilder adapter method to send a message from the bot to the messaging API""" responses = list() @@ -63,7 +118,7 @@ async def update_activity(self, context: TurnContext, activity: Activity) -> Non raise NotImplementedError() async def delete_activity(self, context: TurnContext, reference: ConversationReference) -> None: - """ Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic """ + """Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic""" raise NotImplementedError() async def process_activity(self, request: Request, logic: callable): diff --git a/botgen/core.py b/botgen/core.py index 80725db..bb1c670 100644 --- a/botgen/core.py +++ b/botgen/core.py @@ -1,6 +1,8 @@ from abc import ABC from dataclasses import dataclass +from typing import Any from typing import Callable +from typing import Dict from typing import Optional from aiohttp import web @@ -53,6 +55,33 @@ class Middleware: interpret: Callable +class BotPlugin: + """ + A plugin for Bot that can be loaded into the core bot object. + """ + + def __init__( + self, + name: str, + middlewares: Optional[Dict[str, Any]] = None, + init: Optional[Callable] = None, + **kwargs: Any, + ) -> None: + """ + Create a new BotPlugin instance. + + Args: + name (str): The name of the plugin. + middlewares (dict): A dictionary of middleware functions that can be used to extend the bot's functionality. + init (Callable): A function that will be called when the plugin is loaded. + **kwargs: Additional arguments that can be used to configure the plugin. + """ + self.name = name + self.middlewares = middlewares + self.init = init + self.__annotations__ = kwargs + + class Bot: version: str middleware = Middleware @@ -99,6 +128,7 @@ def __init__( self._boot_complete_handlers: list[Callable] = [] self.booted = False + self.add_dep("booted") self._storage = MemoryStorage() @@ -113,6 +143,14 @@ def __init__( if self.webserver: self.configure_webhook() + self.plugin_list = [] + self._plugins = {} + + if self.adapter: + self.use_plugin(self.adapter) + + self.complete_dep("booted") + async def process_incoming_message(self, request: web.Request): """ """ body = await self.adapter.process_activity(request, self.handle_turn) @@ -121,7 +159,9 @@ async def process_incoming_message(self, request: web.Request): def configure_webhook(self): """ """ + self.add_dep("webadapter") self.webserver.add_routes([web.post(self.webhook_uri, self.process_incoming_message)]) + self.complete_dep("webadapter") async def handle_turn(self, turn_context: TurnContext): """ """ @@ -192,7 +232,7 @@ async def _test_trigger(self, trigger: BotTrigger, message: BotMessage): return False - async def ready(self, handler: Callable) -> None: + def ready(self, handler: Callable) -> None: """ """ if self.booted: @@ -242,3 +282,85 @@ async def spawn( def start(self): web.run_app(self.webserver) + + def add_dep(self, name: str) -> None: + """ + Add a dependency to Bot's bootup process that must be marked as completed using complete_dep(). + + Parameters: + name (str): The name of the dependency that is being loaded. + """ + logger.debug(f"Waiting for {name}") + self._dependencies[name] = False + + def complete_dep(self, name: str) -> bool: + """ + Mark a bootup dependency as loaded and ready to use. + + Parameters: + name (str): The name of the dependency that has completed loading. + + Returns: + bool: True if all dependencies have been marked complete, otherwise False. + """ + logger.debug(f"{name} ready") + + self._dependencies[name] = True + + if all(self._dependencies.values()): + # Everything is done! + self._signal_boot_complete() + return True + else: + return False + + def _signal_boot_complete(self) -> None: + """ + This function gets called when all of the bootup dependencies are completely loaded. + """ + self.booted = True + for handler in self._boot_complete_handlers: + handler() + + def use_plugin(self, plugin_or_function: Callable | BotPlugin) -> None: + """ + Load a plugin module and bind all included middlewares to their respective endpoints. + + Parameters: + plugin_or_function (Callable or BotPlugin): A plugin module in the form of a function(bot) {...} + that returns {name, middlewares, init} or an object in the same form. + """ + if callable(plugin_or_function): + plugin = plugin_or_function(self) + else: + plugin = plugin_or_function + + if plugin.name: + try: + self._register_plugin(plugin.name, plugin) + except Exception as err: + logger.warning(f"ERROR IN PLUGIN REGISTER: {err}") + + def _register_plugin(self, name: str, endpoints: BotPlugin) -> None: + """ + Called from usePlugin to do the actual binding of middlewares for a plugin that is being loaded. + + Parameters: + name (str): Name of the plugin. + endpoints (BotPlugin): The plugin object that contains middleware endpoint definitions. + """ + + if name in self.plugin_list: + logger.debug("Plugin already enabled:", name) + return + + self.plugin_list.append(name) + + if endpoints.init: + try: + endpoints.init(self) + except Exception as err: + if err: + raise err + + logger.debug("Plugin Enabled:", name) diff --git a/poetry.lock b/poetry.lock index d7fd973..afffccc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -1852,6 +1852,87 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + [[package]] name = "win32-setctime" version = "1.1.0" @@ -1972,4 +2053,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "34bb769b5ff6797376a30d4a091ad2c41c0f475bfd826226e5cc339febc6c12d" +content-hash = "60a6a0480b248c5f73e44d53300b6988076570c332d91b5b7a08c72846063512" diff --git a/pyproject.toml b/pyproject.toml index 4ce13bb..3ab6e17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ botbuilder-core = "^4.14.8" aiohttp = "^3.9.3" botbuilder-dialogs = "^4.14.8" loguru = "^0.7.2" +websockets = "^12.0" [tool.poetry.group.dev.dependencies]