maxbot-api-client-python 1.1.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

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,10 +1,12 @@
1
- import aiofiles, asyncio, httpx, logging, mimetypes, os, time
1
+ import aiofiles, asyncio, httpx, logging, mimetypes, os, time, re
2
+ from aiofiles import tempfile
2
3
  from typing import Any, List, Optional
3
4
  from urllib.parse import urlparse
4
5
  from pathlib import Path
5
6
 
7
+ from maxbot_api_client_python.exceptions import MaxBotError
6
8
  from maxbot_api_client_python.tools.uploads import Uploads
7
- from maxbot_api_client_python.client import Client, decode, adecode
9
+ from maxbot_api_client_python.client import Client
8
10
  from maxbot_api_client_python.types.constants import AttachmentType, Paths, UploadType
9
11
  from maxbot_api_client_python.types.models import Message, SendFileReq, SendMessageReq, UploadFileReq, Attachment
10
12
 
@@ -15,66 +17,73 @@ class Helpers:
15
17
  self.client = client
16
18
  self.uploads = Uploads(client)
17
19
 
18
- def SendFile(self, **kwargs) -> Optional[Message]:
20
+ def send_file(self, req: SendFileReq) -> Optional[Message]:
19
21
  """
20
22
  A helper that simplifies sending files to a chat.
21
23
  It automatically determines whether the provided file_source is a direct URL or a local file path.
22
24
 
23
25
  Example:
24
26
  # Sending a file via URL:
25
- response = api.helpers.SendFile(
27
+ response = bot.helpers.send_file(SendFileReq(
26
28
  chat_id=123456789,
27
29
  text="Check out this image!",
28
30
  file_source="https://example.com/image.png"
29
- )
31
+ ))
30
32
 
31
33
  # Sending a local file:
32
- response = api.helpers.SendFile(
34
+ response = bot.helpers.send_file(SendFileReq(
33
35
  chat_id=123456789,
34
36
  text="Here is the report.",
35
37
  file_source="/local/path/to/report.pdf"
36
- )
38
+ ))
37
39
  """
38
- req = SendFileReq(**kwargs)
39
- if self._is_url(req.file_source):
40
- return self.sendFileByUrl(req)
41
- return self.sendFileByUpload(req)
40
+ is_url = urlparse(req.file_source).scheme in ("http", "https")
41
+ if not is_url and not os.path.exists(req.file_source):
42
+ raise ValueError("Invalid file path or URL.")
43
+
44
+ if urlparse(req.file_source).scheme in ("http", "https"):
45
+ return self._send_file_by_url(req)
46
+
47
+ return self._send_file_by_upload(req)
42
48
 
43
- def sendFileByUrl(self, req: SendFileReq) -> Optional[Message]:
49
+ def _send_file_by_url(self, req: SendFileReq) -> Optional[Message]:
44
50
  ext = self._get_extension(req.file_source)
45
51
  upload_type = self._determine_upload_type(ext)
46
52
 
47
53
  if upload_type == UploadType.IMAGE:
48
54
  attachment = Attachment(type=AttachmentType.IMAGE, payload={"url": req.file_source})
49
- return self.sendFileInternal(req, attachment)
55
+ return self._send_file_internal(req, attachment)
50
56
 
51
57
  temp_path = None
52
58
  try:
53
59
  temp_path = self._download_temp_file(req.file_source)
54
- req.file_source = temp_path
55
- return self.sendFileByUpload(req)
60
+ req_copy = req.model_copy(update={"file_source": temp_path})
61
+ return self._send_file_by_upload(req_copy)
56
62
  except Exception as e:
57
63
  logger.error(f"File processing/upload error: {e}")
58
- return None
64
+ raise
59
65
  finally:
60
- if temp_path and os.path.exists(temp_path):
61
- os.remove(temp_path)
66
+ if temp_path:
67
+ try:
68
+ os.remove(temp_path)
69
+ except FileNotFoundError:
70
+ pass
62
71
 
63
- def sendFileByUpload(self, req: SendFileReq) -> Optional[Message]:
72
+ def _send_file_by_upload(self, req: SendFileReq) -> Optional[Message]:
64
73
  ext = self._get_extension(req.file_source)
65
74
  upload_type = self._determine_upload_type(ext)
66
75
 
