pyrobale 0.3.7__py3-none-any.whl → 0.3.8__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.
@@ -43,3 +43,24 @@ class StateMachine:
43
43
  del self.__states[user_id]
44
44
  else:
45
45
  raise KeyError
46
+
47
+ def save_local(self, file_name: str):
48
+ """Saves the state of all users to a file
49
+
50
+ Args:
51
+ file_name (string): name of file to save the state of users
52
+ """
53
+ with open(file_name, "w") as f:
54
+ for user_id, state in self.__states.items():
55
+ f.write(f"{user_id} {state}\n")
56
+
57
+ def load_local(self, file_name: str):
58
+ """Loads the state of all users from a file
59
+
60
+ Args:
61
+ file_name (string): name of file to load the state of users
62
+ """
63
+ with open(file_name, "r") as f:
64
+ for line in f:
65
+ user_id, state = line.split()
66
+ self.__states[user_id] = state
pyrobale/__init__.py CHANGED
@@ -31,7 +31,7 @@ from pyrobale.objects import Message, UpdatesTypes
31
31
  bot = Client("YOUR_BOT_TOKEN")
32
32
 
33
33
  @bot.on_message()
34
- async def message_handler(message: User):
34
+ async def message_handler(message: Message):
35
35
  await message.reply("Hello, world!")
36
36
 
37
37
  bot.run()
@@ -116,8 +116,10 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
116
116
  - 📖 [Documentation](https://pyrobale.readthedocs.io)
117
117
  - 🐛 [Issue Tracker](https://github.com/pyrobale/pyrobale/issues)
118
118
  - 💬 [Discussions](https://github.com/pyrobale/pyrobale/discussions)
119
+
119
120
  """
120
121
 
121
122
  from .objects.utils import *
122
123
  from .exceptions import *
123
124
  from .objects import *
125
+ from .client import Client
@@ -40,7 +40,9 @@ from ..objects.utils import *
40
40
  import asyncio
41
41
  from enum import Enum
42
42
  from ..objects.enums import UpdatesTypes, ChatAction, ChatType
43
+ from ..filters import Filters, equals
43
44
  from ..StateMachine import StateMachine
45
+ from ..exceptions import NotFoundException, InvalidTokenException, PyroBaleException
44
46
 
45
47
 
46
48
  class Client:
@@ -81,7 +83,12 @@ class Client:
81
83
  self.requests_base
82
84
  + f"/getUpdates?offset={offset}&limit={limit}&timeout={timeout}"
83
85
  )
84
- return data["result"]
86
+ if data['ok']:
87
+ if 'result' in data.keys():
88
+ return data["result"]
89
+ else:
90
+ if data['error_code'] == 403:
91
+ raise InvalidTokenException("Forbidden 403 : --ENTERED TOKEN IS NOT VALID--")
85
92
 
86
93
  async def set_webhook(self, url: str) -> bool:
87
94
  """Set the webhook for the bot.
@@ -421,6 +428,7 @@ class Client:
421
428
  chat_id: int,
422
429
  latitude: float,
423
430
  longitude: float,
431
+ horizontal_accuracy: Optional[float] = None,
424
432
  reply_to_message_id: Optional[int] = None,
425
433
  reply_markup: Optional[Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]] = None,
426
434
  ) -> Message:
@@ -441,6 +449,7 @@ class Client:
441
449
  "chat_id": chat_id,
442
450
  "latitude": latitude,
443
451
  "longitude": longitude,
452
+ "horizontal_accuracy": horizontal_accuracy,
444
453
  "reply_to_message_id": reply_to_message_id,
445
454
  "reply_markup": reply_markup.to_dict() if reply_markup else None,
446
455
  },
@@ -507,6 +516,9 @@ class Client:
507
516
  Returns:
508
517
  Message: returns the sent message with invoice
