diff --git a/.env.example b/.env.example deleted file mode 100644 index 8c9b4bc..0000000 --- a/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# This is for deploying mb.py's docs -cloudflare_token= -account_id= -project_name= \ No newline at end of file diff --git a/MeowerBot/__init__.py b/MeowerBot/__init__.py index a0bbbab..a0b1470 100644 --- a/MeowerBot/__init__.py +++ b/MeowerBot/__init__.py @@ -10,6 +10,6 @@ # Public library imports from . import bot as botm -from .bot import Bot, cbids, CallBackIds +from .bot import Bot, CallBackIds -__all__ = ["__version__", "Bot", "botm", "cbids", "CallBackIds"] +__all__ = ["__version__", "Bot", "botm", "CallBackIds"] diff --git a/MeowerBot/_cache.py b/MeowerBot/_cache.py index c211155..20c2d29 100644 --- a/MeowerBot/_cache.py +++ b/MeowerBot/_cache.py @@ -1,7 +1,7 @@ import time -from typing import Dict, List, Tuple +from typing import Dict, Tuple, Any -from MeowerBot.context import Chat, PartialChat, PartialUser, User +from MeowerBot.context import Chat, User from MeowerBot.data.generic import UUID # 5 hours @@ -9,16 +9,15 @@ class Cache: - chats: Dict[UUID, Tuple[float, Chat | PartialChat]] - users: Dict[str, Tuple[float, User | PartialUser]] - bots: Dict[str, float] + chats: Dict[str, Tuple[float, Chat]] + users: Dict[str, Tuple[float, User]] def __init__(self): self.bots = {} self.chats = {} self.users = {} - def get_user(self, user_id: str) -> User | PartialUser | None: + def get_user(self, user_id: str) -> User | None | bool: if user_id not in self.users: return None @@ -27,6 +26,10 @@ def get_user(self, user_id: str) -> User | PartialUser | None: self.users.pop(user_id) return None + if not isinstance(user[1], User): + self.users.pop(user_id) + return False + return user[1] def add_user(self, user: User): @@ -38,7 +41,7 @@ def add_user(self, user: User): self.users[user.id] = (time.time(), user) - def get_chat(self, chat_id: UUID) -> Chat | PartialChat | None: + def get_chat(self, chat_id: UUID) -> Chat | None: if chat_id not in self.chats: return None @@ -59,38 +62,4 @@ def add_chat(self, chat: Chat): self.chats[chat.id] = (time.time(), chat) - def try_clear_bots(self): - ret = [] - rem = [] - for username, timeout in self.bots.items(): - if timeout + CASHE_EXPIRATION < time.time(): - rem.append(username) - continue - - ret.append(username) - - for user in rem: - del self.bots[user] - - return ret - - def add_bot(self, user): - if user in self.bots: - self.bots[user] = time.time() - return - - self.bots[user] = time.time() - if user not in self.users: - return - - self.users[user][1].is_bot = True - - def remove_bot(self, user): - if user not in self.bots: - return - - del self.bots[user] - if user not in self.users: - return - self.users[user][1].is_bot = False diff --git a/MeowerBot/_version.py b/MeowerBot/_version.py index 6093f83..bb78542 100644 --- a/MeowerBot/_version.py +++ b/MeowerBot/_version.py @@ -1 +1 @@ -__version__ = '3.3.2' +__version__ = '4.0.0-alpha.2' diff --git a/MeowerBot/api/user.py b/MeowerBot/api/user.py index 719bf6d..c2a9648 100644 --- a/MeowerBot/api/user.py +++ b/MeowerBot/api/user.py @@ -7,7 +7,8 @@ from ..data.api.chats import ChatGroup from ..data.api.reports import PagedRequest from ..data.api.user import ( - Relationship + Relationship, + User as RawUser ) from ..data.generic import Post @@ -26,6 +27,9 @@ async def _get(self, username, url, page=1, query=None, params=None): return await self.client.get(urljoin(f"/users/{username}/", url), params={"q": query, "p": page, **params}) + async def get(self, username: str): + return api_resp(RawUser, await self._get(username, "")) + async def get_posts(self, username, query, page=1): return api_resp(PagedRequest[Post], await self._get(username, "posts", query=query, page=page, params={"autoget": None})) diff --git a/MeowerBot/bot.py b/MeowerBot/bot.py index a2f9cec..80e9653 100644 --- a/MeowerBot/bot.py +++ b/MeowerBot/bot.py @@ -2,20 +2,27 @@ import copy import json import logging +import secrets import shlex import traceback from enum import StrEnum -from typing import Optional, Callable, Dict, List +from typing import Optional, Callable, Dict, List, cast + +import httpx + +from MeowerBot.data.api.chats import ChatGroup from ._cache import Cache from .api import MeowerAPI from .cl import Client from .cog import Cog from .command import AppCommand -from .context import Context, PartialChat, PartialUser, Post, User +from .context import Context, Chat, Post, User, create_message, get_user from .data.generic import UUID from types import CoroutineType +from .events import Events + class CallBackIds(StrEnum): """Callbacks that the bot calls. You can find more documentation in :class:`MeowerBot.bot.Bot`""" @@ -29,17 +36,16 @@ class CallBackIds(StrEnum): direct = "direct" statuscode = "statuscode" -cbids = CallBackIds -callbacks = [i for i in CallBackIds] # type: ignore +callbacks = [i.value for i in CallBackIds] # type: ignore - -class Bot(Client): +class Bot(Client, Events): """A class that holds all the networking for a Meower bot to function and run""" messages: List[Post] = [] #: :meta private: :meta hide-value: - message_condition = asyncio.Condition() #: :meta private: :meta hide-value: - user: PartialUser | User # Parcial user when bot is not logged in + post_condition = asyncio.Condition() #: :meta private: :meta hide-value: + user: User | None # Parcial user when bot is not logged in cache: Cache + api: MeowerAPI __bridges__ = [ #: :meta public: :meta hide-value: "Discord", @@ -55,8 +61,6 @@ class Bot(Client): "I: 100 | Bot" ] - userlist: List[str] = None #: :meta hide-value: #type: ignore - @property def latency(self) -> float: """Gets the latency of the bot @@ -71,15 +75,15 @@ async def _t_ping(self): try: await asyncio.sleep(5) - await self.sendPacket({"cmd": "ping", "val": ""}) + await self.send_packet({"cmd": "ping", "val": ""}) except Exception as e: await self._error(e) break - def __init__(self, prefix=None): # type: ignore + def __init__(self, prefix=None): # type: ignore super().__init__() - self.api: MeowerAPI = None # type: ignore + self.api: MeowerAPI = None # type: ignore self.callbacks: Dict[str, List[CoroutineType]] = {str(i): [] for i in callbacks} self.callbacks["__raw__"] = [] self.userlist = [] @@ -89,12 +93,13 @@ def __init__(self, prefix=None): # type: ignore self.password: str = None # type: ignore #: :meta hide-value: self.commands = {} - self.prefix = prefix + self.prefix: str = prefix # type: ignore self.logger = logging.getLogger("MeowerBot") self.server: str = None # type: ignore self.cache = Cache() self.cogs: Dict[str, Cog] = {} + # Interface def event(self, func: Callable): @@ -111,7 +116,7 @@ def event(self, func: Callable): setattr(self, func.__name__, func) - def listen(self, callback: Optional[str] = None): + def listen(self, callback: CallBackIds): """ Does the same thing as :meth MeowerBot.bot.Bot.event:but does not replace the bot's original functionality @@ -120,7 +125,6 @@ def listen(self, callback: Optional[str] = None): """ def inner(func): nonlocal callback - callback = callback if callback is not None else func.__name__ if callback not in callbacks: raise TypeError(f"{callback} is not a valid listener") @@ -138,7 +142,6 @@ def update_commands(self): for i in cog.callbacks.keys(): self.callbacks[str(i)].append(cog.callbacks[str(i)]) - async def error(self, err: Exception): """Handles errors for the bot. @@ -146,57 +149,23 @@ async def error(self, err: Exception): """ self.logger.error(traceback.print_exception(err)) - async def __raw__(self, packet: dict): - """Callback for raw packets. Gets called before the bot does any processing. - - This is a callback for :meth:`MeowerBot.bot.Bot.event` - """ - pass - - async def login(self, token: str): - """Gets called when the bot is fully ready and logged into meower - - This is a callback for :meth:`MeowerBot.bot.Bot.event` - """ - pass - - async def disconnect(self): - """Gets called when the bot gets disconnected from meower - - This is a callback for :meth:`MeowerBot.bot.Bot.event` - """ - pass - - async def ulist(self, ulist: List[str]): - """Gets called when a user connects to meower. - - This is a callback for :meth:`MeowerBot.bot.Bot.event` - """ - pass - async def message(self, message: Post): """Method for overiding how the bot handles messages. This is a callback for :meth:`MeowerBot.bot.Bot.event` """ - message = await self.handle_bridges(message) - if not message.data.startswith(self.prefix): + if not message.content.startswith(self.prefix): return - message.data = message.data.removeprefix(self.prefix) + message.content= message.content.removeprefix(self.prefix) await self.run_commands(message) - async def statuscode(self, status, listerner): pass - async def raw_message(self, data: dict): pass - async def direct(self, data: dict): pass - - - async def _run_event(self, event: cbids, *args, **kwargs): - events: List[Callable] = [getattr(self, str(event))] + async def _run_event(self, event: CallBackIds, *args, **kwargs): + events: List[Callable] = [getattr(self, str(event.value))] - for i in self.callbacks[str(event)]: + for i in self.callbacks[event]: if type(i) is list: events.extend(i) # type: ignore elif callable(i): # Check if the element is Callable @@ -204,13 +173,13 @@ async def _run_event(self, event: cbids, *args, **kwargs): err = await asyncio.gather(*[i(*args, **kwargs) for i in events if callable(i)], return_exceptions=True) for i in err: - if i is not None: - if isinstance(i, Exception) and event != cbids.error: - await self._error(i) + if i is not None and isinstance(i, Exception) and event != CallBackIds.error: + await self._error(i) # websocket - async def sendPacket(self, message: dict): + + async def send_packet(self, message: dict): if message.get("listener") != "mb.py_login": self.logger.debug("Sending Packet: " + json.dumps(message)) else: @@ -218,28 +187,14 @@ async def sendPacket(self, message: dict): message_sensitive["val"]["val"]["pswd"] = "" self.logger.debug("Sending Packet: " + json.dumps(message_sensitive)) - await super().sendPacket(message) - - async def handle_bridges(self, message: Post): - fetch = False - if isinstance(message.user, User): - fetch = True - - if message.user.username in self.__bridges__ and ":" in message.data: - split = message.data.split(":", 1) - message.data = split[1].strip() - message.user = PartialUser(split[0].strip(), self) - if fetch: - data = self.cache.get_user() - if not isinstance(data, User): - data = await message.user.fetch() - - if data: - message.user = data + await super().send_packet(message) - - if message.data.startswith(self.prefix + "#0000"): - message.data = message.data.replace("#0000", "") + async def handle_bridges(self, message: dict): + if message["u"] in self.__bridges__ and ":" in message["p"]: + _ = message["p"].split(": ") + message["u"] = _[0] + message["p"] = _[1] + message["p"] = message["p"].replace("#0000", "") return message @@ -250,13 +205,12 @@ async def run_commands(self, message: Post): args = shlex.split(str(message)) - if (err := await self.commands[args[0]].run_cmd( self.get_context(message), *args[1:] )) is not None: await self._run_event(CallBackIds.error, err) - + return err def command(self, name=None, args=0, aliases: Optional[List[str]] = None): # type: ignore def inner(func): @@ -265,23 +219,21 @@ def inner(func): self.commands = AppCommand.add_command(self.commands, cmd) - return cmd return inner - async def _connect(self): await self._send_initial_commands() packet = await self._authenticate() await self._process_login_response(packet) async def _send_initial_commands(self): - await self.sendPacket({"cmd": "direct", "val": "meower", "listener": "send_tkey"}) - await self.sendPacket({"cmd": "direct", "val": {"cmd": "type", "val": "py"}}) + await self.send_packet({"cmd": "direct", "val": "meower", "listener": "send_tkey"}) + await self.send_packet({"cmd": "direct", "val": {"cmd": "type", "val": "py"}}) async def _authenticate(self): - async with self.message_condition: - await self.sendPacket({ + async with self.packet_condition: + await self.send_packet({ "cmd": "direct", "val": { "cmd": "authpswd", @@ -294,12 +246,11 @@ async def _authenticate(self): }) while True: - await self.message_condition.wait() + await self.packet_condition.wait() if self._packets[-1].get("listener") != "mb.py_login": continue - print(self._packets[-1]) 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']}") elif self._packets[-1]["cmd"] == "statuscode": @@ -311,12 +262,11 @@ async def _authenticate(self): return self._packets.pop(-1) - - async def _process_login_response(self, packet): await self.api.login(packet['val']['payload']['token']) + self.user = User(packet['val']['payload']["account"]) + await self._run_event(CallBackIds.login, packet['val']['payload']['token']) - self.user = await self.user.fetch() def register_cog(self, cog: Cog): self.cogs[cog.__class__.__name__] = cog @@ -324,17 +274,27 @@ def register_cog(self, cog: Cog): self.update_commands() async def _disconnect(self): + # type: ignore # Chat is accessed. await self._run_event(CallBackIds.disconnect) - def get_chat(self, chat_id: str): - chat = self.cache.get_chat(UUID(chat_id)) - if chat is None: - return PartialChat(chat_id, self) + async def get_chat(self, chat_id: str): + + if (chat := self.cache.get_chat(UUID(chat_id))): + return chat + + resp = await self.api.chats.get(UUID(chat_id)) + if (resp[1] != 200): + return + + chat = Chat(resp[0].to_dict(), self) # type: ignore + + self.cache.add_chat(chat) + return chat + async def _message(self, message: dict): - # noinspection PyBroadException try: if message.get("cmd") == "direct" and message.get("listener") == 'mb.py_login': message_sensitive = copy.deepcopy(message) @@ -350,62 +310,42 @@ async def _message(self, message: dict): return await self._run_event(CallBackIds.statuscode, message["val"], message.get("listener")) case "ulist": - self.userlist = message["val"].split(";") - await self._check_bot_users(self.userlist) + usernames = message["val"].split(";") + usernames.pop() + self.userlist = [] + coros = [] + for user in usernames: + coros.append(get_user(self, user)) + self.userlist = list(*(await asyncio.gather(*coros))) return await self._run_event(CallBackIds.ulist, self.userlist) case "direct": await self._handle_direct(message) - if message["cmd"] == "pmsg": - if message["val"] not in self.BOT_NO_PMSG_RESPONSE: - await self.sendPacket({ - "cmd": "pmsg", - "val": "I:500 | Bot", - "id": message["origin"] - }) - else: - await self.handle_bot_pmsg(message["origin"]) - - async def _handle_direct(self, message): - if "post_origin" not in message["val"]: # post + await self._run_event(CallBackIds.direct, message) + + if "post_origin" not in message["val"]: + return - return await self._run_event(CallBackIds.direct, message) + await self._run_event(CallBackIds.raw_message, message["val"]) # type: ignore[call-arg] - await self._run_event(CallBackIds.__raw__, message["val"]) # type: ignore[call-arg] - post = Post(self, message["val"], chat=message["val"]["post_origin"]) - async with self.message_condition: + post = await create_message(self, await self.handle_bridges(message["val"])) + async with self.post_condition: self.messages.append(post) - self.message_condition.notify_all() + self.post_condition.notify_all() self.messages = self.messages[0: 50] await self._run_event(CallBackIds.message, post) - async def _check_user(self, user): - if user in self.__bridges__ or "bot" in user.lower(): - self.cache.add_bot(user) - return - - + if self.cache.bots.get(user): return - await self.sendPacket({ - "cmd": "pmsg", - "val": { - "library": "MeowerBot.py", - "known_bots": self.cache.bots - }, - "id": user - }) - - async def handle_bot_pmsg(self, origin): - if origin == self.username: + resp = httpx.get(f"https://meower-utils.showierdata.xyz/bots/{user}") + if resp.status_code != 200: return - self.cache.add_bot(origin) - - + self.cache.add_bot(user, resp.json()) async def _check_bot_users(self, userlist): assert self.api is not None @@ -421,31 +361,29 @@ async def _check_bot_users(self, userlist): # noinspection PyAsyncCall asyncio.gather(*[self._check_user(username) for username in userlist]) - - async def _error(self, error): await self._run_event(CallBackIds.error, error) - async def start(self, username, password, server="wss://server.meower.org", ): """ Runs The bot (Blocking) """ - self.username = username + self._username = username self.password = password - self.user = PartialUser(self.username, self) + self.update_commands() - # noinspection PyAsyncCall + 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 self.api = MeowerAPI(username=username) - await self.connect(server) def run(self, username, password, server="wss://server.meower.org", ): @@ -459,9 +397,4 @@ def run(self, username, password, server="wss://server.meower.org", ): return fut - - - - - -__all__ = ["Bot", "CallBackIds", 'cbids'] +__all__ = ["Bot", "CallBackIds"] diff --git a/MeowerBot/cl/__init__.py b/MeowerBot/cl/__init__.py index 8e3d1e1..31c87a9 100644 --- a/MeowerBot/cl/__init__.py +++ b/MeowerBot/cl/__init__.py @@ -1,21 +1,25 @@ import asyncio import json +from typing import List import websockets +from MeowerBot.context import User + class Client: """ MeowerBot.py's async websocket wrapper. """ ws: websockets.WebSocketClientProtocol - message_condition: asyncio.Condition + packet_condition: asyncio.Condition _packets: list + userlist: List[User] = None #: :meta hide-value: #type: ignore def __init__(self): - self.ws = None + self.ws = None # type: ignore self._packets = [] - self.message_condition = asyncio.Condition() + self.packet_condition = asyncio.Condition() pass async def _connect(self): @@ -30,15 +34,15 @@ async def _message(self, message): async def _error(self, error): pass - async def sendPacket(self, message): + async def send_packet(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: str | None =None): + await self.ws.close(reason=reason or "") 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. + async for websocket in websockets.connect(server): try: self.ws = websocket @@ -50,15 +54,13 @@ async def connect(self, server): async for message in websocket: try: data = json.loads(message) - async with self.message_condition: + async with self.packet_condition: self._packets.append(data) - self.message_condition.notify_all() + self.packet_condition.notify_all() self._packets = self._packets[:50] - await self._message(data) - except websockets.ConnectionClosed: await self._disconnect() diff --git a/MeowerBot/context.py b/MeowerBot/context.py index 2b01308..d46cb95 100644 --- a/MeowerBot/context.py +++ b/MeowerBot/context.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Optional, TYPE_CHECKING +from typing import Literal, Optional, TYPE_CHECKING, cast from .api.shared import api_resp from .data.api.chats import ChatGroup @@ -9,110 +9,104 @@ from . import Bot -class PartialChat: - def __init__(self, id, bot): - self.id = id - self.bot = bot - - async def send_msg(self, message) -> Optional["Post"]: - data, status = await self.bot.api.send_post(self.id, message) - - if status != 200: - return None - return Post(self.bot, data.to_dict(), self) - - async def fetch(self) -> Optional["Chat"]: - chat = self.bot.cache.get_chat(self.id) - if isinstance(chat, Chat): return chat - - data, status = await self.bot.api.chats.get(self.id) - - if status != 200: - return None - - chat = Chat(data, self.bot) - self.bot.cache.add_chat(chat) - return chat +class Chat: + def __init__(self, data: ChatGroup, bot: "Bot"): + self.bot = bot -class Chat(PartialChat): - def __init__(self, data: ChatGroup, bot): - super().__init__(data._id, bot) + self.id: str = data._id self.created = data.created self.deleted = data.deleted self.last_active = data.last_active self.members = data.members + self.owner = data.owner + self.nickname = data.nickname + self.data = data - self.owner = data.owner - self.type = data.type - self.nickname = data.nickname - self.data = data - - -class PartialUser: - def __init__(self, username: str, bot): - self.username: str = username - self.bot: "Bot" = bot - self.is_bot: bool = username in self.bot.cache.bots - - async def fetch(self) -> Optional["User"]: - user = self.bot.cache.get_user(self.username) - if isinstance(user, User): return user - - data, status = api_resp(RawUser, await self.bot.api.users._get(self.username, "", None)) + async def send_msg(self, message) -> Optional["Post"]: + data, status = await self.bot.api.send_post(self.id, message) - user = User(self.username, self.bot, data) if status == 200 else None - self.bot.cache.add_user(user) - return user + if status != 200: + return None + return await create_message(self.bot, data) # type: ignore -class User(PartialUser): - def __init__(self, username, bot, data: RawUser): - super().__init__(username, bot) +class Asset: + def __init__(self, data: dict): # TODO: Type the raw format. + self.filename = data["filename"] + self.hight = data["height"] + self.id = data["id"] + self.mime = data["mime"] + self.size = data["size"] + self.width = data["width"] +class User: + def __init__(self, data: RawUser): 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 + self.name = self.data.name + self.permissions = self.data.permissions + self.quote = self.data.quote + self.id = self.data.uuid + self.avatar_color = self.data.avatar_color + if (self.data.avatar): + self.pfp = f"https://uploads.meower.org/icons/{self.data.avatar}" + else: + self.pfp = f"https://raw.githubusercontent.com/3r1s_s/meo/main/images/avatars-webp/icon_{self.data.pfp_data}.webp" class Post: - def __init__(self, bot, _raw: dict, chat): - self.bot = bot + def __init__(self, bot, user: User, _raw: dict, chat: Chat): + self._bot = bot self._raw = _raw - self.user: PartialUser | User = PartialUser(self._raw["u"], bot) - - self.chat: PartialChat = PartialChat(chat, bot) - self.data: str = self._raw["p"] - self._id = self._raw["post_id"] - self.type = self._raw["type"] - self.date = datetime.fromtimestamp(self._raw["t"]["e"]) + self.user: User = user + self.chat: Chat = chat + self.content: str = self._raw["p"] + self._id: str = self._raw["post_id"] + self.date = datetime.fromtimestamp(self._raw["t"]["e"]) + self.attachments: list[Asset] = [Asset(data) for data in self._raw["assets"]] + def __str__(self): - return str(self.data) + return str(self.content) async def reply(self, message): - await self.chat.send_msg(f"@{self.user.username} [{self._id}] {message}") + return await self.chat.send_msg( + f"@{self.user.name} \"{self.content[:20]}\" ({self._id})\n" + \ + f"{message}" + ) class Context: def __init__(self, post: Post, bot): self.message = post self.user = self.message.user - self.bot = bot + self._bot = bot async def send_msg(self, msg): return await self.message.chat.send_msg(msg) async def reply(self, msg): await self.message.reply(msg) + +async def create_message(bot: "Bot", message: dict): + return Post( + bot, + cast(User, await get_user(bot, message["u"])), + message, + cast(Chat, await bot.get_chat(message["post_origin"])) + ) + +async def get_user(bot: "Bot", name: str) -> Optional[User]: + if (user := bot.cache.get_user(name)): + return cast(User, user) + + data = (await bot.api.users.get(name)) + if (data[1] != 200): return + + user = User(cast(RawUser, data[0])) + bot.cache.add_user(user) + return user + + diff --git a/MeowerBot/data/api/user.py b/MeowerBot/data/api/user.py index 1b75b6a..e0e0759 100644 --- a/MeowerBot/data/api/user.py +++ b/MeowerBot/data/api/user.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, field +from typing_extensions import deprecated from dataclasses_json import config, dataclass_json @@ -52,22 +53,25 @@ class Restrictions: @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 + name: str = field(metadata=config(field_name="_id")) + avatar: str + avatar_color: str + 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 + @dataclass_json @dataclass class Relationship: username: str state: int - updated_at: int \ No newline at end of file + updated_at: int diff --git a/MeowerBot/events.py b/MeowerBot/events.py new file mode 100644 index 0000000..ec5e428 --- /dev/null +++ b/MeowerBot/events.py @@ -0,0 +1,53 @@ +from typing import List + +from MeowerBot.context import Post + + +class Events: + async def __raw__(self, packet: dict): + """Callback for raw packets. Gets called before the bot does any processing. + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass + + async def login(self, token: str): + """Gets called when the bot is fully ready and logged into meower + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass + + async def disconnect(self): + """Gets called when the bot gets disconnected from meower + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass + + async def ulist(self, ulist: List[str]): + """Gets called when a user connects to meower. + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass + + async def statuscode(self, status, listerner): pass + + async def raw_message(self, data: dict): pass + + async def direct(self, data: dict): pass + + async def error(self, err: Exception): + """Handles errors for the bot. + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass + + async def message(self, message: Post): + """Method for overiding how the bot handles messages. + + This is a callback for :meth:`MeowerBot.bot.Bot.event` + """ + pass \ No newline at end of file diff --git a/MeowerBot/ext/help.py b/MeowerBot/ext/help.py index 579eec1..22fd2ec 100644 --- a/MeowerBot/ext/help.py +++ b/MeowerBot/ext/help.py @@ -16,7 +16,7 @@ def _get_index_or(lst, i, d): class Help(Cog): - __instance__: "Help" + __instance__: "Help" | None # type: ignore # such a pain, its a subclass so it does not matter _generated: bool = False def __init__(self, bot, disable_command_newlines=False, *args, **kwargs): diff --git a/build.py b/build.py index ed4f5ed..e1747ee 100644 --- a/build.py +++ b/build.py @@ -5,7 +5,6 @@ import sys -from dotenv import load_dotenv # type: ignore import tomllib import httpx @@ -13,9 +12,6 @@ def main(): - if os.path.exists('.env'): - load_dotenv(override=True) - assert os.system("npx wrangler -v") == 0, "Install Wrangler! \n npm install wrangler --global" os.system("rm -rf ./build/") @@ -32,11 +28,11 @@ def main(): return print("Uploading docs...") - os.system("npx wrangler pages deploy ./build/html --project-name meowerbot --commit-dirty=true --branch=master") - + os.system("npx wrangler pages deploy ./build/html --project-name meowerbot --commit-dirty=true --branch=v4") print("Building MB.py...") os.system("poetry build") + main() diff --git a/docs/source/Callbacks.rst b/docs/source/Callbacks.rst index 8fdccc1..aeabe5c 100644 --- a/docs/source/Callbacks.rst +++ b/docs/source/Callbacks.rst @@ -85,14 +85,6 @@ The error callback takes exactly one argument. It is the exception object that w .. _raw: -This callback don't work as intended, and mistakingly replaces the `raw_message` callback, -so the following docs are meant for a future reference when MeowerBot.py 4.0 is released. -Until then, raw\_message has the correct docs. - -++++++++++++++ -Intendend docs -++++++++++++++ - This callback is called every single time a packet is recieved by the bot, and therfore it is spammed. It takes a single :py:data:`dict` as an argument. More docs on what this can contain can be found on `the meower server documentation `_ @@ -154,10 +146,6 @@ The only argument is a list of strings signifing the currently online users. =========== raw_message =========== - - -This callback does the exact same thing as :ref:` raw` currently. - It takes a single dictinary, and the layout of this dictinary is the raw form of a post, which can be found in the `the meower server documentation `_ diff --git a/poetry.lock b/poetry.lock index 66408c1..7951aad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -13,13 +13,13 @@ files = [ [[package]] name = "anyio" -version = "4.2.0" +version = "4.3.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, - {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, ] [package.dependencies] @@ -221,13 +221,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.2" +version = "1.0.4" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, - {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, + {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"}, + {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"}, ] [package.dependencies] @@ -238,17 +238,17 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.23.0)"] +trio = ["trio (>=0.22.0,<0.25.0)"] [[package]] name = "httpx" -version = "0.25.2" +version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.25.2-py3-none-any.whl", hash = "sha256:a05d3d052d9b2dfce0e3896636467f8a5342fb2b902c819428e1ac65413ca118"}, - {file = "httpx-0.25.2.tar.gz", hash = "sha256:8b8fcaa0c8ea7b05edd69a094e63a2094c4efcb48129fb757361bc423c0ad9e8"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] @@ -374,22 +374,21 @@ files = [ [[package]] name = "marshmallow" -version = "3.20.2" +version = "3.21.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, - {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["pre-commit (>=2.4,<4.0)"] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -405,38 +404,38 @@ files = [ [[package]] name = "mypy" -version = "1.8.0" +version = "1.9.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] @@ -462,72 +461,72 @@ files = [ [[package]] name = "orjson" -version = "3.9.13" +version = "3.9.15" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.9.13-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fa6b67f8bef277c2a4aadd548d58796854e7d760964126c3209b19bccc6a74f1"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b812417199eeb169c25f67815cfb66fd8de7ff098bf57d065e8c1943a7ba5c8f"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ccd5bd222e5041069ad9d9868ab59e6dbc53ecde8d8c82b919954fbba43b46b"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaaf80957c38e9d3f796f355a80fad945e72cd745e6b64c210e635b7043b673e"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:60da7316131185d0110a1848e9ad15311e6c8938ee0b5be8cbd7261e1d80ee8f"}, - {file = "orjson-3.9.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b98cd948372f0eb219bc309dee4633db1278687161e3280d9e693b6076951d2"}, - {file = "orjson-3.9.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3869d65561f10071d3e7f35ae58fd377056f67d7aaed5222f318390c3ad30339"}, - {file = "orjson-3.9.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43fd6036b16bb6742d03dae62f7bdf8214d06dea47e4353cde7e2bd1358d186f"}, - {file = "orjson-3.9.13-cp310-none-win32.whl", hash = "sha256:0d3ba9d88e20765335260d7b25547d7c571eee2b698200f97afa7d8c7cd668fc"}, - {file = "orjson-3.9.13-cp310-none-win_amd64.whl", hash = "sha256:6e47153db080f5e87e8ba638f1a8b18995eede6b0abb93964d58cf11bcea362f"}, - {file = "orjson-3.9.13-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4584e8eb727bc431baaf1bf97e35a1d8a0109c924ec847395673dfd5f4ef6d6f"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f37f0cdd026ef777a4336e599d8194c8357fc14760c2a5ddcfdf1965d45504b"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d714595d81efab11b42bccd119977d94b25d12d3a806851ff6bfd286a4bce960"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9171e8e1a1f221953e38e84ae0abffe8759002fd8968106ee379febbb5358b33"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ab9dbdec3f13f3ea6f937564ce21651844cfbf2725099f2f490426acf683c23"}, - {file = "orjson-3.9.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811ac076855e33e931549340288e0761873baf29276ad00f221709933c644330"}, - {file = "orjson-3.9.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:860d0f5b42d0c0afd73fa4177709f6e1b966ba691fcd72175affa902052a81d6"}, - {file = "orjson-3.9.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:838b898e8c1f26eb6b8d81b180981273f6f5110c76c22c384979aca854194f1b"}, - {file = "orjson-3.9.13-cp311-none-win32.whl", hash = "sha256:d3222db9df629ef3c3673124f2e05fb72bc4a320c117e953fec0d69dde82e36d"}, - {file = "orjson-3.9.13-cp311-none-win_amd64.whl", hash = "sha256:978117122ca4cc59b28af5322253017f6c5fc03dbdda78c7f4b94ae984c8dd43"}, - {file = "orjson-3.9.13-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:031df1026c7ea8303332d78711f180231e3ae8b564271fb748a03926587c5546"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fd9a2101d04e85086ea6198786a3f016e45475f800712e6833e14bf9ce2832f"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:446d9ad04204e79229ae19502daeea56479e55cbc32634655d886f5a39e91b44"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b57c0954a9fdd2b05b9cec0f5a12a0bdce5bf021a5b3b09323041613972481ab"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:266e55c83f81248f63cc93d11c5e3a53df49a5d2598fa9e9db5f99837a802d5d"}, - {file = "orjson-3.9.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31372ba3a9fe8ad118e7d22fba46bbc18e89039e3bfa89db7bc8c18ee722dca8"}, - {file = "orjson-3.9.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3b0c4da61f39899561e08e571f54472a09fa71717d9797928af558175ae5243"}, - {file = "orjson-3.9.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cc03a35bfc71c8ebf96ce49b82c2a7be6af4b3cd3ac34166fdb42ac510bbfff"}, - {file = "orjson-3.9.13-cp312-none-win_amd64.whl", hash = "sha256:49b7e3fe861cb246361825d1a238f2584ed8ea21e714bf6bb17cebb86772e61c"}, - {file = "orjson-3.9.13-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:62e9a99879c4d5a04926ac2518a992134bfa00d546ea5a4cae4b9be454d35a22"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d92a3e835a5100f1d5b566fff79217eab92223ca31900dba733902a182a35ab0"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23f21faf072ed3b60b5954686f98157e073f6a8068eaa58dbde83e87212eda84"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:828c502bb261588f7de897e06cb23c4b122997cb039d2014cb78e7dabe92ef0c"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16946d095212a3dec552572c5d9bca7afa40f3116ad49695a397be07d529f1fa"}, - {file = "orjson-3.9.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3deadd8dc0e9ff844b5b656fa30a48dbee1c3b332d8278302dd9637f6b09f627"}, - {file = "orjson-3.9.13-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9b1b5adc5adf596c59dca57156b71ad301d73956f5bab4039b0e34dbf50b9fa0"}, - {file = "orjson-3.9.13-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ddc089315d030c54f0f03fb38286e2667c05009a78d659f108a8efcfbdf2e585"}, - {file = "orjson-3.9.13-cp38-none-win32.whl", hash = "sha256:ae77275a28667d9c82d4522b681504642055efa0368d73108511647c6499b31c"}, - {file = "orjson-3.9.13-cp38-none-win_amd64.whl", hash = "sha256:730385fdb99a21fce9bb84bb7fcbda72c88626facd74956bda712834b480729d"}, - {file = "orjson-3.9.13-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7e8e4a571d958910272af8d53a9cbe6599f9f5fd496a1bc51211183bb2072cbd"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfad553a36548262e7da0f3a7464270e13900b898800fb571a5d4b298c3f8356"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d691c44604941945b00e0a13b19a7d9c1a19511abadf0080f373e98fdeb6b31"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8c83718346de08d68b3cb1105c5d91e5fc39885d8610fdda16613d4e3941459"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ef57a53bfc2091a7cd50a640d9ae866bd7d92a5225a1bab6baa60ef62583f2"}, - {file = "orjson-3.9.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9156b96afa38db71344522f5517077eaedf62fcd2c9148392ff93d801128809c"}, - {file = "orjson-3.9.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31fb66b41fb2c4c817d9610f0bc7d31345728d7b5295ac78b63603407432a2b2"}, - {file = "orjson-3.9.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8a730bf07feacb0863974e67b206b7c503a62199de1cece2eb0d4c233ec29c11"}, - {file = "orjson-3.9.13-cp39-none-win32.whl", hash = "sha256:5ef58869f3399acbbe013518d8b374ee9558659eef14bca0984f67cb1fbd3c37"}, - {file = "orjson-3.9.13-cp39-none-win_amd64.whl", hash = "sha256:9bcf56efdb83244cde070e82a69c0f03c47c235f0a5cb6c81d9da23af7fbaae4"}, - {file = "orjson-3.9.13.tar.gz", hash = "sha256:fc6bc65b0cf524ee042e0bc2912b9206ef242edfba7426cf95763e4af01f527a"}, + {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, + {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, + {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, + {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, + {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, + {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, + {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, + {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, + {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, + {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, + {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, + {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, + {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, + {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, + {file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"}, + {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"}, + {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"}, + {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"}, + {file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"}, + {file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"}, + {file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"}, + {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"}, + {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"}, + {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"}, + {file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"}, + {file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"}, + {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, ] [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" 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"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -604,13 +603,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" description = "Sniff out which async library your code is running under" 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"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] @@ -798,13 +797,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -898,13 +897,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, - {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] @@ -997,4 +996,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "05c08d755c2a5c2fdd21cb04a23d6f1d872f63aa7f8c59195ca987a8099379b5" +content-hash = "a5f6c7ea3a9ab66d16c7dfe0b643a2e0731ebe90af40d62d6e9b1b709f4e7ce9" diff --git a/pyproject.toml b/pyproject.toml index 2b92faf..e790b77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "MeowerBot" -version = "3.3.2" +version = "4.0.0-alpha.2" description = "A meower bot lib for py" authors = ["showierdata9978 <68120127+showierdata9978@users.noreply.github.com>"] license = "MIT" @@ -14,8 +14,8 @@ repository = "https://github.com/MeowerBots/MeowerBot.py" [tool.poetry.dependencies] python = "^3.12" websockets = "^12.0" -orjson = "^3.9.4" -httpx = "^0.25.0" +orjson = "^3.9.15" +httpx = "^0.27.0" dataclasses-json = "^0.6.1" ujson = "^5.8.0" @@ -25,7 +25,6 @@ ujson = "^5.8.0" types-ujson = "^5.8.0.1" flake8 = "^6.1.0" python-dotenv = "^1.0.0" -httpx = "^0.25.2" mypy = "^1.8.0" sphinx-rtd-theme = "^2.0.0" sphinx = "^7.2.6" diff --git a/tests/intergration/integration_login.py b/tests/intergration/integration_login.py index 85b50b5..ec89921 100644 --- a/tests/intergration/integration_login.py +++ b/tests/intergration/integration_login.py @@ -1,3 +1,5 @@ +from typing import List + from MeowerBot import Bot, CallBackIds from MeowerBot.context import Context, PartialUser, Post, User from MeowerBot.cog import Cog @@ -5,13 +7,13 @@ import logging -from dotenv import load_dotenv # type: ignore - -load_dotenv() # type: ignore +from dotenv import load_dotenv # type: ignore from os import environ as env from MeowerBot.ext.help import Help as HelpExt +load_dotenv() # type: ignore + logging.basicConfig( level=logging.DEBUG, handlers=[ @@ -77,17 +79,18 @@ async def pong(ctx: Context, *message: str): class Ping(Cog): - def __init__(self, bot: Bot): + def __init__(self, _bot: Bot): super().__init__() - self.bot = bot + self._bot = _bot @command() async def cog_ping(self, ctx: Context): - await ctx.send_msg("Pong!\n My latency is: " + str(self.bot.latency)) + await ctx.send_msg("Pong!\n My latency is: " + str(self._bot.latency)) print(bot.api.headers.get("token")) + @cog_ping.subcommand() async def ping(self, ctx: Context): - await ctx.send_msg("Pong!\n My latency is: " + str(self.bot.latency)) + await ctx.send_msg("Pong!\n My latency is: " + str(self._bot.latency)) @bot.event @@ -97,6 +100,10 @@ async def login(token): assert await PartialUser(bot.user.username, bot).fetch() is not None assert await PartialUser("A" * 21, bot).fetch() is None +@bot.event +async def ulist(userlist: List[User]): + assert type(userlist[0]) is User + @bot.listen(CallBackIds.message) async def on_message(message: Post): assert isinstance(message, Post) diff --git a/tests/test_bot.py b/tests/test_bot.py new file mode 100644 index 0000000..186ebf3 --- /dev/null +++ b/tests/test_bot.py @@ -0,0 +1,104 @@ +import json +import unittest +from unittest import IsolatedAsyncioTestCase + +from unittest.mock import AsyncMock, patch, Mock +import asyncio +from MeowerBot import Bot, CallBackIds +from MeowerBot import bot +from httpx import Response +import httpx + +from MeowerBot.api import MeowerAPI + + +class BotTest(IsolatedAsyncioTestCase): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.bot = Bot() + self.bot.send_packet = AsyncMock() + self.bot.api = MeowerAPI('') + self.bot.api.client = AsyncMock() + self.bot.connect = AsyncMock() + + async def test_bots(self): + # Mocking a successful response for the bot "Bot" + bot_response = { + "_id": "Bot", + "owner": "EngineerRunner", + "verified": True, + "library": "MeowerBot.py" + } + bot_mock_response = Mock(status_code=200) + bot_mock_response.json.return_value = bot_response + bot_mock_response.text = "mocked response" + bot_mock_response.raise_for_status.return_value = None + bot_mock_get = Mock(return_value=bot_mock_response) + + # Mocking a failed response for a non-bot user "NotBot" + not_bot_mock_response = Mock(status_code=404) + not_bot_mock_response.raise_for_status.return_value = None + not_bot_mock_get = Mock(return_value=not_bot_mock_response) + + # Patching httpx.get to return the mocked responses + with patch('httpx.get', bot_mock_get) as mock_httpx_get: + # Triggering _check_user for "Bot" + await self.bot._check_user("Bot") + # Asserting that "Bot" is in the cache + self.assertIn("Bot", self.bot.cache.bots.keys()) + + with patch('httpx.get', not_bot_mock_get) as mock_httpx_get: + # Triggering _check_user for "NotBot" + await self.bot._check_user("NotBot") + # Asserting that "NotBot" is not in the cache + self.assertNotIn("NotBot", self.bot.cache.bots.keys()) + + async def test_ulist(self): + # Mocking responses for bot and user API calls + bot_response = { + "_id": "Bot", + "owner": "EngineerRunner", + "verified": True, + "library": "MeowerBot.py" + } + bot_mock_response = Mock(status_code=200) + bot_mock_response.json.return_value = bot_response + bot_mock_response.text = "mocked response" + bot_mock_response.raise_for_status.return_value = None + bot_mock_get = Mock(return_value=bot_mock_response) + + user_api_response = { + "_id": "ShowierData9978", + "avatar": "76rI2XjHKGXRX8qt9gtIdBib", + "avatar_color": "000000", + "banned": False, + "created": 1656462125, + "error": False, + "experiments": 0, + "flags": 0, + "last_seen": 1710820932, + "lower_username": "showierdata9978", + "lvl": 0, + "permissions": 65534, + "pfp_data": 21, + "quote": "Owns MeowerBot.py and RoboMeowy. Is Part of the Meower team, don't add me to random chats", + "uuid": "4f4d986b-63ef-4f7d-9b8b-87368c6e0280" + } + + bot_api_mock = AsyncMock(return_value=Mock(status_code=200)) + bot_api_mock.return_value.text = json.dumps(user_api_response) + + # Patching httpx.get for bot API call and bot API call wrapper + with patch('httpx.get', bot_mock_get) as httpx_get_patch: + with patch('MeowerBot.api.user.User._get', bot_api_mock) as bot_api_patch: + # Triggering _message with a ulist command + await self.bot._message({ + "cmd": "ulist", + "val": "A;B;C;D;E" + }) + + # Asserting that httpx.get is called 5 times (once for each user) + bot_api_mock.assert_called() +if __name__ == "__main__": + unittest.main()