67
76
  upload_req = UploadFileReq(type=upload_type, file_path=req.file_source)
68
- upload_resp = self.uploads.UploadFile(**upload_req.model_dump_json(indent=4))
77
+ upload_resp = self.uploads.upload_file(upload_req)
69
78
 
70
79
  if not upload_resp or not upload_resp.token:
71
80
  logger.error("Upload failed: No token returned.")
72
81
  return None
73
82
 
74
83
  attachment = self._build_attachment_from_token(upload_type, upload_resp.token, Path(req.file_source).name)
75
- return self.sendFileInternal(req, attachment)
84
+ return self._send_file_internal(req, attachment)
76
85
 
77
- def sendFileInternal(self, req: SendFileReq, attachment: Attachment) -> Message:
86
+ def _send_file_internal(self, req: SendFileReq, attachment: Attachment) -> Message:
78
87
  attachments: List[Attachment] = req.attachments or []
79
88
  attachments.append(attachment)
80
89
 
@@ -89,13 +98,15 @@ class Helpers:
89
98
  disable_link_preview=req.disable_link_preview
90
99
  )
91
100
 
101
+ query, payload = self.client.split_request(request_data)
92
102
  last_err: Optional[Exception] = None
103
+
93
104
  for _ in range(self.client.max_retries):
94
105
  try:
95
- return decode(self.client, "POST", Paths.MESSAGES, Message, query=request_data.model_dump(exclude_none=True), payload=request_data)
96
- except Exception as e:
106
+ return self.client.decode("POST", Paths.MESSAGES, Message, query=query, payload=payload)
107
+ except MaxBotError as e:
97
108
  last_err = e
98
- if "not.ready" in str(e).lower():
109
+ if e.status_code == 422 or "not.ready" in str(e.response).lower():
99
110
  time.sleep(self.client.retry_delay_sec)
100
111
  continue
101
112
  break
@@ -104,73 +115,80 @@ class Helpers:
104
115
  raise last_err
105
116
  raise Exception("Unknown error in sendFileInternal")
106
117
 
107
- async def SendFileAsync(self, **kwargs) -> Optional[Message]:
118
+ async def send_file_async(self, req: SendFileReq) -> Optional[Message]:
108
119
  """
109
- Async version of SendFile.
120
+ Async version of send_file.
110
121
 
111
122
  Example:
112
123
  # Sending a local file asynchronously:
113
- response = await api.helpers.SendFileAsync(
124
+ response = await bot.helpers.send_file_async(SendFileReq(
114
125
  chat_id=123456789,
115
126
  text="Here is the report.",
116
127
  file_source="/local/path/to/report.pdf"
117
- )
128
+ ))
118
129
  """
119
- req = SendFileReq(**kwargs)
120
- if self._is_url(req.file_source):
121
- return await self.sendFileByUrlAsync(req)
122
- return await self.sendFileByUploadAsync(req)
130
+ if urlparse(req.file_source).scheme in ("http", "https"):
131
+ return await self._send_file_by_url_async(req)
132
+
133
+ return await self._send_file_by_upload_async(req)
123
134
 
124
- async def sendFileByUrlAsync(self, req: SendFileReq) -> Optional[Message]:
135
+ async def _send_file_by_url_async(self, req: SendFileReq) -> Optional[Message]:
125
136
  ext = self._get_extension(req.file_source)
126
137
  upload_type = self._determine_upload_type(ext)
127
138
 
128
139
  if upload_type == UploadType.IMAGE:
129
140
  attachment = Attachment(type=AttachmentType.IMAGE, payload={"url": req.file_source})
130
- return await self.sendFileInternalAsync(req, attachment)
141
+ return await self._send_file_internal_async(req, attachment)
131
142
 
132
143
  temp_path = None
133
144
  try:
134
145
  temp_path = await self._download_temp_file_async(req.file_source)
135
- req.file_source = temp_path
136
- return await self.sendFileByUploadAsync(req)
146
+ req_copy = req.model_copy(update={"file_source": temp_path})
147
+ return await self._send_file_by_upload_async(req_copy)
137
148
  except Exception as e:
138
149
  logger.error(f"Async file processing/upload error: {e}")
139
- return None
150
+ raise
140
151
  finally:
141
152
  if temp_path and os.path.exists(temp_path):
