diff --git a/misspy_core_fast/__init__.py b/misspy_core_fast/__init__.py new file mode 100644 index 0000000..4527409 --- /dev/null +++ b/misspy_core_fast/__init__.py @@ -0,0 +1,2 @@ +from . import http +from . import ws \ No newline at end of file diff --git a/misspy_core_fast/exception.py b/misspy_core_fast/exception.py new file mode 100644 index 0000000..f76c1d3 --- /dev/null +++ b/misspy_core_fast/exception.py @@ -0,0 +1,37 @@ +class MisskeyException(Exception): + """Base exception class for misspy + + Ideally, this should be caught and the exception handled. + """ + + pass + + +class WebsocketError(MisskeyException): + """websocket connection error class for misspy + + Ideally, this should be caught and the exception handled. + """ + + pass + + +class ClientException(MisskeyException): + """Parent class of error classes such as login failure.""" + +class RateLimitError(ClientException): + """RateLimitError trigger: RATE_LIMIT_EXCEEDED""" + +class AuthenticationFailed(ClientException): + """Class called if MisskeyAPI authentication fails.""" + + +class MiAuthFailed(AuthenticationFailed): + """Class called if MiAuth fails.""" + + +class HTTPException(MisskeyException): + """Error class called when a request to the misskey server's API fails. + + Called when httpx.HTTPError is caught. + """ diff --git a/misspy_core_fast/http.py b/misspy_core_fast/http.py new file mode 100644 index 0000000..b4d19b1 --- /dev/null +++ b/misspy_core_fast/http.py @@ -0,0 +1,104 @@ +import json +import requests + +import aiohttp +import httpx + +from .exception import HTTPException, ClientException, RateLimitError + +h_t = {"Content-Type": "application/json"} + +def request_sync( + address, + i=None, + endpoint="test", + jobj: dict = {"required": True}, + header: dict = h_t, + ssl: bool=True +): + """request_sync (internal function) + + Args: + address (string): instance address + i (string): user token + endpoint (string): endpoint (example: notes/create) + jobj (dict): request params. Do not include the i. Defaults to {"Content-Type": "application/json"}. + header (dict, optional): request header. Defaults to {"Content-Type": "application/json"}. + + Returns: + dict: request result + """ + url = address + "/api/" + endpoint + if i is not None: + jobj["i"] = i + res = requests.post(url, data=json.dumps(jobj, ensure_ascii=False), headers=header, verify=ssl) + try: + return res.json() + except: + return True + + +async def request( + address, + i=None, + endpoint="ping", + jobj: dict = {}, + files=None, + header=h_t, +): + """request (internal function) + + Args: + address (string): instance address + i (string): user token + endpoint (string): endpoint (example: notes/create) + jobj (dict): request params. Do not include the i. + + Returns: + dict: request result + """ + async with aiohttp.ClientSession() as client: + url = address + "/api/" + endpoint + if i is not None: + jobj["i"] = i + if files is not None: + async with httpx.AsyncClient() as client: + if header is not None: + res = await client.post( + url, + data=jobj, + files=files, + headers=header, + ) + else: + res = await client.post(url, files=files, data=jobj) + try: + resp = res.json() + except json.JSONDecodeError: + resp = json.loads(res.text) + else: + if header is not None: + res = await client.post( + url, data=json.dumps(jobj, ensure_ascii=False), headers=header + ) + try: + return await res.json() + except json.JSONDecodeError: + return True + else: + res = await client.post(url, data=json.dumps(jobj, ensure_ascii=False)) + try: + resp = await res.json() + except json.JSONDecodeError: + resp = json.loads(await res.text) + if resp.get("error").get("kind") is not None: + if resp["error"]["code"] == "RATE_LIMIT_EXCEEDED": + raise RateLimitError("We are being rate limited. Please try again in a few moments.") + raise ClientException( + resp["error"]["message"] + + "\nid: " + + resp["error"]["id"] + ) + else: + print(type(resp)) + return resp \ No newline at end of file diff --git a/misspy_core_fast/ws.py b/misspy_core_fast/ws.py new file mode 100644 index 0000000..95768e6 --- /dev/null +++ b/misspy_core_fast/ws.py @@ -0,0 +1,64 @@ +import asyncio +import json + +from attrdictionary import AttrDict +import aiohttp + +from misspy.hook import hook +from . import exception + + +class MiWS: + def __init__(self, address, i, ssl=True) -> None: + self.i = i + self.address = address + self.ssl = ssl + + async def ws_handler(self): + try: + procotol = "ws://" + if self.ssl: + procotol = "wss://" + session = aiohttp.ClientSession() + async with session.ws_connect( + f"{procotol}{self.address}/streaming?i={self.i}" + ) as self.connection: + try: + await hook.functions["ready"]() + except KeyError: + pass + while True: + try: + recv = await self.connection.receive_json() + except AttributeError: + pass + try: + if recv["type"] == "channel": + if recv["body"]["type"] == "note": + await hook.functions[recv["body"]["type"]]( + AttrDict(recv["body"]["body"]) + ) + elif recv["body"]["type"] == "notification": + await hook.functions[recv["body"]["body"]["type"]]( + AttrDict(recv["body"]["body"]) + ) + elif recv["body"]["type"] == "follow": + await hook.functions[recv["body"]["type"]]( + AttrDict(recv["body"]["body"]) + ) + elif recv["body"]["type"] == "followed": + await hook.functions[recv["body"]["type"]]( + AttrDict(recv["body"]["body"]) + ) + elif recv["type"] == "noteUpdated": + await hook.functions[recv["body"]["type"]]( + AttrDict(recv["body"]) + ) + else: + await hook.functions[recv["body"]["type"]]( + AttrDict(recv["body"]) + ) + except KeyError: + pass + except Exception as e: + raise exception.WebsocketError(e) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3e5e4a3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[tool.poetry] +name = "misspy-core-fast" +version = "0.0.0" +description = "fast Core library for misspy using aiohttp for the most part." +authors = ["sonyakun "] +packages = [{include = "misspy_core_fast"}] +license = "MIT" +readme = "README.md" +homepage = "https://misspy.xyz/" +documentation = "https://docs.misspy.xyz/" +repository = "https://github.com/misspy-development/core-fast" +keywords = [ + "misskey", + "misskey-api", + "misskey-bot" +] +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3 :: Only", + "License :: OSI Approved :: MIT License", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +[tool.poe.tasks] +build = "poetry build" +publish = "poetry publish" + +[tool.poetry.urls] +Changelog = "https://github.com/misspy-development/misspy/blob/master/CHANGELOG.md" + +[tool.poetry.dependencies] +python = "^3.8" + +attrdictionary = "^1.0.0" +aiodns = "^3.0.0" +aiohttp = "^3.8.5" +faust-cchardet = "^2.1.19" +requests = "^2.31.0" +httpx = "^0.25.0" +poetry = {version = "^1.6.1", allow-prereleases = true} + +[tool.poetry-version-plugin] +source = "git-tag" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file