zalobot-python 0.1.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.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.3
2
+ Name: zalobot-python
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Zalo Bot API
5
+ Author: NovaH00
6
+ Author-email: NovaH00 <trantay2006super@gmail.com>
7
+ Requires-Dist: httpx>=0.28.1
8
+ Requires-Dist: pydantic>=2.12.5
9
+ Requires-Dist: requests>=2.32.5
10
+ Requires-Python: >=3.14
11
+ Description-Content-Type: text/markdown
12
+
13
+ # WIP: Python SDK for the ZaloBot API
14
+
15
+ ## Installation
16
+ For uv-astral
17
+ ```bash
18
+ uv add zalobot_python
19
+ ```
20
+ For pip
21
+ ```bash
22
+ pip install zalobot_python
23
+ ```
24
+
25
+ Usage
26
+ ```python
27
+ import asyncio
28
+ from zalobot_python import ZaloBot, BotInfo
29
+
30
+ bot = ZaloBot(BOT_TOKEN="<BOT TOKEN>")
31
+
32
+ async def main():
33
+ bot_info: BotInfo = await bot.getMe()
34
+
35
+ print(f"Bot ID: {bot_info.id}")
36
+ print(f"Bot Name: {bot_info.display_name}")
37
+
38
+ asyncio.run(main())
39
+ ```
@@ -0,0 +1,27 @@
1
+ # WIP: Python SDK for the ZaloBot API
2
+
3
+ ## Installation
4
+ For uv-astral
5
+ ```bash
6
+ uv add zalobot_python
7
+ ```
8
+ For pip
9
+ ```bash
10
+ pip install zalobot_python
11
+ ```
12
+
13
+ Usage
14
+ ```python
15
+ import asyncio
16
+ from zalobot_python import ZaloBot, BotInfo
17
+
18
+ bot = ZaloBot(BOT_TOKEN="<BOT TOKEN>")
19
+
20
+ async def main():
21
+ bot_info: BotInfo = await bot.getMe()
22
+
23
+ print(f"Bot ID: {bot_info.id}")
24
+ print(f"Bot Name: {bot_info.display_name}")
25
+
26
+ asyncio.run(main())
27
+ ```
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "zalobot-python"
3
+ version = "0.1.0"
4
+ description = "Python SDK for the Zalo Bot API"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "NovaH00", email = "trantay2006super@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "httpx>=0.28.1",
12
+ "pydantic>=2.12.5",
13
+ "requests>=2.32.5",
14
+ ]
15
+
16
+ [build-system]
17
+ requires = ["uv_build>=0.9.17,<0.10.0"]
18
+ build-backend = "uv_build"
@@ -0,0 +1,25 @@
1
+ from .zalobot import ZaloBot
2
+ from .models import (
3
+ BotInfo,
4
+ WebhookInfo,
5
+ MessageInfo,
6
+ EventName,
7
+ From,
8
+ Chat,
9
+ Message,
10
+ Event,
11
+ ZaloAPIError
12
+ )
13
+
14
+ __all__ = [
15
+ "ZaloBot",
16
+ "BotInfo",
17
+ "WebhookInfo",
18
+ "MessageInfo",
19
+ "EventName",
20
+ "From",
21
+ "Chat",
22
+ "Message",
23
+ "Event",
24
+ "ZaloAPIError"
25
+ ]
@@ -0,0 +1,2 @@
1
+ class ZaloAPIConfig:
2
+ BASE_URL: str = "https://bot-api.zaloplatforms.com"
@@ -0,0 +1,72 @@
1
+ from enum import StrEnum
2
+ from typing import Literal, TypeVar, override
3
+ from pydantic import BaseModel, Field
4
+
5
+ class Result(BaseModel):
6
+ """Represents the result field in the API successful response"""
7
+ pass
8
+
9
+ T = TypeVar("T", bound=Result)
10
+ class SuccessfulResponse[T](BaseModel):
11
+ ok: Literal[True] = True
12
+ result: T = Field(description="The result when the API call is successful.")
13
+
14
+ class ErrorResponse(BaseModel):
15
+ ok: Literal[False] = False
16
+ description: str = Field(description="The description of the error")
17
+ error_code: int = Field(description="The error code")
18
+
19
+ type APIResponse[T] = SuccessfulResponse[T] | ErrorResponse
20
+
21
+ class ZaloAPIError(BaseException):
22
+ def __init__(self, error_code: int, description: str):
23
+ self.error_code: int = error_code
24
+ self.description: str = description
25
+ super().__init__()
26
+
27
+ @override
28
+ def __str__(self):
29
+ return f"error_code: {self.error_code}; description: {self.description}"
30
+
31
+ class BotInfo(Result):
32
+ id: str
33
+ account_name: str
34
+ account_type: str
35
+ can_join_groups: bool
36
+ display_name: str
37
+
38
+ class WebhookInfo(Result):
39
+ url: str
40
+ updated_at: int
41
+
42
+ class MessageInfo(Result):
43
+ message_id: str
44
+ date: int
45
+
46
+
47
+ class EventName(StrEnum):
48
+ TEXT_RECEIVED = "message.text.received"
49
+ IMAGE_RECEIVED = "message.image.received"
50
+ STICKER_RECEIVED = "message.sticker.received"
51
+ UNSUPPORTED_RECEIVED = "message.unsupported.received"
52
+
53
+
54
+ class From(BaseModel):
55
+ id: str
56
+ display_name: str
57
+ is_bot: bool
58
+
59
+ class Chat(BaseModel):
60
+ id: str
61
+ chat_type: str
62
+
63
+ class Message(BaseModel):
64
+ sender: From = Field(alias="from")
65
+ chat: Chat
66
+ text: str
67
+ message_id: str
68
+ date: int
69
+
70
+ class Event(BaseModel):
71
+ event_name: EventName
72
+ message: Message
File without changes
@@ -0,0 +1,144 @@
1
+ from typing import Any, Literal
2
+ import httpx
3
+
4
+ from .models import (
5
+ APIResponse,
6
+ SuccessfulResponse,
7
+ ErrorResponse,
8
+ ZaloAPIError,
9
+ BotInfo,
10
+ WebhookInfo,
11
+ MessageInfo,
12
+ Event
13
+ )
14
+ from .config import ZaloAPIConfig
15
+
16
+
17
+ async def fetch[T](
18
+ url: str,
19
+ *,
20
+ method: Literal["GET", "POST"] = "GET",
21
+ body: dict[str, Any] | None = None,
22
+ ) -> APIResponse[T]:
23
+ async with httpx.AsyncClient() as client:
24
+
25
+ if method == "GET":
26
+ response = await client.get(url)
27
+
28
+ elif method == "POST":
29
+ response = await client.post(url, json=body)
30
+
31
+ response_json = response.json()
32
+
33
+ if response_json.get("ok"):
34
+ return SuccessfulResponse(**response_json)
35
+
36
+ return ErrorResponse(**response_json)
37
+
38
+ class ZaloBot:
39
+ def __init__(self, BOT_TOKEN: str):
40
+ self._BOT_TOKEN: str = BOT_TOKEN
41
+ self._base_url: str = f"{ZaloAPIConfig.BASE_URL}/bot{BOT_TOKEN}"
42
+
43
+ async def getMe(self) -> BotInfo:
44
+ url = f"{self._base_url}/getMe"
45
+
46
+ res: APIResponse[BotInfo] = await fetch(url)
47
+
48
+ if isinstance(res, ErrorResponse):
49
+ raise ZaloAPIError(
50
+ res.error_code,
51
+ res.description
52
+ )
53
+
54
+
55
+ return res.result
56
+
57
+
58
+ async def getUpdates(self, timeout: int = 30) -> Event:
59
+ url = f"{self._base_url}/getUpdates"
60
+
61
+ payload = {
62
+ "timeout": timeout
63
+ }
64
+
65
+ res: APIResponse[Event] = await fetch(url, method="POST", body=payload)
66
+
67
+ if isinstance(res, ErrorResponse):
68
+ raise ZaloAPIError(
69
+ res.error_code,
70
+ res.description
71
+ )
72
+
73
+ return res.result
74
+
75
+ async def setWebhook(self, url: str, secret_token: str) -> WebhookInfo:
76
+ url = f"{self._base_url}/setWebhook"
77
+
78
+ payload = {
79
+ "url": url,
80
+ "secret_token": secret_token
81
+ }
82
+
83
+ res: APIResponse[WebhookInfo] = await fetch(url, method="POST", body=payload)
84
+
85
+ if isinstance(res, ErrorResponse):
86
+ raise ZaloAPIError(
87
+ res.error_code,
88
+ res.description
89
+ )
90
+
91
+ return res.result
92
+
93
+ async def deleteWebhook(self) -> WebhookInfo:
94
+ url = f"{self._base_url}/deleteWebhook"
95
+
96
+ res: APIResponse[WebhookInfo] = await fetch(url)
97
+
98
+ if isinstance(res, ErrorResponse):
99
+ raise ZaloAPIError(
100
+ res.error_code,
101
+ res.description
102
+ )
103
+
104
+ return res.result
105
+
106
+ async def getWebhookInfo(self) -> WebhookInfo:
107
+ url = f"{self._base_url}/getWebhookInfo"
108
+
109
+ res: APIResponse[WebhookInfo] = await fetch(url)
110
+
111
+ if isinstance(res, ErrorResponse):
112
+ raise ZaloAPIError(
113
+ res.error_code,
114
+ res.description
115
+ )
116
+
117
+ return res.result
118
+
119
+ async def sendMessage(self, chat_id: str, text: str) -> MessageInfo:
120
+ url = f"{self._base_url}/sendMessage"
121
+
122
+ payload = {
123
+ "chat_id": chat_id,
124
+ "text": text
125
+ }
126
+ res: APIResponse[MessageInfo] = await fetch(url, method="POST", body=payload)
127
+
128
+ if isinstance(res, ErrorResponse):
129
+ raise ZaloAPIError(
130
+ res.error_code,
131
+ res.description
132
+ )
133
+
134
+ return res.result
135
+
136
+
137
+ async def sendPhoto(self, chat_id: str, caption: str, photo_url: str) -> None:
138
+ raise NotImplementedError()
139
+
140
+ async def sendSticker(self, chat_id: str, sticker: str) -> None:
141
+ raise NotImplementedError()
142
+
143
+ async def sendChatAction(self, chat_id: str, action: str) -> None:
144
+ raise NotImplementedError()