karboai 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
karboai-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: karboai
3
+ Version: 1.0.0
4
+ Summary: Python library for https://karboai.com
5
+ Home-page: https://github.com/yourusername/karboai-python
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: aiohttp>=3.9
10
+ Requires-Dist: python-socketio>=5.10
11
+ Requires-Dist: pydantic>=2.0
12
+ Dynamic: description
13
+ Dynamic: description-content-type
14
+ Dynamic: home-page
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
19
+
20
+ # KarboAI
21
+
22
+ Python library for [KarboAI](https://karboai.com) bot development.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install karboai
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ from karboai import KarboAI, Router, bold
34
+
35
+ karbo = KarboAI(token="your-token", id="your-bot-id", enable_logging=True)
36
+ router = Router()
37
+
38
+ @router.command("/start")
39
+ async def start(ctx):
40
+ await karbo.text(ctx.message.chat_id, bold("Welcome!"))
41
+
42
+ @router.on("message")
43
+ async def echo(ctx):
44
+ await karbo.text(ctx.message.chat_id, ctx.message.content)
45
+
46
+ karbo.bind(router)
47
+ karbo.attach()
48
+ ```
49
+
50
+ ## API
51
+
52
+ | Method | Description |
53
+ |--------|-------------|
54
+ | `me()` | Bot info |
55
+ | `text(chat_id, content, reply_message_id?)` | Send text |
56
+ | `image(chat_id, images, reply_message_id?)` | Send images |
57
+ | `upload(buffer, file_name?)` | Upload image, returns URL |
58
+ | `message(chat_id, message_id)` | Get message |
59
+ | `members(chat_id, limit?, offset?)` | Chat members |
60
+ | `user(user_id)` | User info |
61
+ | `leave(chat_id)` | Leave chat |
62
+ | `kick(chat_id, user_id)` | Kick user |
63
+ | `attach(callback?)` | Connect to WebSocket |
64
+ | `bind(*routers)` | Bind routers |
65
+
66
+ ## Events
67
+
68
+ `message`, `join`, `leave`, `voiceStart`, `voiceEnd`, `sticker`
69
+
70
+ ## Text formatting
71
+
72
+ `bold()`, `italic()`, `centralize()`, `code()`, `strikethrough()`, `underline()`, `hyperlink(text, url)`
73
+
74
+ ## Error handling
75
+
76
+ ```python
77
+ from karboai import KarboError
78
+
79
+ try:
80
+ await karbo.me()
81
+ except KarboError as e:
82
+ print(e.status_code, e.name, e)
83
+ ```
@@ -0,0 +1,64 @@
1
+ # KarboAI
2
+
3
+ Python library for [KarboAI](https://karboai.com) bot development.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install karboai
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from karboai import KarboAI, Router, bold
15
+
16
+ karbo = KarboAI(token="your-token", id="your-bot-id", enable_logging=True)
17
+ router = Router()
18
+
19
+ @router.command("/start")
20
+ async def start(ctx):
21
+ await karbo.text(ctx.message.chat_id, bold("Welcome!"))
22
+
23
+ @router.on("message")
24
+ async def echo(ctx):
25
+ await karbo.text(ctx.message.chat_id, ctx.message.content)
26
+
27
+ karbo.bind(router)
28
+ karbo.attach()
29
+ ```
30
+
31
+ ## API
32
+
33
+ | Method | Description |
34
+ |--------|-------------|
35
+ | `me()` | Bot info |
36
+ | `text(chat_id, content, reply_message_id?)` | Send text |
37
+ | `image(chat_id, images, reply_message_id?)` | Send images |
38
+ | `upload(buffer, file_name?)` | Upload image, returns URL |
39
+ | `message(chat_id, message_id)` | Get message |
40
+ | `members(chat_id, limit?, offset?)` | Chat members |
41
+ | `user(user_id)` | User info |
42
+ | `leave(chat_id)` | Leave chat |
43
+ | `kick(chat_id, user_id)` | Kick user |
44
+ | `attach(callback?)` | Connect to WebSocket |
45
+ | `bind(*routers)` | Bind routers |
46
+
47
+ ## Events
48
+
49
+ `message`, `join`, `leave`, `voiceStart`, `voiceEnd`, `sticker`
50
+
51
+ ## Text formatting
52
+
53
+ `bold()`, `italic()`, `centralize()`, `code()`, `strikethrough()`, `underline()`, `hyperlink(text, url)`
54
+
55
+ ## Error handling
56
+
57
+ ```python
58
+ from karboai import KarboError
59
+
60
+ try:
61
+ await karbo.me()
62
+ except KarboError as e:
63
+ print(e.status_code, e.name, e)
64
+ ```
@@ -0,0 +1,51 @@
1
+ from .client import KarboAI
2
+ from .router import Router
3
+ from .types import (
4
+ Author,
5
+ Context,
6
+ Frame,
7
+ Member,
8
+ MembersResponse,
9
+ Message,
10
+ MessageResponse,
11
+ MeResponse,
12
+ OkResponse,
13
+ Reaction,
14
+ UploadResponse,
15
+ User,
16
+ )
17
+ from .utils import (
18
+ bold,
19
+ centralize,
20
+ code,
21
+ hyperlink,
22
+ italic,
23
+ strikethrough,
24
+ underline,
25
+ )
26
+ from .errors import KarboError
27
+
28
+ __all__ = [
29
+ "KarboAI",
30
+ "Router",
31
+ "KarboError",
32
+ "Context",
33
+ "Message",
34
+ "Author",
35
+ "User",
36
+ "Member",
37
+ "Frame",
38
+ "Reaction",
39
+ "MeResponse",
40
+ "MessageResponse",
41
+ "UploadResponse",
42
+ "MembersResponse",
43
+ "OkResponse",
44
+ "bold",
45
+ "italic",
46
+ "centralize",
47
+ "code",
48
+ "strikethrough",
49
+ "underline",
50
+ "hyperlink",
51
+ ]
@@ -0,0 +1,19 @@
1
+ KARBO_API = "https://api.karboai.com"
2
+
3
+ SOCKET_TOPICS = {
4
+ 0: "message",
5
+ 1: "join",
6
+ 2: "leave",
7
+ 7: "voiceStart",
8
+ 8: "voiceEnd",
9
+ 12: "sticker",
10
+ }
11
+
12
+ ERRORS = {
13
+ 400: ("KarboAI.BadRequest", "May empty message, content too long, too many images"),
14
+ 401: ("KarboAI.Unauthorized", "Invalid bot token"),
15
+ 403: ("KarboAI.Forbidden", "Access denied"),
16
+ 404: ("KarboAI.NotFound", "Content doesn't exist"),
17
+ 413: ("KarboAI.FileTooLarge", "Please upload files smaller than XX bytes length"),
18
+ 429: ("KarboAI.TooManyRequests", "Rate limit exceeded"),
19
+ }
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable
4
+
5
+ from .dispatcher import Dispatcher
6
+ from .http import Http
7
+ from .logging import setup_logging
8
+ from .router import Router
9
+ from .types import (
10
+ MembersResponse,
11
+ Message,
12
+ MessageResponse,
13
+ MeResponse,
14
+ OkResponse,
15
+ UploadResponse,
16
+ User,
17
+ )
18
+
19
+
20
+ class KarboAI:
21
+ def __init__(self, token: str, id: str, enable_logging: bool = False):
22
+ self._token = token
23
+ self._id = id
24
+ self._http = Http(token)
25
+ self._dispatcher = Dispatcher(self)
26
+ setup_logging(enable_logging)
27
+
28
+ @property
29
+ def id(self) -> str:
30
+ return self._id
31
+
32
+ async def me(self) -> MeResponse:
33
+ return await self._http.get("/bot/me", MeResponse)
34
+
35
+ async def text(self, chat_id: str, content: str, reply_message_id: str | None = None) -> MessageResponse:
36
+ return await self._send(chat_id=chat_id, content=content, reply_message_id=reply_message_id)
37
+
38
+ async def image(self, chat_id: str, images: list[str], reply_message_id: str | None = None) -> MessageResponse:
39
+ return await self._send(chat_id=chat_id, images=images, reply_message_id=reply_message_id)
40
+
41
+ async def upload(self, buffer: bytes, file_name: str = "file.png") -> str:
42
+ resp = await self._http.upload("/bot/upload/image", buffer, file_name, UploadResponse)
43
+ return resp.url
44
+
45
+ async def message(self, chat_id: str, message_id: str) -> Message:
46
+ return await self._http.get(f"/bot/chat/{chat_id}/message/{message_id}", Message)
47
+
48
+ async def members(self, chat_id: str, limit: int = 100, offset: int = 0) -> MembersResponse:
49
+ return await self._http.get(f"/bot/chat/{chat_id}/members?limit={limit}&offset={offset}", MembersResponse)
50
+
51
+ async def user(self, user_id: str) -> User:
52
+ return await self._http.get(f"/bot/user/{user_id}", User)
53
+
54
+ async def leave(self, chat_id: str) -> bool:
55
+ resp = await self._http.post(f"/bot/leave-chat/{chat_id}", {}, OkResponse)
56
+ return resp.ok
57
+
58
+ async def kick(self, chat_id: str, user_id: str) -> bool:
59
+ resp = await self._http.post(f"/bot/chat/{chat_id}/kick", {"user_id": user_id}, OkResponse)
60
+ return resp.ok
61
+
62
+ def attach(self, callback: Callable[[], None] | None = None) -> None:
63
+ async def _cb():
64
+ pass
65
+ self._dispatcher.attach(self._token, callback or _cb)
66
+
67
+ def bind(self, *routers: Router) -> None:
68
+ self._dispatcher.bind(*routers)
69
+
70
+ async def _send(self, *, chat_id: str, content: str | None = None, images: list[str] | None = None, reply_message_id: str | None = None) -> MessageResponse:
71
+ body: dict = {"chat_id": chat_id}
72
+ if content:
73
+ body["content"] = content
74
+ if images:
75
+ body["images"] = images
76
+ if reply_message_id:
77
+ body["reply_message_id"] = reply_message_id
78
+ return await self._http.post("/bot/send-message", body, MessageResponse)
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import socketio
6
+
7
+ from ._const import KARBO_API, SOCKET_TOPICS
8
+ from .logging import logger
9
+ from .router import Router
10
+ from .types import Context, Message, _Listener
11
+
12
+ if TYPE_CHECKING:
13
+ from .client import KarboAI
14
+
15
+
16
+ class Dispatcher:
17
+ def __init__(self, client: KarboAI):
18
+ self._client = client
19
+ self._socket = None
20
+ self._listeners: dict[str, list[_Listener]] = {}
21
+
22
+ def bind(self, *routers: Router) -> None:
23
+ for router in routers:
24
+ for event, items in router.listeners.items():
25
+ if event not in self._listeners:
26
+ self._listeners[event] = []
27
+ self._listeners[event].extend(items)
28
+ logger.info("bound router: %s", router.name)
29
+
30
+ def attach(self, token: str, callback) -> None:
31
+ sio = socketio.AsyncClient()
32
+
33
+ @sio.on("connect")
34
+ async def _on_connect():
35
+ logger.info("connected to KarboAI")
36
+ await callback()
37
+
38
+ @sio.on("new_message")
39
+ async def _on_message(data):
40
+ msg = Message.model_validate(data)
41
+ if msg.author.user_id == self._client.id:
42
+ return
43
+
44
+ event = SOCKET_TOPICS.get(msg.type, "message")
45
+ logger.info(
46
+ "event=%s chatId=%s userId=%s type=%d",
47
+ event, msg.chat_id, msg.author.user_id, msg.type,
48
+ )
49
+
50
+ for listener in self._listeners.get(event, []):
51
+ ok = True
52
+ for mw in listener.middlewares:
53
+ if not await mw(Context(self._client, msg)):
54
+ ok = False
55
+ break
56
+ if ok:
57
+ await listener.callback(Context(self._client, msg))
58
+
59
+ self._socket = sio
60
+ sio.connect(
61
+ KARBO_API,
62
+ socketio_path="/bot/ws",
63
+ transports=["websocket"],
64
+ auth={"bot_token": token},
65
+ )
@@ -0,0 +1,9 @@
1
+ from ._const import ERRORS
2
+
3
+
4
+ class KarboError(Exception):
5
+ def __init__(self, status_code: int):
6
+ self.status_code = status_code
7
+ name, msg = ERRORS.get(status_code, ("KarboAI.Unknown", "Unknown error"))
8
+ self.name = name
9
+ super().__init__(msg)
@@ -0,0 +1,49 @@
1
+ import json
2
+ from typing import Type, TypeVar
3
+
4
+ import aiohttp
5
+ from pydantic import BaseModel
6
+
7
+ from ._const import KARBO_API
8
+ from .errors import KarboError
9
+ from .logging import logger
10
+
11
+ T = TypeVar("T", bound=BaseModel)
12
+
13
+
14
+ class Http:
15
+ def __init__(self, token: str):
16
+ self._headers = {
17
+ "Content-Type": "application/json",
18
+ "Bot-Token": token,
19
+ }
20
+
21
+ async def _request(self, method: str, path: str, schema: Type[T], **kwargs) -> T:
22
+ url = f"{KARBO_API}{path}"
23
+ async with aiohttp.ClientSession() as session:
24
+ async with session.request(method, url, headers=self._headers, **kwargs) as resp:
25
+ if not resp.ok:
26
+ logger.error("%s %s -> %d", method, path, resp.status)
27
+ raise KarboError(resp.status)
28
+ logger.info("%s %s -> %d", method, path, resp.status)
29
+ return schema.model_validate(await resp.json())
30
+
31
+ async def get(self, path: str, schema: Type[T]) -> T:
32
+ return await self._request("GET", path, schema)
33
+
34
+ async def post(self, path: str, body: dict, schema: Type[T]) -> T:
35
+ return await self._request("POST", path, schema, data=json.dumps(body))
36
+
37
+ async def upload(self, path: str, buffer: bytes, file_name: str, schema: Type[T]) -> T:
38
+ import mimetypes
39
+
40
+ form = aiohttp.FormData()
41
+ form.add_field(
42
+ "file",
43
+ buffer,
44
+ filename=file_name,
45
+ content_type=mimetypes.guess_type(file_name)[0] or "image/png",
46
+ )
47
+
48
+ headers = {k: v for k, v in self._headers.items() if k != "Content-Type"}
49
+ return await self._request("POST", path, schema, data=form, headers=headers)
@@ -0,0 +1,20 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger("karboai")
4
+ _logger_init = False
5
+
6
+
7
+ def setup_logging(enabled: bool = False) -> None:
8
+ global _logger_init
9
+ if _logger_init:
10
+ return
11
+ _logger_init = True
12
+
13
+ logger.disabled = not enabled
14
+ if enabled:
15
+ handler = logging.StreamHandler()
16
+ handler.setFormatter(logging.Formatter(
17
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
18
+ ))
19
+ logger.addHandler(handler)
20
+ logger.setLevel(logging.INFO)
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from .types import Context, EventHandler, Middleware, _Listener
4
+ from .utils import _router_name
5
+
6
+
7
+ def _cmd_middleware(prefix: str) -> Middleware:
8
+ async def _mw(ctx: Context) -> bool | None:
9
+ return ctx.message.content.startswith(prefix) or None
10
+ return _mw
11
+
12
+
13
+ class Router:
14
+ def __init__(self, name: str | None = None):
15
+ self._name = name or _router_name()
16
+ self._listeners: dict[str, list[_Listener]] = {}
17
+ self._middlewares: list[Middleware] = []
18
+
19
+ @property
20
+ def name(self) -> str:
21
+ return self._name
22
+
23
+ @property
24
+ def listeners(self) -> dict[str, list[_Listener]]:
25
+ return self._listeners
26
+
27
+ def pre(self, middleware: Middleware) -> None:
28
+ self._middlewares.append(middleware)
29
+
30
+ def on(self, event: str, callback: EventHandler) -> None:
31
+ if event not in self._listeners:
32
+ self._listeners[event] = []
33
+ self._listeners[event].append(
34
+ _Listener(callback, list(self._middlewares))
35
+ )
36
+
37
+ def command(self, prefix: str, callback: EventHandler) -> None:
38
+ if "message" not in self._listeners:
39
+ self._listeners["message"] = []
40
+ self._listeners["message"].append(
41
+ _Listener(callback, [_cmd_middleware(prefix)] + list(self._middlewares))
42
+ )
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable
5
+
6
+ from pydantic import BaseModel
7
+
8
+ if TYPE_CHECKING:
9
+ from .client import KarboAI
10
+
11
+
12
+ class BotStatus(str, Enum):
13
+ NOT_OFFICIAL = "NOT_OFFICIAL"
14
+ OFFICIAL = "OFFICIAL"
15
+ BANNED = "BANNED"
16
+
17
+
18
+ class Frame(BaseModel):
19
+ frame_id: str
20
+ file: str
21
+
22
+
23
+ class Reaction(BaseModel):
24
+ reaction: str
25
+ is_sticker: bool
26
+ count: int
27
+ me: bool
28
+
29
+
30
+ class Author(BaseModel):
31
+ user_id: str
32
+ nickname: str
33
+ avatar_url: str
34
+ avatar_frame: Frame | None = None
35
+ role: int | None = None
36
+ app_role: int | None = None
37
+ panel_color: str | None = None
38
+ level: int | None = None
39
+ nickname_color: str | None = None
40
+ nickname_emoji: str | None = None
41
+
42
+
43
+ class User(BaseModel):
44
+ user_id: str
45
+ nickname: str
46
+ avatar: str
47
+ role: int
48
+ short_info: str
49
+
50
+
51
+ class Member(BaseModel):
52
+ user_id: str
53
+ nickname: str
54
+ avatar: str
55
+ role: int
56
+
57
+
58
+ class Message(BaseModel):
59
+ message_id: str
60
+ chat_id: str
61
+ content: str
62
+ created_time: int
63
+ type: int
64
+ reply_message_id: str | None = None
65
+ audio: str | None = None
66
+ sticker: str | None = None
67
+ images: list[str] = []
68
+ author: Author
69
+ audio_duration_ms: int | None = None
70
+ waveform: list[Any] | None = None
71
+ video_note: str | None = None
72
+ video_note_duration_ms: int | None = None
73
+ transparent: bool | None = None
74
+ bubble_id: str | None = None
75
+ bubble_version: int | None = None
76
+ reactions: list[Reaction] | None = None
77
+
78
+
79
+ class MeResponse(BaseModel):
80
+ bot_id: str
81
+ name: str
82
+ status: BotStatus
83
+
84
+
85
+ class MessageResponse(BaseModel):
86
+ message_id: str
87
+ created_time: int
88
+
89
+
90
+ class UploadResponse(BaseModel):
91
+ url: str
92
+
93
+
94
+ class MembersResponse(BaseModel):
95
+ items: list[User]
96
+
97
+
98
+ class OkResponse(BaseModel):
99
+ ok: bool
100
+
101
+
102
+ class Context:
103
+ __slots__ = ("karbo", "message")
104
+
105
+ def __init__(self, karbo: KarboAI, message: Message):
106
+ self.karbo = karbo
107
+ self.message = message
108
+
109
+
110
+ EventHandler = Callable[[Context], Awaitable[None]]
111
+ Middleware = Callable[[Context], Awaitable[bool | None]]
112
+ ConnectCallback = Callable[[], Awaitable[None]]
113
+
114
+
115
+ class _Listener:
116
+ def __init__(self, callback: EventHandler, middlewares: list[Middleware]):
117
+ self.callback = callback
118
+ self.middlewares = middlewares
@@ -0,0 +1,35 @@
1
+ import random
2
+ import string
3
+
4
+
5
+ def bold(text: str) -> str:
6
+ return f"**{text}**"
7
+
8
+
9
+ def italic(text: str) -> str:
10
+ return f"__{text}__"
11
+
12
+
13
+ def centralize(text: str) -> str:
14
+ return f"[C]{text}"
15
+
16
+
17
+ def code(text: str) -> str:
18
+ return f"`{text}`"
19
+
20
+
21
+ def strikethrough(text: str) -> str:
22
+ return f"~~{text}~~"
23
+
24
+
25
+ def underline(text: str) -> str:
26
+ return f"++{text}++"
27
+
28
+
29
+ def hyperlink(text: str, url: str) -> str:
30
+ return f"[{text}]({url})"
31
+
32
+
33
+ def _router_name() -> str:
34
+ rand = "".join(random.choices(string.ascii_lowercase + string.digits, k=7))
35
+ return f"router-{rand}"
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: karboai
3
+ Version: 1.0.0
4
+ Summary: Python library for https://karboai.com
5
+ Home-page: https://github.com/yourusername/karboai-python
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: aiohttp>=3.9
10
+ Requires-Dist: python-socketio>=5.10
11
+ Requires-Dist: pydantic>=2.0
12
+ Dynamic: description
13
+ Dynamic: description-content-type
14
+ Dynamic: home-page
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
19
+
20
+ # KarboAI
21
+
22
+ Python library for [KarboAI](https://karboai.com) bot development.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install karboai
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ from karboai import KarboAI, Router, bold
34
+
35
+ karbo = KarboAI(token="your-token", id="your-bot-id", enable_logging=True)
36
+ router = Router()
37
+
38
+ @router.command("/start")
39
+ async def start(ctx):
40
+ await karbo.text(ctx.message.chat_id, bold("Welcome!"))
41
+
42
+ @router.on("message")
43
+ async def echo(ctx):
44
+ await karbo.text(ctx.message.chat_id, ctx.message.content)
45
+
46
+ karbo.bind(router)
47
+ karbo.attach()
48
+ ```
49
+
50
+ ## API
51
+
52
+ | Method | Description |
53
+ |--------|-------------|
54
+ | `me()` | Bot info |
55
+ | `text(chat_id, content, reply_message_id?)` | Send text |
56
+ | `image(chat_id, images, reply_message_id?)` | Send images |
57
+ | `upload(buffer, file_name?)` | Upload image, returns URL |
58
+ | `message(chat_id, message_id)` | Get message |
59
+ | `members(chat_id, limit?, offset?)` | Chat members |
60
+ | `user(user_id)` | User info |
61
+ | `leave(chat_id)` | Leave chat |
62
+ | `kick(chat_id, user_id)` | Kick user |
63
+ | `attach(callback?)` | Connect to WebSocket |
64
+ | `bind(*routers)` | Bind routers |
65
+
66
+ ## Events
67
+
68
+ `message`, `join`, `leave`, `voiceStart`, `voiceEnd`, `sticker`
69
+
70
+ ## Text formatting
71
+
72
+ `bold()`, `italic()`, `centralize()`, `code()`, `strikethrough()`, `underline()`, `hyperlink(text, url)`
73
+
74
+ ## Error handling
75
+
76
+ ```python
77
+ from karboai import KarboError
78
+
79
+ try:
80
+ await karbo.me()
81
+ except KarboError as e:
82
+ print(e.status_code, e.name, e)
83
+ ```
@@ -0,0 +1,17 @@
1
+ README.md
2
+ setup.py
3
+ karboai/__init__.py
4
+ karboai/_const.py
5
+ karboai/client.py
6
+ karboai/dispatcher.py
7
+ karboai/errors.py
8
+ karboai/http.py
9
+ karboai/logging.py
10
+ karboai/router.py
11
+ karboai/types.py
12
+ karboai/utils.py
13
+ karboai.egg-info/PKG-INFO
14
+ karboai.egg-info/SOURCES.txt
15
+ karboai.egg-info/dependency_links.txt
16
+ karboai.egg-info/requires.txt
17
+ karboai.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ aiohttp>=3.9
2
+ python-socketio>=5.10
3
+ pydantic>=2.0
@@ -0,0 +1 @@
1
+ karboai
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
karboai-1.0.0/setup.py ADDED
@@ -0,0 +1,21 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="karboai",
8
+ version="1.0.0",
9
+ description="Python library for https://karboai.com",
10
+ long_description=long_description,
11
+ long_description_content_type="text/markdown",
12
+ url="https://github.com/yourusername/karboai-python",
13
+ packages=find_packages(),
14
+ python_requires=">=3.8",
15
+ install_requires=[
16
+ "aiohttp>=3.9",
17
+ "python-socketio>=5.10",
18
+ "pydantic>=2.0",
19
+ ],
20
+ license="MIT",
21
+ )