509
518
  """
519
+ new_prices = []
520
+ for price in prices:
521
+ new_prices.append(price.json)
510
522
  data = await make_post(
511
523
  self.requests_base + "/sendInvoice",
512
524
  data={
@@ -515,7 +527,7 @@ class Client:
515
527
  "description": description,
516
528
  "payload": payload,
517
529
  "provider_token": provider_token,
518
- "prices": prices,
530
+ "prices": new_prices,
519
531
  "photo_url": photo_url,
520
532
  "reply_to_message_id": reply_to_message_id,
521
533
  },
@@ -922,51 +934,136 @@ class Client:
922
934
  if not future.done():
923
935
  future.set_result(event)
924
936
  self._waiters.remove(waiter)
925
- return update
937
+ return
926
938
 
927
939
  for handler in self.handlers:
928
940
  update_type = handler["type"].value
929
941
  if update_type in update:
930
- event = update[update_type]
931
-
932
- event = self._convert_event(handler["type"], event)
933
-
942
+ raw_event = update[update_type]
943
+ event = self._convert_event(handler["type"], raw_event)
944
+
945
+ if handler["type"] == UpdatesTypes.COMMAND:
946
+ if hasattr(event, 'text') and event.text and event.text.startswith('/'):
947
+ command_text = event.text[1:]
948
+ command_parts = command_text.split()
949
+ if command_parts:
950
+ actual_command = command_parts[0]
951
+ expected_command = handler.get("command", "")
952
+
953
+ if actual_command != expected_command:
954
+ continue
955
+
956
+ flt = handler.get("filter")
957
+ if flt is not None:
958
+ if callable(flt):
959
+ try:
960
+ if not flt(event):
961
+ continue
962
+ except Exception as e:
963
+ print(f"[Filter Error] {e}")
964
+ continue
965
+ elif isinstance(flt, Filters):
966
+ if not hasattr(event, flt.value):
967
+ continue
968
+
934
969
  if asyncio.iscoroutinefunction(handler["callback"]):
935
970
  asyncio.create_task(handler["callback"](event))
936
971
  else:
937
972
  handler["callback"](event)
938
973
 
974
+
975
+
939
976
  def base_handler_decorator(self, update_type: UpdatesTypes):
977
+ """Base decorator for handling different types of updates.
978
+
979
+ Args:
980
+ update_type (UpdatesTypes): The type of update to handle.
981
+
982
+ Returns:
983
+ Callable: A decorator function that registers the callback for the specified update type.
984
+ """
985
+ def wrapper(filter: Optional[Filters] = None):
986
+ def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
987
+ self.add_handler(update_type, callback, filter)
988
+ return callback
989
+ return decorator
990
+ return wrapper
991
+
992
+ def on_command(self, command: str, filter: Optional[Filters] = None):
993
+ """Decorator for handling command updates.
994
+
995
+ Args:
996
+ command (str): The command to handle.
997
+ filter (Optional[Filters]): An optional filter to apply to the command.
998
+ Returns:
999
+ Callable: A decorator function that registers the callback for the specified command.
1000
+ """
940
1001
  def decorator(callback: Callable[[Any], Union[None, Awaitable[None]]]):
941
- self.add_handler(update_type, callback)
1002
+ self.add_handler(UpdatesTypes.COMMAND, callback, filter, command=command)
942
1003
  return callback
943
-
944
1004
  return decorator
945
1005
 
946
- def on_message(self):
947
- return self.base_handler_decorator(UpdatesTypes.MESSAGE)
1006
+
1007
+ def on_message(self, filter: Optional[Filters] = None):
1008
+ """Decorator for handling new message updates.
1009
+
1010
+ Returns:
1011
+ Callable: A decorator function that registers the callback for message updates.
1012
+ """
1013
+ return self.base_handler_decorator(UpdatesTypes.MESSAGE)(filter)
1014
+
948
1015
 
949
1016
  def on_edited_message(self):
1017
+ """Decorator for handling edited message updates.
1018
+
1019
+ Returns:
1020
+ Callable: A decorator function that registers the callback for edited message updates.
1021
+ """
950
1022
  return self.base_handler_decorator(UpdatesTypes.MESSAGE_EDITED)
951
1023
 
952
1024
  def on_callback_query(self):
1025
+ """Decorator for handling callback query updates.
1026
+
1027
+ Returns:
1028
+ Callable: A decorator function that registers the callback for callback query updates.
1029
+ """
953
1030
  return self.base_handler_decorator(UpdatesTypes.CALLBACK_QUERY)
954
1031
 
955
1032
  def on_new_members(self):
1033
+ """Decorator for handling new chat members updates.
1034
+
1035
+ Returns:
1036
+ Callable: A decorator function that registers the callback for new members updates.
1037
+ """
956
1038
  return self.base_handler_decorator(UpdatesTypes.MEMBER_JOINED)
957
1039
 
958
- def on_memebers_left(self):
1040
+ def on_members_left(self):
959
1041
  return self.base_handler_decorator(UpdatesTypes.MEMBER_LEFT)
960
1042
 
961
1043
  def on_pre_checkout_query(self):
1044
+ """Decorator for handling pre-checkout query updates.
1045
+
1046
+ Returns:
1047
+ Callable: A decorator function that registers the callback for pre-checkout query updates.
1048
+ """
962
1049
  return self.base_handler_decorator(UpdatesTypes.PRE_CHECKOUT_QUERY)
963
1050
 
964
1051
  def on_photo(self):
1052
+ """Decorator for handling photo updates.
1053
+
1054
+ Returns:
1055
+ Callable: A decorator function that registers the callback for photo updates.
1056
+ """
965
1057
  return self.base_handler_decorator(UpdatesTypes.PHOTO)
966
1058
 
967
1059
  def on_successful_payment(self):
968
- return self.base_handler_decorator(UpdatesTypes.SUCCESSFUL_PAYMENT)
1060
+ """Decorator for handling successful payment updates.
969
1061
 