142
- await asyncio.to_thread(os.remove, temp_path)
153
+ try:
154
+ await aiofiles.os.remove(temp_path)
155
+ except OSError as e:
156
+ logger.error(f"Failed to cleanup temp file: {e}")
143
157
 
144
- async def sendFileByUploadAsync(self, req: SendFileReq) -> Optional[Message]:
158
+ async def _send_file_by_upload_async(self, req: SendFileReq) -> Optional[Message]:
145
159
  ext = self._get_extension(req.file_source)
146
160
  u_type = self._determine_upload_type(ext)
147
161
 
148
162
  upload_req = UploadFileReq(type=u_type, file_path=req.file_source)
149
- upload_resp = await self.uploads.UploadFileAsync(**upload_req.model_dump())
163
+ upload_resp = await self.uploads.upload_file_async(upload_req)
150
164
 
151
165
  if not upload_resp or not upload_resp.token:
152
166
  return None
153
167
 
154
168
  attachment = self._build_attachment_from_token(u_type, upload_resp.token, Path(req.file_source).name)
155
- return await self.sendFileInternalAsync(req, attachment)
169
+ return await self._send_file_internal_async(req, attachment)
156
170
 
157
- async def sendFileInternalAsync(self, req: SendFileReq, attachment: Attachment) -> Message:
171
+ async def _send_file_internal_async(self, req: SendFileReq, attachment: Attachment) -> Message:
158
172
  attachments: List[Attachment] = req.attachments or []
159
173
  attachments.append(attachment)
160
174
 
161
175
  request_data = SendMessageReq(
162
- **req.model_dump(exclude={"file_source", "attachments"}),
163
- attachments=attachments
176
+ user_id=req.user_id,
177
+ chat_id=req.chat_id,
178
+ text=req.text,
179
+ format=req.format,
180
+ attachments=attachments,
181
+ notify=req.notify,
182
+ link=req.link,
183
+ disable_link_preview=req.disable_link_preview
164
184
  )
165
185
 
186
+ query, payload = self.client.split_request(request_data)
166
187
  last_err: Optional[Exception] = None
188
+
167
189
  for _ in range(self.client.max_retries):
168
190
  try:
169
- return await adecode(
170
- self.client, "POST", Paths.MESSAGES, Message,
171
- query=request_data.model_dump(exclude_none=True),
172
- payload=request_data
173
- )
191
+ return await self.client.adecode("POST", Paths.MESSAGES, Message, query=query, payload=payload)
174
192
  except Exception as e:
175
193
  last_err = e
176
194
  if "not.ready" in str(e).lower():
@@ -181,13 +199,6 @@ class Helpers:
181
199
  if last_err:
182
200
  raise last_err
183
201
  raise Exception("Unknown error in sendFileInternalAsync")
184
-
185
- def _is_url(self, source: str) -> bool:
186
- try:
187
- result = urlparse(source)
188
- return all([result.scheme, result.netloc])
189
- except ValueError:
190
- return False
191
202
 
192
203
  def _get_extension(self, source: str) -> str:
193
204
  parsed = urlparse(source)
@@ -202,6 +213,10 @@ class Helpers:
202
213
  if ext in {".mp3", ".ogg", ".wav"}:
203
214
  return UploadType.AUDIO
204
215
  return UploadType.FILE
216
+
217
+ def _sanitize_filename(self, filename: str) -> str:
218
+ base = os.path.basename(filename)
219
+ return re.sub(r'[^a-zA-Z0-9.\-_]', '_', base)
205
220
 
206
221
  def _build_attachment_from_token(self, u_type: UploadType, token: str, filename: str) -> Attachment:
207
222
  payload: dict[str, Any] = {"token": token}
@@ -211,14 +226,16 @@ class Helpers:
211
226
 
212
227
  def _download_temp_file(self, url_str: str) -> str:
213
228
  headers = {"User-Agent": "maxbot-client/1.0"}
214
- with httpx.Client() as client:
215
- with client.stream("GET", url_str, headers=headers, follow_redirects=True) as resp:
229
+ with httpx.Client(follow_redirects=True, max_redirects=3) as client:
230
+ with client.stream("GET", url_str, headers=headers) as resp:
216
231
  resp.raise_for_status()
217
232
 
218
233
  content_disp = resp.headers.get("Content-Disposition", "")
219
234
  filename = None
