zalobot-python 0.1.0__tar.gz → 0.1.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: zalobot-python
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Python SDK for the Zalo Bot API
5
5
  Author: NovaH00
6
6
  Author-email: NovaH00 <trantay2006super@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "zalobot-python"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "Python SDK for the Zalo Bot API"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,5 +1,8 @@
1
- from .zalobot import ZaloBot
1
+ from .zalobot import ZaloBot, WebhookHandler
2
2
  from .models import (
3
+ SuccessfulResponse,
4
+ ErrorResponse,
5
+ ZaloAPIResponse,
3
6
  BotInfo,
4
7
  WebhookInfo,
5
8
  MessageInfo,
@@ -8,11 +11,15 @@ from .models import (
8
11
  Chat,
9
12
  Message,
10
13
  Event,
11
- ZaloAPIError
14
+ ZaloAPIError,
12
15
  )
13
16
 
14
17
  __all__ = [
18
+ "SuccessfulResponse",
19
+ "ErrorResponse",
20
+ "ZaloAPIResponse",
15
21
  "ZaloBot",
22
+ "WebhookHandler",
16
23
  "BotInfo",
17
24
  "WebhookInfo",
18
25
  "MessageInfo",
@@ -1,24 +1,30 @@
1
1
  from enum import StrEnum
2
- from typing import Literal, TypeVar, override
2
+ from typing import Literal, TypeVar, override, Annotated
3
3
  from pydantic import BaseModel, Field
4
4
 
5
5
  class Result(BaseModel):
6
- """Represents the result field in the API successful response"""
6
+ """Represents the result field in the Zalo API successful response"""
7
7
  pass
8
8
 
9
9
  T = TypeVar("T", bound=Result)
10
10
  class SuccessfulResponse[T](BaseModel):
11
+ """Represents the successful response of the Zalo API"""
11
12
  ok: Literal[True] = True
12
13
  result: T = Field(description="The result when the API call is successful.")
13
14
 
14
15
  class ErrorResponse(BaseModel):
16
+ """Represents the error response of Zalo API"""
15
17
  ok: Literal[False] = False
16
18
  description: str = Field(description="The description of the error")
17
19
  error_code: int = Field(description="The error code")
18
20
 
19
- type APIResponse[T] = SuccessfulResponse[T] | ErrorResponse
21
+ type ZaloAPIResponse[T] = Annotated[
22
+ SuccessfulResponse[T] | ErrorResponse,
23
+ "Represents the response of the Zalo API"
24
+ ]
20
25
 
21
26
  class ZaloAPIError(BaseException):
27
+ """Exception when using the Zalo API"""
22
28
  def __init__(self, error_code: int, description: str):
23
29
  self.error_code: int = error_code
24
30
  self.description: str = description
@@ -43,20 +49,20 @@ class MessageInfo(Result):
43
49
  message_id: str
44
50
  date: int
45
51
 
46
-
47
52
  class EventName(StrEnum):
48
53
  TEXT_RECEIVED = "message.text.received"
49
54
  IMAGE_RECEIVED = "message.image.received"
50
55
  STICKER_RECEIVED = "message.sticker.received"
51
56
  UNSUPPORTED_RECEIVED = "message.unsupported.received"
52
57
 
53
-
54
58
  class From(BaseModel):
59
+ """Sender information"""
55
60
  id: str
56
61
  display_name: str
57
62
  is_bot: bool
58
63
 
59
64
  class Chat(BaseModel):
65
+ """Current chat information"""
60
66
  id: str
61
67
  chat_type: str
62
68
 
@@ -1,8 +1,8 @@
1
- from typing import Any, Literal
1
+ from typing import Any, Literal, Protocol
2
2
  import httpx
3
3
 
4
4
  from .models import (
5
- APIResponse,
5
+ ZaloAPIResponse,
6
6
  SuccessfulResponse,
7
7
  ErrorResponse,
8
8
  ZaloAPIError,
@@ -13,13 +13,12 @@ from .models import (
13
13
  )
14
14
  from .config import ZaloAPIConfig
15
15
 
16
-
17
16
  async def fetch[T](
18
17
  url: str,
19
18
  *,
20
19
  method: Literal["GET", "POST"] = "GET",
21
20
  body: dict[str, Any] | None = None,
22
- ) -> APIResponse[T]:
21
+ ) -> ZaloAPIResponse[T]:
23
22
  async with httpx.AsyncClient() as client:
24
23
 
25
24
  if method == "GET":
@@ -35,15 +34,19 @@ async def fetch[T](
35
34
 
36
35
  return ErrorResponse(**response_json)
37
36
 
37
+ class WebhookHandler(Protocol):
38
+ async def __call__(self, update_event: Event, bot: ZaloBot) -> None: ...
39
+
38
40
  class ZaloBot:
39
41
  def __init__(self, BOT_TOKEN: str):
40
42
  self._BOT_TOKEN: str = BOT_TOKEN
41
43
  self._base_url: str = f"{ZaloAPIConfig.BASE_URL}/bot{BOT_TOKEN}"
42
-
44
+ self._webhook_handlers: list[WebhookHandler] = []
45
+
43
46
  async def getMe(self) -> BotInfo:
44
47
  url = f"{self._base_url}/getMe"
45
48
 
46
- res: APIResponse[BotInfo] = await fetch(url)
49
+ res: ZaloAPIResponse[BotInfo] = await fetch(url)
47
50
 
48
51
  if isinstance(res, ErrorResponse):
49
52
  raise ZaloAPIError(
@@ -54,7 +57,6 @@ class ZaloBot:
54
57
 
55
58
  return res.result
56
59
 
57
-
58
60
  async def getUpdates(self, timeout: int = 30) -> Event:
59
61
  url = f"{self._base_url}/getUpdates"
60
62
 
@@ -62,7 +64,7 @@ class ZaloBot:
62
64
  "timeout": timeout
63
65
  }
64
66
 
65
- res: APIResponse[Event] = await fetch(url, method="POST", body=payload)
67
+ res: ZaloAPIResponse[Event] = await fetch(url, method="POST", body=payload)
66
68
 
67
69
  if isinstance(res, ErrorResponse):
68
70
  raise ZaloAPIError(
@@ -72,7 +74,11 @@ class ZaloBot:
72
74
 
73
75
  return res.result
74
76
 
75
- async def setWebhook(self, url: str, secret_token: str) -> WebhookInfo:
77
+ async def setWebhook(
78
+ self,
79
+ url: str,
80
+ secret_token: str,
81
+ ) -> WebhookInfo:
76
82
  url = f"{self._base_url}/setWebhook"
77
83
 
78
84
  payload = {
@@ -80,7 +86,7 @@ class ZaloBot:
80
86
  "secret_token": secret_token
81
87
  }
82
88
 
83
- res: APIResponse[WebhookInfo] = await fetch(url, method="POST", body=payload)
89
+ res: ZaloAPIResponse[WebhookInfo] = await fetch(url, method="POST", body=payload)
84
90
 
85
91
  if isinstance(res, ErrorResponse):
86
92
  raise ZaloAPIError(
@@ -93,7 +99,7 @@ class ZaloBot:
93
99
  async def deleteWebhook(self) -> WebhookInfo:
94
100
  url = f"{self._base_url}/deleteWebhook"
95
101
 
96
- res: APIResponse[WebhookInfo] = await fetch(url)
102
+ res: ZaloAPIResponse[WebhookInfo] = await fetch(url)
97
103
 
98
104
  if isinstance(res, ErrorResponse):
99
105
  raise ZaloAPIError(
@@ -106,7 +112,7 @@ class ZaloBot:
106
112
  async def getWebhookInfo(self) -> WebhookInfo:
107
113
  url = f"{self._base_url}/getWebhookInfo"
108
114
 
109
- res: APIResponse[WebhookInfo] = await fetch(url)
115
+ res: ZaloAPIResponse[WebhookInfo] = await fetch(url)
110
116
 
111
117
  if isinstance(res, ErrorResponse):
112
118
  raise ZaloAPIError(
@@ -116,6 +122,15 @@ class ZaloBot:
116
122
 
117
123
  return res.result
118
124
 
125
+ def on_webhook_update(self, handler: WebhookHandler) -> None:
126
+ """Registers a handler to handle on webhook event update"""
127
+ self._webhook_handlers.append(handler)
128
+
129
+ async def dispatch_webhook_handlers(self, update_event: Event) -> None:
130
+ """Run all handlers given a webhook event"""
131
+ for handler in self._webhook_handlers:
132
+ await handler(update_event, self)
133
+
119
134
  async def sendMessage(self, chat_id: str, text: str) -> MessageInfo:
120
135
  url = f"{self._base_url}/sendMessage"
121
136
 
@@ -123,7 +138,7 @@ class ZaloBot:
123
138
  "chat_id": chat_id,
124
139
  "text": text
125
140
  }
126
- res: APIResponse[MessageInfo] = await fetch(url, method="POST", body=payload)
141
+ res: ZaloAPIResponse[MessageInfo] = await fetch(url, method="POST", body=payload)
127
142
 
128
143
  if isinstance(res, ErrorResponse):
129
144
  raise ZaloAPIError(
File without changes