1062
+ Returns:
1063
+ Callable: A decorator function that registers the callback for successful payment updates.
1064
+ """
1065
+ return self.base_handler_decorator(UpdatesTypes.SUCCESSFUL_PAYMENT)
1066
+
970
1067
  def _convert_event(self, handler_type: UpdatesTypes, event: Dict[str, Any]) -> Any:
971
1068
  """Convert raw event data to appropriate object type.
972
1069
 
@@ -983,6 +1080,7 @@ class Client:
983
1080
  UpdatesTypes.MEMBER_JOINED,
984
1081
  UpdatesTypes.MEMBER_LEFT,
985
1082
  UpdatesTypes.SUCCESSFUL_PAYMENT,
1083
+ UpdatesTypes.COMMAND
986
1084
  ):
987
1085
  if (
988
1086
  event.get("new_chat_member", False)
@@ -1033,18 +1131,22 @@ class Client:
1033
1131
 
1034
1132
  return event
1035
1133
 
1036
- def add_handler(
1037
- self,
1038
- update_type: UpdatesTypes,
1039
- callback: Callable[[Any], Union[None, Awaitable[None]]],
1040
- ) -> None:
1134
+
1135
+ def add_handler(self, update_type, callback, filter: Optional[Filters] = None, **kwargs):
1041
1136
  """Register a handler for specific update type.
1042
1137
 
1043
1138
  Args:
1044
1139
  update_type (UpdatesTypes): Type of update to handle
1045
1140
  callback (Callable): Function to call when update is received
1046
1141
  """
1047
- self.handlers.append({"type": update_type, "callback": callback})
1142
+ data = {
1143
+ "type": update_type,
1144
+ "callback": callback,
1145
+ "filter": filter,
1146
+ }
1147
+ data.update(kwargs)
1148
+ self.handlers.append(data)
1149
+
1048
1150
 
1049
1151
  def remove_handler(
1050
1152
  self, callback: Callable[[Any], Union[None, Awaitable[None]]]
@@ -1077,17 +1179,16 @@ class Client:
1077
1179
 
1078
1180
  self.running = True
1079
1181
  while self.running:
1080
- if 1:
1182
+ try:
1081
1183
  updates = await self.get_updates(
1082
1184
  offset=self.last_update_id, limit=limit, timeout=timeout
1083
1185
  )
1084
1186
 
1085
1187
  for update in updates:
1086
1188
  await self.process_update(update)
1087
-
1088
- else: # except Exception as e:
1089
- # print(f"Error while polling updates: {e}")
1090
- await asyncio.sleep(5)
1189
+
1190
+ except Exception as e:
1191
+ raise e
1091
1192
 
1092
1193
  async def stop_polling(self) -> None:
1093
1194
  """Stop polling updates."""
@@ -0,0 +1 @@
1
+ from .common import *
@@ -8,3 +8,9 @@ class InvalidTokenException(PyroBaleException):
8
8
 
9
9
  class NotFoundException(PyroBaleException):
10
10
  pass
11
+
12
+ class ForbiddenException(PyroBaleException):
13
+ pass
14
+
15
+ class InternalServerException(PyroBaleException):
16
+ pass
@@ -0,0 +1,2 @@
1
+ from .enum_filters import *
2
+ from .func_filters import *
@@ -0,0 +1,12 @@
1
+ from enum import Enum
2
+
3
+ class Filters(Enum):
4
+ """Filters that you can use in handlers"""
5
+
6
+ TEXT = "text"
7
+ PHOTO = "photo"
8
+ VIDEO = "video"
9
+ AUDIO = "audio"
10
+ VOICE = "voice"
11
+ CONTACT = "contact"
12
+ LOCATION = "location"
@@ -0,0 +1,17 @@
1
+ def equals(expected_text: str):
2
+ """
3
+ Check if the event text or caption is equal to the expected text.
4
+
5
+ Args:
6
+ expected_text (str): The expected text to compare with.
7
+
8
+ Returns:
9
+ Callable: A function that checks if the event text or caption is equal to the expected text.
10
+ """
11
+ def check(event):
12
+ try:
13
+ return getattr(event, "text", None) == expected_text or getattr(event, "caption", None) == expected_text
14
+ except:
15
+ return False
16
+ return check
17
+
pyrobale/objects/enums.py CHANGED
@@ -11,18 +11,10 @@ class UpdatesTypes(Enum):
11
11
  MEMBER_JOINED = "member_joined"
12
12
  MEMBER_LEFT = "member_left"
13
13
  SUCCESSFUL_PAYMENT = "successful_payment"
14
+ COMMAND = "message"
14
15
 
15
16
 
16
- class Filters(Enum):
17
- """Filters that you can use in handlers"""
18
17
 
19
- TEXT = "text"
20
- PHOTO = "photo"
21
- VIDEO = "video"
22
- AUDIO = "audio"
23
- VOICE = "voice"
24
- CONTACT = "contact"
25
- LOCATION = "location"
26
18
 
27
19
 
28
20
  class ChatAction(Enum):
@@ -15,7 +15,7 @@ class InlineKeyboardMarkup:
15
15
  callback_data: Optional[str] = None,
16
16
  url: Optional[str] = None,
17
17
  web_app: Optional[Union["WebAppInfo", str]] = None,
18
- copy_text_button: Optional["CopyTextButton"] = None,
18
+ copy_text_button: Optional[Union["CopyTextButton", str]] = None,
19
19
  **kwargs
20
20
  ) -> "InlineKeyboardMarkup":
21
21
  """Adds a button to the inline keyboard.
@@ -24,8 +24,8 @@ class InlineKeyboardMarkup:
24
24
  text (str): The text to display on the button.
25
25
  callback_data (str, optional): The callback data to send when the button is clicked.
26
26
  url (str, optional): The URL to open when the button is clicked.
27
- web_app (WebAppInfo, optional): The web app to open when the button is clicked.
28
- copy_text_button (CopyTextButton, optional): The copy text button to add to the button.
27
+ web_app (WebAppInfo OR string, optional): The web app to open when the button is clicked.
28
+ copy_text_button (CopyTextButton OR string, optional): The copy text button to add to the button.
29
29
 
30
30
  Returns:
31
31
  InlineKeyboardMarkup: The updated InlineKeyboardMarkup object.
@@ -59,7 +59,14 @@ class InlineKeyboardMarkup:
59
59
  "web_app must be a string URL or an object with to_dict() method."
60
60
  )
61
61
  elif copy_text_button:
62
- button["copy_text"] = {"text": copy_text_button.text}
62
+ if isinstance(copy_text_button, str):
63
+ button["copy_text"] = {"text": copy_text_button}
64
+ elif hasattr(copy_text_button, "text"):
65
+ button["copy_text"] = {"text": copy_text_button.text}
66
+ else:
67
+ raise ValueError(
68
+ "copy_text_button must be a string or an object with a 'text' attribute."
69
+ )
63
70
 
64
71
  if not self.inline_keyboard:
65
72
  self.inline_keyboard.append([])
@@ -124,6 +124,7 @@ class Message:
124
124
  reply_markup: Inline keyboard markup
125
125
  **kwargs: Additional keyword arguments
126
126
  """
127
+ self.client: Client = kwargs.get("kwargs", {}).get("client")
127
128
  self.id: int = message_id
128
129
  self.user: "User" = (
129
130
  User(**from_user, kwargs={"client": self.client}) if from_user else None
@@ -154,7 +155,7 @@ class Message:
154
155
  self.successful_payment: Optional["SuccessfulPayment"] = successful_payment
155
156
  self.web_app_data: Optional["WebAppData"] = web_app_data
156
157
  self.reply_markup: Optional["InlineKeyboardMarkup"] = reply_markup
157
- self.client: Client = kwargs.get("kwargs", {}).get("client")
158
+
158
159
 
159
160
  async def reply(
160
161
  self,
@@ -313,6 +314,7 @@ class Message:
313
314
  self,
314
315
  latitude: float,
315
316
  longitude: float,
317
+ horizontal_accuracy: Optional[float] = None,
316
318
  reply_markup: Union["ReplyKeyboardMarkup", "InlineKeyboardMarkup"] = None,
317
319
  ):
318
320
  """Reply with a location to the current message.
@@ -327,6 +329,7 @@ class Message:
327
329
  self.chat.id,
328
330
  latitude=latitude,
329
331
  longitude=longitude,
332
+ horizontal_accuracy=horizontal_accuracy,
330
333
  reply_to_message_id=self.id,
331
334
  reply_markup=reply_markup,
332
335
  )
@@ -359,9 +362,7 @@ class Message:
359
362
  description: str,
360
363
  payload: str,
361
364
  provider_token: str,
362
- currency: str,
363
- prices: list,
364
- reply_markup: Union["ReplyKeyboardMarkup", "InlineKeyboardMarkup"] = None,
365
+ prices: list
365
366
  ):
366
367
  """Reply with an invoice to the current message.
367
368
 
@@ -370,7 +371,6 @@ class Message:
370
371
  description: Product description
371
372
  payload: Bot-defined invoice payload
372
373
  provider_token: Payment provider token
373
- currency: Three-letter ISO 4217 currency code
374
374
  prices: Price breakdown (amount in smallest units)
375
375
  reply_markup: Optional keyboard markup
376
376
  """
@@ -381,7 +381,5 @@ class Message:
381
381
  description=description,
382
382
  payload=payload,
383
383
  provider_token=provider_token,
384
- currency=currency,
385
- prices=prices,
386
- reply_markup=reply_markup,
384
+ prices=prices
387
385
  )
pyrobale/objects/utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ from ..exceptions import *
1
2
  import aiohttp
2
3
 
3
4
 
@@ -8,13 +9,33 @@ def build_api_url(base: str, endpoint: str) -> str:
8
9
  async def make_post(url: str, data: dict = None, headers: dict = None) -> dict:
9
10
  async with aiohttp.ClientSession() as session:
10
11
  async with session.post(url, json=data, headers=headers) as response:
11
- return await response.json()
12
+ json = await response.json()
13
+ if json['ok']:
14
+ if 'result' in json.keys():
15
+ return json
16
+ else:
17
+ if json['error_code'] == 404:
18
+ raise NotFoundException(f"Error not found 404 : {json['description'] if json['description'] else 'No description returned in error'}")
19
+ elif json['error_code'] == 403:
20
+ raise ForbiddenException(f"Error Forbidden 403 : {json['description'] if json['description'] else 'No description returned in error'}")
21
+ else:
22
+ raise PyroBaleException(f"unknown error : {json['description'] if json['description'] else 'No description!'}")
12
23
 
13
24
 
14
25
  async def make_get(url: str, headers: dict = None) -> dict:
15
26
  async with aiohttp.ClientSession() as session:
16
27
  async with session.get(url, headers=headers) as response:
17
- return await response.json()
28
+ json = await response.json()
29
+ if json['ok']:
30
+ if 'result' in json.keys():
31
+ return json
32
+ else:
33
+ if json['error_code'] == 404:
34
+ raise NotFoundException(f"Error not found 404 : {json['description'] if json['description'] else 'No description returned in error'}")
35
+ elif json['error_code'] == 403:
36
+ raise ForbiddenException(f"Error Forbidden 403 : {json['description'] if json['description'] else 'No description returned in error'}")
37
+ else:
38
+ raise PyroBaleException(f"unknown error : {json['description'] if json['description'] else 'No description'}")
18
39
 
19
40
 
20
41
  def pythonize(dictionary: dict) -> dict:
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrobale
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: A python wrapper for bale api
5
5
  Project-URL: github, https://github.com/pyrobale/pyrobale
6
6
  Project-URL: website, https://pyrobale.github.io
7
7
  Author-email: Ali Safamanesh <darg.q.a.a@gmail.com>, Aydin Rahbaran <codewizaard9@gmail.com>
8
8
  License: MIT License
9
9
 
10
- Copyright (c) 2025 Ali Safamanesh
10
+ Copyright (c) 2025 PyroBale Team
11
11
 
12
12
  Permission is hereby granted, free of charge, to any person obtaining a copy
13
13
  of this software and associated documentation files (the "Software"), to deal
@@ -49,7 +49,7 @@ A modern, easy-to-use Python wrapper for the Bale Bot API that makes building Ba
49
49
  - 📁 **File Handling** - Easy upload and download of media files
50
50
  - 🛡️ **Error Handling** - Comprehensive exception handling
51
51
  - 📖 **Type Hints** - Full typing support for better development experience
52
- - ⚡ **Async Support** - Both synchronous and asynchronous operations
52
+ - ⚡ **Async Support** - asynchronous operations
53
53
 
54
54
  ## Installation
55
55
 
@@ -66,7 +66,7 @@ from pyrobale.objects import Message, UpdatesTypes
66
66
  bot = Client("YOUR_BOT_TOKEN")
67
67
 
68
68
  @bot.on_message()
69
- async def message_handler(message: User):
69
+ async def message_handler(message: Message):
70
70
  await message.reply("Hello, world!")
71
71
 
72
72
  bot.run()
@@ -78,13 +78,12 @@ bot.run()
78
78
  ```python
79
79
  from pyrobale.objects import *
80
80
  from pyrobale.client import Client, Message, UpdatesTypes
81
- import asyncio
82
81
 
83
82
  client = Client("YOUR_BOT_TOKEN")
84
83
 
85
84
  async def handle_message(message: Message):
86
85
  if message.text == "/start":
87
- await message.reply("سلام! من یک ربات PyRoBale هستم!")
86
+ await message.reply("Hi! Im a pyrobale RoBot!")
88
87
  await client.wait_for(UpdatesTypes.MESSAGE)
89
88
  await message.reply("Okay! wait_for Test Compeleted")
90
89
 
@@ -1,8 +1,11 @@
1
- pyrobale/__init__.py,sha256=h5jjU9rJHxzD3V7nBzKQDXskmtlQKbTNROUm_kO8Olw,3620
2
- pyrobale/StateMachine/__init__.py,sha256=StMaUqpvJw_ViywGO0c-NRKcglpWyZxg-2kEkqgGG8o,1213
3
- pyrobale/client/__init__.py,sha256=V2SqIOf52iXQxRF1oc2KQib0oX8BMETbUm9oLAO4beo,41447
4
- pyrobale/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- pyrobale/exceptions/common.py,sha256=waAfMFzCsrDr9lS11N0Yzs2v1BX_2ucwV15K1Dq9tYA,159
1
+ pyrobale/__init__.py,sha256=tC4gr6RIg4tYdzoBDFQCU2CDigXytc-MQ7yH6BJzTzM,3651
2
+ pyrobale/StateMachine/__init__.py,sha256=TRCBwcr4UlnpU2Olik0vOtNASUOK7_O6RMAG6RrVYlM,1911
3
+ pyrobale/client/__init__.py,sha256=xl7TyimdjJLLEPmhpRGV_0-JIN8hCPISmYwle7a6eSI,45539
4
+ pyrobale/exceptions/__init__.py,sha256=oa412OuokRm9Vf3XlCJLqpZjz9ZcuxAKxnEBvOK7u2M,21
5
+ pyrobale/exceptions/common.py,sha256=6ubWjGNmbRbNuzM_ohPuLvGhLdCiCZ-RscEERnv3JMA,273
6
+ pyrobale/filters/__init__.py,sha256=Q-VFK1U5W9jMWneAqNLNlYSTZI2gjnAkwA-lcqF9GbU,55
7
+ pyrobale/filters/enum_filters.py,sha256=5kVtmZ1dCbhUx-_T7tPFbgx8KxAz0W3clwCjOvZy5R0,239
8
+ pyrobale/filters/func_filters.py,sha256=C4cIckRkHk5ISvWFdrvLVFZPmYA1nuNCzC84BpEdkc0,523
6
9
  pyrobale/objects/__init__.py,sha256=uy0xBJc5d-1TnlPD6CgxAaUjBnIvQs-GuAgLeEKoXSQ,2239
7
10
  pyrobale/objects/animation.py,sha256=jpM9V8AOaGLWkcENTDmsxKf57Abf-MArYBLfU6GwkD0,978
8
11
  pyrobale/objects/audio.py,sha256=q14wz5WpC3SgKZkAlH-D9M554RYQNDwuCT2z7v7IZYU,696
@@ -13,17 +16,17 @@ pyrobale/objects/chatphoto.py,sha256=vOb5xzpr3oBiCloi2_ufBnVlUfmcPqSTmMMMTTBYdJo
13
16
  pyrobale/objects/contact.py,sha256=6LDxT5RoGbY5Tsw64lg5-jvKSRmmvOiIJha1HqdTaG4,411
14
17
  pyrobale/objects/copytextbutton.py,sha256=CbFWeY9Ebor7dnHJpQUMB1zYhJWl7b5a2T_cdmo1qQ0,134
15
18
  pyrobale/objects/document.py,sha256=cI9_McPzySNKZaQmOma9eX75a3YsyzkVrLvUBDIuwsA,661
16
- pyrobale/objects/enums.py,sha256=EPTKrc_ueAY_KinaVMu9ZQLCSj1VoUjHHCn1p9bjf0A,912
19
+ pyrobale/objects/enums.py,sha256=-8SQ6jvAk4n5oYTSU--E3rZ2kGABy51SbK9QruMQf-Y,720
17
20
  pyrobale/objects/file.py,sha256=4PBfjouP0knd1vFDNs7mx_jwBoDQVBKS7ESjzF4Y6E8,360
18
21
  pyrobale/objects/inlinekeyboardbutton.py,sha256=w4HehdisD7RQodS66gG-DJjaf0gO2U-6wYaVXaatTN8,617
19
- pyrobale/objects/inlinekeyboardmarkup.py,sha256=shNpYUT25EqT0PQFa-c1tEma_6mJIpBwXQ0IqcTGg4M,2887
22
+ pyrobale/objects/inlinekeyboardmarkup.py,sha256=pMcJDCI3iTvn7OZFPOCV_xnsq9Atx7ixdfKyObdGG1M,3254
20
23
  pyrobale/objects/inputfile.py,sha256=vjw96uJZzp4UargWd2p3OQKyvTpHu2PH_dR_RwwshRI,621
21
24
  pyrobale/objects/inputmedias.py,sha256=2h5YKWNjrrLD1QKZ5J_iOfpCZIRpAZST9q7eBd8edo0,6248
22
25
  pyrobale/objects/invoice.py,sha256=hI225mtsSSV7fYre6aFHp-o1NxUT8MLugJAD6NfAeIs,375
23
26
  pyrobale/objects/keyboardbutton.py,sha256=J7c1ma7swjk3zkgcD7vnMdIMsBGExicmbm1bnP_puzU,459
24
27
  pyrobale/objects/labeledprice.py,sha256=d-fbOLzpkBslfor13BXGeVoySKCcSxsXY4QVwub2Z9o,228
25
28
  pyrobale/objects/location.py,sha256=w_9sRJRntLpNbn8Jk6JhRJVVigD9Bd4BxbjFyglxWhU,143
26
- pyrobale/objects/message.py,sha256=cKN7gCJNm55E8tLU7wyITOy4_irqGmFUl4UTu2107pk,14657
29
+ pyrobale/objects/message.py,sha256=cCe0qU0ClGc66S7tW6TexnDzqyAafeKgN_h0h8UZMfs,14532
27
30
  pyrobale/objects/messageid.py,sha256=DrWKV8y9m9H8G_Fj68KgiJKwDJOGduNJGJ-Dyn8N580,95
28
31
  pyrobale/objects/photosize.py,sha256=I4hnmi46uoU-cW7hPN_tTT9kn0u80BtG5QJ3uRjoUiE,298
29
32
  pyrobale/objects/precheckoutquery.py,sha256=Rtjh1qqRc9TRZMJU1W5r0B8y7K5dayLMXX6WH9h1Ul4,470
@@ -33,12 +36,12 @@ pyrobale/objects/stickerset.py,sha256=xlSdlnb5TCzI_MCkB_bJsZPgELVevbO7R3BAYN2jIt
33
36
  pyrobale/objects/successfulpayment.py,sha256=aEAApcd60dByqPw253jEifG_CVsbLYiphB1ep3CEm2E,502
34
37
  pyrobale/objects/update.py,sha256=cDR9hpcC8r4bJjixgMQD84Dg_-8ZrlZq1HninVVVDuQ,716
35
38
  pyrobale/objects/user.py,sha256=uCvG3k7rIbGP73fFQt_uy7-shS-BRWAL2aEcCEZWyWY,554
36
- pyrobale/objects/utils.py,sha256=2ZghxTTjVOUru06Ka_qOjn8U1PvM37-GJ6X1toaovCI,846
39
+ pyrobale/objects/utils.py,sha256=Odyy6y9wKexYBUrk-iErtrV-iEHHOWw-32yTU_iS_AU,2288
37
40
  pyrobale/objects/video.py,sha256=DuZMGHio_w8yQvi87Vc3XpJOpEaltkM4_bNRSeocPZo,509
38
41
  pyrobale/objects/voice.py,sha256=ZdsJFH2IsBwelEk4rb4oZfX9xrfJ2_DgfjsMc0e4Tmg,148
39
42
  pyrobale/objects/webappdata.py,sha256=QlZlCa8Mylt8VmcdgdoHeyita5CVnz2WsT1yueEY1tY,78
40
43
  pyrobale/objects/webappinfo.py,sha256=qnTvfNqx91Yzbc1gO5y4XQ3w6g0RpMUqMuF5wk_EZMc,75
41
- pyrobale-0.3.7.dist-info/METADATA,sha256=yrnMA_qlgmJsudQHqs0xUA9rHZld8lY7pj6zEqmtc48,5319
42
- pyrobale-0.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- pyrobale-0.3.7.dist-info/licenses/LICENSE,sha256=F8U4JY2aXNJspsrcVW0zf6watzK22FMPTF3NkS36tss,1070
44
- pyrobale-0.3.7.dist-info/RECORD,,
44
+ pyrobale-0.3.8.dist-info/METADATA,sha256=386ZydRqEYd61G8-SgfJH_3eTz4YnNFgcb0l-nhp80A,5262
45
+ pyrobale-0.3.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
46
+ pyrobale-0.3.8.dist-info/licenses/LICENSE,sha256=rf_1ZkId-bcjw1kHBq6bV5t5TVob3RgjUyjfKr1CHAk,1070
47
+ pyrobale-0.3.8.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Ali Safamanesh
3
+ Copyright (c) 2025 PyroBale Team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.