220
235
  if "filename=" in content_disp:
221
- filename = content_disp.split("filename=")[1].strip('"')
236
+ raw_part = content_disp.split("filename=")[1].split(";")[0]
237
+ raw_filename = raw_part.strip('"').strip("'")
238
+ filename = self._sanitize_filename(raw_filename)
222
239
 
223
240
  if not filename:
224
241
  content_type = resp.headers.get("Content-Type", "")
@@ -227,14 +244,17 @@ class Helpers:
227
244
  filename = f"file{ext}"
228
245
 
229
246
  if not filename:
230
- filename = Path(urlparse(url_str).path).name or "temp_file.bin"
231
-
232
- temp_path = f"temp_{int(time.time())}_{filename}"
233
-
234
- with open(temp_path, "wb") as f:
235
- for chunk in resp.iter_bytes(chunk_size=8192):
236
- f.write(chunk)
237
- return temp_path
247
+ filename = os.path.basename(Path(urlparse(url_str).path).name) or "temp_file.bin"
248
+
249
+ fd, temp_path = tempfile.mkstemp(prefix="temp_", suffix=f"_{os.path.basename(filename)}")
250
+ try:
251
+ with os.fdopen(fd, "wb") as f:
252
+ for chunk in resp.iter_bytes(chunk_size=8192):
253
+ f.write(chunk)
254
+ return temp_path
255
+ except Exception:
256
+ os.remove(temp_path)
257
+ raise
238
258
 
239
259
  async def _download_temp_file_async(self, url_str: str) -> str:
240
260
  headers = {"User-Agent": "maxbot-client/1.0"}
@@ -245,13 +265,15 @@ class Helpers:
245
265
  content_type = resp.headers.get("Content-Type", "").split(";")[0]
246
266
  extension = mimetypes.guess_extension(content_type) or ".bin"
247
267
 
248
- url_path_name = Path(urlparse(url_str).path).name
268
+ url_path_name = os.path.basename(Path(urlparse(url_str).path).name)
249
269
  if url_path_name == "uc" or not url_path_name:
250
270
  filename = f"file{extension}"
251
271
  else:
252
272
  filename = url_path_name if "." in url_path_name else f"{url_path_name}{extension}"
253
273
 
254
- temp_path = f"temp_{int(time.time())}_{filename}"
274
+ temp_file = tempfile.NamedTemporaryFile(prefix="temp_", suffix=f"_{os.path.basename(filename)}", delete=False)
275
+ temp_path = temp_file.name
276
+ temp_file.close()
255
277
 
256
278
  async with aiofiles.open(temp_path, "wb") as f:
257
279
  async for chunk in resp.aiter_bytes(chunk_size=8192):
@@ -1,194 +1,200 @@
1
- from maxbot_api_client_python.client import Client, decode, adecode
1
+ from typing import Any
2
+
3
+ from maxbot_api_client_python.client import Client
2
4
  from maxbot_api_client_python.types.constants import Paths
3
- from maxbot_api_client_python.types import models
5
+ from maxbot_api_client_python.types.models import (
6
+ AnswerCallbackReq, DeleteMessageReq, EditMessageReq, GetMessageReq, GetMessagesReq,
7
+ GetVideoInfoReq, GetVideoInfoResp, Message, MessagesList, SendMessageReq,
8
+ SendMessageResp, SimpleQueryResult, NewMessageBody
9
+ )
4
10
 
5
11
  class Messages:
6
12
  def __init__(self, client: Client):
7
13
  self.client = client
8
14
 
9
- def GetMessages(self, **kwargs) -> models.MessagesList:
15
+ def get_messages(self, req: GetMessagesReq) -> MessagesList:
10
16
  """
11
17
  Retrieves a list of messages.
12
18
  It can fetch messages belonging to a specific ChatID or by an exact list of MessageIDs.
13
19
 
14
20
  Example:
15
21
  # Fetch messages by Chat ID
16
- response = api.messages.GetMessages(chat_id=123456)
22
+ response = bot.messages.get_messages(GetMessagesReq(chat_id=123456))
17
23
 
18
24
  # Fetch specific messages:
19
- response = api.messages.GetMessages(message_ids=["mid:1", "mid:2"])
25
+ response = bot.messages.get_messages(GetMessagesReq(message_ids=["mid:1", "mid:2"]))
20
26
  """
