From 34fc5a44a5e0c3e456fe3bf30009468ba0845931 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Wed, 9 Aug 2023 15:16:36 -0500 Subject: [PATCH 01/13] init --- MeowerBot/Bot.py | 825 ++++++++++++++--------------- MeowerBot/__init__.py | 1 + MeowerBot/cl/__init__.py | 120 ++++- MeowerBot/cl/cloudlink.py | 237 --------- MeowerBot/cl/waitedPacked.py | 9 + MeowerBot/command.py | 166 +++--- MeowerBot/context.py | 12 +- MeowerBot/ext/discord.py | 101 ++-- MeowerBot/ext/help.py | 8 +- poetry.lock | 449 ++++++++++------ pyproject.toml | 7 +- tests/__init__.py | 0 tests/integration_login.py | 26 + tests/intergration/API.py | 20 - tests/intergration/cogs.py | 31 -- tests/intergration/command.py | 30 -- tests/intergration/join_chat.py | 19 - tests/intergration/login.py | 28 - tests/intergration/test_help.py | 34 -- tests/intergration/type_forever.py | 31 -- tests/test_api.py | 89 ---- tests/test_bot.py | 287 ---------- tests/test_cog.py | 39 -- tests/test_command.py | 43 -- 24 files changed, 988 insertions(+), 1624 deletions(-) delete mode 100644 MeowerBot/cl/cloudlink.py create mode 100644 MeowerBot/cl/waitedPacked.py delete mode 100644 tests/__init__.py create mode 100644 tests/integration_login.py delete mode 100644 tests/intergration/API.py delete mode 100644 tests/intergration/cogs.py delete mode 100644 tests/intergration/command.py delete mode 100644 tests/intergration/join_chat.py delete mode 100644 tests/intergration/login.py delete mode 100644 tests/intergration/test_help.py delete mode 100644 tests/intergration/type_forever.py delete mode 100644 tests/test_api.py delete mode 100644 tests/test_bot.py delete mode 100644 tests/test_cog.py delete mode 100644 tests/test_command.py diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index 26d1db0..2b5e045 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -1,7 +1,6 @@ -import threading import shlex -from .cl import CloudLink +from .cl import Client import sys import json @@ -20,451 +19,401 @@ from .API import MeowerAPI -from websocket._exceptions import WebSocketConnectionClosedException, WebSocketException - +import asyncio import sys if sys.version_info >= (3, 11): - from enum import StrEnum + from enum import StrEnum else: - from backports.strenum import StrEnum + from backports.strenum import StrEnum from typing import Union class cbids(StrEnum): - error = "error" - __raw__ = "__raw__" - login = "login" - close = "close" - statuscode = "statuscode" - ulist = "ulist" - message = "message" - raw_message = "raw_message" - chat_list = "chat_list" - direct = "direct" - -class Bot: - """ - A class that holds all of the networking for a meower bot to function and run - - """ - __bridges__ = [ + error = "error" + __raw__ = "__raw__" + login = "login" + close = "close" + statuscode = "statuscode" + ulist = "ulist" + message = "message" + raw_message = "raw_message" + chat_list = "chat_list" + direct = "direct" + +class Bot(Client): + """ + A class that holds all of the networking for a meower bot to function and run + """ + __bridges__ = [ "Discord", "Revower", "revolt" ] - - BOT_TAKEN_LISTENERS = [ - "__meowerbot__send_ip", - "__meowerbot__send_message", - "__meowerbot__login", - "__meowerbot__cloudlink_trust", - ] - - BOT_NO_PMSG_RESPONSE = [ - "I:500 | Bot", - "I: 500 | Bot" - ] - - def _t_ping(self): - while True: - time.sleep(60) - - self.wss.sendPacket({"cmd": "ping", "val": ""}) - - def __init__(self, prefix=None, autoreload: int or None = None ): #type: ignore - self.wss = CloudLink() - self.callbacks = {} - self._last_to = "Home" - - self.wss.callback( - "on_packet", self._debug_fix - ) # self._debug_fix catches all errors - self.wss.callback("on_error", self.__handle_error__) # handle uncought errors - self.wss.callback("on_close", self.__handle_close__) # Websocket disconnected - self.wss.callback( - "on_connect", self.__handle_on_connect__ - ) # signing in and stuff like that - - # to be used in start - self.username = None - self.password = None - self.logger_in = False - - if autoreload: - self.autoreload = True - self.autoreload_time = min(autoreload, 1) - self.autoreload_original = min(autoreload, 1) - else: - self.autoreload = False - self.autoreload_time = 0 - self.autoreload_original = 0 - - self.commands = {} - self.prefix = prefix - self._t_ping_thread = threading.Thread(target=self._t_ping, daemon=True) # (: - self.logger = logging.getLogger("MeowerBot") - self.bad_exit = False - self.server = None - - self.cogs = {} - - def run_cb(self, cbid, args=(), kwargs=None): # cq: ignore - if cbid not in self.callbacks: - return # ignore - - if not kwargs: - kwargs = {} - - if cbid == "error" and isinstance(args[0], KeyboardInterrupt()): - self.logger.error("KeyboardInterrupt") - self.bad_exit = True - self.stop() - return - - kwargs["bot"] = self - for callback in self.callbacks[cbid]: - try: - callback( - *args, **kwargs - ) # multi callback per id is supported (unlike cloudlink 0.1.7.3 LOL) - except Exception as e: # cq ignore - - self.logger.error(traceback.format_exc()) - self.run_cb("error", args=(e,)) - - def __handle_error__(self, e): - self.run_cb("error", args=(e,)) - if type(e) == WebSocketConnectionClosedException and self.autoreload: - self.__handle_close__() - return - - if (type(e)) == KeyboardInterrupt: - #kill all bot threads - self.bad_exit = True - - self.wss = None # effectively kill the bot - self.__handle_close__( ) - return - - def _debug_fix(self, packet): - packet = json.loads(packet) # Server bug workaround - - try: - self.__handle_packet__(packet) - except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP - self.__handle_error__(e) - self.logger.error(traceback.format_exc()) - self.run_cb("error", args=(e, )) - - try: - self.run_cb("__raw__", args=(packet, )) # raw packets - except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP - self.__handle_error__(e) - self.logger.error(traceback.format_exc()) - self.run_cb("error", args=(e, )) - - def __handle_on_connect__(self): - self.wss.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "type", "val": "py"}, - } - ) - self.wss.sendPacket( - { - "cmd": "direct", - "val": "meower", - "listener": "__meowerbot__cloudlink_trust", - } - ) - - def command(self, aname=None, args=0): - def inner(func): - if aname is None: - name = func.__name__ - else: - name = aname - - cmd = AppCommand(func, name=name, args=args) - - info = cmd.info() - info[cmd.name]["command"] = cmd - - self.commands.update(info) - - return cmd #allow subcommands without a cog - - return inner - - def register_cog(self, cog): - info = cog.get_info() - self.cogs[cog.__class__.__name__] = cog - self.commands.update(info) - - def deregister_cog(self, cogname): - for cmd in self.cogs[cogname].get_info().values(): - del self.commands[cmd.name] - del self.cogs[cogname] - - def _handle_status(self, status, listener): - if status == "I:112 | Trusted Access enabled": - return - - if self.logger_in: - self.logger_in = False - - if status != "I:100 | OK": - raise RuntimeError("CloudLink Trust Failed") - - auth_packet = { - "cmd": "direct", - "val": { - "cmd": "authpswd", - "val": {"username": self.username, "pswd": self._password}, - }, - "listener": "__meowerbot__login", - } - self.wss.sendPacket(auth_packet) - - elif listener == "__meowerbot__login": - if status == "E:104 | Internal": - requests.post( - "https://webhooks.meower.org/post/home", - json={ - "post": "ERROR: MeowerBot.py Webhooks Logging\n\n Account Softlocked.", - "username": self.username, - }, - timeout=5 - ) - print("CRITICAL ERROR! ACCOUNT SOFTLOCKED!!!!.", file=sys.__stdout__) - self.bad_exit = True - del self.wss - - return - - if status != "I:100 | OK": - raise RuntimeError("Password Or Username Is Incorrect") - - time.sleep(0.5) - self.run_cb("login", args=(), kwargs={}) - - elif listener == "__meowerbot__send_message": - if status == "I:100 | OK": - self.autoreload_time = self.autoreload_original - return - - raise RuntimeError("Post Failed to send") - - def callback(self, callback, cbid: Union[Union[cbids, None], str] =None): - """Connects a callback ID to a callback - """ - if cbid is None: - cbid = callback.__name__ - - if cbid not in self.callbacks: - self.callbacks[cbid] = [] - self.callbacks[cbid].append(callback) - - def __handle_close__(self, *args, **kwargs): - if self.autoreload: - self.autoreload = False #to stop race condisons - self.logger_in = True - self.autoreload_time *= 1.2 - - - time.sleep(self.autoreload_time) - self.autoreload = True #reset this, as i set it to false above. - - self.wss.state = 0 #type: ignore - self.wss.client(self.server) #type: ignore - return #dont want the close callback to be called here - - self.run_cb("close", args=args, kwargs=kwargs) - - def handle_bridges(self, packet): - if packet["val"]["u"] in self.__bridges__ and ": " in packet["val"]["p"]: - split = packet["val"]["p"].split(": ", 1) - packet["val"]["p"] = split[1] - packet["val"]["u"] = split[0] - - if packet["val"]["p"].startswith(self.prefix+"#0000"): - packet["val"]["p"] = packet["val"]["p"].replace("#0000", "") - - return packet - - def __handle_packet__(self, packet): - if packet["cmd"] == "statuscode": - - self._handle_status(packet["val"], packet.get("listener", None)) - - listener = packet.get("listener", None) - return self.run_cb("statuscode", args=(packet["val"], listener)) - - elif packet["cmd"] == "ulist": - self.run_cb("ulist", self.wss.statedata["ulist"]["usernames"]) - - elif packet["cmd"] == "direct" and "post_origin" in packet["val"]: - packet = self.handle_bridges(packet) - - ctx = CTX(packet["val"], self) - if "message" in self.callbacks: - self.run_cb("message", args=(ctx.message,)) - - else: - - if ctx.user.username == self.username: - return - if not ctx.message.data.startswith(self.prefix): - return - - ctx.message.data = ctx.message.data.split(self.prefix, 1)[1] - - self.run_command(ctx.message) - - self.run_cb("raw_message", args=(packet["val"],)) - - elif packet["cmd"] == "direct": - listener = packet.get("listener") - - if listener == "mb_get_chat_list": - self.run_cb("chat_list", args=(packet["val"]["payload"], listener)) - elif listener == "__meowerbot__login": - self.api.login(packet['val']['payload']['token']) - self.run_cb("direct", args=(packet["val"], listener)) - - - - else: - listener = packet.get("listener") - self.run_cb(packet["cmd"], args=(packet["val"], listener)) - - - if (packet["cmd"] == "pmsg") and (packet["val"] not in self.BOT_NO_PMSG_RESPONSE): - self.wss.sendPacket({ - "cmd": "pmsg", - "val": "I:500 | Bot", - "id": packet["origin"] - }) - - def run_command(self, message): - args = shlex.split(str(message)) - - try: - self.commands[args[0]]["command"].run_cmd(message.ctx, *args[1:]) - except KeyError as e: - self.logger.error(traceback.format_exc()) - self.run_cb("error", args=(e,)) - - def send_msg(self, msg, to="home"): - self._last_to = to - self._last_sent = msg - try: - if to == "home": - self.wss.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "post_home", "val": msg}, - "listener": "__meowerbot__send_message", - } - ) - else: - self.wss.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "post_chat", "val": {"chatid": to, "p": msg}}, - "listener": "__meowerbot__send_message", - } - ) - #socket is closed, use webhooks - except WebSocketException as e: - self.run_cb(cbid="error", args=(e,)) - - def send_typing(self, to="home"): - if to == "home": - self.wss.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": "livechat", - "state": 101, - }, - }, - } - ) - else: - self.wss.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": to, - "state": 100, - }, - }, - } - ) - - def enter_chat(self, chatid="livechat"): - self.wss.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": chatid, - "state": 1, - }, - }, - } - ) - - def create_chat(self, name): - """ - Unstable, use at your own risk - - comes with callbacks: chat_list - """ - self.wss.sendPacket({ - "cmd": "direct", - "val": { - "cmd": "create_chat", - "val": name - }, - "listener": "mb_create_chat" - }) - - time.sleep(secs=0.5) - - self.wss.sendPacket({ - "cmd": "direct", - "val": { - "cmd": "get_chat_list", - "val": { - "page": 1 - } - }, - "listener": "mb_get_chat_list" - }) - - def run(self, username, password, server="wss://server.meower.org"): - """ - Runs The bot (Blocking) - """ - self.username = username - self._password = password - self.logger_in = True - - self._t_ping_thread.start() - if self.prefix is None: - self.prefix = "@" + self.username - self.logger = logging.getLogger(f"MeowerBot {self.username}") - self.server = server - self.api = MeowerAPI(username=username) - self.wss.client(server) - - if self.bad_exit: - raise BaseException("Bot Account Softlocked") + + + + BOT_NO_PMSG_RESPONSE = [ + "I:500 | Bot", + "I: 500 | Bot" + ] + ulist = None + + @property + def latency(self): + return self.ws.latency + + async def _t_ping(self): + while True: + await asyncio.sleep(30) + + await self.sendPacket({"cmd": "ping", "val": ""}) + + def __init__(self, prefix=None ): #type: ignore + super().__init__() + self.callbacks = {} + self._last_to = "Home" + self.ulist = [] + + # to be used in start + self.username = None + self.password = None + self.logger_in = False + + self.commands = {} + self.prefix = prefix + self.logger = logging.getLogger("MeowerBot") + self.bad_exit = False + self.server = None + + self.cogs = {} + + async def run_cb(self, cbid, args=(), kwargs=None): # cq: ignore + if cbid not in self.callbacks: + return # ignore + + if not kwargs: + kwargs = {} + + if cbid == "error" and isinstance(args[0], KeyboardInterrupt()): + self.logger.error("KeyboardInterrupt") + self.bad_exit = True + self.stop() + return + + kwargs["bot"] = self + await asyncio.gather(*[cb(*args, **kwargs) for cb in self.callbacks[cbid]]) + + async def _error(self, e): + await self.run_cb("error", args=(e,)) + + if (type(e)) == KeyboardInterrupt: + self.bad_exit = True + + self.wss = None # effectively kill the bot + + async def _message(self, packet): + + try: + await self.__handle_packet__(packet) + except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP + self.logger.error(traceback.format_exc()) + await self.run_cb("error", args=(e, )) + + + try: + await self.run_cb("__raw__", args=(packet, )) # raw packets + except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP + self.logger.error(traceback.format_exc()) + await self.run_cb("error", args=(e, )) + + async def _connect(self): + await self.sendPacket( + { + "cmd": "direct", + "val": {"cmd": "type", "val": "py"}, + } + ) + await self.sendPacket( + { + "cmd": "direct", + "val": "meower", + "listener": "__meowerbot__cloudlink_trust", + } + ) + + def command(self, aname=None, args=0): + def inner(func): + if aname is None: + name = func.__name__ + else: + name = aname + + cmd = AppCommand(func, name=name, args=args) + + info = cmd.info() + info[cmd.name]["command"] = cmd + + self.commands.update(info) + + return cmd #allow subcommands without a cog + + return inner + + def register_cog(self, cog): + info = cog.get_info() + self.cogs[cog.__class__.__name__] = cog + self.commands.update(info) + + def deregister_cog(self, cogname): + for cmd in self.cogs[cogname].get_info().values(): + del self.commands[cmd.name] + del self.cogs[cogname] + + async def _handle_status(self, status, listener): + if status == "I:112 | Trusted Access enabled": + return + + if self.logger_in: + self.logger_in = False + + if status != "I:100 | OK": + raise RuntimeError("CloudLink Trust Failed") + + auth_packet = { + "cmd": "direct", + "val": { + "cmd": "authpswd", + "val": {"username": self.username, "pswd": self._password}, + }, + "listener": "__meowerbot__login", + } + await self.sendPacket(auth_packet) + + elif listener == "__meowerbot__login": + if status == "E:104 | Internal": + requests.post( + "https://webhooks.meower.org/post/home", + json={ + "post": "ERROR: MeowerBot.py Webhooks Logging\n\n Account Softlocked.", + "username": self.username, + }, + timeout=5 + ) + print("CRITICAL ERROR! ACCOUNT SOFTLOCKED!!!!.", file=sys.__stdout__) + self.bad_exit = True + return + + if status != "I:100 | OK": + raise RuntimeError("Password Or Username Is Incorrect") + + time.sleep(0.5) + await self.run_cb("login", args=(), kwargs={}) + + elif listener == "__meowerbot__send_message": + if status == "I:100 | OK": + return + + raise RuntimeError("Post Failed to send") + + def callback(self, callback, cbid: Union[Union[cbids, None], str] =None): + """Connects a callback ID to a callback + """ + if cbid is None: + cbid = callback.__name__ + + if cbid not in self.callbacks: + self.callbacks[cbid] = [] + self.callbacks[cbid].append(callback) + + async def _close(self, *args, **kwargs): + await self.run_cb("close", args=args, kwargs=kwargs) + + def handle_bridges(self, packet): + if packet["val"]["u"] in self.__bridges__ and ": " in packet["val"]["p"]: + split = packet["val"]["p"].split(": ", 1) + packet["val"]["p"] = split[1] + packet["val"]["u"] = split[0] + + if packet["val"]["p"].startswith(self.prefix+"#0000"): + packet["val"]["p"] = packet["val"]["p"].replace("#0000", "") + + return packet + + async def __handle_packet__(self, packet): + if packet["cmd"] == "statuscode": + + await self._handle_status(packet["val"], packet.get("listener", None)) + + listener = packet.get("listener", None) + return await self.run_cb("statuscode", args=(packet["val"], listener)) + + elif packet["cmd"] == "ulist": + self.ulist = packet["val"].split(";") + await self.run_cb("ulist", self.ulist) + + elif packet["cmd"] == "direct" and "post_origin" in packet["val"]: + packet = self.handle_bridges(packet) + + ctx = CTX(packet["val"], self) + if "message" in self.callbacks: + await self.run_cb("message", args=(ctx.message,)) + + else: + + if ctx.user.username == self.username: + return + if not ctx.message.data.startswith(self.prefix): + return + + ctx.message.data = ctx.message.data.split(self.prefix, 1)[1] + + await self.run_command(ctx.message) + + await self.run_cb("raw_message", args=(packet["val"],)) + + elif packet["cmd"] == "direct": + listener = packet.get("listener") + + if listener == "mb_get_chat_list": + await self.run_cb("chat_list", args=(packet["val"]["payload"], listener)) + elif listener == "__meowerbot__login": + self.api.login(packet['val']['payload']['token']) + await self.run_cb("direct", args=(packet["val"], listener)) + + + + else: + listener = packet.get("listener") + await self.run_cb(packet["cmd"], args=(packet["val"], listener)) + + + if (packet["cmd"] == "pmsg") and (packet["val"] not in self.BOT_NO_PMSG_RESPONSE): + await self.sendPacket({ + "cmd": "pmsg", + "val": "I:500 | Bot", + "id": packet["origin"] + }) + + async def run_command(self, message): + args = shlex.split(str(message)) + + try: + await self.commands[args[0]]["command"].run_cmd(message.ctx, *args[1:]) + except KeyError as e: + self.logger.error(traceback.format_exc()) + await self.run_cb("error", args=(e,)) + + async def send_msg(self, msg, to="home"): + self._last_to = to + self._last_sent = msg + + if to == "home": + await self.sendPacket( + { + "cmd": "direct", + "val": {"cmd": "post_home", "val": msg}, + "listener": "__meowerbot__send_message", + } + ) + else: + await self.sendPacket( + { + "cmd": "direct", + "val": {"cmd": "post_chat", "val": {"chatid": to, "p": msg}}, + "listener": "__meowerbot__send_message", + } + ) + + + async def send_typing(self, to="home"): + if to == "home": + await self.sendPacket( + { + "cmd": "direct", + "val": { + "cmd": "set_chat_state", + "val": { + "chatid": "livechat", + "state": 101, + }, + }, + } + ) + else: + await self.sendPacket( + { + "cmd": "direct", + "val": { + "cmd": "set_chat_state", + "val": { + "chatid": to, + "state": 100, + }, + }, + } + ) + + async def enter_chat(self, chatid="livechat"): + await self.sendPacket( + { + "cmd": "direct", + "val": { + "cmd": "set_chat_state", + "val": { + "chatid": chatid, + "state": 1, + }, + }, + } + ) + + async def create_chat(self, name): + """ + Unstable, use at your own risk + + comes with callbacks: chat_list + """ + await self.sendPacket({ + "cmd": "direct", + "val": { + "cmd": "create_chat", + "val": name + }, + "listener": "mb_create_chat" + }) + + time.sleep(secs=0.5) + + await self.sendPacket({ + "cmd": "direct", + "val": { + "cmd": "get_chat_list", + "val": { + "page": 1 + } + }, + "listener": "mb_get_chat_list" + }) + + async def run(self, username, password, server="wss://server.meower.org"): + """ + Runs The bot (Blocking) + """ + self.username = username + self._password = password + self.logger_in = True + + asyncio.create_task(self._t_ping()) + if self.prefix is None: + self.prefix = "@" + self.username + self.logger = logging.getLogger(f"MeowerBot {self.username}") + self.server = server + try: + self.api = MeowerAPI(username=username) + except: # nosec + pass + await self.connect(server) + + if self.bad_exit: + raise BaseException("Bot Account Softlocked") diff --git a/MeowerBot/__init__.py b/MeowerBot/__init__.py index 7ff7fec..8fd3162 100644 --- a/MeowerBot/__init__.py +++ b/MeowerBot/__init__.py @@ -12,5 +12,6 @@ from . import Bot as botm from .Bot import Bot +from .Bot import cbids __all__ = ["__version__", "Bot", "botm"] diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index 13cb868..ab96340 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -1 +1,119 @@ -from .cloudlink import * +import websockets +import orjson +import json + +import asyncio + +class Client: + __waiting = {} + __packets = [] + ws: websockets.WebSocketClientProtocol + def __init__(self): + self.ws = None + pass + + async def _connect(self): + pass + + async def _disconnect(self): + pass + + async def _message(self, message): + pass + + async def _error(self, error): + pass + + async def sendPacket(self, message): + await self.ws.send(json.dumps(message)) + + async def sendPacketAndWait(self, message): + await self.ws.send(json.dumps(message)) + + self.__waiting[message["listener"]] = asyncio.Event() + + got_response = False + statuscode = "" + waited = None + + + + async def predicate(): + + + nonlocal got_response + nonlocal statuscode + + #get the last added packet + packet = self.__packets[-1] + + if message["listener"] != packet["listener"]: + return False + + if packet["cmd"] == "statuscode": + statuscode = packet["val"] + + else: + got_response = True + nonlocal waited + waited = waitedPacked.WaitedPacked(statuscode == "I: 100 | OK", packet, packet["listener"]) + + + + + return message["listener"] in self.__waiting and got_response + + await self.__waiting[message["listener"]].wait_for(predicate) + + return waited + + + + + async def close(self, reason=None): + await self.ws.close(reason=reason) + + + + async def connect(self, server): + async for websocket in websockets.connect(server): + try: + + self.ws = websocket + await self._connect() + + async for message in websocket: + try: + + packet = json.loads(message) + + for listener in self.__waiting: + if listener == packet["listener"]: + self.__waiting[listener].notify_all() + + + await self._message(json.loads(message)) + + #cut packet queue to 50 + if len(self.__packets) > 50: + self.__packets = self.__packets[-50:] + + + except websockets.ConnectionClosed: + raise + + except Exception as e: + + await self._error(e) + pass + + except websockets.ConnectionClosed: + await self._disconnect() + pass + + except Exception as e: + await self._error(e) + pass + + + await self._disconnect() diff --git a/MeowerBot/cl/cloudlink.py b/MeowerBot/cl/cloudlink.py deleted file mode 100644 index 8209213..0000000 --- a/MeowerBot/cl/cloudlink.py +++ /dev/null @@ -1,237 +0,0 @@ -# type: ignore - -#!/usr/bin/env python3 - - - -version = "0.1.7.4" - -# Server based on https://github.com/Pithikos/python-websocket-server -# Client based on https://github.com/websocket-client/websocket-client - -""" -CloudLink by MikeDEV, ShowierData9978 -Please see https://github.com/MikeDev101/cloudlink for more details. -0BSD License -Copyright (C) 2020-2022 MikeDEV Software, Co. -Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. -THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Modified for use in MeowerBot.py by ShowierData9978 - -- stripped everything server related -- made lib use logging insted of print -""" - -import json -import threading -import websocket as ws_client # type: ignore -import logging -import logging as log - -""" -Code formatting -(Type):(Code) | (Description) -Type: Letter - I - Info - E - Error -Code: Number, defines the code -Description: String, Describes the code -""" - - -class API: - def __init__(self, logging=None): - self.logging = logging or log.getLogger("CloudLink") - self.logging.debug("CloudLink API initialized") - - self.callback_function = { - "error": self._on_error_client, # type: ignore - "connection": self._on_connection_client, # type: ignore - "closed_connection": self._closed_connection_client, # type: ignore - "packet": self._on_packet_client, # type: ignore - } - - self.statedata = {} - - def client(self, ip="ws://127.0.0.1:3000/"): # Runs CloudLink in client mode. - try: - - # Change the link state to 2 (Client mode) - self.wss = ws_client.WebSocketApp( - ip, - on_message=self._on_packet_client, # type: ignore - on_error=self._on_error_client, # type: ignore - on_open=self._on_connection_client, # type: ignore - on_close=self._closed_connection_client, # type: ignore - ) - - # Format dict for storing this mode's specific data - self.statedata = { - "ulist": {"usernames": []}, - } - - # Run the client - self.wss.run_forever() - except BaseException as e: - self.logging.error(f"Error at client: {e}") - self._on_error_client(self.wss, e) - - def stop(self, abrupt=False): # Stops CloudLink (not sure if working) - self.wss.close() - - def callback( - self, callback_id, function - ): # Add user-friendly callbacks for CloudLink to be useful as a module - try: - if callback_id in self.callback_function: - self.callback_function[callback_id] = function - - self.logging.debug(f"Binded callback {callback_id}.") - else: - self.logging.error( - f"Error: Callback {callback_id} is not a valid callback id!" - ) - except BaseException as e: - self.logging.error(f"Error at callback: {e}") - self._on_error_client(self.wss, e) - - def sendPacket( - self, msg - ): # User-friendly message sender for both server and client. - try: - - self.logging.debug(f"Sending {json.dumps(msg)}") - self.wss.send(json.dumps(msg)) - except BaseException as e: - self.logging.error(f"Error on sendPacket (client): {e}") - self._on_error_client(self.wss, e) - - def getUsernames(self): # Returns the username list. - return self.statedata["ulist"]["usernames"] - - -class CloudLink(API): - def __init__(self): # Initializes CloudLink - self.wss = None # Websocket Object - self.userlist = [] # Stores usernames set on link - self.callback_function = { # For linking external code, use with functions - "on_connect": None, # Handles new connections (server) or when connected to a server (client) - "on_error": None, # Error reporter - "on_packet": None, # Packet handler - "on_close": None, # Runs code when disconnected (client) or server stops (server) - } - - self.statedata = {} # Place to store other garbage for modes - self.logging = logging.getLogger("Cloudlink") - - self.logging.info(f"CloudLink v{str(version)}") # Report version number - - @staticmethod - def _is_json(data): # Checks if something is JSON - if type(data) is dict: - return True - try: - tmp = json.loads(data) - return True - except Exception as e: - return False - - def _on_connection_client(self, ws): # Client-side connection handler - try: - - self.logging.info("Connected") - self.wss.send( - json.dumps({"cmd": "direct", "val": {"cmd": "type", "val": "py"}}) - ) # Specify to the server that the client is based on Python - if not self.callback_function["on_connect"] is None: - - def run(*args): - try: - self.callback_function["on_connect"]() - except BaseException as e: - self.logging.error(f"Error on _on_connection_client: {e}") - self._on_error_client(self.wss, error) - - threading.Thread(target=run).start() - except BaseException as e: - self.logging.info(f"Error on _on_connection_client: {e}") - self._on_error_client(self.wss, e) - - - def _on_packet_client(self, ws, message): # Client-side packet handler - try: - - self.logging.debug(f"New packet: {message}") - - tmp = json.loads(message) - if (("cmd" in tmp) and (tmp["cmd"] == "ulist")) and ("val" in tmp): - self.statedata["ulist"]["usernames"] = str(tmp["val"]).split(";") - del self.statedata["ulist"]["usernames"][ - len(self.statedata["ulist"]["usernames"]) - 1 - ] - - self.logging.info( - f"Username list: {str(self.statedata['ulist']['usernames'])}" - ) - - if not self.callback_function["on_packet"] == None: - - def run(*args): - try: - self.callback_function["on_packet"](message) - except BaseException as e: - - self.logging.error(f"Error on _on_packet_client: {e}") - self._on_error_client(self.wss, e) - - threading.Thread(target=run).start() - except BaseException as e: - self.logging.error(f"Error on _on_packet_client: {e}") - self._on_error_client(self.wss, e) - - def _on_error_client(self, ws, error): # Client-side error handler - try: - - self.logging.error(f"Error: {error}") - if not self.callback_function["on_error"] is None: - - def run(*args): - try: - self.callback_function["on_error"](error) - except BaseException as e: - self.logging.error(f"Error on _on_error_client: {e}") - - self._on_error_client(self.wss, e) - - threading.Thread(target=run).start() - except BaseException as e: - - self.logging.error(f"Error on _on_error_client: {e}") - self._on_error_client(self.wss, e) - - def _closed_connection_client( - self, ws, close_status_code, close_msg - ): # Client-side closed connection handler - try: - - self.logging.info( - f"Closed, status: {close_status_code} with code {close_msg}" - ) - if not self.callback_function["on_close"] is None: - - def run(*args): - try: - self.callback_function["on_close"]() - except BaseException as e: - - self.logging.error(f"Error on _closed_connection_client: {e}") - self._on_error_client(self.wss, e) - - threading.Thread(target=run).start() - except BaseException as e: - self.logging.error(f"Error on _closed_connection_client: {e}") - self._on_error_client(self.wss, e) diff --git a/MeowerBot/cl/waitedPacked.py b/MeowerBot/cl/waitedPacked.py new file mode 100644 index 0000000..dcd39f2 --- /dev/null +++ b/MeowerBot/cl/waitedPacked.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass + +@dataclass +class WaitedPacked: + ok: bool + packet: dict + listener: str + + \ No newline at end of file diff --git a/MeowerBot/command.py b/MeowerBot/command.py index 8c96c55..e77bdb0 100644 --- a/MeowerBot/command.py +++ b/MeowerBot/command.py @@ -7,118 +7,120 @@ class AppCommand: - connected = None + connected = None - def __init__(self, func, name=None, args=0, is_subcommand=False): - if name is None: - name = func.__name__ + def __init__(self, func, name=None, args=0, is_subcommand=False): + if name is None: + name = func.__name__ + + self.name = name + self.func = func + self.args_num = args - self.name = name - self.func = func - self.args_num = args + spec = inspect.signature(func) - spec = inspect.signature(func) + # Get the names and annotations of the arguments + self.args = [ + (param.name, param.annotation) + for param in spec.parameters.values() + if param.name not in ["self", "ctx"] + ] - # Get the names and annotations of the arguments - self.args = [ - (param.name, param.annotation) - for param in spec.parameters.values() - if param.name not in ["self", "ctx"] - ] + # Check if the function has an arbitrary number of positional arguments + self.has_unamed_args = any( + param.kind == inspect.Parameter.VAR_POSITIONAL for param in spec.parameters.values() + ) - # Check if the function has an arbitrary number of positional arguments - self.has_unamed_args = any( - param.kind == inspect.Parameter.VAR_POSITIONAL for param in spec.parameters.values() - ) + self.is_subcommand = is_subcommand - self.is_subcommand = is_subcommand + # Get the names, annotations, and default values of the keyword-only arguments + self.optional_args = [ + (param.name, param.annotation, param.default) + for param in spec.parameters.values() + if param.kind == inspect.Parameter.KEYWORD_ONLY + ] - # Get the names, annotations, and default values of the keyword-only arguments - self.optional_args = [ - (param.name, param.annotation, param.default) - for param in spec.parameters.values() - if param.kind == inspect.Parameter.KEYWORD_ONLY - ] + # Set the namespace based on whether the command is a subcommand or not + self.namespace = self.is_subcommand if type(self.is_subcommand) is str else self.name - # Set the namespace based on whether the command is a subcommand or not - self.namespace = self.is_subcommand if type(self.is_subcommand) is str else self.name + self.subcommands = {} - self.subcommands = {} + def __call__(self, *args): + raise RuntimeError("AppCommand is not callable") - def __call__(self, *args): - raise RuntimeError("AppCommand is not callable") + def register_class(self, con): + self.connected = con - def register_class(self, con): - self.connected = con + for subcommand in self.subcommands.values(): + subcommand.register_class(con) - for subcommand in self.subcommands.values(): - subcommand.register_class(con) + def subcommand(self, name=None, args=0): + def inner(func): - def subcommand(self, name=None, args=0): - def inner(func): + cmd = AppCommand(func, name=name, args=args) + cmd.register_class(self.connected) - cmd = AppCommand(func, name=name, args=args) - cmd.register_class(self.connected) + self.subcommands.update(cmd.info()) - self.subcommands.update(cmd.info()) + return func #dont want mb to register this as a root command + return inner + - return func #dont want mb to register this as a root command - return inner - + async def run_cmd(self, ctx, *args): + + try: + await self.subcommands[args[0]]["command"].run_cmd(ctx, *args[1:]) + return + except KeyError: + #print(f"KeyError: {args}") + #print(f"Subcommands: {self.subcommands}") + logger.debug(f"Cant find subcommand {args[0]}") + except IndexError: + pass - def run_cmd(self, ctx, *args): - - try: - self.subcommands[args[0]]["command"].run_cmd(ctx, *args[1:]) - return - except KeyError: - #print(f"KeyError: {args}") - #print(f"Subcommands: {self.subcommands}") - logger.debug(f"Cant find subcommand {args[0]}") + except IndexError: + logger.debug(traceback.format_exc()) + + if not self.args_num == 0: + args = args[:self.args_num] - except IndexError: - logger.debug(traceback.format_exc()) - - if not self.args_num == 0: - args = args[:self.args_num] + if self.connected is None: + await self.func(ctx, *args) + else: + await self.func(self.connected, ctx, *args) - if self.connected is None: - self.func(ctx, *args) - else: - self.func(self.connected, ctx, *args) + def info(self): + return { + self.name: { + "args": self.args, + "args_num": self.args_num, + "optional_args": self.optional_args, + "has_unamed_args": self.has_unamed_args, + "subcommands": self.subcommands, + "command": self, + "func": self.func, - def info(self): - return { - self.name: { - "args": self.args, - "args_num": self.args_num, - "optional_args": self.optional_args, - "has_unamed_args": self.has_unamed_args, - "subcommands": self.subcommands, - "command": self, - "func": self.func, - - } - } + } + } class _Command(AppCommand): - def __init__(self, func, *args, name=None, **kwargs): - super().__init__(func, *args, name=name, **kwargs) - warnings.warn( - "MeowerBot.command._Command has been renamed to MeowerBot.command.AppCommand" - ) + def __init__(self, func, *args, name=None, **kwargs): + super().__init__(func, *args, name=name, **kwargs) + warnings.warn( + "MeowerBot.command._Command has been renamed to MeowerBot.command.AppCommand" + ) def command(name=None, args=0): - def inner(func): + def inner(func): - cmd = AppCommand(func, name=name, args=args) + cmd = AppCommand(func, name=name, args=args) - return cmd + return cmd - return inner + return inner diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 3695cb6..89eeb10 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -23,8 +23,8 @@ def __init__(self, bot, username): - def ping(self, msg, to="home"): - self.bot.send_msg(f"@{self.username} {msg}", to=to) + async def ping(self, msg, to="home"): + await self.bot.send_msg(f"@{self.username} {msg}", to=to) def __str__(self): return str(self.__dict__) @@ -54,8 +54,8 @@ def __init__(self, post, bot): self.bot = bot self.message.ctx = weakref.proxy(self) # type: ignore - def send_msg(self, msg): - self.bot.send_msg(msg, to=self.message.chat) + async def send_msg(self, msg): + await self.bot.send_msg(msg, to=self.message.chat) - def reply(self, msg): - self.user.ping(msg, to=self.message.chat) + async def reply(self, msg): + await self.user.ping(msg, to=self.message.chat) diff --git a/MeowerBot/ext/discord.py b/MeowerBot/ext/discord.py index 1415d84..b6d428e 100644 --- a/MeowerBot/ext/discord.py +++ b/MeowerBot/ext/discord.py @@ -1,25 +1,22 @@ from MeowerBot import Bot - - - class DiscBotMeower(Bot): - def __init__(self, prefix=None, autoreload: int or None = None): - super().__init__(prefix, autoreload) - - self.callback(self.on_message, "message") - self.callback(self.on_login, "login") - self.callback(self.on_disconnect, "close") - self.callback(self.on_error, "error") - self.callback(self.on_raw_message, "raw_message") - self.callback(self.on_pmsg, "pmsg") - self.callback(self.on_ulist, "ulist") - self.callback(self.on_statuscode, "statuscode") - self.callback(self.on_chatlist, "chatlist") - self.callback(self.on_raw_packet, "__raw__") - - def on_message(self, msg): + def __init__(self, prefix=None, autoreload: int or None = None): + super().__init__(prefix, autoreload) + + self.callback(self.on_message, "message") + self.callback(self.on_login, "login") + self.callback(self.on_disconnect, "close") + self.callback(self.on_error, "error") + self.callback(self.on_raw_message, "raw_message") + self.callback(self.on_pmsg, "pmsg") + self.callback(self.on_ulist, "ulist") + self.callback(self.on_statuscode, "statuscode") + self.callback(self.on_chatlist, "chatlist") + self.callback(self.on_raw_packet, "__raw__") + + async def on_message(self, msg): if ctx.user.username == self.username: return if not ctx.message.data.startswith(self.prefix): @@ -27,47 +24,47 @@ def on_message(self, msg): ctx.message.data = ctx.message.data.split(self.prefix, 1)[1] - self.run_command(ctx.message) + await self.run_command(ctx.message) + + async def on_login(self): + pass - def on_login(self): - pass + async def on_disconnect(self): + pass - def on_disconnect(self): - pass + async def on_error(self, err): + pass - def on_error(self, err): - pass + async def on_raw_message(self, msg): + pass - def on_raw_message(self, msg): - pass + async def on_pmsg(self, msg): + pass - def on_pmsg(self, msg): - pass + async def on_ulist(self, ulist): + pass - def on_ulist(self, ulist): - pass + async def on_statuscode(self, status, listener): + pass - def on_statuscode(self, status, listener): - pass + async def on_chatlist(self, chatlist, listener): + pass - def on_chatlist(self, chatlist, listener): - pass + async def on_raw_packet(self, packet): + pass - def on_raw_packet(self, packet): - pass + def watch(self, event=None): + def decorator(func): + if event is None: + event = func.__name__ + self.callback(func, event) + return func + return decorator - def watch(self, event=None): - def decorator(func): - if event is None: - event = func.__name__ - self.callback(func, event) - return func - return decorator - - def event(self, event=None): - def decorator(func): - if event is None: - event = func.__name__ - setattr(self, event, func) - return func - return decorator \ No newline at end of file + def event(self, event=None): + def decorator(func): + if event is None: + event = func.__name__ + setattr(self, event, func) + return func + return decorator diff --git a/MeowerBot/ext/help.py b/MeowerBot/ext/help.py index 0f328af..d110561 100644 --- a/MeowerBot/ext/help.py +++ b/MeowerBot/ext/help.py @@ -72,7 +72,7 @@ def handle_command(self, name, cmd: AppCommand): if cmd.func.__doc__ is not None: self.page += f"\n\t{cmd.func.__doc__}" - + self.page += "\n" @@ -85,15 +85,15 @@ def handle_command(self, name, cmd: AppCommand): self.page += "\n" @command(name="help") - def help(self, ctx, page: int=0): + async def help(self, ctx, page: int=0): if page >= len(self.pages): page = len(self.pages) - 1 - ctx.send_msg(self.pages[page]) + await ctx.send_msg(self.pages[page]) - def _login(self, bot): + async def _login(self, bot): self.generate_help() #generate help on login, bugfix for default prefix and people being dumb \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index d3d277e..9da961d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "backports-strenum" version = "1.2.4" description = "Base class for creating enumerated constants that are also subclasses of str" +category = "main" optional = false python-versions = ">=3.8, <4" files = [ @@ -14,6 +15,7 @@ files = [ name = "bandit" version = "1.7.5" description = "Security oriented static analyser for python code." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -37,6 +39,7 @@ yaml = ["PyYAML"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -46,92 +49,94 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.1.0" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -143,6 +148,7 @@ files = [ name = "gitdb" version = "4.0.10" description = "Git Object Database" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -155,13 +161,14 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.31" +version = "3.1.32" description = "GitPython is a Python library used to interact with Git repositories" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, - {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, + {file = "GitPython-3.1.32-py3-none-any.whl", hash = "sha256:e3d59b1c2c6ebb9dfa7a184daf3b6dd4914237e7488a1730a6d8f6f5d0b4187f"}, + {file = "GitPython-3.1.32.tar.gz", hash = "sha256:8d9b8cb1e80b9735e8717c9362079d3ce4c6e5ddeebedd0361b228c3a67a62f6"}, ] [package.dependencies] @@ -171,6 +178,7 @@ gitdb = ">=4.0.1,<5" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -180,13 +188,14 @@ files = [ [[package]] name = "markdown-it-py" -version = "2.2.0" +version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] @@ -199,13 +208,14 @@ compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0 linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -213,10 +223,77 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "orjson" +version = "3.9.4" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "orjson-3.9.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2e83ec1ee66d83b558a6d273d8a01b86563daa60bea9bc040e2c1cb8008de61f"}, + {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32a9e0f140c7d0d52f79553cabd1a471f6a4f187c59742239939f1139258a053"}, + {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb429c56ea645e084e34976c2ea0efca7661ee961f61e51405f28bc5a9d1fb24"}, + {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fb7963c17ab347428412a0689f5c89ea480f5d5f7ba3e46c6c2f14f3159ee4"}, + {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224ad19dcdc21bb220d893807f2563e219319a8891ead3c54243b51a4882d767"}, + {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4974cc2ebb53196081fef96743c02c8b073242b20a40b65d2aa2365ba8c949df"}, + {file = "orjson-3.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b39747f8e57728b9d8c26bd1d28e9a31c028717617a5938a179244b9436c0b31"}, + {file = "orjson-3.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a31c2cab0ba86998205c2eba550c178a8b4ee7905cadeb402eed45392edb178"}, + {file = "orjson-3.9.4-cp310-none-win32.whl", hash = "sha256:04cd7f4a4f4cd2fe43d104eb70e7435c6fcbdde7aa0cde4230e444fbc66924d3"}, + {file = "orjson-3.9.4-cp310-none-win_amd64.whl", hash = "sha256:4fdb59cfa00e10c82e09d1c32a9ce08a38bd29496ba20a73cd7f498e3a0a5024"}, + {file = "orjson-3.9.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:daeed2502ddf1f2b29ec8da2fe2ea82807a5c4acf869608ce6c476db8171d070"}, + {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73d9507a547202f0dd0672e529ce3ca45582d152369c684a9ce75677ce5ae089"}, + {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144a3b8c7cbdd301e1b8cd7dd33e3cbfe7b011df2bebd45b84bacc8cb490302d"}, + {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef7119ebc9b76d5e37c330596616c697d1957779c916aec30cefd28df808f796"}, + {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b75f0fc7a64a95027c6f0c70f17969299bdf2b6a85e342b29fc23be2788bad6f"}, + {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e4b20164809b21966b63e063f894927bc85391e60d0a96fa0bb552090f1319c"}, + {file = "orjson-3.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e7c3b7e29572ef2d845a59853475f40fdabec53b8b7d6effda4bb26119c07f5"}, + {file = "orjson-3.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d73c0fd54a52a1a1abfad69d4f1dfb7048cd0b3ef1828ddb4920ef2d3739d8fb"}, + {file = "orjson-3.9.4-cp311-none-win32.whl", hash = "sha256:e12492ce65cb10f385e70a88badc6046bc720fa7d468db27b7429d85d41beaeb"}, + {file = "orjson-3.9.4-cp311-none-win_amd64.whl", hash = "sha256:3b9f8bf43a5367d5522f80e7d533c98d880868cd0b640b9088c9237306eca6e8"}, + {file = "orjson-3.9.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0b400cf89c15958cd829c8a4ade8f5dd73588e63d2fb71a00483e7a74e9f92da"}, + {file = "orjson-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d3b6f2706cb324661899901e6b1fcaee4f5aac7d7588306df3f43e68173840"}, + {file = "orjson-3.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3932b06abf49135c93816c74139c7937fa54079fce3f44db2d598859894c344a"}, + {file = "orjson-3.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:562cf24f9f11df8099e0e78859ba6729e7caa25c2f3947cb228d9152946c854b"}, + {file = "orjson-3.9.4-cp312-none-win_amd64.whl", hash = "sha256:a533e664a0e3904307d662c5d45775544dc2b38df6e39e213ff6a86ceaa3d53c"}, + {file = "orjson-3.9.4-cp37-cp37m-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:149d1b7630771222f73ecb024ab5dd8e7f41502402b02015494d429bacc4d5c1"}, + {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:004f0d307473af210717260dab2ddceab26750ef5d2c6b1f7454c33f7bb69f0c"}, + {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba21fe581a83555024f3cfc9182a2390a61bc50430364855022c518b8ba285a4"}, + {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1fb36efdf2a35286fb87cfaa195fc34621389da1c7b28a8eb51a4d212d60e56d"}, + {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644728d803200d7774164d252a247e2fcb0d19e4ef7a4a19a1a139ae472c551b"}, + {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bae10f4e7a9145b120e37b6456f1d3853a953e5131fe4740a764e46420289f5"}, + {file = "orjson-3.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c416c50f63bfcf453b6e28d1df956938486191fd1a15aeb95107e810e6e219c8"}, + {file = "orjson-3.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:220ca4125416636a3d6b53a77d50434987a83da243f9080ee4cce7ac6a34bb4a"}, + {file = "orjson-3.9.4-cp37-none-win32.whl", hash = "sha256:bcda6179eb863c295eb5ea832676d33ef12c04d227b4c98267876c8322e5a96e"}, + {file = "orjson-3.9.4-cp37-none-win_amd64.whl", hash = "sha256:3d947366127abef192419257eb7db7fcee0841ced2b49ccceba43b65e9ce5e3f"}, + {file = "orjson-3.9.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a7d029fc34a516f7eae29b778b30371fcb621134b2acfe4c51c785102aefc6cf"}, + {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c65df12f92e771361dca45765fcac3d97491799ee8ab3c6c5ecf0155a397a313"}, + {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b749d06a3d84ac27311cb85fb5e8f965efd1c5f27556ad8fcfd1853c323b4d54"}, + {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:161cc72dd3ff569fd67da4af3a23c0c837029085300f0cebc287586ae3b559e0"}, + {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:edcbccfe852d1d3d56cc8bfc5fa3688c866619328a73cb2394e79b29b4ab24d2"}, + {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0725260a12d7102b6e66f9925a027f55567255d8455f8288b02d5eedc8925c3e"}, + {file = "orjson-3.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53b417cc9465dbb42ec9cd7be744a921a0ce583556315d172a246d6e71aa043b"}, + {file = "orjson-3.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ab3720fba68cc1c0bad00803d2c5e2c70177da5af12c45e18cc4d14426d56d8"}, + {file = "orjson-3.9.4-cp38-none-win32.whl", hash = "sha256:94d15ee45c2aaed334688e511aa73b4681f7c08a0810884c6b3ae5824dea1222"}, + {file = "orjson-3.9.4-cp38-none-win_amd64.whl", hash = "sha256:336ec8471102851f0699198031924617b7a77baadea889df3ffda6000bd59f4c"}, + {file = "orjson-3.9.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2f57ccb50e9e123709e9f2d7b1a9e09e694e49d1fa5c5585e34b8e3f01929dc3"}, + {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e876ef36801b3d4d3a4b0613b6144b0b47f13f3043fd1fcdfafd783c174b538"}, + {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f009c1a02773bdecdd1157036918fef1da47f7193d4ad599c9edb1e1960a0491"}, + {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0a4cf31bfa94cd235aa50030bef3df529e4eb2893ea6a7771c0fb087e4e53b2"}, + {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c32dea3b27a97ac88783c1eb61ccb531865bf478a37df3707cbc96ca8f34a04"}, + {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:264637cad35a1755ab90a8ea290076d444deda20753e55a0eb75496a4645f7bc"}, + {file = "orjson-3.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a4f12e9ec62679c3f2717d9ec41b497a2c2af0b1361229db0dc86ef078a4c034"}, + {file = "orjson-3.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c4fcd1ac0b7850f85398fd9fdbc7150ac4e82d2ae6754cc6acaf49ca7c30d79a"}, + {file = "orjson-3.9.4-cp39-none-win32.whl", hash = "sha256:b5b5038187b74e2d33e5caee8a7e83ddeb6a21da86837fa2aac95c69aeb366e6"}, + {file = "orjson-3.9.4-cp39-none-win_amd64.whl", hash = "sha256:915da36bc93ef0c659fa50fe7939d4f208804ad252fc4fc8d55adbbb82293c48"}, + {file = "orjson-3.9.4.tar.gz", hash = "sha256:a4c9254d21fc44526a3850355b89afd0d00ed73bdf902a5ab416df14a61eac6b"}, +] + [[package]] name = "pbr" version = "5.11.1" description = "Python Build Reasonableness" +category = "dev" optional = false python-versions = ">=2.6" files = [ @@ -226,13 +303,14 @@ files = [ [[package]] name = "pygments" -version = "2.15.1" +version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] @@ -240,57 +318,59 @@ plugins = ["importlib-metadata"] [[package]] name = "pyyaml" -version = "6.0" +version = "6.0.1" description = "YAML parser and emitter for Python" +category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -310,17 +390,18 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.3.5" +version = "13.5.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.3.5-py3-none-any.whl", hash = "sha256:69cdf53799e63f38b95b9bf9c875f8c90e78dd62b2f00c13a911c7a3b9fa4704"}, - {file = "rich-13.3.5.tar.gz", hash = "sha256:2d11b9b8dd03868f09b4fffadc84a6a8cda574e40dc90821bd845720ebb8e89c"}, + {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, + {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, ] [package.dependencies] -markdown-it-py = ">=2.2.0,<3.0.0" +markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} @@ -331,6 +412,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "smmap" version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -342,6 +424,7 @@ files = [ name = "stevedore" version = "5.1.0" description = "Manage dynamic plugins for Python applications" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -354,24 +437,26 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "typing-extensions" -version = "4.6.2" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, - {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" -version = "2.0.2" +version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, - {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, + {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, + {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, ] [package.extras] @@ -381,22 +466,86 @@ socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] -name = "websocket-client" -version = "1.5.2" -description = "WebSocket client for Python with low level API options" +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"}, - {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, ] -[package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "ca2f8fcd8eae8b8c7c027d852f4c90c593d892f955660dd939809ea655623a79" +content-hash = "f7f02ca6f524460ae83bd99aaf436382f29ed4483b8f85dbdbe9f4e778294efa" diff --git a/pyproject.toml b/pyproject.toml index 39f6630..020a5d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "MeowerBot" -version = "2.6.3" +version = "3.0-BETA" description = "A meower bot lib for py" authors = ["showierdata9978 <68120127+showierdata9978@users.noreply.github.com>"] license = "MIT" @@ -13,9 +13,10 @@ repository = "https://github.com/MeowerBots/MeowerBot.py" [tool.poetry.dependencies] python = "^3.8" -requests = "^2.28.0" -websocket-client = "*" backports-strenum = { version = "^1.2.4", python = "<3.11" } +websockets = "^11.0.3" +orjson = "^3.9.4" +requests = "^2.31.0" [tool.poetry.group.extras.dependencies] diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/integration_login.py b/tests/integration_login.py new file mode 100644 index 0000000..c3c86e2 --- /dev/null +++ b/tests/integration_login.py @@ -0,0 +1,26 @@ +from MeowerBot import Bot +from MeowerBot import cbids +from MeowerBot.context import CTX +import asyncio +import logging +from os import environ as env + +logging.basicConfig(level=logging.DEBUG) +logging.getLogger("websockets.client").setLevel(level=logging.INFO) + +bot = Bot() + +async def on_login(bot): + print("Logged in!") + + +bot.callback(on_login, cbids.login) + +@bot.command(aname="ping") +async def ping(ctx: CTX): + await ctx.send_msg("Pong!\n My latency is: " + str(bot.latency)) + +async def main(): + await bot.run(env["uname"], env["password"]) + +asyncio.run(main()) \ No newline at end of file diff --git a/tests/intergration/API.py b/tests/intergration/API.py deleted file mode 100644 index 127dbcf..0000000 --- a/tests/intergration/API.py +++ /dev/null @@ -1,20 +0,0 @@ -# type: ignore # disable linter - -from MeowerBot import Bot, __version__ -from MeowerBot.API import MeowerAPI - -from os import environ as env - -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) - -bot = Bot() -bot.api = None -def direct(val, listener, bot=bot): - if listener == "__meowerbot__login": - bot.send_msg("Meower Stats: " + str(bot.api.statistics())) - -bot.callback(direct, cbid="direct") - -bot.run(env['uname'], env['password']) diff --git a/tests/intergration/cogs.py b/tests/intergration/cogs.py deleted file mode 100644 index 13df6a1..0000000 --- a/tests/intergration/cogs.py +++ /dev/null @@ -1,31 +0,0 @@ - -from MeowerBot import Bot, __version__ -from MeowerBot.cog import Cog -from MeowerBot.command import command -from os import environ as env - -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) -class MyBotCog(Cog): - def __init__(self, bot): - super().__init__() - self.bot = bot - - @command() - def test2(self, ctx, *args): - if not hasattr(self, "bot"): - ctx.reply("No bot in cog") - - ctx.send_msg(" ".join(args)) - -bot = Bot(prefix="/") -cog = MyBotCog(bot) -bot.register_cog(cog) - -@bot.command() -def test(ctx, *args): - ctx.send_msg(" ".join(args) + "\n mb.py " + __version__) - -bot.run(env['username'], env['password']) - diff --git a/tests/intergration/command.py b/tests/intergration/command.py deleted file mode 100644 index e67de42..0000000 --- a/tests/intergration/command.py +++ /dev/null @@ -1,30 +0,0 @@ - -from MeowerBot import Bot, __version__ -from MeowerBot.command import AppCommand -from os import environ as env -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) - -bot: Bot = Bot(prefix="/", autoreload=1) - -test: AppCommand - -@bot.command() -def test(ctx, *args): - ctx.send_msg(" ".join(args) + "\n mb.py " + __version__) - -@test.subcommand() -def sub(ctx, *args): - ctx.send_msg(" ".join(args)) - - -@bot.command(args=0) -def reloadtime(ctx): - "__doc__ test" - ctx.send_msg(f"My reload time is {round(bot.autoreload_time)}" ) - - - -bot.run(env['uname'], env['password']) - diff --git a/tests/intergration/join_chat.py b/tests/intergration/join_chat.py deleted file mode 100644 index b635d76..0000000 --- a/tests/intergration/join_chat.py +++ /dev/null @@ -1,19 +0,0 @@ -from MeowerBot import Bot, __version__ -from os import environ as env -bot = Bot(prefix="/") -import time -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) - -exited = False - -def login_callback(bot=bot): - print("Logged in") - - bot.enter_chat("livechat") - - -bot.callback(login_callback, cbid="login") - -bot.run(env['username'], env['password']) \ No newline at end of file diff --git a/tests/intergration/login.py b/tests/intergration/login.py deleted file mode 100644 index 82b2c6f..0000000 --- a/tests/intergration/login.py +++ /dev/null @@ -1,28 +0,0 @@ - - -from MeowerBot import Bot, __version__ -from os import environ as env -from logging import basicConfig, DEBUG - - -basicConfig(level=DEBUG) - -bot = Bot() - -def login(*_, **__): - print("login CB") - bot.send_msg("TESTING!!!!!!!! O_O", to='home') - bot.send_msg("MeowerBot.py " + __version__, to='home') - - -def msg(msg, *args, **_): - print("msg CB") - if msg.user.username == "Discord": - bot.send_msg("TEEEEEST", to="livechat") - -bot.callback(login, cbid="login") -bot.callback(msg, cbid="message") - -bot.run(env['username'], env['password']) - - diff --git a/tests/intergration/test_help.py b/tests/intergration/test_help.py deleted file mode 100644 index 6bead93..0000000 --- a/tests/intergration/test_help.py +++ /dev/null @@ -1,34 +0,0 @@ - -from MeowerBot import Bot, __version__ -from MeowerBot.command import AppCommand -from MeowerBot.ext.help import Help - -from os import environ as env -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) - -bot: Bot = Bot(autoreload=1) - -test: AppCommand - -@bot.command() -def test(ctx, *args): - ctx.send_msg(" ".join(args) + "\n mb.py " + __version__) - -@test.subcommand() -def sub(ctx, *args): - ctx.send_msg(" ".join(args)) - - -@bot.command(args=0) -def reloadtime(ctx): - "__doc__ test" - ctx.send_msg(f"My reload time is {round(bot.autoreload_time)}" ) - -help = Help(bot) - - -bot.register_cog(help) -bot.run(env['uname'], env['password']) - diff --git a/tests/intergration/type_forever.py b/tests/intergration/type_forever.py deleted file mode 100644 index 8231a9b..0000000 --- a/tests/intergration/type_forever.py +++ /dev/null @@ -1,31 +0,0 @@ -from MeowerBot import Bot, __version__ -from os import environ as env -bot = Bot(prefix="/") -import time -from logging import basicConfig, DEBUG - -basicConfig(level=DEBUG) - -exited = False - -def login_callback(bot=bot): - print("Logged in") - - while True: - - bot.send_typing() - - time.sleep(1) - - if exited: - break - -def exit_callback(bot=bot): - global exited - exited = True - - -bot.callback(login_callback, cbid="login") -bot.callback(exit_callback, cbid="close") - -bot.run(env['username'], env['password']) \ No newline at end of file diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index ea4c4ff..0000000 --- a/tests/test_api.py +++ /dev/null @@ -1,89 +0,0 @@ -#lint:disable - -import unittest -from unittest.mock import MagicMock, patch -from MeowerBot.API import MeowerAPI - -class TestMeowerAPI(unittest.TestCase): - def setUp(self): - self.api = MeowerAPI("testuser") - self.mock_session = MagicMock() - self.api.session = self.mock_session - self.api.session.headers = {} - - def test_login(self): - # Test that the login method sets the token header - self.api.login("testtoken") - - self.assertIn("token", self.mock_session.headers) - self.assertEqual(self.mock_session.headers["token"], "testtoken") - - def test_get_page_home(self): - # Test that the get_page method returns the correct response for home page - self.mock_session.get.return_value.json.return_value = {"page": 1} - - response = self.api.get_page() - - self.mock_session.get.assert_called_with( - "https://api.meower.org/home?autoget&page=1" - ) - self.assertEqual(response, {"page": 1}) - - def test_get_page_chat(self): - # Test that the get_page method returns the correct response for chat page - self.mock_session.get.return_value.json.return_value = {"page": 1} - - response = self.api.get_page(chatid="testchat") - - self.mock_session.get.assert_called_with( - "https://api.meower.org/posts/testchat?autoget&page=1" - ) - self.assertEqual(response, {"page": 1}) - - def test_get_user(self): - # Test that the get_user method returns the correct response - self.mock_session.get.return_value.json.return_value = {"username": "testuser"} - - response = self.api.get_user("testuser") - - self.mock_session.get.assert_called_with( - "https://api.meower.org/users/testuser" - ) - self.assertEqual(response, {"username": "testuser"}) - - def test_get_user_posts(self): - # Test that the get_user_posts method returns the correct response - self.mock_session.get.return_value.json.return_value = {"page": 1} - - response = self.api.get_user_posts("testuser") - - self.mock_session.get.assert_called_with( - "https://api.meower.org/users/testuser/posts?autoget&page=1" - ) - self.assertEqual(response, {"page": 1}) - - def test_statistics(self): - # Test that the statistics method returns the correct response - self.mock_session.get.return_value.json.return_value = {"users": 10} - - response = self.api.statistics() - - self.mock_session.get.assert_called_with( - "https://api.meower.org/statistics" - ) - self.assertEqual(response, {"users": 10}) - - def test_status(self): - # Test that the status method returns the correct response - self.mock_session.get.return_value.json.return_value = {"status": "ok"} - - response = self.api.status() - - self.mock_session.get.assert_called_with( - "https://api.meower.org/status" - ) - self.assertEqual(response, {"status": "ok"}) - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/tests/test_bot.py b/tests/test_bot.py deleted file mode 100644 index b4c40f1..0000000 --- a/tests/test_bot.py +++ /dev/null @@ -1,287 +0,0 @@ -# nosec -import unittest -from unittest.mock import MagicMock - -from MeowerBot import Bot, botm - -from io import StringIO -from unittest.mock import patch - -#Import the bot module here - - -class TestBot(unittest.TestCase): - def setUp(self): - self.bot = Bot() - self.bot.username = "testuser" - self.bot._password = "testpassword" # nosec - self.bot.wss = MagicMock() - - def test_handle_packet_statuscode(self): - packet = {"cmd": "statuscode", "val": 200, "listener": None} - - run_cb = self.bot.run_cb - self.bot.run_cb = MagicMock() - - try: - result = self.bot.__handle_packet__(packet) - - self.bot.run_cb.assert_called_with("statuscode", args=(200, None)) - except: - raise - finally: - self.bot.run_cb = run_cb - - - def test_handle_packet_ulist(self): - packet = {"cmd": "ulist", "val":"a;b;c;d"} - runcb = self.bot.run_cb - self.bot.run_cb = MagicMock() - - try: - result = self.bot.__handle_packet__(packet) - self.bot.run_cb.assert_called_with("ulist", self.bot.wss.statedata["ulist"]["usernames"]) - except: - raise - finally: - self.bot.run_cb = runcb - - - - - - def test_handle_packet_direct_with_message_callback(self): - packet = {"cmd": "direct", "val": {"u":"aa", "p":"aa", "origin":"a","post_origin":"a"}} - - self.bot.run_command = MagicMock() - - - - #edit bots imports - - botm.CTX = MagicMock() - botm.CTX.return_value = botm.CTX - botm.CTX.message = MagicMock() - - run_cb = self.bot.run_cb - self.bot.run_cb = MagicMock() - self.bot.prefix = "!" - - try: - result = self.bot.__handle_packet__(packet) - - self.bot.run_cb.assert_called_with("raw_message", args=(packet["val"], )) - - except: - raise - finally: - self.bot.run_cb = run_cb - - def test_handle_packet_direct_without_message_callback(self): - packet = {"cmd": "direct", "val": {"p": "!help", "u": "A", "origin": "123", "post_origin": "123"}} - self.bot.callbacks = {} - self.bot.run_command = MagicMock() - self.bot.prefix = "!" - - #edit bots imports - - botm.CTX = MagicMock() - botm.CTX.return_value = botm.CTX - - result = self.bot.__handle_packet__(packet) - - - botm.CTX.assert_called_with(packet["val"], self.bot) - - assert self.bot.run_command.called # nosec - assert self.bot.run_command.call_args[0][0] == botm.CTX.message # nosec - - - - def test_handle_packet_direct_with_prefix(self): - packet = {"cmd": "direct", "val": {"p": ".help","u":"A", "origin": "123", "post_origin": "123"}} - self.bot.callbacks = {} - - - self.bot.run_command = MagicMock() - self.bot.prefix = "!" - - result = self.bot.__handle_packet__(packet) - - self.assertTrue(self.bot.run_command.called) - - def test_handle_packet_other_command(self): - packet = {"cmd": "other", "val": "value", "listener": None} - self.bot.run_cb = MagicMock() - - result = self.bot.__handle_packet__(packet) - - self.bot.run_cb.assert_called_with("other", args=("value", None)) - - def test_handle_packet_pmsg(self): - packet = {"cmd": "pmsg", "val": "Hello, world!", "origin": "123" } - self.bot.BOT_NO_PMSG_RESPONSE = [""] - self.bot.wss.sendPacket = MagicMock() - self.bot.prefix = "!" - result = self.bot.__handle_packet__(packet) - - self.bot.wss.sendPacket.assert_called_with({"cmd": "pmsg", "val": "I:500 | Bot", "id": "123"}) - botm.requests = MagicMock() - - - - def test_handle_bridges_with_bridge(self): - packet = {"val": {"u": "user", "p": "user: message", "origin": "123", "post_origin": "123"}} - self.bot.__bridges__ = ["user"] - - self.bot.prefix = "!" - result = self.bot.handle_bridges(packet) - - self.assertEqual(result["val"]["u"], "user") - self.assertEqual(result["val"]["p"], "message") - - def test_handle_bridges_without_bridge(self): - packet = {"val": {"u": "user", "p": "message"}} - self.bot.__bridges__ = ["False"] - - self.bot.prefix = "!" - result = self.bot.handle_bridges(packet) - - self.assertEqual(result["val"]["u"], "user") - self.assertEqual(result["val"]["p"], "message") - - def test_handle_bridges_with_prefix(self): - packet = {"val": {"u": "user", "p": "!#0000command"}} - self.bot.__bridges__ = ["False"] - - self.bot.prefix = "!" - - result = self.bot.handle_bridges(packet) - - self.assertEqual(result["val"]["u"], "user") - self.assertEqual(result["val"]["p"], "!command") - - - def test_handle_status_trusted_access_enabled(self): - status = "I:112 | Trusted Access enabled" - listener = None - - result = self.bot._handle_status(status, listener) - - self.assertIsNone(result) - - def test_handle_status_logger_in(self): - status = "I:100 | OK" - listener = None - self.bot.logger_in = True - self.bot.wss.sendPacket = MagicMock() - - result = self.bot._handle_status(status, listener) - - self.bot.wss.sendPacket.assert_called_with( - { - "cmd": "direct", - "val": { - "cmd": "authpswd", - "val": {"username": self.bot.username, "pswd": self.bot._password}, - }, - "listener": "__meowerbot__login", - } - ) - self.assertFalse(self.bot.logger_in) - - def test_handle_status_login_success(self): - status = "I:100 | OK" - listener = "__meowerbot__login" - self.bot.run_cb = MagicMock() - - result = self.bot._handle_status(status, listener) - - self.bot.run_cb.assert_called_with("login", args=(), kwargs={}) - - def test_handle_status_login_failure(self): - status = "E:104 | Internal" - listener = "__meowerbot__login" - - - - - with patch("builtins.print", MagicMock()): - self.bot._handle_status(status, listener) - - assert self.bot.bad_exit # nosec - - def test_handle_status_send_message_success(self): - status = "I:100 | OK" - listener = "__meowerbot__send_message" - self.bot.autoreload_time = 10 - - result = self.bot._handle_status(status, listener) - - self.assertEqual(self.bot.autoreload_time, self.bot.autoreload_original) - - def test_handle_status_send_message_failure(self): - status = "E:100 | Internal" - listener = "__meowerbot__send_message" - - with self.assertRaises(RuntimeError): - self.bot._handle_status(status, listener) - -import unittest -from unittest.mock import MagicMock -from MeowerBot import Bot - -class TestBotRun(unittest.TestCase): - def setUp(self): - self.bot = Bot() - self.bot._t_ping_thread = MagicMock() - self.bot.logger = MagicMock() - self.bot.api = MagicMock() - self.bot.wss = MagicMock() - - def test_run_sets_username_and_password(self): - self.bot.run("testuser", "testpassword") - - self.assertEqual(self.bot.username, "testuser") - self.assertEqual(self.bot._password, "testpassword") - - def test_run_starts_ping_thread(self): - self.bot.run("testuser", "testpassword") - - self.bot._t_ping_thread.start.assert_called_once() - - def test_run_sets_prefix_if_none(self): - self.bot.run("testuser", "testpassword") - - self.assertEqual(self.bot.prefix, "@testuser") - - def test_run_sets_logger_in_to_true(self): - self.bot.run("testuser", "testpassword") - - self.assertTrue(self.bot.logger_in) - - def test_run_sets_logger_name(self): - self.bot.run("testuser", "testpassword") - - self.assertEqual(self.bot.logger.name, "MeowerBot testuser") - - def test_run_sets_server(self): - self.bot.run("testuser", "testpassword", server="wss://testserver.com") - - self.assertEqual(self.bot.server, "wss://testserver.com") - - def test_run_calls_wss_client_with_server(self): - self.bot.run("testuser", "testpassword", server="wss://testserver.com") - - self.bot.wss.client.assert_called_once_with("wss://testserver.com") - - def test_run_raises_exception_if_bad_exit(self): - self.bot.bad_exit = True - - with self.assertRaises(BaseException): - with patch("print", MagicMock()): - self.bot.run("testuser", "testpassword") - - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/tests/test_cog.py b/tests/test_cog.py deleted file mode 100644 index 7e784fa..0000000 --- a/tests/test_cog.py +++ /dev/null @@ -1,39 +0,0 @@ -import unittest -from unittest.mock import MagicMock -from MeowerBot.cog import Cog -from MeowerBot.command import command, AppCommand - -class CogTestingCog(Cog): - @command("command1") - def command1(self): - pass - - @command("command2") - def command2(self): - pass - -class TestCog(unittest.TestCase): - def setUp(self): - self.cog = CogTestingCog() - - def test_commands(self): - # Test that all commands are registered - self.assertEqual(len(self.cog.__commands__), 2) - - - - - # Test that the commands are registered correctly - self.assertIn("command1", self.cog.__commands__) - self.assertIn("command2", self.cog.__commands__) - - def test_get_info(self): - # Test that the get_info method returns the correct commands - info = self.cog.get_info() - - self.assertIn("command1", info) - self.assertIn("command2", info) - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/tests/test_command.py b/tests/test_command.py deleted file mode 100644 index 00466a0..0000000 --- a/tests/test_command.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest -from unittest.mock import MagicMock -from MeowerBot.command import AppCommand - -class TestAppCommand(unittest.TestCase): - def setUp(self): - self.mock_connected = MagicMock() - self.command = AppCommand(lambda ctx,a,b: None, name="testcmd", args=2) - - def test_call(self): - # Test that calling an AppCommand raises a RuntimeError - cmd = AppCommand(lambda: None) - with self.assertRaises(RuntimeError): - cmd() - - def test_register_class(self): - # Test that registering a class sets the connected attribute - cmd = AppCommand(lambda: None) - cmd.register_class(self.mock_connected) - self.assertEqual(cmd.connected, self.mock_connected) - - def test_subcommand(self): - # Test that adding a subcommand updates the subcommands dictionary - - self.command.subcommand(name="subcmd")(lambda: None) - - self.assertIn("subcmd", self.command.subcommands) - self.assertEqual(len(self.command.subcommands), 1) - - def test_run_cmd(self): - # Test that running a command calls the correct function with the correct arguments - mock_func = MagicMock() - mock_func.__annotations__ = {} - mock_func.__name__ = "mock_func" - cmd = AppCommand(mock_func, args=2) - - cmd.run_cmd(None, "arg1", "arg2") - mock_func.assert_called_with(None, "arg1", "arg2") - - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file From 1c81b350bc3440526ef103bb8470933245a7e871 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Wed, 9 Aug 2023 17:01:56 -0500 Subject: [PATCH 02/13] Message getter --- .vscode/settings.json | 2 +- MeowerBot/Bot.py | 19 ++++++ MeowerBot/cl/__init__.py | 128 ++++++++++------------------------- MeowerBot/cl/waitedPacked.py | 9 --- tests/integration_login.py | 7 +- 5 files changed, 63 insertions(+), 102 deletions(-) delete mode 100644 MeowerBot/cl/waitedPacked.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 2458869..eb464aa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,6 @@ "python.linting.banditEnabled": true, "python.linting.enabled": true, "[python]": { - "editor.defaultFormatter": "ms-python.autopep8" + "editor.defaultFormatter": "ms-python.python" } } \ No newline at end of file diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index 2b5e045..7019f2f 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -42,6 +42,8 @@ class cbids(StrEnum): direct = "direct" class Bot(Client): + messages = [] + message_condition = asyncio.Condition() """ A class that holds all of the networking for a meower bot to function and run """ @@ -256,8 +258,15 @@ async def __handle_packet__(self, packet): packet = self.handle_bridges(packet) ctx = CTX(packet["val"], self) + async with self.message_condition: + self.messages.insert(0, ctx.message) + self.message_condition.notify_all() + + if "message" in self.callbacks: + await self.run_cb("message", args=(ctx.message,)) + else: @@ -395,6 +404,16 @@ async def create_chat(self, name): }, "listener": "mb_get_chat_list" }) + + async def wait_for_message(self, activation, timeout=100): + timeout = min(timeout, 50) + def wrapper(): + return activation(self.messages[0]) + + async with self.message_condition: + await asyncio.wait_for(self.message_condition.wait_for(wrapper), timeout) + + return self.messages[0] async def run(self, username, password, server="wss://server.meower.org"): """ diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index ab96340..e1ad3ef 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -1,119 +1,65 @@ import websockets import orjson import json - import asyncio -class Client: - __waiting = {} - __packets = [] - ws: websockets.WebSocketClientProtocol - def __init__(self): - self.ws = None - pass - - async def _connect(self): - pass - - async def _disconnect(self): - pass - - async def _message(self, message): - pass - - async def _error(self, error): - pass - - async def sendPacket(self, message): - await self.ws.send(json.dumps(message)) - - async def sendPacketAndWait(self, message): - await self.ws.send(json.dumps(message)) - self.__waiting[message["listener"]] = asyncio.Event() - - got_response = False - statuscode = "" - waited = None - - - - async def predicate(): - - - nonlocal got_response - nonlocal statuscode - - #get the last added packet - packet = self.__packets[-1] - - if message["listener"] != packet["listener"]: - return False +class Client: + ws: websockets.WebSocketClientProtocol - if packet["cmd"] == "statuscode": - statuscode = packet["val"] - - else: - got_response = True - nonlocal waited - waited = waitedPacked.WaitedPacked(statuscode == "I: 100 | OK", packet, packet["listener"]) + def __init__(self): + self.ws = None + pass - + async def _connect(self): + pass + async def _disconnect(self): + pass - return message["listener"] in self.__waiting and got_response - - await self.__waiting[message["listener"]].wait_for(predicate) + async def _message(self, message): + pass - return waited + async def _error(self, error): + pass + async def sendPacket(self, message): + await self.ws.send(json.dumps(message)) + async def close(self, reason=None): + await self.ws.close(reason=reason) - async def close(self, reason=None): - await self.ws.close(reason=reason) + async def connect(self, server): + async for websocket in websockets.connect(server): + try: + self.ws = websocket + await self._connect() + async for message in websocket: + try: - async def connect(self, server): - async for websocket in websockets.connect(server): - try: - self.ws = websocket - await self._connect() - - async for message in websocket: - try: + await self._message(json.loads(message)) - packet = json.loads(message) - for listener in self.__waiting: - if listener == packet["listener"]: - self.__waiting[listener].notify_all() + except websockets.ConnectionClosed: + raise + except Exception as e: - await self._message(json.loads(message)) + await self._error(e) + pass - #cut packet queue to 50 - if len(self.__packets) > 50: - self.__packets = self.__packets[-50:] - - - except websockets.ConnectionClosed: - raise + except websockets.ConnectionClosed: + await self._disconnect() + pass - except Exception as e: - - await self._error(e) - pass + except Exception as e: + await self._error(e) + pass - except websockets.ConnectionClosed: - await self._disconnect() - pass + await self._disconnect() - except Exception as e: - await self._error(e) - pass - - await self._disconnect() diff --git a/MeowerBot/cl/waitedPacked.py b/MeowerBot/cl/waitedPacked.py deleted file mode 100644 index dcd39f2..0000000 --- a/MeowerBot/cl/waitedPacked.py +++ /dev/null @@ -1,9 +0,0 @@ -from dataclasses import dataclass - -@dataclass -class WaitedPacked: - ok: bool - packet: dict - listener: str - - \ No newline at end of file diff --git a/tests/integration_login.py b/tests/integration_login.py index c3c86e2..ae4801f 100644 --- a/tests/integration_login.py +++ b/tests/integration_login.py @@ -1,6 +1,6 @@ from MeowerBot import Bot from MeowerBot import cbids -from MeowerBot.context import CTX +from MeowerBot.context import CTX, Post import asyncio import logging from os import environ as env @@ -20,6 +20,11 @@ async def on_login(bot): async def ping(ctx: CTX): await ctx.send_msg("Pong!\n My latency is: " + str(bot.latency)) + def predicate(msg: Post): + return ctx.user.username == msg.user.username and msg.chat == ctx.message.chat + + msg = await bot.wait_for_message(predicate, timeout=100) + await ctx.send_msg("You said: " + str(msg)) async def main(): await bot.run(env["uname"], env["password"]) From 691f4e1cb2f67b78af66034d25f120c3198c4581 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sat, 14 Oct 2023 00:01:23 -0500 Subject: [PATCH 03/13] basic better mod update impl --- MeowerBot/API.py | 151 +++++++--- MeowerBot/Bot.py | 363 +++--------------------- MeowerBot/_Commands.py | 5 - MeowerBot/cl/__init__.py | 72 ++--- MeowerBot/context.py | 23 +- MeowerBot/types/__init__.py | 0 MeowerBot/types/api/__init__.py | 20 ++ MeowerBot/types/api/chats.py | 21 ++ MeowerBot/types/api/reports.py | 54 ++++ MeowerBot/types/api/request/Users.py | 8 + MeowerBot/types/api/request/__init__.py | 9 + MeowerBot/types/api/request/chats.py | 8 + MeowerBot/types/api/request/reports.py | 42 +++ MeowerBot/types/api/user.py | 63 ++++ MeowerBot/types/generic.py | 33 +++ poetry.lock | 299 +++++++++++-------- pyproject.toml | 4 +- 17 files changed, 641 insertions(+), 534 deletions(-) delete mode 100644 MeowerBot/_Commands.py create mode 100644 MeowerBot/types/__init__.py create mode 100644 MeowerBot/types/api/__init__.py create mode 100644 MeowerBot/types/api/chats.py create mode 100644 MeowerBot/types/api/reports.py create mode 100644 MeowerBot/types/api/request/Users.py create mode 100644 MeowerBot/types/api/request/__init__.py create mode 100644 MeowerBot/types/api/request/chats.py create mode 100644 MeowerBot/types/api/request/reports.py create mode 100644 MeowerBot/types/api/user.py create mode 100644 MeowerBot/types/generic.py diff --git a/MeowerBot/API.py b/MeowerBot/API.py index c9ab29a..d794552 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -1,45 +1,120 @@ -import requests -from urllib.parse import urljoin +from urllib.parse import urljoin +from httpx import AsyncClient, Auth +import ujson as json +from .types import generic +from .types.api.reports import ReportRequest, Report, AdminNotesResponse +from .types.api.request.reports import UpdateReportBody, UpdateNotesBody +from .types.generic import Post class MeowerAPI: base_uri = "https://api.meower.org/" def __init__(self, username): + self.headers = {"username": username} + self.client = AsyncClient(headers=self.headers, base_url=self.base_uri) + + + + async def login(self, token): + self.headers.update({"token": token}) + + async def admin_get_reports(self, timeout=None) -> ReportRequest: + resp = await self.client.get("/admin/reports", timeout=timeout, params={"autoget": None}) + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return ReportRequest.from_json( + + ) + + async def admin_get_report(self, uuid: generic.UUID) -> Report: + resp = await self.client.get(f"/admin/reports/{uuid}/") + + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to look at reports") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Report.from_json(resp.text) + + async def admin_edit_report(self, uuid: generic.UUID, **kwargs: UpdateReportBody) -> Report: + resp = await self.client.patch(f"/admin/reports/{uuid}", json=kwargs) + + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to edit reports") + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Report.from_json(resp.text) + + async def admin_escalate_report(self, uuid: generic.UUID) -> Report: + resp = await self.client.post(f"/admin/reports/{uuid}/escalate/") + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to edit reports") + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Report.from_json(resp.text) + + async def admin_fetch_note(self, indentifier: str) -> AdminNotesResponse: + resp = await self.client.get(f"/admin/notes/{indentifier}") + + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to look at notes") + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return AdminNotesResponse.from_json(resp.text) + + async def admin_create_note(self, indentifier: str, **kwargs: UpdateNotesBody) -> AdminNotesResponse: + resp = await self.client.put(f"/admin/notes/{indentifier}", json=kwargs) + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to edit/create notes") + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return AdminNotesResponse.from_json(resp.text) + + async def admin_get_post(self, uuid: generic.UUID) -> Post: + resp = await self.client.get(f"/admin/posts/{uuid}") + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to look at posts") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Post.from_json(resp.text) + + async def admin_delete_post(self, uuid: generic.UUID) -> Post: + resp = await self.client.delete(f"/admin/posts/{uuid}") + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to delete posts") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Post.from_json(resp.text) + + async def admin_restore_deleted_post(self, uuid: generic.UUID) -> Post: + resp = await self.client.post(f"/admin/posts/{uuid}/restore") + if resp.status_code == 403: + raise RuntimeError("[API] 403 Found: You are not allowed to restore posts") + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Not found") + + return Post.from_json(resp.text) + + # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 + - self.session = requests.session() - self.session.headers.update({"usename": username}) - - def login(self, token): - self.session.headers.update({"token": token}) - - def get_page(self, page=1, chatid="home"): - if chatid == "home": - return self.session.get( - urljoin(self.base_uri, "/home?autoget&page={0}".format(page)) - ).json() - else: - return self.session.get( - urljoin( - self.base_uri, "/posts/{0}?autoget&page={1}".format(chatid, page) - ) - ).json() - - def get_user(self, username): - return self.session.get( - urljoin(self.base_uri, "/users/{0}".format(username)) - ).json() - - def get_user_posts(self, username, page=1): - return self.session.get( - urljoin( - self.base_uri, - "/users/{0}/posts?autoget&page={1}".format(username, page), - ) - ).json() - - def statistics(self): - return self.session.get(urljoin(self.base_uri, "statistics")).json() - - def status(self): - return self.session.get(urljoin(self.base_uri, "/status")).json() diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index 7019f2f..5531598 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -41,6 +41,8 @@ class cbids(StrEnum): chat_list = "chat_list" direct = "direct" +callbacks = [i for i in cbids] + class Bot(Client): messages = [] message_condition = asyncio.Condition() @@ -67,353 +69,85 @@ def latency(self): async def _t_ping(self): while True: - await asyncio.sleep(30) + try: + await asyncio.sleep(5) - await self.sendPacket({"cmd": "ping", "val": ""}) + await self.sendPacket({"cmd": "ping", "val": ""}) + self.logger.debug(msg="Sent ping") + except Exception as e: + self.logger.error(e) + await self._error(e) + break def __init__(self, prefix=None ): #type: ignore super().__init__() - self.callbacks = {} - self._last_to = "Home" + self.callbacks = {i: [] for i in callbacks} self.ulist = [] # to be used in start self.username = None self.password = None - self.logger_in = False self.commands = {} self.prefix = prefix self.logger = logging.getLogger("MeowerBot") - self.bad_exit = False self.server = None self.cogs = {} - async def run_cb(self, cbid, args=(), kwargs=None): # cq: ignore - if cbid not in self.callbacks: - return # ignore - - if not kwargs: - kwargs = {} - - if cbid == "error" and isinstance(args[0], KeyboardInterrupt()): - self.logger.error("KeyboardInterrupt") - self.bad_exit = True - self.stop() - return - - kwargs["bot"] = self - await asyncio.gather(*[cb(*args, **kwargs) for cb in self.callbacks[cbid]]) - - async def _error(self, e): - await self.run_cb("error", args=(e,)) - - if (type(e)) == KeyboardInterrupt: - self.bad_exit = True - - self.wss = None # effectively kill the bot - - async def _message(self, packet): - - try: - await self.__handle_packet__(packet) - except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP - self.logger.error(traceback.format_exc()) - await self.run_cb("error", args=(e, )) - - - try: - await self.run_cb("__raw__", args=(packet, )) # raw packets - except BaseException as e: # cq: skip #IDC ABOUT GENERAL EXCP - self.logger.error(traceback.format_exc()) - await self.run_cb("error", args=(e, )) + # Interface - async def _connect(self): - await self.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "type", "val": "py"}, - } - ) - await self.sendPacket( - { - "cmd": "direct", - "val": "meower", - "listener": "__meowerbot__cloudlink_trust", - } - ) - - def command(self, aname=None, args=0): - def inner(func): - if aname is None: - name = func.__name__ - else: - name = aname - - cmd = AppCommand(func, name=name, args=args) - - info = cmd.info() - info[cmd.name]["command"] = cmd - - self.commands.update(info) - - return cmd #allow subcommands without a cog - - return inner - - def register_cog(self, cog): - info = cog.get_info() - self.cogs[cog.__class__.__name__] = cog - self.commands.update(info) - - def deregister_cog(self, cogname): - for cmd in self.cogs[cogname].get_info().values(): - del self.commands[cmd.name] - del self.cogs[cogname] - - async def _handle_status(self, status, listener): - if status == "I:112 | Trusted Access enabled": - return - - if self.logger_in: - self.logger_in = False - - if status != "I:100 | OK": - raise RuntimeError("CloudLink Trust Failed") - - auth_packet = { - "cmd": "direct", - "val": { - "cmd": "authpswd", - "val": {"username": self.username, "pswd": self._password}, - }, - "listener": "__meowerbot__login", - } - await self.sendPacket(auth_packet) - - elif listener == "__meowerbot__login": - if status == "E:104 | Internal": - requests.post( - "https://webhooks.meower.org/post/home", - json={ - "post": "ERROR: MeowerBot.py Webhooks Logging\n\n Account Softlocked.", - "username": self.username, - }, - timeout=5 - ) - print("CRITICAL ERROR! ACCOUNT SOFTLOCKED!!!!.", file=sys.__stdout__) - self.bad_exit = True - return - - if status != "I:100 | OK": - raise RuntimeError("Password Or Username Is Incorrect") - - time.sleep(0.5) - await self.run_cb("login", args=(), kwargs={}) - - elif listener == "__meowerbot__send_message": - if status == "I:100 | OK": - return - - raise RuntimeError("Post Failed to send") - - def callback(self, callback, cbid: Union[Union[cbids, None], str] =None): - """Connects a callback ID to a callback + def event(self, func): """ - if cbid is None: - cbid = callback.__name__ + Creates a callback that takes over the original functionality of the bot. + throws an error if the function name is not a valid callback - if cbid not in self.callbacks: - self.callbacks[cbid] = [] - self.callbacks[cbid].append(callback) - - async def _close(self, *args, **kwargs): - await self.run_cb("close", args=args, kwargs=kwargs) - - def handle_bridges(self, packet): - if packet["val"]["u"] in self.__bridges__ and ": " in packet["val"]["p"]: - split = packet["val"]["p"].split(": ", 1) - packet["val"]["p"] = split[1] - packet["val"]["u"] = split[0] + throws: + - TypeError - if packet["val"]["p"].startswith(self.prefix+"#0000"): - packet["val"]["p"] = packet["val"]["p"].replace("#0000", "") + """ + if func.__name__ not in callbacks: + raise TypeError(f"{func.__name__} is not a valid callback") - return packet - - async def __handle_packet__(self, packet): - if packet["cmd"] == "statuscode": - - await self._handle_status(packet["val"], packet.get("listener", None)) - - listener = packet.get("listener", None) - return await self.run_cb("statuscode", args=(packet["val"], listener)) - - elif packet["cmd"] == "ulist": - self.ulist = packet["val"].split(";") - await self.run_cb("ulist", self.ulist) - - elif packet["cmd"] == "direct" and "post_origin" in packet["val"]: - packet = self.handle_bridges(packet) - - ctx = CTX(packet["val"], self) - async with self.message_condition: - self.messages.insert(0, ctx.message) - self.message_condition.notify_all() - - - if "message" in self.callbacks: - - await self.run_cb("message", args=(ctx.message,)) - - - else: - - if ctx.user.username == self.username: - return - if not ctx.message.data.startswith(self.prefix): - return - - ctx.message.data = ctx.message.data.split(self.prefix, 1)[1] - - await self.run_command(ctx.message) + setattr(self, func.__name__, func) + + def listen(self, callback=None): + """ + Does the same thing as @link Bot.event but does not replace the bots original functionality, most usefull for librarys + throws an error if the function name is not a valid callback - await self.run_cb("raw_message", args=(packet["val"],)) + throws: + - TypeError + """ + def inner(func): + callback = callback if callback is not None else func.__name__ - elif packet["cmd"] == "direct": - listener = packet.get("listener") + if func.__name__ not in callbacks: + raise TypeError(f"{func.__name__} is not a valid listener") + + self.callbacks[callback].append(func) + - if listener == "mb_get_chat_list": - await self.run_cb("chat_list", args=(packet["val"]["payload"], listener)) - elif listener == "__meowerbot__login": - self.api.login(packet['val']['payload']['token']) - await self.run_cb("direct", args=(packet["val"], listener)) + - + - else: - listener = packet.get("listener") - await self.run_cb(packet["cmd"], args=(packet["val"], listener)) - - if (packet["cmd"] == "pmsg") and (packet["val"] not in self.BOT_NO_PMSG_RESPONSE): - await self.sendPacket({ - "cmd": "pmsg", - "val": "I:500 | Bot", - "id": packet["origin"] - }) - async def run_command(self, message): - args = shlex.split(str(message)) - try: - await self.commands[args[0]]["command"].run_cmd(message.ctx, *args[1:]) - except KeyError as e: - self.logger.error(traceback.format_exc()) - await self.run_cb("error", args=(e,)) - - async def send_msg(self, msg, to="home"): - self._last_to = to - self._last_sent = msg - - if to == "home": - await self.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "post_home", "val": msg}, - "listener": "__meowerbot__send_message", - } - ) - else: - await self.sendPacket( - { - "cmd": "direct", - "val": {"cmd": "post_chat", "val": {"chatid": to, "p": msg}}, - "listener": "__meowerbot__send_message", - } - ) - + # websocket - async def send_typing(self, to="home"): - if to == "home": - await self.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": "livechat", - "state": 101, - }, - }, - } - ) - else: - await self.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": to, - "state": 100, - }, - }, - } - ) - - async def enter_chat(self, chatid="livechat"): - await self.sendPacket( - { - "cmd": "direct", - "val": { - "cmd": "set_chat_state", - "val": { - "chatid": chatid, - "state": 1, - }, - }, - } - ) - - async def create_chat(self, name): - """ - Unstable, use at your own risk + async def _connect(self): + pass - comes with callbacks: chat_list - """ - await self.sendPacket({ - "cmd": "direct", - "val": { - "cmd": "create_chat", - "val": name - }, - "listener": "mb_create_chat" - }) - - time.sleep(secs=0.5) - - await self.sendPacket({ - "cmd": "direct", - "val": { - "cmd": "get_chat_list", - "val": { - "page": 1 - } - }, - "listener": "mb_get_chat_list" - }) - - async def wait_for_message(self, activation, timeout=100): - timeout = min(timeout, 50) - def wrapper(): - return activation(self.messages[0]) + async def _disconnect(self): + pass - async with self.message_condition: - await asyncio.wait_for(self.message_condition.wait_for(wrapper), timeout) + async def _message(self, message): + pass - return self.messages[0] + async def _error(self, error): + pass async def run(self, username, password, server="wss://server.meower.org"): """ @@ -421,7 +155,6 @@ async def run(self, username, password, server="wss://server.meower.org"): """ self.username = username self._password = password - self.logger_in = True asyncio.create_task(self._t_ping()) if self.prefix is None: @@ -434,5 +167,3 @@ async def run(self, username, password, server="wss://server.meower.org"): pass await self.connect(server) - if self.bad_exit: - raise BaseException("Bot Account Softlocked") diff --git a/MeowerBot/_Commands.py b/MeowerBot/_Commands.py deleted file mode 100644 index 7e36334..0000000 --- a/MeowerBot/_Commands.py +++ /dev/null @@ -1,5 +0,0 @@ -import warnings - -warnings.warn("MeowerBot._Command has been moved to MeowerBot.command") - -from .command import * diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index e1ad3ef..20b564a 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -5,61 +5,61 @@ class Client: - ws: websockets.WebSocketClientProtocol + ws: websockets.WebSocketClientProtocol - def __init__(self): - self.ws = None - pass + def __init__(self): + self.ws = None + pass - async def _connect(self): - pass + async def _connect(self): + pass - async def _disconnect(self): - pass + async def _disconnect(self): + pass - async def _message(self, message): - pass + async def _message(self, message): + pass - async def _error(self, error): - pass + async def _error(self, error): + pass - async def sendPacket(self, message): - await self.ws.send(json.dumps(message)) + async def sendPacket(self, message): + await self.ws.send(json.dumps(message)) - async def close(self, reason=None): - await self.ws.close(reason=reason) + async def close(self, reason=None): + await self.ws.close(reason=reason) - async def connect(self, server): - async for websocket in websockets.connect(server): - try: + async def connect(self, server): + async for websocket in websockets.connect(server): + try: - self.ws = websocket - await self._connect() + self.ws = websocket + await self._connect() - async for message in websocket: - try: + async for message in websocket: + try: - await self._message(json.loads(message)) + await self._message(json.loads(message)) - except websockets.ConnectionClosed: - raise + except websockets.ConnectionClosed: + raise - except Exception as e: + except Exception as e: - await self._error(e) - pass + await self._error(e) + pass - except websockets.ConnectionClosed: - await self._disconnect() - pass + except websockets.ConnectionClosed: + await self._disconnect() + pass - except Exception as e: - await self._error(e) - pass + except Exception as e: + await self._error(e) + pass - await self._disconnect() + await self._disconnect() diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 89eeb10..199e048 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -10,25 +10,12 @@ import requests -class User: - def __init__(self, bot, username): - self.username = username - - self.bot = bot - self._raw = self.bot.api.get_user(self.username) - - self.level = self._raw["lvl"] - self.pfp = self._raw["pfp_data"] - self.quote = self._raw["quote"] - - - - async def ping(self, msg, to="home"): - await self.bot.send_msg(f"@{self.username} {msg}", to=to) - - def __str__(self): - return str(self.__dict__) +class Chat: + def __init__(self, id, display_name): + self.id = id + self.display_name = display_name + class Post: def __init__(self, bot, _raw): diff --git a/MeowerBot/types/__init__.py b/MeowerBot/types/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/MeowerBot/types/api/__init__.py b/MeowerBot/types/api/__init__.py new file mode 100644 index 0000000..13ddce1 --- /dev/null +++ b/MeowerBot/types/api/__init__.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from typing import List, TypeVar, Generic, Optional, NewType + + +from ..generic import Post, UUID +from .reports import PagedRequest +from .user import User + +class Inbox(PagedRequest[Post]): pass +class PostList(PagedRequest[Post]): pass +class UserList(PagedRequest[User]): pass + +@dataclass_json +@dataclass +class Statistics: + chats: int + error: bool + posts: int + users: int diff --git a/MeowerBot/types/api/chats.py b/MeowerBot/types/api/chats.py new file mode 100644 index 0000000..7d87897 --- /dev/null +++ b/MeowerBot/types/api/chats.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from typing import List, TypeVar, Generic, Optional, NewType + + +from ..generic import Post, UUID +from .reports import PagedRequest + +@dataclass_json +@dataclass +class ChatGroup: + _id: str + created: int + deleted: bool + last_active: int + members: List[str] + nickname: Optional[str] + owner: Optional[str] + type: int + +class Chats(PagedRequest[ChatGroup]): pass diff --git a/MeowerBot/types/api/reports.py b/MeowerBot/types/api/reports.py new file mode 100644 index 0000000..baeb405 --- /dev/null +++ b/MeowerBot/types/api/reports.py @@ -0,0 +1,54 @@ +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from typing import List, TypeVar, Generic, Optional, NewType + + +from ..generic import Post, UUID + +T = TypeVar("T") + +@dataclass_json +@dataclass +class PagedRequest(Generic[T]): + error: bool + page: int = field(metadata=config(field_name="page#")) + pages: int + + index: Optional[List[UUID]] + autoget: Optional[List[T]] + +@dataclass_json +@dataclass +class Report: + _id: UUID + content: Content + escalated: bool + reports: List[ReportDetails] + status: str + type_: str = field(metadata=config(field_name="type")) + + + + +@dataclass_json +@dataclass +class ReportDetails: + comment: str + reason: str + time: int + user: str + +class ReportRequest(PagedRequest): + autoget: List[Report] + +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +@dataclass_json +@dataclass +class AdminNotesResponse: + _id: str + notes: str + last_modified_by: str + last_modified_at: int + error: bool diff --git a/MeowerBot/types/api/request/Users.py b/MeowerBot/types/api/request/Users.py new file mode 100644 index 0000000..9658402 --- /dev/null +++ b/MeowerBot/types/api/request/Users.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass +from dataclasses_json import dataclass_json +from typing import Literal + +@dataclass_json +@dataclass +class UpdateRelationshipBody: + state: Literal[0, 1, 2] \ No newline at end of file diff --git a/MeowerBot/types/api/request/__init__.py b/MeowerBot/types/api/request/__init__.py new file mode 100644 index 0000000..ee70091 --- /dev/null +++ b/MeowerBot/types/api/request/__init__.py @@ -0,0 +1,9 @@ + + +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +@dataclass_json +@dataclass +class PostBody: + content: str diff --git a/MeowerBot/types/api/request/chats.py b/MeowerBot/types/api/request/chats.py new file mode 100644 index 0000000..ddff9fd --- /dev/null +++ b/MeowerBot/types/api/request/chats.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass +from dataclasses_json import dataclass_json + +@dataclass_json +@dataclass +class ChatBody: + nickname: str + diff --git a/MeowerBot/types/api/request/reports.py b/MeowerBot/types/api/request/reports.py new file mode 100644 index 0000000..842efeb --- /dev/null +++ b/MeowerBot/types/api/request/reports.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from dataclasses_json import dataclass_json +from typing import Optional, List, Literal + +@dataclass_json +@dataclass +class UpdateReportBody: + status: Literal["no_action_taken", "action_taken"] + +@dataclass_json +@dataclass +class UpdateNotesBody: + notes: str + +@dataclass_json +@dataclass +class UpdateUserBanBody: + state: Literal["none", "temp_restriction", "perm_restriction", "temp_ban", "perm_ban"] + restrictions: int + expires: int + reason: str + +@dataclass_json +@dataclass +class UpdateUserBody: + permissions: Optional[int] = None + +@dataclass_json +@dataclass +class UpdateChatBody: + nickname: str + +@dataclass_json +@dataclass +class InboxMessageBody: + content: str + +@dataclass_json +@dataclass +class NetblockBody: + type: int + diff --git a/MeowerBot/types/api/user.py b/MeowerBot/types/api/user.py new file mode 100644 index 0000000..202b431 --- /dev/null +++ b/MeowerBot/types/api/user.py @@ -0,0 +1,63 @@ + +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from ..generic import UUID, BitFlag + +class UserFlags: + SYSTEM = 1 + DELETED = 2 + PROTECTED = 4 + + +class Permissions: + SYSADMIN = 1 + + VIEW_REPORTS = 2 + EDIT_REPORTS = 4 + + VIEW_NOTES = 8 + EDIT_NOTES = 16 + + VIEW_POSTS = 32 + DELETE_POSTS = 64 + + VIEW_ALTS = 128 + SEND_ALERTS = 256 + KICK_USERS = 512 + CLEAR_USER_QUOTES = 1024 + VIEW_BAN_STATES = 2048 + EDIT_BAN_STATES = 4096 + DELETE_USERS = 8192 + + VIEW_IPS = 16384 + BLOCK_IPS = 32768 + + VIEW_CHATS = 65536 + EDIT_CHATS = 131072 + + SEND_ANNOUNCEMENTS = 262144 + + +class Restrictions: + HOME_POSTS = 1 + CHAT_POSTS = 2 + NEW_CHATS = 4 + EDITING_CHAT_NICKNAMES = 8 + EDITING_QUOTE = 16 + + +@dataclass_json +@dataclass +class User: + name: str = field(metadata=config(field_name="_id")) + banned: bool + created: int + error: bool + flags: BitFlag + last_seen: int + lower_username: str + lvl: int + permissions: BitFlag + pfp_data: int + quote: str + uuid: UUID diff --git a/MeowerBot/types/generic.py b/MeowerBot/types/generic.py new file mode 100644 index 0000000..4b4c93b --- /dev/null +++ b/MeowerBot/types/generic.py @@ -0,0 +1,33 @@ +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from typing import List +from dataclasses import dataclass, field +from dataclasses_json import config, dataclass_json +from typing import List, TypeVar, Generic, Optional, NewType + +T = TypeVar("T") +UUID = NewType("UUID", str) +BitFlag = NewType("BitFlag", int) + +@dataclass_json +@dataclass +class Timestamp: + d: str + e: int + h: str + mi: str + mo: str + s: str + y: str + +@dataclass_json +@dataclass +class Post: + _id: UUID + isDeleted: bool + p: str + post_id: UUID + post_origin: str + t: Timestamp + type: int + u: str \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 9da961d..5ed86cf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,27 @@ # This file is automatically @generated by Poetry and should not be changed by hand. +[[package]] +name = "anyio" +version = "4.0.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, + {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.22)"] + [[package]] name = "backports-strenum" version = "1.2.4" @@ -47,91 +69,6 @@ files = [ {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - [[package]] name = "colorama" version = "0.4.6" @@ -144,6 +81,37 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "dataclasses-json" +version = "0.6.1" +description = "Easily serialize dataclasses to and from JSON." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "dataclasses_json-0.6.1-py3-none-any.whl", hash = "sha256:1bd8418a61fe3d588bb0079214d7fb71d44937da40742b787256fd53b26b6c80"}, + {file = "dataclasses_json-0.6.1.tar.gz", hash = "sha256:a53c220c35134ce08211a1057fd0e5bf76dc5331627c6b241cacbc570a89faae"}, +] + +[package.dependencies] +marshmallow = ">=3.18.0,<4.0.0" +typing-inspect = ">=0.4.0,<1" + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "gitdb" version = "4.0.10" @@ -174,6 +142,64 @@ files = [ [package.dependencies] gitdb = ">=4.0.1,<5" +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "0.18.0" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"}, + {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.25.0" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"}, + {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.18.0,<0.19.0" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + [[package]] name = "idna" version = "3.4" @@ -211,6 +237,27 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "marshmallow" +version = "3.20.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.20.1-py3-none-any.whl", hash = "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c"}, + {file = "marshmallow-3.20.1.tar.gz", hash = "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.13)", "autodocsumm (==0.2.11)", "sphinx (==7.0.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)"] +tests = ["pytest", "pytz", "simplejson"] + [[package]] name = "mdurl" version = "0.1.2" @@ -223,6 +270,18 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + [[package]] name = "orjson" version = "3.9.4" @@ -289,6 +348,18 @@ files = [ {file = "orjson-3.9.4.tar.gz", hash = "sha256:a4c9254d21fc44526a3850355b89afd0d00ed73bdf902a5ab416df14a61eac6b"}, ] +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + [[package]] name = "pbr" version = "5.11.1" @@ -366,28 +437,6 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - [[package]] name = "rich" version = "13.5.2" @@ -420,6 +469,18 @@ files = [ {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + [[package]] name = "stevedore" version = "5.1.0" @@ -439,7 +500,7 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" name = "typing-extensions" version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -448,22 +509,20 @@ files = [ ] [[package]] -name = "urllib3" -version = "2.0.4" -description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "typing-inspect" +version = "0.9.0" +description = "Runtime inspection utilities for typing module." category = "main" optional = false -python-versions = ">=3.7" +python-versions = "*" files = [ - {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, - {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, + {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, + {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, ] -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] +[package.dependencies] +mypy-extensions = ">=0.3.0" +typing-extensions = ">=3.7.4" [[package]] name = "websockets" @@ -548,4 +607,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "f7f02ca6f524460ae83bd99aaf436382f29ed4483b8f85dbdbe9f4e778294efa" +content-hash = "66385225f3a2c0512d0e9f6e50ccc2b7793f778187088f9fc1eb30140b064347" diff --git a/pyproject.toml b/pyproject.toml index 020a5d2..a7f3ca9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,9 @@ python = "^3.8" backports-strenum = { version = "^1.2.4", python = "<3.11" } websockets = "^11.0.3" orjson = "^3.9.4" -requests = "^2.31.0" +httpx = "^0.25.0" +dataclasses-json = "^0.6.1" + [tool.poetry.group.extras.dependencies] From b5d0d088468518e9156d5f830a35ff6c84b28d5b Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sun, 15 Oct 2023 01:10:11 -0500 Subject: [PATCH 04/13] Chat API Wrapper done --- MeowerBot/API.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/MeowerBot/API.py b/MeowerBot/API.py index d794552..c7e364e 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -5,14 +5,16 @@ from .types import generic from .types.api.reports import ReportRequest, Report, AdminNotesResponse from .types.api.request.reports import UpdateReportBody, UpdateNotesBody +from .types.api.request.chats import ChatBody from .types.generic import Post +from .types.api.chats import Chats, ChatGroup class MeowerAPI: base_uri = "https://api.meower.org/" def __init__(self, username): self.headers = {"username": username} - self.client = AsyncClient(headers=self.headers, base_url=self.base_uri) + self.client = AsyncClient(headers=self.headers, base_url=self.base_uri, params={"autoget": None}) @@ -20,7 +22,7 @@ async def login(self, token): self.headers.update({"token": token}) async def admin_get_reports(self, timeout=None) -> ReportRequest: - resp = await self.client.get("/admin/reports", timeout=timeout, params={"autoget": None}) + resp = await self.client.get("/admin/reports", timeout=timeout) if resp.status_code == 404: raise RuntimeError("[API] 404 Not found") @@ -117,4 +119,98 @@ async def admin_restore_deleted_post(self, uuid: generic.UUID) -> Post: # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 + async def get_chats(self) -> Chats: + resp = await self.client.get(f"/chats/") + if resp.status_code == 401: + raise RuntimeError("[API] No Token or username supplied! This is required to send authenticated API requests") + + return Chats.from_json(resp.text) + + async def create_chat(self, **kwargs: ChatBody) -> ChatGroup: + resp = await self.client.post(f"/chats/", json=kwargs) + + if resp.status_code == 401: + raise RuntimeError(json.parse(resp.text)["type"]) + + return ChatGroup.from_json(resp.text) + + async def get_chat(self, uuid: generic.UUID) -> ChatGroup: + resp = await self.client.get(f"/chats/{uuid}") + + if resp.status_code == 401: + raise RuntimeError("[API] No Token or username supplied! This is required to send authenticated API requests") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + + + return ChatGroup.from_json(resp.text) + + async def update_chat(self, uuid: generic.UUID, **kwargs: ChatBody) -> ChatGroup: + resp = await self.client.patch(f"/chats/{uuid}", josn=kwargs) + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Updating chat") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + + return ChatGroup.from_json(resp.text) + + async def leave_chat(self, uuid: generic.UUID) -> dict: + resp = await self.client.delete(f"/chats/{uuid}") + + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Updating chat") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + return {"error": False} + + async def add_user_to_chat(self, uuid: generic.UUID, username: str) -> ChatGroup: + resp = await self.client.put(f"/chats/{uuid}/members/{username}") + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Updating chat") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + return ChatGroup.from_json(resp.text) + + async def transfer_chat_ownership(self, uuid: generic.UUID, username: str) -> ChatGroup: + resp = await self.client.post(f"/chats/{uuid}/members/{username}/transfer") + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Updating chat") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + return ChatGroup.from_json(resp.text) \ No newline at end of file From 2f8e15738dd71a12a9e5258d4d96a6db69b41f25 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sun, 15 Oct 2023 01:13:19 -0500 Subject: [PATCH 05/13] psuh TODO: 's --- MeowerBot/API.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/MeowerBot/API.py b/MeowerBot/API.py index c7e364e..b3177f9 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -117,7 +117,7 @@ async def admin_restore_deleted_post(self, uuid: generic.UUID) -> Post: return Post.from_json(resp.text) - # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 + async def get_chats(self) -> Chats: resp = await self.client.get(f"/chats/") @@ -145,8 +145,6 @@ async def get_chat(self, uuid: generic.UUID) -> ChatGroup: if resp.status_code == 404: raise RuntimeError("[API] 404 Chat Not found") - - return ChatGroup.from_json(resp.text) async def update_chat(self, uuid: generic.UUID, **kwargs: ChatBody) -> ChatGroup: @@ -213,4 +211,11 @@ async def transfer_chat_ownership(self, uuid: generic.UUID, username: str) -> Ch if resp.status_code == 404: raise RuntimeError("[API] 404 Chat Not found") - return ChatGroup.from_json(resp.text) \ No newline at end of file + return ChatGroup.from_json(resp.text) + + # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 + # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/home.py + # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/posts.py + # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/inbox.py + # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/search.py + # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/users.py From d509d1f3bfe9927d6b3a5287c15eeb90d9ed538f Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sun, 15 Oct 2023 22:35:39 -0500 Subject: [PATCH 06/13] Users and posts? --- MeowerBot/API.py | 156 ++++++++++++++++++++++-- MeowerBot/types/api/request/Users.py | 2 - MeowerBot/types/api/request/__init__.py | 2 - MeowerBot/types/api/request/chats.py | 3 +- MeowerBot/types/api/request/reports.py | 15 +-- MeowerBot/types/api/user.py | 7 ++ poetry.lock | 12 +- pyproject.toml | 1 + 8 files changed, 161 insertions(+), 37 deletions(-) diff --git a/MeowerBot/API.py b/MeowerBot/API.py index b3177f9..3f0f5f1 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -3,11 +3,15 @@ from httpx import AsyncClient, Auth import ujson as json from .types import generic -from .types.api.reports import ReportRequest, Report, AdminNotesResponse +from .types.api.reports import ReportRequest, Report, AdminNotesResponse, PagedRequest from .types.api.request.reports import UpdateReportBody, UpdateNotesBody from .types.api.request.chats import ChatBody from .types.generic import Post from .types.api.chats import Chats, ChatGroup +from .types.api.request import PostBody +from .types.api.user import User, Relationship +from .types.api.request.Users import UpdateRelationshipBody +from typing_extensions import Unpack class MeowerAPI: base_uri = "https://api.meower.org/" @@ -22,13 +26,13 @@ async def login(self, token): self.headers.update({"token": token}) async def admin_get_reports(self, timeout=None) -> ReportRequest: - resp = await self.client.get("/admin/reports", timeout=timeout) + resp = await self.client.get("/admin/reports", timeout=timeout, params=kwargs) if resp.status_code == 404: raise RuntimeError("[API] 404 Not found") return ReportRequest.from_json( - + resp.text ) async def admin_get_report(self, uuid: generic.UUID) -> Report: @@ -75,8 +79,8 @@ async def admin_fetch_note(self, indentifier: str) -> AdminNotesResponse: return AdminNotesResponse.from_json(resp.text) - async def admin_create_note(self, indentifier: str, **kwargs: UpdateNotesBody) -> AdminNotesResponse: - resp = await self.client.put(f"/admin/notes/{indentifier}", json=kwargs) + async def admin_create_note(self, indentifier: str, **kwargs: Unpack[UpdateNotesBody]) -> AdminNotesResponse: + resp = await self.client.put(f"/admin/notes/{indentifier}", params=kwargs, json=kwargs) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to edit/create notes") @@ -127,7 +131,7 @@ async def get_chats(self) -> Chats: return Chats.from_json(resp.text) - async def create_chat(self, **kwargs: ChatBody) -> ChatGroup: + async def create_chat(self, **kwargs: Unpack[ChatBody]) -> ChatGroup: resp = await self.client.post(f"/chats/", json=kwargs) if resp.status_code == 401: @@ -147,7 +151,7 @@ async def get_chat(self, uuid: generic.UUID) -> ChatGroup: return ChatGroup.from_json(resp.text) - async def update_chat(self, uuid: generic.UUID, **kwargs: ChatBody) -> ChatGroup: + async def update_chat(self, uuid: generic.UUID, **kwargs: Unpack[ChatBody]) -> ChatGroup: resp = await self.client.patch(f"/chats/{uuid}", josn=kwargs) if resp.status_code == 429: @@ -213,9 +217,139 @@ async def transfer_chat_ownership(self, uuid: generic.UUID, username: str) -> Ch return ChatGroup.from_json(resp.text) + async def get_posts(self, chat: str | generic.UUID, page: int = 1) -> PagedRequest[Post]: + if chat == "home": + resp = await self.client.get(f"/home/", params={"page": page}) + else: + resp = await self.client.get(f"/posts/{chat}", params={"page": page}) + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + + return PagedRequest[Post].from_json(resp.text) + + async def send_post(self, chat: str | generic.UUID, **kwargs: Unpack[PostBody]) -> Post: + if chat == "home": + resp = await self.client.post(f"/home/", json=kwargs) + else: + resp = await self.client.post(f"/posts/{chat}", json=kwargs) + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Sending posts") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Chat Not found") + + return Post.from_json(resp.text) + + async def get_post(self, uuid: generic.UUID) -> Post: + resp = await self.client.get(f"/posts", params={"id": uuid}) + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Post Not found") + + return Post.from_json(resp.text) + + async def update_post(self, uuid: generic.UUID, **kwargs: Unpack[PostBody]) -> Post: + resp = await self.client.patch(f"/posts", params={"id": uuid}, json=kwargs) + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Sending posts") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Post Not found") + + return Post.from_json(resp.text) + + async def delete_post(self, uuid: generic.UUID, **kwargs: Unpack[PostBody]) -> Post: + resp = await self.client.patch(f"/posts", params={"id": uuid}) + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Deleting posts") + + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 404: + raise RuntimeError("[API] 404 Post Not found") + + return Post.from_json(resp.text) + + async def get_inbox(self) -> PagedRequest[PostBody]: + resp = await self.client.get("/inbox") + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + return PagedRequest[PostBody].from_json(resp.text) + + async def search_users(self, query: str, page: int = 1 ) -> PagedRequest[User]: + resp = await self.client.get("/search/users", params={"q": query, "p": page},) + + return PagedRequest[User].from_json(resp.text) + + async def search_home(self, query: str, page: int = 1) -> PagedRequest[Post]: + resp = await self.client.get("/search/home", params={"q": query, "p": page}) + return PagedRequest[Post].from_json(resp.text) + # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 - # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/home.py - # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/posts.py - # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/inbox.py - # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/search.py # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/users.py + + async def _get_user(self, username, url, json=None, page=1, query = None): + resp = await self.client.get(urljoin(f"/users/{username}/", url), json=json, params={"q": query, "p": page}) + if resp.status_code == 404: + raise RuntimeError("[API] User does not exist") + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + if resp.status_code == 429: + raise RuntimeError("[API] Ratelimited: Updating chat") + + if resp.status_code == 403: + raise RuntimeError("[API] Blocked from doing this action") + + return resp.text + + async def get_user_posts(self, username, query, page = 1) -> PagedRequest[Post]: + return PagedRequest[Post].from_json(await self._get_user(username, "posts", query=query, page=page)) + + async def get_user_relationship(self, username) -> Relationship: + return Relationship.from_json(await self._get_user(username, "relationship")) + + async def edit_user_relationship(self, username, **kwargs: Unpack[UpdateRelationshipBody]) -> Relationship: + resp = await self.client.patch(f"/users/{username}/relationship", json=kwargs) + + if resp.status_code == 404: + raise RuntimeError("[API] User does not exist") + + if resp.status_code == 401: + raise RuntimeError("[API] No Auth to do this action") + + + + + + return Relationship.from_json(resp.text) + + async def dm_user(self, username) -> ChatGroup: + return ChatGroup.from_json(self._get_user(username, 'dm')) diff --git a/MeowerBot/types/api/request/Users.py b/MeowerBot/types/api/request/Users.py index 9658402..083b1ae 100644 --- a/MeowerBot/types/api/request/Users.py +++ b/MeowerBot/types/api/request/Users.py @@ -2,7 +2,5 @@ from dataclasses_json import dataclass_json from typing import Literal -@dataclass_json -@dataclass class UpdateRelationshipBody: state: Literal[0, 1, 2] \ No newline at end of file diff --git a/MeowerBot/types/api/request/__init__.py b/MeowerBot/types/api/request/__init__.py index ee70091..1bc92d6 100644 --- a/MeowerBot/types/api/request/__init__.py +++ b/MeowerBot/types/api/request/__init__.py @@ -3,7 +3,5 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json -@dataclass_json -@dataclass class PostBody: content: str diff --git a/MeowerBot/types/api/request/chats.py b/MeowerBot/types/api/request/chats.py index ddff9fd..3ee847d 100644 --- a/MeowerBot/types/api/request/chats.py +++ b/MeowerBot/types/api/request/chats.py @@ -1,8 +1,7 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json -@dataclass_json -@dataclass + class ChatBody: nickname: str diff --git a/MeowerBot/types/api/request/reports.py b/MeowerBot/types/api/request/reports.py index 842efeb..2457c1f 100644 --- a/MeowerBot/types/api/request/reports.py +++ b/MeowerBot/types/api/request/reports.py @@ -2,41 +2,28 @@ from dataclasses_json import dataclass_json from typing import Optional, List, Literal -@dataclass_json -@dataclass + class UpdateReportBody: status: Literal["no_action_taken", "action_taken"] -@dataclass_json -@dataclass class UpdateNotesBody: notes: str -@dataclass_json -@dataclass class UpdateUserBanBody: state: Literal["none", "temp_restriction", "perm_restriction", "temp_ban", "perm_ban"] restrictions: int expires: int reason: str -@dataclass_json -@dataclass class UpdateUserBody: permissions: Optional[int] = None -@dataclass_json -@dataclass class UpdateChatBody: nickname: str -@dataclass_json -@dataclass class InboxMessageBody: content: str -@dataclass_json -@dataclass class NetblockBody: type: int diff --git a/MeowerBot/types/api/user.py b/MeowerBot/types/api/user.py index 202b431..981b244 100644 --- a/MeowerBot/types/api/user.py +++ b/MeowerBot/types/api/user.py @@ -61,3 +61,10 @@ class User: pfp_data: int quote: str uuid: UUID + +@dataclass_json +@dataclass +class Relationship: + username: str + state: int + updated_at: int \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 5ed86cf..fcfa1ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -498,14 +498,14 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] @@ -607,4 +607,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "66385225f3a2c0512d0e9f6e50ccc2b7793f778187088f9fc1eb30140b064347" +content-hash = "a9b815054efcc6c0887ae2e3dde70fd22772b83d89049f17ed0ea0fe8603d447" diff --git a/pyproject.toml b/pyproject.toml index a7f3ca9..2c76812 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ websockets = "^11.0.3" orjson = "^3.9.4" httpx = "^0.25.0" dataclasses-json = "^0.6.1" +typing-extensions = "^4.8.0" [tool.poetry.group.extras.dependencies] From a7c3c18a08dba66329f36df9cd56a86e460c8a94 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sun, 15 Oct 2023 23:17:55 -0500 Subject: [PATCH 07/13] ;-; i cant get it to type correctly --- MeowerBot/Bot.py | 9 +++++---- MeowerBot/context.py | 8 ++++++-- MeowerBot/types/api/request/Users.py | 3 ++- MeowerBot/types/api/request/__init__.py | 4 +++- MeowerBot/types/api/request/chats.py | 3 ++- MeowerBot/types/api/request/reports.py | 15 ++++++++------- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index 5531598..254b88c 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -128,6 +128,7 @@ def inner(func): self.callbacks[callback].append(func) + @@ -161,9 +162,9 @@ async def run(self, username, password, server="wss://server.meower.org"): self.prefix = "@" + self.username self.logger = logging.getLogger(f"MeowerBot {self.username}") self.server = server - try: - self.api = MeowerAPI(username=username) - except: # nosec - pass + + self.api = MeowerAPI(username=username) + + await self.connect(server) diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 199e048..e94d766 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -12,10 +12,14 @@ class Chat: - def __init__(self, id, display_name): + def __init__(self, id, display_name, bot: "Bot"): self.id = id self.display_name = display_name - + self.bot: "Bot" = bot + + async def send_msg(self, message): + self.bot.api.send_post(self.id, message) + class Post: def __init__(self, bot, _raw): diff --git a/MeowerBot/types/api/request/Users.py b/MeowerBot/types/api/request/Users.py index 083b1ae..d7969a1 100644 --- a/MeowerBot/types/api/request/Users.py +++ b/MeowerBot/types/api/request/Users.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json from typing import Literal +from typing import TypedDict -class UpdateRelationshipBody: +class UpdateRelationshipBody(TypedDict): state: Literal[0, 1, 2] \ No newline at end of file diff --git a/MeowerBot/types/api/request/__init__.py b/MeowerBot/types/api/request/__init__.py index 1bc92d6..fcd5df3 100644 --- a/MeowerBot/types/api/request/__init__.py +++ b/MeowerBot/types/api/request/__init__.py @@ -3,5 +3,7 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json -class PostBody: +from typing import TypedDict + +class PostBody(TypedDict): content: str diff --git a/MeowerBot/types/api/request/chats.py b/MeowerBot/types/api/request/chats.py index 3ee847d..c8089c7 100644 --- a/MeowerBot/types/api/request/chats.py +++ b/MeowerBot/types/api/request/chats.py @@ -1,7 +1,8 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json +from typing import TypedDict -class ChatBody: +class ChatBody( TypedDict ): nickname: str diff --git a/MeowerBot/types/api/request/reports.py b/MeowerBot/types/api/request/reports.py index 2457c1f..ab19670 100644 --- a/MeowerBot/types/api/request/reports.py +++ b/MeowerBot/types/api/request/reports.py @@ -1,29 +1,30 @@ from dataclasses import dataclass from dataclasses_json import dataclass_json from typing import Optional, List, Literal +from typing import TypedDict -class UpdateReportBody: +class UpdateReportBody(TypedDict): status: Literal["no_action_taken", "action_taken"] -class UpdateNotesBody: +class UpdateNotesBody(TypedDict): notes: str -class UpdateUserBanBody: +class UpdateUserBanBody(TypedDict): state: Literal["none", "temp_restriction", "perm_restriction", "temp_ban", "perm_ban"] restrictions: int expires: int reason: str -class UpdateUserBody: +class UpdateUserBody(TypedDict): permissions: Optional[int] = None -class UpdateChatBody: +class UpdateChatBody(TypedDict): nickname: str -class InboxMessageBody: +class InboxMessageBody(TypedDict): content: str -class NetblockBody: +class NetblockBody(TypedDict): type: int From 16c8a926795c7f3620e8bd4a5bdefc66eef0d625 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Mon, 16 Oct 2023 17:21:51 -0500 Subject: [PATCH 08/13] redo context --- MeowerBot/API.py | 38 ++++++------- MeowerBot/context.py | 73 ++++++++++++++++++++----- MeowerBot/types/api/request/Users.py | 7 --- MeowerBot/types/api/request/__init__.py | 9 --- MeowerBot/types/api/request/chats.py | 8 --- MeowerBot/types/api/request/reports.py | 30 ---------- 6 files changed, 75 insertions(+), 90 deletions(-) delete mode 100644 MeowerBot/types/api/request/Users.py delete mode 100644 MeowerBot/types/api/request/__init__.py delete mode 100644 MeowerBot/types/api/request/chats.py delete mode 100644 MeowerBot/types/api/request/reports.py diff --git a/MeowerBot/API.py b/MeowerBot/API.py index 3f0f5f1..d09920d 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -4,14 +4,10 @@ import ujson as json from .types import generic from .types.api.reports import ReportRequest, Report, AdminNotesResponse, PagedRequest -from .types.api.request.reports import UpdateReportBody, UpdateNotesBody -from .types.api.request.chats import ChatBody from .types.generic import Post from .types.api.chats import Chats, ChatGroup -from .types.api.request import PostBody from .types.api.user import User, Relationship -from .types.api.request.Users import UpdateRelationshipBody -from typing_extensions import Unpack +from typing import Literal class MeowerAPI: base_uri = "https://api.meower.org/" @@ -47,8 +43,8 @@ async def admin_get_report(self, uuid: generic.UUID) -> Report: return Report.from_json(resp.text) - async def admin_edit_report(self, uuid: generic.UUID, **kwargs: UpdateReportBody) -> Report: - resp = await self.client.patch(f"/admin/reports/{uuid}", json=kwargs) + async def admin_edit_report(self, uuid: generic.UUID, status: Literal["no_action_taken", "action_taken"]) -> Report: + resp = await self.client.patch(f"/admin/reports/{uuid}", json={"status": status}) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to edit reports") @@ -79,8 +75,8 @@ async def admin_fetch_note(self, indentifier: str) -> AdminNotesResponse: return AdminNotesResponse.from_json(resp.text) - async def admin_create_note(self, indentifier: str, **kwargs: Unpack[UpdateNotesBody]) -> AdminNotesResponse: - resp = await self.client.put(f"/admin/notes/{indentifier}", params=kwargs, json=kwargs) + async def admin_create_note(self, indentifier: str, notes: str) -> AdminNotesResponse: + resp = await self.client.put(f"/admin/notes/{indentifier}", json={"notes": notes}) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to edit/create notes") @@ -131,8 +127,8 @@ async def get_chats(self) -> Chats: return Chats.from_json(resp.text) - async def create_chat(self, **kwargs: Unpack[ChatBody]) -> ChatGroup: - resp = await self.client.post(f"/chats/", json=kwargs) + async def create_chat(self, nickname: str) -> ChatGroup: + resp = await self.client.post(f"/chats/", json={"nickname": nickname}) if resp.status_code == 401: raise RuntimeError(json.parse(resp.text)["type"]) @@ -151,8 +147,8 @@ async def get_chat(self, uuid: generic.UUID) -> ChatGroup: return ChatGroup.from_json(resp.text) - async def update_chat(self, uuid: generic.UUID, **kwargs: Unpack[ChatBody]) -> ChatGroup: - resp = await self.client.patch(f"/chats/{uuid}", josn=kwargs) + async def update_chat(self, uuid: generic.UUID, nickname: str) -> ChatGroup: + resp = await self.client.patch(f"/chats/{uuid}", josn={"nickname": nickname}) if resp.status_code == 429: raise RuntimeError("[API] Ratelimited: Updating chat") @@ -233,11 +229,11 @@ async def get_posts(self, chat: str | generic.UUID, page: int = 1) -> PagedReque return PagedRequest[Post].from_json(resp.text) - async def send_post(self, chat: str | generic.UUID, **kwargs: Unpack[PostBody]) -> Post: + async def send_post(self, chat: str | generic.UUID, content: str) -> Post: if chat == "home": - resp = await self.client.post(f"/home/", json=kwargs) + resp = await self.client.post(f"/home/", json={"content": content}) else: - resp = await self.client.post(f"/posts/{chat}", json=kwargs) + resp = await self.client.post(f"/posts/{chat}", json={"content": content}) if resp.status_code == 429: raise RuntimeError("[API] Ratelimited: Sending posts") @@ -261,8 +257,8 @@ async def get_post(self, uuid: generic.UUID) -> Post: return Post.from_json(resp.text) - async def update_post(self, uuid: generic.UUID, **kwargs: Unpack[PostBody]) -> Post: - resp = await self.client.patch(f"/posts", params={"id": uuid}, json=kwargs) + async def update_post(self, uuid: generic.UUID, content: str) -> Post: + resp = await self.client.patch(f"/posts", params={"id": uuid}, json={"content": content}) if resp.status_code == 429: raise RuntimeError("[API] Ratelimited: Sending posts") @@ -277,7 +273,7 @@ async def update_post(self, uuid: generic.UUID, **kwargs: Unpack[PostBody]) -> P return Post.from_json(resp.text) - async def delete_post(self, uuid: generic.UUID, **kwargs: Unpack[PostBody]) -> Post: + async def delete_post(self, uuid: generic.UUID) -> Post: resp = await self.client.patch(f"/posts", params={"id": uuid}) if resp.status_code == 429: @@ -336,8 +332,8 @@ async def get_user_posts(self, username, query, page = 1) -> PagedRequest[Post]: async def get_user_relationship(self, username) -> Relationship: return Relationship.from_json(await self._get_user(username, "relationship")) - async def edit_user_relationship(self, username, **kwargs: Unpack[UpdateRelationshipBody]) -> Relationship: - resp = await self.client.patch(f"/users/{username}/relationship", json=kwargs) + async def edit_user_relationship(self, username, state: Literal[0, 1, 2]) -> Relationship: + resp = await self.client.patch(f"/users/{username}/relationship", json={"state": state}) if resp.status_code == 404: raise RuntimeError("[API] User does not exist") diff --git a/MeowerBot/context.py b/MeowerBot/context.py index e94d766..99dc885 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -8,45 +8,88 @@ import weakref import requests +from .types.api.chats import ChatGroup +from .types.api.user import User as RawUser - - -class Chat: - def __init__(self, id, display_name, bot: "Bot"): +class PartialChat: + def __init__(self, id, bot: "Bot"): self.id = id - self.display_name = display_name self.bot: "Bot" = bot - async def send_msg(self, message): - self.bot.api.send_post(self.id, message) + async def send_msg(self, message) -> Post: + return await Post(self.bot, self.bot.api.send_post(self.id, message).to_dict(), self) + + async def fetch(self): + return Chat(await self.bot.api.get_chat(self.id)) +class Chat(PartialChat): + def __init__(self, data: ChatGroup, bot): + super().__init__(data._id, bot) + + self.created = data.created + self.deleted = data.deleted + self.last_active = data.last_active + self.members = data.members + + self.owner = data.owner + self.type = data.type + self.nickname = data.nickname + +class PartialUser: + def __init__(self, username, bot: "Bot"): + self.username = username + self.bot = bot + + async def fetch(self): + return User(self.username, RawUser.from_json(await self.bot.api._get_user(username, ""))) + +class User(PartialUser): + def __init__(self, username, bot, data: RawUser): + super().__init__(username, bot) + + self.data: RawUser = data + + self.banned = self.data.banned + self.created = self.data.created + self.flags = self.data.flags + self.last_seen = self.data.last_seen + self.data.lower_username = self.data.lower_username + self.lvl = self.data.lvl + self.name = self.data.name + self.permissions = self.data.permissions + self.pfp_data = self.data.pfp_data + self.quote = self.data.quote + self.id = self.data.uuid + class Post: - def __init__(self, bot, _raw): + def __init__(self, bot, _raw, chat): self.bot = bot self._raw = _raw - self.user = User(bot, self._raw["u"]) + self.user: User = User(bot, self._raw["u"]) - self.chat = self._raw["post_origin"] + self.chat: PartialChat = PartialChat(chat, bot) self.data = self._raw["p"] self._id = self._raw["post_id"] self.type = self._raw["type"] self.date = datetime.fromtimestamp(self._raw["t"]["e"]) - self.ctx: CTX = None # type: ignore def __str__(self): return str(self.data) + + async def reply(self, message): + self.chat.send_msg(f"@{self.user.name} [{self.id}]") -class CTX: +class Context: def __init__(self, post, bot): self.message = Post(bot, post) self.user = self.message.user self.bot = bot - self.message.ctx = weakref.proxy(self) # type: ignore async def send_msg(self, msg): - await self.bot.send_msg(msg, to=self.message.chat) + self.message.chat.send_msg(msg) async def reply(self, msg): - await self.user.ping(msg, to=self.message.chat) + await self.message.reply(msg) + diff --git a/MeowerBot/types/api/request/Users.py b/MeowerBot/types/api/request/Users.py deleted file mode 100644 index d7969a1..0000000 --- a/MeowerBot/types/api/request/Users.py +++ /dev/null @@ -1,7 +0,0 @@ -from dataclasses import dataclass -from dataclasses_json import dataclass_json -from typing import Literal -from typing import TypedDict - -class UpdateRelationshipBody(TypedDict): - state: Literal[0, 1, 2] \ No newline at end of file diff --git a/MeowerBot/types/api/request/__init__.py b/MeowerBot/types/api/request/__init__.py deleted file mode 100644 index fcd5df3..0000000 --- a/MeowerBot/types/api/request/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ - - -from dataclasses import dataclass -from dataclasses_json import dataclass_json - -from typing import TypedDict - -class PostBody(TypedDict): - content: str diff --git a/MeowerBot/types/api/request/chats.py b/MeowerBot/types/api/request/chats.py deleted file mode 100644 index c8089c7..0000000 --- a/MeowerBot/types/api/request/chats.py +++ /dev/null @@ -1,8 +0,0 @@ -from dataclasses import dataclass -from dataclasses_json import dataclass_json -from typing import TypedDict - - -class ChatBody( TypedDict ): - nickname: str - diff --git a/MeowerBot/types/api/request/reports.py b/MeowerBot/types/api/request/reports.py deleted file mode 100644 index ab19670..0000000 --- a/MeowerBot/types/api/request/reports.py +++ /dev/null @@ -1,30 +0,0 @@ -from dataclasses import dataclass -from dataclasses_json import dataclass_json -from typing import Optional, List, Literal -from typing import TypedDict - - -class UpdateReportBody(TypedDict): - status: Literal["no_action_taken", "action_taken"] - -class UpdateNotesBody(TypedDict): - notes: str - -class UpdateUserBanBody(TypedDict): - state: Literal["none", "temp_restriction", "perm_restriction", "temp_ban", "perm_ban"] - restrictions: int - expires: int - reason: str - -class UpdateUserBody(TypedDict): - permissions: Optional[int] = None - -class UpdateChatBody(TypedDict): - nickname: str - -class InboxMessageBody(TypedDict): - content: str - -class NetblockBody(TypedDict): - type: int - From 16d75d520e4290af40547f1a1c380a3b26613f4d Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Mon, 16 Oct 2023 17:23:05 -0500 Subject: [PATCH 09/13] add missing await --- MeowerBot/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 99dc885..c078360 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -88,7 +88,7 @@ def __init__(self, post, bot): self.bot = bot async def send_msg(self, msg): - self.message.chat.send_msg(msg) + await self.message.chat.send_msg(msg) async def reply(self, msg): await self.message.reply(msg) From 5f20263dfca4936c5dfc09ac0c28635413e50dcd Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Wed, 18 Oct 2023 19:45:10 -0500 Subject: [PATCH 10/13] start impl --- MeowerBot/Bot.py | 97 ++++++++++++++++-- MeowerBot/cl/__init__.py | 64 +++++++++++- MeowerBot/cog.py | 15 ++- MeowerBot/command.py | 52 +++++----- SECURITY.md | 3 +- poetry.lock | 212 +++++++++++++++------------------------ pyproject.toml | 3 +- 7 files changed, 270 insertions(+), 176 deletions(-) diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index 254b88c..bb5fdd4 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -12,7 +12,7 @@ import time from .command import AppCommand -from .context import CTX +from .context import Context, Post, PartialUser, User import time import logging @@ -22,10 +22,7 @@ import asyncio import sys -if sys.version_info >= (3, 11): - from enum import StrEnum -else: - from backports.strenum import StrEnum +from enum import StrEnum from typing import Union @@ -34,11 +31,9 @@ class cbids(StrEnum): __raw__ = "__raw__" login = "login" close = "close" - statuscode = "statuscode" ulist = "ulist" message = "message" raw_message = "raw_message" - chat_list = "chat_list" direct = "direct" callbacks = [i for i in cbids] @@ -46,6 +41,8 @@ class cbids(StrEnum): class Bot(Client): messages = [] message_condition = asyncio.Condition() + + """ A class that holds all of the networking for a meower bot to function and run """ @@ -128,21 +125,101 @@ def inner(func): self.callbacks[callback].append(func) + def subcommand(self, name=None, args=0, aliases = None): + def inner(func): - + cmd = AppCommand(func, name=name, args=args) + cmd.register_class(self.connected) + + self.commands = AppCommand.add_command(self.commands, cmd) + + + return cmd #dont want mb to register this as a root command + return inner + def update_commands(): + for cog in self.cogs: + cog.update_commands() + + self.commands = self.commands.update(cog.__commands__) + + async def error(self, err: Exception): pass + async def __raw__(self, packet: dict): pass + async def login(self, token: str): pass + async def close(self): pass + async def ulist(self, ulist): pass + + async def message(self, message: Post): + message = self.handle_bridges(message) + async def raw_message(self, data: dict): pass + async def direct(self, data: dict): pass + + async def _run_event(self, event: cbids, *args, **kwargs): + err = await asyncio.gather(*([getattr(self, event)] + self.callbacks[event]), return_exceptions=True) + for i in err: + if isinstance(i, Exception): + await self._error(i) # websocket + + def handle_bridges(self, message: Post): + fetch = False + if isinstance(message.user, User): + fetch = True + + if message.user.name in self.__bridges__ and ":" in message.data: + split = message.data.split(": ", 1) + message.data = split[1] + message.user = PartialUser(split[0], self) + if fetch: + message.user = await message.user.fetch() + + + + if message.data.startswith(self.prefix + "#0000"): + message.data = message.data.replace("#0000", "") + + return packet + + async def _connect(self): - pass + await self.sendPacket({"cmd": "direct", "val": { + "cmd": "type", "val": "MeowerBot.py" + }}) + + if (await self.send_statuscode_request({ "cmd": "direct", val: "meower", "listener": "send_tkey" }))["val"] != "I: 100 | OK": + raise RuntimeError("Meower Trust Failed!") + + resp = await self.send_data_request( + { + "cmd": "direct", + "val": { + "cmd": "authpswd", + "val": { + "username": self.username, + "pswd": self.password + } + } + } + ) + + if resp[1]["val"] != "I: 100 | OK": + raise Exception(f"Wrong Username or Password!\n {resp[1]['val']}") + + self.api.login(resp[0]["val"["val"]["pswd"]]) + + await self._run_event(cbids.login, resp[0]["val"]["pswd"]) + + + async def _disconnect(self): - pass + await self._run_event(cbids.close) async def _message(self, message): pass diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index 20b564a..477aa8d 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -2,13 +2,16 @@ import orjson import json import asyncio - +import random class Client: ws: websockets.WebSocketClientProtocol + message_condition = asyncio.Condition() + __messages: str def __init__(self): self.ws = None + self.__messages = [] pass async def _connect(self): @@ -26,6 +29,54 @@ async def _error(self, error): async def sendPacket(self, message): await self.ws.send(json.dumps(message)) + async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): + """ + Wait for multiple JSON packets, each meeting a specific condition, in the bot's messages list. + Args: + - conditions (list of dict): A list of dictionaries, each representing a condition to check for. + - expected_count (int): Optional. The number of conditions to wait for. If None, wait for all conditions. + - timeout (float): Optional. Maximum time to wait in seconds for the specified conditions. If None, it will wait indefinitely. + Returns: + - A list of messages that meet the specified conditions, or None if timeout is reached. + """ + start_time = time.time() + received_packets = [] + + while True: + async with self.message_condition: + for condition in conditions: + if expected_count is not None and len(received_packets) >= expected_count: + return received_packets + found = False + for message in self.__messages: + if all(message.get(key) == value for key, value in condition.items()): + received_packets.append(message) + found = True + break + if not found: + break + + if expected_count is None and len(received_packets) == len(conditions): + return received_packets + if timeout is not None and time.time() - start_time >= timeout: + return None + await asyncio.sleep(0.1) + + async def send_data_request(self, packet, timeout = None): + if packet.get("listener") == None: + # This does not need to be sucure lmao + packet["listener"] = random.random() # nosec + + await self.sendPacket(packet) + return self._wait_for_packet({"listener": packet["listener"]}, expected_count=2, timeout=timeout) + + async def send_statuscode_request(self, packet, timeout = 0): + if packet.get("listener") == None: + # This does not need to be sucure lmao + packet["listener"] = random.random() # nosec + + await self.sendPacket(packet) + return self._wait_for_packet({"listener": packet["listener"]}, expected_count=1, timeout=timeout)[0] async def close(self, reason=None): await self.ws.close(reason=reason) @@ -39,9 +90,14 @@ async def connect(self, server): async for message in websocket: try: - - - await self._message(json.loads(message)) + data = json.loads(message) + with self.message_condition: + self.__messages.append(data) + self.message_condition.notify_all() + self.__messages = self.__messages[:50] + + + await self._message(data) except websockets.ConnectionClosed: diff --git a/MeowerBot/cog.py b/MeowerBot/cog.py index fa3caa9..6268344 100644 --- a/MeowerBot/cog.py +++ b/MeowerBot/cog.py @@ -3,19 +3,30 @@ class Cog: - __commands__ = None + commands = None __instence__ = None def __init__(self) -> None: + if isinstance(self.__instence__, Cog): + return + + self.__class__.__instence__ = self commands = {} + for command in self.__dir__(): attr = getattr(self, command) if isinstance(attr, AppCommand): attr.register_class(self) commands.update(attr.info()) - self.__commands__ = commands + self.commands = commands + + def update_commands(self): + for command in self.__dir__(): + attr = getattr(self, command) + if isinstance(attr, AppCommand): + self.commands = AppCommand.add_command(self.commands, attr) def __new__(cls, *args, **kwargs): if cls.__instence__ is None: diff --git a/MeowerBot/command.py b/MeowerBot/command.py index e77bdb0..3c9a965 100644 --- a/MeowerBot/command.py +++ b/MeowerBot/command.py @@ -9,14 +9,31 @@ class AppCommand: connected = None - def __init__(self, func, name=None, args=0, is_subcommand=False): + @staticmethod + def add_command(obj: dict, command: AppCommand, ignore_subcommands = True): + if command.is_subcommand and ignore_subcommands: + return obj + + for alias in cmd.alias + [cmd.name]: + obj[alias] = cmd + + + + + return obj + + + def __init__(self, func, alias = None, name=None, args=0, is_subcommand=False): if name is None: name = func.__name__ + if alias is None: + alias = [] + self.name = name self.func = func self.args_num = args - + self.alias = alias spec = inspect.signature(func) # Get the names and annotations of the arguments @@ -57,16 +74,16 @@ def register_class(self, con): - def subcommand(self, name=None, args=0): + def subcommand(self, name=None, args=0, aliases = None): def inner(func): cmd = AppCommand(func, name=name, args=args) cmd.register_class(self.connected) - self.subcommands.update(cmd.info()) - + self.subcommand = AppCommand.add_command(self.subcommands, cmd) + self.connected.update_commands() - return func #dont want mb to register this as a root command + return cmd #dont want mb to register this as a root command return inner @@ -93,27 +110,8 @@ async def run_cmd(self, ctx, *args): else: await self.func(self.connected, ctx, *args) - def info(self): - return { - self.name: { - "args": self.args, - "args_num": self.args_num, - "optional_args": self.optional_args, - "has_unamed_args": self.has_unamed_args, - "subcommands": self.subcommands, - "command": self, - "func": self.func, - - } - } - - -class _Command(AppCommand): - def __init__(self, func, *args, name=None, **kwargs): - super().__init__(func, *args, name=name, **kwargs) - warnings.warn( - "MeowerBot.command._Command has been renamed to MeowerBot.command.AppCommand" - ) + + def command(name=None, args=0): diff --git a/SECURITY.md b/SECURITY.md index 1b235f0..e8ced35 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,8 @@ | Version | Supported | | ------- | ------------------ | -| 2.x.x | :white_check_mark: | +| 3.x.x | :white_check_mark: | +| 2.x.x | :x: | | 1.x.x | :x: | diff --git a/poetry.lock b/poetry.lock index fcfa1ec..dc6177a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 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 = "anyio" version = "4.0.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -13,7 +12,6 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" @@ -22,22 +20,10 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.22)"] -[[package]] -name = "backports-strenum" -version = "1.2.4" -description = "Base class for creating enumerated constants that are also subclasses of str" -category = "main" -optional = false -python-versions = ">=3.8, <4" -files = [ - {file = "backports.strenum-1.2.4.tar.gz", hash = "sha256:87b67fd1413af3ce959b565d84ddef99776cdd97a62e2fd2d0550cdacc210dee"}, -] - [[package]] name = "bandit" version = "1.7.5" description = "Security oriented static analyser for python code." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -61,7 +47,6 @@ yaml = ["PyYAML"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -73,7 +58,6 @@ files = [ name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -85,7 +69,6 @@ files = [ name = "dataclasses-json" version = "0.6.1" description = "Easily serialize dataclasses to and from JSON." -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -97,26 +80,10 @@ files = [ marshmallow = ">=3.18.0,<4.0.0" typing-inspect = ">=0.4.0,<1" -[[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "gitdb" version = "4.0.10" description = "Git Object Database" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -129,24 +96,25 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.32" +version = "3.1.38" description = "GitPython is a Python library used to interact with Git repositories" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.32-py3-none-any.whl", hash = "sha256:e3d59b1c2c6ebb9dfa7a184daf3b6dd4914237e7488a1730a6d8f6f5d0b4187f"}, - {file = "GitPython-3.1.32.tar.gz", hash = "sha256:8d9b8cb1e80b9735e8717c9362079d3ce4c6e5ddeebedd0361b228c3a67a62f6"}, + {file = "GitPython-3.1.38-py3-none-any.whl", hash = "sha256:9e98b672ffcb081c2c8d5aa630d4251544fb040fb158863054242f24a2a2ba30"}, + {file = "GitPython-3.1.38.tar.gz", hash = "sha256:4d683e8957c8998b58ddb937e3e6cd167215a180e1ffd4da769ab81c620a89fe"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] + [[package]] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -158,7 +126,6 @@ files = [ name = "httpcore" version = "0.18.0" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -170,17 +137,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httpx" version = "0.25.0" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -196,15 +162,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -216,7 +181,6 @@ files = [ name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -241,7 +205,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "marshmallow" version = "3.20.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -262,7 +225,6 @@ tests = ["pytest", "pytz", "simplejson"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -274,7 +236,6 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -284,75 +245,67 @@ files = [ [[package]] name = "orjson" -version = "3.9.4" +version = "3.9.9" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "orjson-3.9.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2e83ec1ee66d83b558a6d273d8a01b86563daa60bea9bc040e2c1cb8008de61f"}, - {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32a9e0f140c7d0d52f79553cabd1a471f6a4f187c59742239939f1139258a053"}, - {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb429c56ea645e084e34976c2ea0efca7661ee961f61e51405f28bc5a9d1fb24"}, - {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fb7963c17ab347428412a0689f5c89ea480f5d5f7ba3e46c6c2f14f3159ee4"}, - {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224ad19dcdc21bb220d893807f2563e219319a8891ead3c54243b51a4882d767"}, - {file = "orjson-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4974cc2ebb53196081fef96743c02c8b073242b20a40b65d2aa2365ba8c949df"}, - {file = "orjson-3.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b39747f8e57728b9d8c26bd1d28e9a31c028717617a5938a179244b9436c0b31"}, - {file = "orjson-3.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a31c2cab0ba86998205c2eba550c178a8b4ee7905cadeb402eed45392edb178"}, - {file = "orjson-3.9.4-cp310-none-win32.whl", hash = "sha256:04cd7f4a4f4cd2fe43d104eb70e7435c6fcbdde7aa0cde4230e444fbc66924d3"}, - {file = "orjson-3.9.4-cp310-none-win_amd64.whl", hash = "sha256:4fdb59cfa00e10c82e09d1c32a9ce08a38bd29496ba20a73cd7f498e3a0a5024"}, - {file = "orjson-3.9.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:daeed2502ddf1f2b29ec8da2fe2ea82807a5c4acf869608ce6c476db8171d070"}, - {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73d9507a547202f0dd0672e529ce3ca45582d152369c684a9ce75677ce5ae089"}, - {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144a3b8c7cbdd301e1b8cd7dd33e3cbfe7b011df2bebd45b84bacc8cb490302d"}, - {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef7119ebc9b76d5e37c330596616c697d1957779c916aec30cefd28df808f796"}, - {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b75f0fc7a64a95027c6f0c70f17969299bdf2b6a85e342b29fc23be2788bad6f"}, - {file = "orjson-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e4b20164809b21966b63e063f894927bc85391e60d0a96fa0bb552090f1319c"}, - {file = "orjson-3.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e7c3b7e29572ef2d845a59853475f40fdabec53b8b7d6effda4bb26119c07f5"}, - {file = "orjson-3.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d73c0fd54a52a1a1abfad69d4f1dfb7048cd0b3ef1828ddb4920ef2d3739d8fb"}, - {file = "orjson-3.9.4-cp311-none-win32.whl", hash = "sha256:e12492ce65cb10f385e70a88badc6046bc720fa7d468db27b7429d85d41beaeb"}, - {file = "orjson-3.9.4-cp311-none-win_amd64.whl", hash = "sha256:3b9f8bf43a5367d5522f80e7d533c98d880868cd0b640b9088c9237306eca6e8"}, - {file = "orjson-3.9.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0b400cf89c15958cd829c8a4ade8f5dd73588e63d2fb71a00483e7a74e9f92da"}, - {file = "orjson-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d3b6f2706cb324661899901e6b1fcaee4f5aac7d7588306df3f43e68173840"}, - {file = "orjson-3.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3932b06abf49135c93816c74139c7937fa54079fce3f44db2d598859894c344a"}, - {file = "orjson-3.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:562cf24f9f11df8099e0e78859ba6729e7caa25c2f3947cb228d9152946c854b"}, - {file = "orjson-3.9.4-cp312-none-win_amd64.whl", hash = "sha256:a533e664a0e3904307d662c5d45775544dc2b38df6e39e213ff6a86ceaa3d53c"}, - {file = "orjson-3.9.4-cp37-cp37m-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:149d1b7630771222f73ecb024ab5dd8e7f41502402b02015494d429bacc4d5c1"}, - {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:004f0d307473af210717260dab2ddceab26750ef5d2c6b1f7454c33f7bb69f0c"}, - {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba21fe581a83555024f3cfc9182a2390a61bc50430364855022c518b8ba285a4"}, - {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1fb36efdf2a35286fb87cfaa195fc34621389da1c7b28a8eb51a4d212d60e56d"}, - {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644728d803200d7774164d252a247e2fcb0d19e4ef7a4a19a1a139ae472c551b"}, - {file = "orjson-3.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bae10f4e7a9145b120e37b6456f1d3853a953e5131fe4740a764e46420289f5"}, - {file = "orjson-3.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c416c50f63bfcf453b6e28d1df956938486191fd1a15aeb95107e810e6e219c8"}, - {file = "orjson-3.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:220ca4125416636a3d6b53a77d50434987a83da243f9080ee4cce7ac6a34bb4a"}, - {file = "orjson-3.9.4-cp37-none-win32.whl", hash = "sha256:bcda6179eb863c295eb5ea832676d33ef12c04d227b4c98267876c8322e5a96e"}, - {file = "orjson-3.9.4-cp37-none-win_amd64.whl", hash = "sha256:3d947366127abef192419257eb7db7fcee0841ced2b49ccceba43b65e9ce5e3f"}, - {file = "orjson-3.9.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a7d029fc34a516f7eae29b778b30371fcb621134b2acfe4c51c785102aefc6cf"}, - {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c65df12f92e771361dca45765fcac3d97491799ee8ab3c6c5ecf0155a397a313"}, - {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b749d06a3d84ac27311cb85fb5e8f965efd1c5f27556ad8fcfd1853c323b4d54"}, - {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:161cc72dd3ff569fd67da4af3a23c0c837029085300f0cebc287586ae3b559e0"}, - {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:edcbccfe852d1d3d56cc8bfc5fa3688c866619328a73cb2394e79b29b4ab24d2"}, - {file = "orjson-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0725260a12d7102b6e66f9925a027f55567255d8455f8288b02d5eedc8925c3e"}, - {file = "orjson-3.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53b417cc9465dbb42ec9cd7be744a921a0ce583556315d172a246d6e71aa043b"}, - {file = "orjson-3.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ab3720fba68cc1c0bad00803d2c5e2c70177da5af12c45e18cc4d14426d56d8"}, - {file = "orjson-3.9.4-cp38-none-win32.whl", hash = "sha256:94d15ee45c2aaed334688e511aa73b4681f7c08a0810884c6b3ae5824dea1222"}, - {file = "orjson-3.9.4-cp38-none-win_amd64.whl", hash = "sha256:336ec8471102851f0699198031924617b7a77baadea889df3ffda6000bd59f4c"}, - {file = "orjson-3.9.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2f57ccb50e9e123709e9f2d7b1a9e09e694e49d1fa5c5585e34b8e3f01929dc3"}, - {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e876ef36801b3d4d3a4b0613b6144b0b47f13f3043fd1fcdfafd783c174b538"}, - {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f009c1a02773bdecdd1157036918fef1da47f7193d4ad599c9edb1e1960a0491"}, - {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0a4cf31bfa94cd235aa50030bef3df529e4eb2893ea6a7771c0fb087e4e53b2"}, - {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c32dea3b27a97ac88783c1eb61ccb531865bf478a37df3707cbc96ca8f34a04"}, - {file = "orjson-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:264637cad35a1755ab90a8ea290076d444deda20753e55a0eb75496a4645f7bc"}, - {file = "orjson-3.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a4f12e9ec62679c3f2717d9ec41b497a2c2af0b1361229db0dc86ef078a4c034"}, - {file = "orjson-3.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c4fcd1ac0b7850f85398fd9fdbc7150ac4e82d2ae6754cc6acaf49ca7c30d79a"}, - {file = "orjson-3.9.4-cp39-none-win32.whl", hash = "sha256:b5b5038187b74e2d33e5caee8a7e83ddeb6a21da86837fa2aac95c69aeb366e6"}, - {file = "orjson-3.9.4-cp39-none-win_amd64.whl", hash = "sha256:915da36bc93ef0c659fa50fe7939d4f208804ad252fc4fc8d55adbbb82293c48"}, - {file = "orjson-3.9.4.tar.gz", hash = "sha256:a4c9254d21fc44526a3850355b89afd0d00ed73bdf902a5ab416df14a61eac6b"}, + {file = "orjson-3.9.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f28090060a31f4d11221f9ba48b2273b0d04b702f4dcaa197c38c64ce639cc51"}, + {file = "orjson-3.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8038ba245d0c0a6337cfb6747ea0c51fe18b0cf1a4bc943d530fd66799fae33d"}, + {file = "orjson-3.9.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:543b36df56db195739c70d645ecd43e49b44d5ead5f8f645d2782af118249b37"}, + {file = "orjson-3.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e7877256b5092f1e4e48fc0f1004728dc6901e7a4ffaa4acb0a9578610aa4ce"}, + {file = "orjson-3.9.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12b83e0d8ba4ca88b894c3e00efc59fe6d53d9ffb5dbbb79d437a466fc1a513d"}, + {file = "orjson-3.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef06431f021453a47a9abb7f7853f04f031d31fbdfe1cc83e3c6aadde502cce"}, + {file = "orjson-3.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0a1a4d9e64597e550428ba091e51a4bcddc7a335c8f9297effbfa67078972b5c"}, + {file = "orjson-3.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:879d2d1f6085c9c0831cec6716c63aaa89e41d8e036cabb19a315498c173fcc6"}, + {file = "orjson-3.9.9-cp310-none-win32.whl", hash = "sha256:d3f56e41bc79d30fdf077073072f2377d2ebf0b946b01f2009ab58b08907bc28"}, + {file = "orjson-3.9.9-cp310-none-win_amd64.whl", hash = "sha256:ab7bae2b8bf17620ed381e4101aeeb64b3ba2a45fc74c7617c633a923cb0f169"}, + {file = "orjson-3.9.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:31d676bc236f6e919d100fb85d0a99812cff1ebffaa58106eaaec9399693e227"}, + {file = "orjson-3.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:678ffb5c0a6b1518b149cc328c610615d70d9297e351e12c01d0beed5d65360f"}, + {file = "orjson-3.9.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a71b0cc21f2c324747bc77c35161e0438e3b5e72db6d3b515310457aba743f7f"}, + {file = "orjson-3.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae72621f216d1d990468291b1ec153e1b46e0ed188a86d54e0941f3dabd09ee8"}, + {file = "orjson-3.9.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:512e5a41af008e76451f5a344941d61f48dddcf7d7ddd3073deb555de64596a6"}, + {file = "orjson-3.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f89dc338a12f4357f5bf1b098d3dea6072fb0b643fd35fec556f4941b31ae27"}, + {file = "orjson-3.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:957a45fb201c61b78bcf655a16afbe8a36c2c27f18a998bd6b5d8a35e358d4ad"}, + {file = "orjson-3.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1c01cf4b8e00c7e98a0a7cf606a30a26c32adf2560be2d7d5d6766d6f474b31"}, + {file = "orjson-3.9.9-cp311-none-win32.whl", hash = "sha256:397a185e5dd7f8ebe88a063fe13e34d61d394ebb8c70a443cee7661b9c89bda7"}, + {file = "orjson-3.9.9-cp311-none-win_amd64.whl", hash = "sha256:24301f2d99d670ded4fb5e2f87643bc7428a54ba49176e38deb2887e42fe82fb"}, + {file = "orjson-3.9.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd55ea5cce3addc03f8fb0705be0cfed63b048acc4f20914ce5e1375b15a293b"}, + {file = "orjson-3.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b28c1a65cd13fff5958ab8b350f0921121691464a7a1752936b06ed25c0c7b6e"}, + {file = "orjson-3.9.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b97a67c47840467ccf116136450c50b6ed4e16a8919c81a4b4faef71e0a2b3f4"}, + {file = "orjson-3.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75b805549cbbcb963e9c9068f1a05abd0ea4c34edc81f8d8ef2edb7e139e5b0f"}, + {file = "orjson-3.9.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5424ecbafe57b2de30d3b5736c5d5835064d522185516a372eea069b92786ba6"}, + {file = "orjson-3.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d2cd6ef4726ef1b8c63e30d8287225a383dbd1de3424d287b37c1906d8d2855"}, + {file = "orjson-3.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c959550e0705dc9f59de8fca1a316da0d9b115991806b217c82931ac81d75f74"}, + {file = "orjson-3.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ece2d8ed4c34903e7f1b64fb1e448a00e919a4cdb104fc713ad34b055b665fca"}, + {file = "orjson-3.9.9-cp312-none-win_amd64.whl", hash = "sha256:f708ca623287186e5876256cb30599308bce9b2757f90d917b7186de54ce6547"}, + {file = "orjson-3.9.9-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:335406231f9247f985df045f0c0c8f6b6d5d6b3ff17b41a57c1e8ef1a31b4d04"}, + {file = "orjson-3.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d9b5440a5d215d9e1cfd4aee35fd4101a8b8ceb8329f549c16e3894ed9f18b5"}, + {file = "orjson-3.9.9-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e98ca450cb4fb176dd572ce28c6623de6923752c70556be4ef79764505320acb"}, + {file = "orjson-3.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3bf6ca6bce22eb89dd0650ef49c77341440def966abcb7a2d01de8453df083a"}, + {file = "orjson-3.9.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb50d869b3c97c7c5187eda3759e8eb15deb1271d694bc5d6ba7040db9e29036"}, + {file = "orjson-3.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fcf06c69ccc78e32d9f28aa382ab2ab08bf54b696dbe00ee566808fdf05da7d"}, + {file = "orjson-3.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9a4402e7df1b5c9a4c71c7892e1c8f43f642371d13c73242bda5964be6231f95"}, + {file = "orjson-3.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b20becf50d4aec7114dc902b58d85c6431b3a59b04caa977e6ce67b6fee0e159"}, + {file = "orjson-3.9.9-cp38-none-win32.whl", hash = "sha256:1f352117eccac268a59fedac884b0518347f5e2b55b9f650c2463dd1e732eb61"}, + {file = "orjson-3.9.9-cp38-none-win_amd64.whl", hash = "sha256:c4eb31a8e8a5e1d9af5aa9e247c2a52ad5cf7e968aaa9aaefdff98cfcc7f2e37"}, + {file = "orjson-3.9.9-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a308aeac326c2bafbca9abbae1e1fcf682b06e78a54dad0347b760525838d85"}, + {file = "orjson-3.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e159b97f5676dcdac0d0f75ec856ef5851707f61d262851eb41a30e8fadad7c9"}, + {file = "orjson-3.9.9-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f692e7aabad92fa0fff5b13a846fb586b02109475652207ec96733a085019d80"}, + {file = "orjson-3.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cffb77cf0cd3cbf20eb603f932e0dde51b45134bdd2d439c9f57924581bb395b"}, + {file = "orjson-3.9.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c63eca397127ebf46b59c9c1fb77b30dd7a8fc808ac385e7a58a7e64bae6e106"}, + {file = "orjson-3.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f0c024a75e8ba5d9101facb4fb5a028cdabe3cdfe081534f2a9de0d5062af2"}, + {file = "orjson-3.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8cba20c9815c2a003b8ca4429b0ad4aa87cb6649af41365821249f0fd397148e"}, + {file = "orjson-3.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:906cac73b7818c20cf0f6a7dde5a6f009c52aecc318416c7af5ea37f15ca7e66"}, + {file = "orjson-3.9.9-cp39-none-win32.whl", hash = "sha256:50232572dd300c49f134838c8e7e0917f29a91f97dbd608d23f2895248464b7f"}, + {file = "orjson-3.9.9-cp39-none-win_amd64.whl", hash = "sha256:920814e02e3dd7af12f0262bbc18b9fe353f75a0d0c237f6a67d270da1a1bb44"}, + {file = "orjson-3.9.9.tar.gz", hash = "sha256:02e693843c2959befdd82d1ebae8b05ed12d1cb821605d5f9fe9f98ca5c9fd2b"}, ] [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -364,7 +317,6 @@ files = [ name = "pbr" version = "5.11.1" description = "Python Build Reasonableness" -category = "dev" optional = false python-versions = ">=2.6" files = [ @@ -376,7 +328,6 @@ files = [ name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -391,7 +342,6 @@ plugins = ["importlib-metadata"] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -400,6 +350,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -407,8 +358,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -425,6 +383,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -432,6 +391,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -439,41 +399,37 @@ files = [ [[package]] name = "rich" -version = "13.5.2" +version = "13.6.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, - {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, + {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, + {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "smmap" -version = "5.0.0" +version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, ] [[package]] name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -485,7 +441,6 @@ files = [ name = "stevedore" version = "5.1.0" description = "Manage dynamic plugins for Python applications" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -500,7 +455,6 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -512,7 +466,6 @@ files = [ name = "typing-inspect" version = "0.9.0" description = "Runtime inspection utilities for typing module." -category = "main" optional = false python-versions = "*" files = [ @@ -528,7 +481,6 @@ typing-extensions = ">=3.7.4" name = "websockets" version = "11.0.3" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -606,5 +558,5 @@ files = [ [metadata] lock-version = "2.0" -python-versions = "^3.8" -content-hash = "a9b815054efcc6c0887ae2e3dde70fd22772b83d89049f17ed0ea0fe8603d447" +python-versions = "^3.12" +content-hash = "d33a2dfe29e380ab5c767bc2fd68a0731c71526587a686281f531472e968fe12" diff --git a/pyproject.toml b/pyproject.toml index 2c76812..2a703ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,8 +12,7 @@ repository = "https://github.com/MeowerBots/MeowerBot.py" [tool.poetry.dependencies] -python = "^3.8" -backports-strenum = { version = "^1.2.4", python = "<3.11" } +python = "^3.12" websockets = "^11.0.3" orjson = "^3.9.4" httpx = "^0.25.0" From da743ff5a17c72fc3ad91aaaa20cdd443a7439f6 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Sat, 21 Oct 2023 02:12:08 -0500 Subject: [PATCH 11/13] pushing before testing via debugger --- MeowerBot/API.py | 6 +- MeowerBot/Bot.py | 127 ++++++++++++++++++++++++--------- MeowerBot/__init__.py | 5 +- MeowerBot/cl/__init__.py | 21 ++++-- MeowerBot/command.py | 6 +- MeowerBot/context.py | 8 +-- MeowerBot/types/api/reports.py | 21 +++--- poetry.lock | 106 ++++++++++++++++++++++++++- pyproject.toml | 2 + tests/integration_login.py | 23 +++--- 10 files changed, 249 insertions(+), 76 deletions(-) diff --git a/MeowerBot/API.py b/MeowerBot/API.py index d09920d..39de8ac 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -22,7 +22,7 @@ async def login(self, token): self.headers.update({"token": token}) async def admin_get_reports(self, timeout=None) -> ReportRequest: - resp = await self.client.get("/admin/reports", timeout=timeout, params=kwargs) + resp = await self.client.get("/admin/reports", timeout=timeout) if resp.status_code == 404: raise RuntimeError("[API] 404 Not found") @@ -289,13 +289,13 @@ async def delete_post(self, uuid: generic.UUID) -> Post: return Post.from_json(resp.text) - async def get_inbox(self) -> PagedRequest[PostBody]: + async def get_inbox(self) -> PagedRequest[Post]: resp = await self.client.get("/inbox") if resp.status_code == 401: raise RuntimeError("[API] No Auth to do this action") - return PagedRequest[PostBody].from_json(resp.text) + return PagedRequest[Post].from_json(resp.text) async def search_users(self, query: str, page: int = 1 ) -> PagedRequest[User]: resp = await self.client.get("/search/users", params={"q": query, "p": page},) diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index bb5fdd4..bb51c0b 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -12,7 +12,7 @@ import time from .command import AppCommand -from .context import Context, Post, PartialUser, User +from .context import Context, Post, PartialUser, User, PartialChat import time import logging @@ -35,6 +35,7 @@ class cbids(StrEnum): message = "message" raw_message = "raw_message" direct = "direct" + statuscode = "statuscode" callbacks = [i for i in cbids] @@ -76,7 +77,7 @@ async def _t_ping(self): await self._error(e) break - def __init__(self, prefix=None ): #type: ignore + def __init__(self, prefix=None): #type: ignore super().__init__() self.callbacks = {i: [] for i in callbacks} self.ulist = [] @@ -119,10 +120,13 @@ def listen(self, callback=None): def inner(func): callback = callback if callback is not None else func.__name__ - if func.__name__ not in callbacks: - raise TypeError(f"{func.__name__} is not a valid listener") + if func.__name__ not in callbacks: + raise TypeError(f"{func.__name__} is not a valid listener") - self.callbacks[callback].append(func) + self.callbacks[callback].append(func) + + return func + return inner def subcommand(self, name=None, args=0, aliases = None): @@ -137,12 +141,14 @@ def inner(func): return cmd #dont want mb to register this as a root command return inner - def update_commands(): + def update_commands(self): for cog in self.cogs: cog.update_commands() self.commands = self.commands.update(cog.__commands__) + + async def error(self, err: Exception): pass async def __raw__(self, packet: dict): pass async def login(self, token: str): pass @@ -150,8 +156,9 @@ async def close(self): pass async def ulist(self, ulist): pass async def message(self, message: Post): - message = self.handle_bridges(message) - + message = await self.handle_bridges(message) + async def statuscode(self, status, listerner): + pass async def raw_message(self, data: dict): pass async def direct(self, data: dict): pass @@ -166,7 +173,7 @@ async def _run_event(self, event: cbids, *args, **kwargs): # websocket - def handle_bridges(self, message: Post): + async def handle_bridges(self, message: Post): fetch = False if isinstance(message.user, User): fetch = True @@ -183,56 +190,102 @@ def handle_bridges(self, message: Post): if message.data.startswith(self.prefix + "#0000"): message.data = message.data.replace("#0000", "") - return packet + return message + + + def command(self, name=None, args=0, aliases = None): + def inner(func): + + cmd = AppCommand(func, name=name, args=args) + cmd.register_class(self) + + self.subcommand = AppCommand.add_command(self.commands, cmd) + + return cmd + return inner + async def _connect(self): + await self.sendPacket({ "cmd": "direct", "val": "meower", "listener": "send_tkey" }) + await self.sendPacket({"cmd": "direct", "val": { - "cmd": "type", "val": "MeowerBot.py" + "cmd": "type", "val": "py" }}) - if (await self.send_statuscode_request({ "cmd": "direct", val: "meower", "listener": "send_tkey" }))["val"] != "I: 100 | OK": - raise RuntimeError("Meower Trust Failed!") - - resp = await self.send_data_request( - { - "cmd": "direct", - "val": { - "cmd": "authpswd", - "val": { - "username": self.username, - "pswd": self.password - } - } - } - ) + resp = await self.send_data_request({ + "cmd": "direct", + "val": { + "cmd": "authpswd", + "val": { + "username": str(self.username).strip(), + "pswd": str(self.password).strip() + } + } + }) if resp[1]["val"] != "I: 100 | OK": raise Exception(f"Wrong Username or Password!\n {resp[1]['val']}") - self.api.login(resp[0]["val"["val"]["pswd"]]) + self.api.login(resp[0]['val']['payload']['token']) - await self._run_event(cbids.login, resp[0]["val"]["pswd"]) + await self._run_event(cbids.login, resp[0]['val']['payload']['token']) async def _disconnect(self): + await self._run_event(cbids.close) + def get_chat(self, id): + return PartialChat(id, self) + async def _message(self, message): - pass + if (message.get("listener")) != "mb_login": + self.logger.debug(message) + match message["cmd"]: + case "statuscode": + return await self._run_event(cbids.statuscode, message["val"], message.get("listener")) + + case "ulist": + self.ulist = message["val"].split(";") + + return await self._run_event(cbids.ulist, self.ulist) + + case "direct": + if "post_origin" in message["val"]: # post + post = Post(self, message["val"], chat=self.get_chat()) + with self.message_condition: + self.messages.append(post) + self.message_condition.notify_all() + else: + return await self._run_event(cbids.direct, message) + + + + if (message["cmd"] == "pmsg") and (message["val"] not in self.BOT_NO_PMSG_RESPONSE): + self.wss.sendPacket({ + "cmd": "pmsg", + "val": "I:500 | Bot", + "id": message["origin"] + }) + + + + + async def _error(self, error): pass - async def run(self, username, password, server="wss://server.meower.org"): + async def start(self, username, password, server="wss://server.meower.org", ): """ Runs The bot (Blocking) """ self.username = username - self._password = password + self.password = password asyncio.create_task(self._t_ping()) if self.prefix is None: @@ -244,4 +297,14 @@ async def run(self, username, password, server="wss://server.meower.org"): await self.connect(server) - + + def run(self, username, password, server="wss://server.meower.org", ): + """ + Runs the bot (Blocking) + """ + loop = asyncio.get_event_loop() + fut = loop.create_task( self.start(username, password, server=server) ) + loop.run_forever() + + + diff --git a/MeowerBot/__init__.py b/MeowerBot/__init__.py index 8fd3162..81554c8 100644 --- a/MeowerBot/__init__.py +++ b/MeowerBot/__init__.py @@ -11,7 +11,6 @@ # Public library imports from . import Bot as botm -from .Bot import Bot -from .Bot import cbids +from .Bot import Bot, cbids -__all__ = ["__version__", "Bot", "botm"] +__all__ = ["__version__", "Bot", "botm", "cbids"] diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index 477aa8d..2380bc2 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -3,7 +3,7 @@ import json import asyncio import random - +import time class Client: ws: websockets.WebSocketClientProtocol message_condition = asyncio.Condition() @@ -39,11 +39,14 @@ async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): Returns: - A list of messages that meet the specified conditions, or None if timeout is reached. """ + + start_time = time.time() received_packets = [] while True: async with self.message_condition: + await self.message_condition.wait() for condition in conditions: if expected_count is not None and len(received_packets) >= expected_count: return received_packets @@ -53,6 +56,8 @@ async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): received_packets.append(message) found = True break + elif message["cmd"] == "statuscode": + return ([None]*expected_count - 1) + [message] if not found: break @@ -60,7 +65,6 @@ async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): return received_packets if timeout is not None and time.time() - start_time >= timeout: return None - await asyncio.sleep(0.1) async def send_data_request(self, packet, timeout = None): if packet.get("listener") == None: @@ -68,7 +72,7 @@ async def send_data_request(self, packet, timeout = None): packet["listener"] = random.random() # nosec await self.sendPacket(packet) - return self._wait_for_packet({"listener": packet["listener"]}, expected_count=2, timeout=timeout) + return await self._wait_for_packet({"listener": packet["listener"]}, expected_count=2, timeout=timeout) async def send_statuscode_request(self, packet, timeout = 0): if packet.get("listener") == None: @@ -76,17 +80,20 @@ async def send_statuscode_request(self, packet, timeout = 0): packet["listener"] = random.random() # nosec await self.sendPacket(packet) - return self._wait_for_packet({"listener": packet["listener"]}, expected_count=1, timeout=timeout)[0] + return (await self._wait_for_packet({"listener": packet["listener"]}, expected_count=1, timeout=timeout))[0] async def close(self, reason=None): await self.ws.close(reason=reason) - async def connect(self, server): - async for websocket in websockets.connect(server): + async def connect(self, server, cookies=None): + async for websocket in websockets.connect(server, ping_interval = None): # Meower uses its own implementation, crashes the connection if left on. try: self.ws = websocket - await self._connect() + try: + await self._connect() + except Exception as e: + print(e) async for message in websocket: try: diff --git a/MeowerBot/command.py b/MeowerBot/command.py index 3c9a965..707c227 100644 --- a/MeowerBot/command.py +++ b/MeowerBot/command.py @@ -10,12 +10,12 @@ class AppCommand: connected = None @staticmethod - def add_command(obj: dict, command: AppCommand, ignore_subcommands = True): + def add_command(obj: dict, command: "AppCommand", ignore_subcommands = True): if command.is_subcommand and ignore_subcommands: return obj - for alias in cmd.alias + [cmd.name]: - obj[alias] = cmd + for alias in command.alias + [command.name]: + obj[alias] = command diff --git a/MeowerBot/context.py b/MeowerBot/context.py index c078360..0addf36 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -1,15 +1,13 @@ from datetime import datetime -import typing from typing import TYPE_CHECKING if TYPE_CHECKING: from .Bot import Bot -import weakref -import requests from .types.api.chats import ChatGroup from .types.api.user import User as RawUser +from .types.generic import Post class PartialChat: def __init__(self, id, bot: "Bot"): @@ -41,7 +39,7 @@ def __init__(self, username, bot: "Bot"): self.bot = bot async def fetch(self): - return User(self.username, RawUser.from_json(await self.bot.api._get_user(username, ""))) + return User(self.username, RawUser.from_json(await self.bot.api._get_user(self.username, ""))) class User(PartialUser): def __init__(self, username, bot, data: RawUser): @@ -78,7 +76,7 @@ def __str__(self): return str(self.data) async def reply(self, message): - self.chat.send_msg(f"@{self.user.name} [{self.id}]") + self.chat.send_msg(f"@{self.user.username} [{self.id}] {message}") class Context: diff --git a/MeowerBot/types/api/reports.py b/MeowerBot/types/api/reports.py index baeb405..b8d149c 100644 --- a/MeowerBot/types/api/reports.py +++ b/MeowerBot/types/api/reports.py @@ -17,11 +17,21 @@ class PagedRequest(Generic[T]): index: Optional[List[UUID]] autoget: Optional[List[T]] + + +@dataclass_json +@dataclass +class ReportDetails: + comment: str + reason: str + time: int + user: str + @dataclass_json @dataclass class Report: _id: UUID - content: Content + content: Post escalated: bool reports: List[ReportDetails] status: str @@ -29,15 +39,6 @@ class Report: - -@dataclass_json -@dataclass -class ReportDetails: - comment: str - reason: str - time: int - user: str - class ReportRequest(PagedRequest): autoget: List[Report] diff --git a/poetry.lock b/poetry.lock index dc6177a..4cd7d03 100644 --- a/poetry.lock +++ b/poetry.lock @@ -177,6 +177,25 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -313,6 +332,21 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + [[package]] name = "pbr" version = "5.11.1" @@ -477,6 +511,76 @@ files = [ mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" +[[package]] +name = "ujson" +version = "5.8.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4511560d75b15ecb367eef561554959b9d49b6ec3b8d5634212f9fed74a6df1"}, + {file = "ujson-5.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9399eaa5d1931a0ead49dce3ffacbea63f3177978588b956036bfe53cdf6af75"}, + {file = "ujson-5.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4e7bb7eba0e1963f8b768f9c458ecb193e5bf6977090182e2b4f4408f35ac76"}, + {file = "ujson-5.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40931d7c08c4ce99adc4b409ddb1bbb01635a950e81239c2382cfe24251b127a"}, + {file = "ujson-5.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d53039d39de65360e924b511c7ca1a67b0975c34c015dd468fca492b11caa8f7"}, + {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bdf04c6af3852161be9613e458a1fb67327910391de8ffedb8332e60800147a2"}, + {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a70f776bda2e5072a086c02792c7863ba5833d565189e09fabbd04c8b4c3abba"}, + {file = "ujson-5.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f26629ac531d712f93192c233a74888bc8b8212558bd7d04c349125f10199fcf"}, + {file = "ujson-5.8.0-cp310-cp310-win32.whl", hash = "sha256:7ecc33b107ae88405aebdb8d82c13d6944be2331ebb04399134c03171509371a"}, + {file = "ujson-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:3b27a8da7a080add559a3b73ec9ebd52e82cc4419f7c6fb7266e62439a055ed0"}, + {file = "ujson-5.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:193349a998cd821483a25f5df30b44e8f495423840ee11b3b28df092ddfd0f7f"}, + {file = "ujson-5.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ddeabbc78b2aed531f167d1e70387b151900bc856d61e9325fcdfefb2a51ad8"}, + {file = "ujson-5.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ce24909a9c25062e60653073dd6d5e6ec9d6ad7ed6e0069450d5b673c854405"}, + {file = "ujson-5.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27a2a3c7620ebe43641e926a1062bc04e92dbe90d3501687957d71b4bdddaec4"}, + {file = "ujson-5.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b852bdf920fe9f84e2a2c210cc45f1b64f763b4f7d01468b33f7791698e455e"}, + {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:20768961a6a706170497129960762ded9c89fb1c10db2989c56956b162e2a8a3"}, + {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0147d41e9fb5cd174207c4a2895c5e24813204499fd0839951d4c8784a23bf5"}, + {file = "ujson-5.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e3673053b036fd161ae7a5a33358ccae6793ee89fd499000204676baafd7b3aa"}, + {file = "ujson-5.8.0-cp311-cp311-win32.whl", hash = "sha256:a89cf3cd8bf33a37600431b7024a7ccf499db25f9f0b332947fbc79043aad879"}, + {file = "ujson-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3659deec9ab9eb19e8646932bfe6fe22730757c4addbe9d7d5544e879dc1b721"}, + {file = "ujson-5.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:102bf31c56f59538cccdfec45649780ae00657e86247c07edac434cb14d5388c"}, + {file = "ujson-5.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:299a312c3e85edee1178cb6453645217ba23b4e3186412677fa48e9a7f986de6"}, + {file = "ujson-5.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e385a7679b9088d7bc43a64811a7713cc7c33d032d020f757c54e7d41931ae"}, + {file = "ujson-5.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad24ec130855d4430a682c7a60ca0bc158f8253ec81feed4073801f6b6cb681b"}, + {file = "ujson-5.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16fde596d5e45bdf0d7de615346a102510ac8c405098e5595625015b0d4b5296"}, + {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6d230d870d1ce03df915e694dcfa3f4e8714369cce2346686dbe0bc8e3f135e7"}, + {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9571de0c53db5cbc265945e08f093f093af2c5a11e14772c72d8e37fceeedd08"}, + {file = "ujson-5.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7cba16b26efe774c096a5e822e4f27097b7c81ed6fb5264a2b3f5fd8784bab30"}, + {file = "ujson-5.8.0-cp312-cp312-win32.whl", hash = "sha256:48c7d373ff22366eecfa36a52b9b55b0ee5bd44c2b50e16084aa88b9de038916"}, + {file = "ujson-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ac97b1e182d81cf395ded620528c59f4177eee024b4b39a50cdd7b720fdeec6"}, + {file = "ujson-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a64cc32bb4a436e5813b83f5aab0889927e5ea1788bf99b930fad853c5625cb"}, + {file = "ujson-5.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e54578fa8838ddc722539a752adfce9372474114f8c127bb316db5392d942f8b"}, + {file = "ujson-5.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9721cd112b5e4687cb4ade12a7b8af8b048d4991227ae8066d9c4b3a6642a582"}, + {file = "ujson-5.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d9707e5aacf63fb919f6237d6490c4e0244c7f8d3dc2a0f84d7dec5db7cb54c"}, + {file = "ujson-5.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0be81bae295f65a6896b0c9030b55a106fb2dec69ef877253a87bc7c9c5308f7"}, + {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae7f4725c344bf437e9b881019c558416fe84ad9c6b67426416c131ad577df67"}, + {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9ab282d67ef3097105552bf151438b551cc4bedb3f24d80fada830f2e132aeb9"}, + {file = "ujson-5.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:94c7bd9880fa33fcf7f6d7f4cc032e2371adee3c5dba2922b918987141d1bf07"}, + {file = "ujson-5.8.0-cp38-cp38-win32.whl", hash = "sha256:bf5737dbcfe0fa0ac8fa599eceafae86b376492c8f1e4b84e3adf765f03fb564"}, + {file = "ujson-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:11da6bed916f9bfacf13f4fc6a9594abd62b2bb115acfb17a77b0f03bee4cfd5"}, + {file = "ujson-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:69b3104a2603bab510497ceabc186ba40fef38ec731c0ccaa662e01ff94a985c"}, + {file = "ujson-5.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9249fdefeb021e00b46025e77feed89cd91ffe9b3a49415239103fc1d5d9c29a"}, + {file = "ujson-5.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2873d196725a8193f56dde527b322c4bc79ed97cd60f1d087826ac3290cf9207"}, + {file = "ujson-5.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a4dafa9010c366589f55afb0fd67084acd8added1a51251008f9ff2c3e44042"}, + {file = "ujson-5.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a42baa647a50fa8bed53d4e242be61023bd37b93577f27f90ffe521ac9dc7a3"}, + {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f3554eaadffe416c6f543af442066afa6549edbc34fe6a7719818c3e72ebfe95"}, + {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fb87decf38cc82bcdea1d7511e73629e651bdec3a43ab40985167ab8449b769c"}, + {file = "ujson-5.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:407d60eb942c318482bbfb1e66be093308bb11617d41c613e33b4ce5be789adc"}, + {file = "ujson-5.8.0-cp39-cp39-win32.whl", hash = "sha256:0fe1b7edaf560ca6ab023f81cbeaf9946a240876a993b8c5a21a1c539171d903"}, + {file = "ujson-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:3f9b63530a5392eb687baff3989d0fb5f45194ae5b1ca8276282fb647f8dcdb3"}, + {file = "ujson-5.8.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:efeddf950fb15a832376c0c01d8d7713479fbeceaed1eaecb2665aa62c305aec"}, + {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d8283ac5d03e65f488530c43d6610134309085b71db4f675e9cf5dff96a8282"}, + {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb0142f6f10f57598655340a3b2c70ed4646cbe674191da195eb0985a9813b83"}, + {file = "ujson-5.8.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d459aca895eb17eb463b00441986b021b9312c6c8cc1d06880925c7f51009c"}, + {file = "ujson-5.8.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d524a8c15cfc863705991d70bbec998456a42c405c291d0f84a74ad7f35c5109"}, + {file = "ujson-5.8.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d6f84a7a175c75beecde53a624881ff618e9433045a69fcfb5e154b73cdaa377"}, + {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b748797131ac7b29826d1524db1cc366d2722ab7afacc2ce1287cdafccddbf1f"}, + {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e72ba76313d48a1a3a42e7dc9d1db32ea93fac782ad8dde6f8b13e35c229130"}, + {file = "ujson-5.8.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f504117a39cb98abba4153bf0b46b4954cc5d62f6351a14660201500ba31fe7f"}, + {file = "ujson-5.8.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8c91b6f4bf23f274af9002b128d133b735141e867109487d17e344d38b87d94"}, + {file = "ujson-5.8.0.tar.gz", hash = "sha256:78e318def4ade898a461b3d92a79f9441e7e0e4d2ad5419abed4336d702c7425"}, +] + [[package]] name = "websockets" version = "11.0.3" @@ -559,4 +663,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "d33a2dfe29e380ab5c767bc2fd68a0731c71526587a686281f531472e968fe12" +content-hash = "2002318232c4db3df4795a3cafd9a0ccee6e56418ccef31f69aeaece0bc6bca3" diff --git a/pyproject.toml b/pyproject.toml index 2a703ba..90f7cfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ orjson = "^3.9.4" httpx = "^0.25.0" dataclasses-json = "^0.6.1" typing-extensions = "^4.8.0" +ujson = "^5.8.0" [tool.poetry.group.extras.dependencies] @@ -26,6 +27,7 @@ typing-extensions = "^4.8.0" [tool.poetry.group.dev.dependencies] bandit = "^1.7.5" +jedi = "^0.19.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/integration_login.py b/tests/integration_login.py index ae4801f..f1b574b 100644 --- a/tests/integration_login.py +++ b/tests/integration_login.py @@ -1,23 +1,21 @@ from MeowerBot import Bot -from MeowerBot import cbids -from MeowerBot.context import CTX, Post +from MeowerBot.context import Context, Post +from MeowerBot.API import MeowerAPI import asyncio import logging from os import environ as env logging.basicConfig(level=logging.DEBUG) -logging.getLogger("websockets.client").setLevel(level=logging.INFO) +#logging.getLogger("websockets.client").setLevel(level=logging.INFO) bot = Bot() -async def on_login(bot): +@bot.event +async def login(t): print("Logged in!") - -bot.callback(on_login, cbids.login) - -@bot.command(aname="ping") -async def ping(ctx: CTX): +@bot.command(name="ping") +async def ping(ctx: Context): await ctx.send_msg("Pong!\n My latency is: " + str(bot.latency)) def predicate(msg: Post): @@ -25,7 +23,8 @@ def predicate(msg: Post): msg = await bot.wait_for_message(predicate, timeout=100) await ctx.send_msg("You said: " + str(msg)) -async def main(): - await bot.run(env["uname"], env["password"]) -asyncio.run(main()) \ No newline at end of file +MeowerAPI.base_uri = "http://beta.meower.org:5174/api/" + +bot.run(env['uname'], env['password'], server="wss://beta.meower.org/api/v0/cloudlink") + From 5211e2abe696bae52120ac7a5dcec18ab2c0c3cf Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Tue, 24 Oct 2023 21:27:34 -0500 Subject: [PATCH 12/13] WOOO FINISHED --- MeowerBot/API.py | 40 ++++++------ MeowerBot/Bot.py | 122 +++++++++++++++++++++++-------------- MeowerBot/cl/__init__.py | 34 ++++------- MeowerBot/command.py | 9 +-- MeowerBot/context.py | 6 +- tests/integration_login.py | 12 ++-- 6 files changed, 122 insertions(+), 101 deletions(-) diff --git a/MeowerBot/API.py b/MeowerBot/API.py index 39de8ac..54c89c6 100644 --- a/MeowerBot/API.py +++ b/MeowerBot/API.py @@ -8,21 +8,23 @@ from .types.api.chats import Chats, ChatGroup from .types.api.user import User, Relationship from typing import Literal +from httpx import Response class MeowerAPI: base_uri = "https://api.meower.org/" def __init__(self, username): - self.headers = {"username": username} - self.client = AsyncClient(headers=self.headers, base_url=self.base_uri, params={"autoget": None}) + self.headers = {"username": username, "user-agent": 'Mozilla/5.0 (Android 14; Mobile; rv:109.0) Gecko/118.0 Firefox/118.0'} + self.client = AsyncClient(headers=self.headers, base_url=self.base_uri, follow_redirects=True) + async def login(self, token): - self.headers.update({"token": token}) + self.client.headers.update({"token": token}) async def admin_get_reports(self, timeout=None) -> ReportRequest: - resp = await self.client.get("/admin/reports", timeout=timeout) + resp = await self.client.get("/admin/reports", timeout=timeout, params={"autoget": None}) if resp.status_code == 404: raise RuntimeError("[API] 404 Not found") @@ -32,7 +34,7 @@ async def admin_get_reports(self, timeout=None) -> ReportRequest: ) async def admin_get_report(self, uuid: generic.UUID) -> Report: - resp = await self.client.get(f"/admin/reports/{uuid}/") + resp = await self.client.get(f"/admin/reports/{uuid}/", params={"autoget": None}) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to look at reports") @@ -65,7 +67,7 @@ async def admin_escalate_report(self, uuid: generic.UUID) -> Report: return Report.from_json(resp.text) async def admin_fetch_note(self, indentifier: str) -> AdminNotesResponse: - resp = await self.client.get(f"/admin/notes/{indentifier}") + resp = await self.client.get(f"/admin/notes/{indentifier}", params={"autoget": None}) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to look at notes") @@ -86,7 +88,7 @@ async def admin_create_note(self, indentifier: str, notes: str) -> AdminNotesRes return AdminNotesResponse.from_json(resp.text) async def admin_get_post(self, uuid: generic.UUID) -> Post: - resp = await self.client.get(f"/admin/posts/{uuid}") + resp = await self.client.get(f"/admin/posts/{uuid}", params={"autoget": None}) if resp.status_code == 403: raise RuntimeError("[API] 403 Found: You are not allowed to look at posts") @@ -120,7 +122,7 @@ async def admin_restore_deleted_post(self, uuid: generic.UUID) -> Post: async def get_chats(self) -> Chats: - resp = await self.client.get(f"/chats/") + resp = await self.client.get(f"/chats/", params={"autoget": None}) if resp.status_code == 401: raise RuntimeError("[API] No Token or username supplied! This is required to send authenticated API requests") @@ -136,7 +138,7 @@ async def create_chat(self, nickname: str) -> ChatGroup: return ChatGroup.from_json(resp.text) async def get_chat(self, uuid: generic.UUID) -> ChatGroup: - resp = await self.client.get(f"/chats/{uuid}") + resp = await self.client.get(f"/chats/{uuid}", params={"autoget": None}) if resp.status_code == 401: raise RuntimeError("[API] No Token or username supplied! This is required to send authenticated API requests") @@ -215,9 +217,9 @@ async def transfer_chat_ownership(self, uuid: generic.UUID, username: str) -> Ch async def get_posts(self, chat: str | generic.UUID, page: int = 1) -> PagedRequest[Post]: if chat == "home": - resp = await self.client.get(f"/home/", params={"page": page}) + resp = await self.client.get(f"/home/", params={"page": page, "autoget": None}) else: - resp = await self.client.get(f"/posts/{chat}", params={"page": page}) + resp = await self.client.get(f"/posts/{chat}", params={"page": page, "autoget": None}) if resp.status_code == 401: raise RuntimeError("[API] No Auth to do this action") @@ -235,6 +237,7 @@ async def send_post(self, chat: str | generic.UUID, content: str) -> Post: else: resp = await self.client.post(f"/posts/{chat}", json={"content": content}) + print(resp.text, resp.status_code) if resp.status_code == 429: raise RuntimeError("[API] Ratelimited: Sending posts") @@ -246,6 +249,7 @@ async def send_post(self, chat: str | generic.UUID, content: str) -> Post: if resp.status_code == 404: raise RuntimeError("[API] 404 Chat Not found") + print(resp.text) return Post.from_json(resp.text) async def get_post(self, uuid: generic.UUID) -> Post: @@ -290,7 +294,7 @@ async def delete_post(self, uuid: generic.UUID) -> Post: return Post.from_json(resp.text) async def get_inbox(self) -> PagedRequest[Post]: - resp = await self.client.get("/inbox") + resp = await self.client.get("/inbox", params={"autoget": None}) if resp.status_code == 401: raise RuntimeError("[API] No Auth to do this action") @@ -298,19 +302,19 @@ async def get_inbox(self) -> PagedRequest[Post]: return PagedRequest[Post].from_json(resp.text) async def search_users(self, query: str, page: int = 1 ) -> PagedRequest[User]: - resp = await self.client.get("/search/users", params={"q": query, "p": page},) + resp = await self.client.get("/search/users", params={"q": query, "p": page,"autoget": None},) return PagedRequest[User].from_json(resp.text) async def search_home(self, query: str, page: int = 1) -> PagedRequest[Post]: - resp = await self.client.get("/search/home", params={"q": query, "p": page}) + resp = await self.client.get("/search/home", params={"q": query, "p": page, "autoget": None}) return PagedRequest[Post].from_json(resp.text) # TODO: Implement wrapper for https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/admin.py#L74-L1564 # TODO: https://github.com/meower-media-co/Meower-Server/blob/better-moderation/rest_api/users.py - async def _get_user(self, username, url, json=None, page=1, query = None): - resp = await self.client.get(urljoin(f"/users/{username}/", url), json=json, params={"q": query, "p": page}) + async def _get_user(self, username, url, json=None, page=1, query = None, params=None): + resp: Response = await self.client.get(urljoin(f"/users/{username}/", url), json=json, params={"q": query, "p": page, **params}) if resp.status_code == 404: raise RuntimeError("[API] User does not exist") @@ -324,10 +328,10 @@ async def _get_user(self, username, url, json=None, page=1, query = None): if resp.status_code == 403: raise RuntimeError("[API] Blocked from doing this action") - return resp.text + return resp._text async def get_user_posts(self, username, query, page = 1) -> PagedRequest[Post]: - return PagedRequest[Post].from_json(await self._get_user(username, "posts", query=query, page=page)) + return PagedRequest[Post].from_json(await self._get_user(username, "posts", query=query, page=page, params={"autoget": None})) async def get_user_relationship(self, username) -> Relationship: return Relationship.from_json(await self._get_user(username, "relationship")) diff --git a/MeowerBot/Bot.py b/MeowerBot/Bot.py index bb51c0b..a3b2502 100644 --- a/MeowerBot/Bot.py +++ b/MeowerBot/Bot.py @@ -1,31 +1,13 @@ import shlex - from .cl import Client -import sys - -import json import traceback - -import requests - - -import time - from .command import AppCommand from .context import Context, Post, PartialUser, User, PartialChat - -import time import logging - from .API import MeowerAPI - import asyncio -import sys - from enum import StrEnum -from typing import Union - class cbids(StrEnum): error = "error" __raw__ = "__raw__" @@ -36,7 +18,7 @@ class cbids(StrEnum): raw_message = "raw_message" direct = "direct" statuscode = "statuscode" - +print(str(cbids.error)) callbacks = [i for i in cbids] class Bot(Client): @@ -73,13 +55,13 @@ async def _t_ping(self): await self.sendPacket({"cmd": "ping", "val": ""}) self.logger.debug(msg="Sent ping") except Exception as e: - self.logger.error(e) await self._error(e) break def __init__(self, prefix=None): #type: ignore super().__init__() - self.callbacks = {i: [] for i in callbacks} + self.callbacks = {str(i): [] for i in callbacks} + self.callbacks["__raw__"] = [] self.ulist = [] # to be used in start @@ -92,7 +74,6 @@ def __init__(self, prefix=None): #type: ignore self.server = None self.cogs = {} - # Interface def event(self, func): @@ -149,7 +130,9 @@ def update_commands(self): - async def error(self, err: Exception): pass + async def error(self, err: Exception): + raise err + async def __raw__(self, packet: dict): pass async def login(self, token: str): pass async def close(self): pass @@ -157,6 +140,14 @@ async def ulist(self, ulist): pass async def message(self, message: Post): message = await self.handle_bridges(message) + + if not message.data.startswith(self.prefix): + return + + message.data = message.data.removeprefix(self.prefix) + + await self.run_commands(message) + async def statuscode(self, status, listerner): pass @@ -165,11 +156,19 @@ async def direct(self, data: dict): pass async def _run_event(self, event: cbids, *args, **kwargs): - err = await asyncio.gather(*([getattr(self, event)] + self.callbacks[event]), return_exceptions=True) + events = [getattr(self, str(event))] + for i in self.callbacks[str(event)]: + if type(i) is list: + events.extend(i) + elif callable(i): # Check if the element is callable + events.append(i) + + err = await asyncio.gather(*[i(*args, **kwargs) for i in events if callable(i)], return_exceptions=True) for i in err: - if isinstance(i, Exception): - await self._error(i) - + if i is not None: + if isinstance(i, Exception) and event != cbids.error: + await self._error(i) + # websocket @@ -178,7 +177,7 @@ async def handle_bridges(self, message: Post): if isinstance(message.user, User): fetch = True - if message.user.name in self.__bridges__ and ":" in message.data: + if message.user.username in self.__bridges__ and ":" in message.data: split = message.data.split(": ", 1) message.data = split[1] message.user = PartialUser(split[0], self) @@ -191,7 +190,18 @@ async def handle_bridges(self, message: Post): message.data = message.data.replace("#0000", "") return message + + def get_context(self, message: Post): + return Context(message, self) + + async def run_commands(self, message: Post): + + args = shlex.split(str(message)) + try: + await self.commands[args[0]].run_cmd(self.get_context(message), *args[1:]) + except Exception as e: + await self._error(e) def command(self, name=None, args=0, aliases = None): def inner(func): @@ -214,23 +224,40 @@ async def _connect(self): "cmd": "type", "val": "py" }}) - resp = await self.send_data_request({ - "cmd": "direct", - "val": { - "cmd": "authpswd", + + async with self.message_condition: + await self.sendPacket({ + "cmd": "direct", "val": { - "username": str(self.username).strip(), - "pswd": str(self.password).strip() - } - } - }) - - if resp[1]["val"] != "I: 100 | OK": - raise Exception(f"Wrong Username or Password!\n {resp[1]['val']}") + "cmd": "authpswd", + "val": { + "username": str(self.username).strip(), + "pswd": str(self.password).strip() + } + }, + "listener": "mb.py_login" + }) + + while True: + + await self.message_condition.wait() + + if self._packets[-1].get("listener") != "mb.py_login": + continue - self.api.login(resp[0]['val']['payload']['token']) + if self._packets[-1]["cmd"] == "statuscode" and self._packets[-1]["val"] != "I: 100 | OK": + raise Exception(f"Wrong Username or Password!\n {self._packets[-1]["val"]}") - await self._run_event(cbids.login, resp[0]['val']['payload']['token']) + if not (self._packets[-1]["cmd"] == "direct" and "payload" in self._packets[-1]["val"].keys()): + continue + + break + + await self.api.login(self._packets[-1]['val']['payload']['token']) + + await self._run_event(cbids.login, self._packets[-1]['val']['payload']['token']) + + @@ -256,10 +283,13 @@ async def _message(self, message): case "direct": if "post_origin" in message["val"]: # post - post = Post(self, message["val"], chat=self.get_chat()) - with self.message_condition: + await self._run_event(cbids.__raw__, message["val"]) + post = Post(self, message["val"], chat=message["val"]["post_origin"]) + async with self.message_condition: self.messages.append(post) self.message_condition.notify_all() + + await self._run_event(cbids.message, post) else: return await self._run_event(cbids.direct, message) @@ -278,7 +308,9 @@ async def _message(self, message): async def _error(self, error): - pass + + await self._run_event(cbids.error, error) + async def start(self, username, password, server="wss://server.meower.org", ): """ diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index 2380bc2..ca75078 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -6,12 +6,13 @@ import time class Client: ws: websockets.WebSocketClientProtocol - message_condition = asyncio.Condition() - __messages: str + message_condition: asyncio.Condition + _packets: list def __init__(self): self.ws = None - self.__messages = [] + self._packets = [] + self.message_condition = asyncio.Condition() pass async def _connect(self): @@ -51,7 +52,7 @@ async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): if expected_count is not None and len(received_packets) >= expected_count: return received_packets found = False - for message in self.__messages: + for message in self._packets: if all(message.get(key) == value for key, value in condition.items()): received_packets.append(message) found = True @@ -66,14 +67,6 @@ async def _wait_for_packet(self, conditions, expected_count=None, timeout=None): if timeout is not None and time.time() - start_time >= timeout: return None - async def send_data_request(self, packet, timeout = None): - if packet.get("listener") == None: - # This does not need to be sucure lmao - packet["listener"] = random.random() # nosec - - await self.sendPacket(packet) - return await self._wait_for_packet({"listener": packet["listener"]}, expected_count=2, timeout=timeout) - async def send_statuscode_request(self, packet, timeout = 0): if packet.get("listener") == None: # This does not need to be sucure lmao @@ -85,35 +78,35 @@ async def send_statuscode_request(self, packet, timeout = 0): async def close(self, reason=None): await self.ws.close(reason=reason) - async def connect(self, server, cookies=None): + async def connect(self, server): + loop = asyncio.get_event_loop() async for websocket in websockets.connect(server, ping_interval = None): # Meower uses its own implementation, crashes the connection if left on. try: self.ws = websocket try: - await self._connect() + t = loop.create_task(self._connect()) except Exception as e: - print(e) + await self._error(e) async for message in websocket: try: data = json.loads(message) - with self.message_condition: - self.__messages.append(data) + async with self.message_condition._lock: + self._packets.append(data) self.message_condition.notify_all() - self.__messages = self.__messages[:50] + self._packets = self._packets[:50] await self._message(data) except websockets.ConnectionClosed: - raise + await self._disconnect() except Exception as e: await self._error(e) - pass except websockets.ConnectionClosed: await self._disconnect() @@ -121,7 +114,6 @@ async def connect(self, server, cookies=None): except Exception as e: await self._error(e) - pass await self._disconnect() diff --git a/MeowerBot/command.py b/MeowerBot/command.py index 707c227..1319a58 100644 --- a/MeowerBot/command.py +++ b/MeowerBot/command.py @@ -105,12 +105,9 @@ async def run_cmd(self, ctx, *args): if not self.args_num == 0: args = args[:self.args_num] - if self.connected is None: - await self.func(ctx, *args) - else: - await self.func(self.connected, ctx, *args) - - + + await self.func(ctx, *args) + diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 0addf36..05ae688 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -15,7 +15,7 @@ def __init__(self, id, bot: "Bot"): self.bot: "Bot" = bot async def send_msg(self, message) -> Post: - return await Post(self.bot, self.bot.api.send_post(self.id, message).to_dict(), self) + return await Post(self.bot, (await (self.bot.api.send_post(self.id, message))).to_dict(), self) async def fetch(self): return Chat(await self.bot.api.get_chat(self.id)) @@ -64,7 +64,7 @@ class Post: def __init__(self, bot, _raw, chat): self.bot = bot self._raw = _raw - self.user: User = User(bot, self._raw["u"]) + self.user: PartialUser = PartialUser(bot, self._raw["u"]) self.chat: PartialChat = PartialChat(chat, bot) self.data = self._raw["p"] @@ -81,7 +81,7 @@ async def reply(self, message): class Context: def __init__(self, post, bot): - self.message = Post(bot, post) + self.message = post self.user = self.message.user self.bot = bot diff --git a/tests/integration_login.py b/tests/integration_login.py index f1b574b..33ab489 100644 --- a/tests/integration_login.py +++ b/tests/integration_login.py @@ -6,7 +6,7 @@ from os import environ as env logging.basicConfig(level=logging.DEBUG) -#logging.getLogger("websockets.client").setLevel(level=logging.INFO) +logging.getLogger("websockets.client").setLevel(level=logging.INFO) bot = Bot() @@ -18,13 +18,9 @@ async def login(t): async def ping(ctx: Context): await ctx.send_msg("Pong!\n My latency is: " + str(bot.latency)) - def predicate(msg: Post): - return ctx.user.username == msg.user.username and msg.chat == ctx.message.chat + - msg = await bot.wait_for_message(predicate, timeout=100) - await ctx.send_msg("You said: " + str(msg)) +MeowerAPI.base_uri = "https://beta.meower.org/api/" -MeowerAPI.base_uri = "http://beta.meower.org:5174/api/" - -bot.run(env['uname'], env['password'], server="wss://beta.meower.org/api/v0/cloudlink") +bot.run(env["uname"], env["pswd"], server="wss://beta.meower.org/api/v0/cloudlink") From 0356b781b3af316e7f3d3b012664abdbb96fc469 Mon Sep 17 00:00:00 2001 From: ShowierData9978 Date: Tue, 24 Oct 2023 21:28:26 -0500 Subject: [PATCH 13/13] update version --- MeowerBot/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MeowerBot/__init__.py b/MeowerBot/__init__.py index 81554c8..3029a22 100644 --- a/MeowerBot/__init__.py +++ b/MeowerBot/__init__.py @@ -5,7 +5,7 @@ """ -__version__ = "2.6.2" +__version__ = "3.0.0" # Public library imports diff --git a/pyproject.toml b/pyproject.toml index 90f7cfd..cd665ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "MeowerBot" -version = "3.0-BETA" +version = "3.0" description = "A meower bot lib for py" authors = ["showierdata9978 <68120127+showierdata9978@users.noreply.github.com>"] license = "MIT"