21
- req = models.GetMessagesReq(**kwargs)
22
- return decode(self.client, "GET", Paths.MESSAGES, models.MessagesList, query=req.model_dump(exclude_none=True))
27
+ query, payload = self.client.split_request(req)
28
+ return self.client.decode("GET", Paths.MESSAGES, MessagesList, query=query, payload=payload)
23
29
 
24
- def SendMessage(self, **kwargs) -> models.SendMessageResp:
30
+ def send_message(self, req: SendMessageReq) -> SendMessageResp:
25
31
  """
26
32
  Sends a text message or attachment to a specific user or chat.
27
33
  If Notify is False, no push notification will be sent to the user.
28
34
 
29
35
  Example:
30
- response = api.messages.SendMessage(
36
+ response = bot.messages.send_message(SendMessageReq(
31
37
  chat_id=123456,
32
38
  text="Hello, world!",
33
39
  notify=True
34
- )
40
+ ))
35
41
  """
36
- req = models.SendMessageReq(**kwargs)
37
- return decode(self.client, "POST", Paths.MESSAGES, models.SendMessageResp, query=req.model_dump(exclude_none=True), payload=req)
42
+ query, payload = self.client.split_request(req)
43
+ return self.client.decode("POST", Paths.MESSAGES, SendMessageResp, query=query, payload=payload)
38
44
 
39
- def EditMessage(self, **kwargs) -> models.SimpleQueryResult:
45
+ def edit_message(self, req: EditMessageReq) -> SimpleQueryResult:
40
46
  """
41
47
  Modifies the content of a previously sent message.
42
48
 
43
49
  Example:
44
- response = api.messages.EditMessage(
50
+ response = bot.messages.edit_message(EditMessageReq(
45
51
  message_id="mid:987654321...",
46
52
  text="Updated message text!",
47
53
  format="HTML"
48
- )
54
+ ))
49
55
  """
50
- req = models.EditMessageReq(**kwargs)
51
- return decode(self.client, "PUT", Paths.MESSAGES, models.SimpleQueryResult, query=req.model_dump(exclude_none=True), payload=req)
56
+ query, payload = self.client.split_request(req)
57
+ return self.client.decode("PUT", Paths.MESSAGES, SimpleQueryResult, query=query, payload=payload)
52
58
 
53
- def DeleteMessage(self, **kwargs) -> models.SimpleQueryResult:
59
+ def delete_message(self, req: DeleteMessageReq) -> SimpleQueryResult:
54
60
  """
55
61
  Removes a previously sent message by its ID.
56
62
 
57
63
  Example:
58
- response = api.messages.DeleteMessage(
64
+ response = bot.messages.delete_message(DeleteMessageReq(
59
65
  message_id="mid:987654321..."
60
- )
66
+ ))
61
67
  """
62
- req = models.DeleteMessageReq(**kwargs)
63
- return decode(self.client, "DELETE", Paths.MESSAGES, models.SimpleQueryResult, query=req.model_dump(exclude_none=True))
68
+ query, payload = self.client.split_request(req)
69
+ return self.client.decode("DELETE", Paths.MESSAGES, SimpleQueryResult, query=query, payload=payload)
64
70
 
65
- def GetMessage(self, **kwargs) -> models.Message:
71
+ def get_message(self, req: GetMessageReq) -> Message:
66
72
  """
67
73
  Retrieves full information about a message by its ID.
68
74
 
69
75
  Example:
70
- response = api.messages.GetMessage(
76
+ response = bot.messages.get_message(GetMessageReq(
71
77
  message_id="mid:987654321..."
72
- )
78
+ ))
73
79
  """
74
- req = models.GetMessageReq(**kwargs)
75
80
  path = f"{Paths.MESSAGES}/{req.message_id}"
76
- return decode(self.client, "GET", path, models.Message)
81
+ query, payload = self.client.split_request(req)
82
+ return self.client.decode("GET", path, Message, query=query, payload=payload)
77
83
 
78
- def GetVideoInfo(self, **kwargs) -> models.GetVideoInfoResp:
84
+ def get_video_info(self, req: GetVideoInfoReq) -> GetVideoInfoResp:
79
85
  """
80
86
  Retrieves metadata and the processing status for an uploaded video.
81
87
 
82
88
  Example:
83
- response = api.messages.GetVideoInfo(
89
+ response = bot.messages.get_video_info(GetVideoInfoReq(
84
90
  video_token="vtok_abc123xyz..."
85
- )
91
+ ))
86
92
  """
87
- req = models.GetVideoInfoReq(**kwargs)
88
93
  path = f"{Paths.VIDEOS}/{req.video_token}"
89
- return decode(self.client, "GET", path, models.GetVideoInfoResp)
94
+ query, payload = self.client.split_request(req)
95
+ return self.client.decode("GET", path, GetVideoInfoResp, query=query, payload=payload)
90
96
 
91
- def AnswerCallback(self, **kwargs) -> models.SimpleQueryResult:
97
+ def answer_callback(self, req: AnswerCallbackReq) -> SimpleQueryResult:
92
98
  """
93
99
  Acknowledges and responds to a user clicking an inline button.
94
100
 
95
101
  Example:
96
- response = api.messages.AnswerCallback(
102
+ response = bot.messages.answer_callback(AnswerCallbackReq(
97
103
  callback_id="cbk_12345...",
98
104
  message=NewMessageBody(text="Action confirmed!")
99
- )
105
+ ))
100
106
  """
101
- req = models.AnswerCallbackReq(**kwargs)
102
- return decode(self.client, "POST", Paths.ANSWERS, models.SimpleQueryResult, query=req.model_dump(exclude_none=True), payload=req)
107
+ query, payload = self.client.split_request(req)
108
+ return self.client.decode("POST", Paths.ANSWERS, SimpleQueryResult, query=query, payload=payload)
103
109
 
104
- async def GetMessagesAsync(self, **kwargs) -> models.MessagesList:
105
- """Async version of GetMessages.
110
+ async def get_messages_async(self, req: GetMessagesReq) -> MessagesList:
111
+ """Async version of get_messages.
106
112
 
107
113
  Example:
108
114
  # Fetch messages by Chat ID
109
- response = await api.messages.GetMessagesAsync(chat_id=123456)
115
+ response = await bot.messages.get_messages_async(GetMessagesReq(chat_id=123456))
110
116
 
111
117
  # Fetch specific messages:
112
- response = await api.messages.GetMessagesAsync(message_ids=["mid:1", "mid:2"])
118
+ response = await bot.messages.get_messages_async(GetMessagesReq(message_ids=["mid:1", "mid:2"]))
113
119
  """
114
- req = models.GetMessagesReq(**kwargs)
115
- return await adecode(self.client, "GET", Paths.MESSAGES, models.MessagesList, query=req.model_dump(exclude_none=True))
120
+ query, payload = self.client.split_request(req)
121
+ return await self.client.adecode("GET", Paths.MESSAGES, MessagesList, query=query, payload=payload)
116
122
 
117
- async def SendMessageAsync(self, **kwargs) -> models.SendMessageResp:
123
+ async def send_message_async(self, req: SendMessageReq) -> SendMessageResp:
118
124
  """
119
- Async version of SendMessage.
125
+ Async version of send_message.
120
126
 
121
127
  Example:
122
- response = await api.messages.SendMessageAsync(
128
+ response = await bot.messages.send_message_async(SendMessageReq(
123
129
  chat_id=123456,
124
130
  text="Hello, world!",
125
131
  notify=True
126
- )
132
+ ))
127
133
  """
128
- req = models.SendMessageReq(**kwargs)
129
- return await adecode(self.client, "POST", Paths.MESSAGES, models.SendMessageResp, query=req.model_dump(exclude_none=True), payload=req)
134
+ query, payload = self.client.split_request(req)
135
+ return await self.client.adecode("POST", Paths.MESSAGES, SendMessageResp, query=query, payload=payload)
130
136
 
131
- async def EditMessageAsync(self, **kwargs) -> models.SimpleQueryResult:
137
+ async def edit_message_async(self, req: EditMessageReq) -> SimpleQueryResult:
132
138
  """
133
- Async version of EditMessage.
139
+ Async version of edit_message.
134
140
 
135
141
  Example:
136
- response = await api.messages.EditMessageAsync(
142
+ response = await bot.messages.edit_message_async(EditMessageReq(
137
143
  message_id="mid:987654321...",
138
144
  text="Updated message text!",
139
145
  format="HTML"
140
- )
146
+ ))
141
147
  """
142
- req = models.EditMessageReq(**kwargs)
143
- return await adecode(self.client, "PUT", Paths.MESSAGES, models.SimpleQueryResult, query=req.model_dump(exclude_none=True), payload=req)
148
+ query, payload = self.client.split_request(req)
149
+ return await self.client.adecode("PUT", Paths.MESSAGES, SimpleQueryResult, query=query, payload=payload)
144
150
 
145
- async def DeleteMessageAsync(self, **kwargs) -> models.SimpleQueryResult:
151
+ async def delete_message_async(self, req: DeleteMessageReq) -> SimpleQueryResult:
146
152
  """
147
- Async version of DeleteMessage.
153
+ Async version of delete_message.
148
154
 
149
155
  Example:
150
- response = await api.messages.DeleteMessageAsync(
156
+ response = await bot.messages.delete_message_async(DeleteMessageReq(
151
157
  message_id="mid:987654321..."
152
- )
158
+ ))
153
159
  """
154
- req = models.DeleteMessageReq(**kwargs)
155
- return await adecode(self.client, "DELETE", Paths.MESSAGES, models.SimpleQueryResult, query=req.model_dump(exclude_none=True))
160
+ query, payload = self.client.split_request(req)
161
+ return await self.client.adecode("DELETE", Paths.MESSAGES, SimpleQueryResult, query=query, payload=payload)
156
162
 
157
- async def GetMessageAsync(self, **kwargs) -> models.Message:
163
+ async def get_message_async(self, req: GetMessageReq) -> Message:
158
164
  """
159
- Async version of GetMessage.
165
+ Async version of get_message.
160
166
 
161
167
  Example:
162
- response = await api.messages.GetMessageAsync(
168
+ response = await bot.messages.get_message_async(GetMessageReq(
163
169
  message_id="mid:987654321..."
164
- )
170
+ ))
165
171
  """
166
- req = models.GetMessageReq(**kwargs)
167
172
  path = f"{Paths.MESSAGES}/{req.message_id}"
168
- return await adecode(self.client, "GET", path, models.Message)
173
+ query, payload = self.client.split_request(req)
174
+ return await self.client.adecode("GET", path, Message, query=query, payload=payload)
169
175
 
170
- async def GetVideoInfoAsync(self, **kwargs) -> models.GetVideoInfoResp:
176
+ async def get_video_info_async(self, req: GetVideoInfoReq) -> GetVideoInfoResp:
171
177
  """
172
- Async version of GetVideoInfo.
178
+ Async version of get_video.
173
179
 
174
180
  Example:
175
- response = await api.messages.GetVideoInfoAsync(
181
+ response = await bot.messages.get_video_info_async(GetVideoInfoReq(
176
182
  video_token="vtok_abc123xyz..."
177
- )
183
+ ))
178
184
  """
179
- req = models.GetVideoInfoReq(**kwargs)
180
185
  path = f"{Paths.VIDEOS}/{req.video_token}"
181
- return await adecode(self.client, "GET", path, models.GetVideoInfoResp)
186
+ query, payload = self.client.split_request(req)
187
+ return await self.client.adecode("GET", path, GetVideoInfoResp, query=query, payload=payload)
182
188
 
183
- async def AnswerCallbackAsync(self, **kwargs) -> models.SimpleQueryResult:
189
+ async def answer_callback_async(self, req: AnswerCallbackReq) -> SimpleQueryResult:
184
190
  """
185
- Async version of AnswerCallback.
191
+ Async version of answer_callback.
186
192
 
187
193
  Example:
188
- response = await api.messages.AnswerCallbackAsync(
194
+ response = await bot.messages.answer_callback_async(AnswerCallbackReq(
189
195
  callback_id="cbk_12345...",
190
196
  message=NewMessageBody(text="Action confirmed!")
191
- )
197
+ ))
192
198
  """
193
- req = models.AnswerCallbackReq(**kwargs)
194
- return await adecode(self.client, "POST", Paths.ANSWERS, models.SimpleQueryResult, query=req.model_dump(exclude_none=True), payload=req)
199
+ query, payload = self.client.split_request(req)
200
+ return await self.client.adecode("POST", Paths.ANSWERS, SimpleQueryResult, query=query, payload=payload)