pyrobale 0.2.8__py3-none-any.whl → 0.2.8.1__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrobale
3
- Version: 0.2.8
3
+ Version: 0.2.8.1
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
@@ -0,0 +1,5 @@
1
+ pyrobale.py,sha256=3X2xUh7sdHBCZkp-JcXJ6xL0ggW4BdIcDpmhSR09v7A,89846
2
+ pyrobale-0.2.8.1.dist-info/METADATA,sha256=np0kt27IkkZVub_7t8_n44e9bKOygIuMNaSV0l0v1co,43670
3
+ pyrobale-0.2.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4
+ pyrobale-0.2.8.1.dist-info/licenses/LICENSE,sha256=XQsvifC5HE85232_XgRHpeQ35MfeL2YcpU4yPiAgmW4,35269
5
+ pyrobale-0.2.8.1.dist-info/RECORD,,
pyrobale.py CHANGED
@@ -20,8 +20,15 @@ import re
20
20
  import sys
21
21
  import os
22
22
  from urllib.parse import urlparse, unquote
23
+ from io import BytesIO
24
+ import mimetypes
25
+ from os import PathLike
26
+ from pathlib import Path
27
+ import uuid
28
+ from typing import Iterator
29
+
30
+ __version__ = '0.2.8.1'
23
31
 
24
- __version__ = '0.2.8'
25
32
 
26
33
  class ChatActions:
27
34
  """Represents different chat action states that can be sent to Bale"""
@@ -29,8 +36,10 @@ class ChatActions:
29
36
  PHOTO: str = 'upload_photo'
30
37
  VIDEO: str = 'record_video'
31
38
  CHOOSE_STICKER: str = 'choose_sticker'
39
+
40
+
32
41
  class conditions:
33
-
42
+
34
43
  """
35
44
  A class for defining conditions for message handling.
36
45
  """
@@ -352,18 +361,24 @@ class LabeledPrice:
352
361
 
353
362
  class Document:
354
363
  def __init__(self, data: dict):
364
+ print(data)
355
365
  if data:
356
366
  self.file_id = data.get('file_id')
357
367
  self.file_unique_id = data.get('file_unique_id')
358
368
  self.file_name = data.get('file_name')
359
369
  self.mime_type = data.get('mime_type')
360
370
  self.file_size = data.get('file_size')
371
+ self.input_file = InputFile(self.file_id)
361
372
  else:
362
373
  self.file_id = None
363
374
  self.file_unique_id = None
364
375
  self.file_name = None
365
376
  self.mime_type = None
366
377
  self.file_size = None
378
+ self.input_file = None
379
+
380
+ def __bool__(self):
381
+ return bool(self.file_id)
367
382
 
368
383
 
369
384
  class Invoice:
@@ -545,39 +560,130 @@ class InlineKeyboardMarkup:
545
560
  return {"inline_keyboard": self.inline_keyboard}
546
561
 
547
562
 
563
+ class InputMedia:
564
+ """Base class for input media types"""
565
+
566
+ def __init__(self, media: str, caption: str = None):
567
+ self.media = media
568
+ self.caption = caption
569
+
570
+ @property
571
+ def media_dict(self) -> dict:
572
+ media_dict = {
573
+ 'media': self.media,
574
+ 'type': self.type
575
+ }
576
+ if self.caption:
577
+ media_dict['caption'] = self.caption
578
+ return media_dict
579
+
580
+
581
+ class InputMediaPhoto(InputMedia):
582
+ """Represents a photo to be sent"""
583
+ type = 'photo'
584
+
585
+
586
+ class InputMediaVideo(InputMedia):
587
+ """Represents a video to be sent"""
588
+ type = 'video'
589
+
590
+ def __init__(self, media: str, caption: str = None, width: int = None,
591
+ height: int = None, duration: int = None):
592
+ super().__init__(media, caption)
593
+ self.width = width
594
+ self.height = height
595
+ self.duration = duration
596
+
597
+ @property
598
+ def media_dict(self) -> dict:
599
+ media_dict = super().media_dict
600
+ if self.width:
601
+ media_dict['width'] = self.width
602
+ if self.height:
603
+ media_dict['height'] = self.height
604
+ if self.duration:
605
+ media_dict['duration'] = self.duration
606
+ return media_dict
607
+
608
+
609
+ class InputMediaAnimation(InputMedia):
610
+ """Represents an animation to be sent"""
611
+ type = 'animation'
612
+
613
+ def __init__(self, media: str, caption: str = None, width: int = None,
614
+ height: int = None, duration: int = None):
615
+ super().__init__(media, caption)
616
+ self.width = width
617
+ self.height = height
618
+ self.duration = duration
619
+
620
+ @property
621
+ def media_dict(self) -> dict:
622
+ media_dict = super().media_dict
623
+ if self.width:
624
+ media_dict['width'] = self.width
625
+ if self.height:
626
+ media_dict['height'] = self.height
627
+ if self.duration:
628
+ media_dict['duration'] = self.duration
629
+ return media_dict
630
+
631
+
548
632
  class InputFile:
549
- """Represents a file to be uploaded"""
633
+ """Represents a file to be sent"""
550
634
 
551
- def __init__(self,
552
- client: 'Client',
553
- file: Union[str,
554
- bytes],
555
- filename: Optional[str] = None):
556
- self.client = client
557
- self.filename = filename
558
- if isinstance(file, str):
559
- if file.startswith(('http://', 'https://')):
560
- r = requests.get(file)
561
- r.raise_for_status()
562
- self.file = r.content
563
- else:
564
- try:
565
- self.file = open(file, 'rb')
566
- except IOError:
567
- raise BaleException(
568
- f"Failed to open file: {traceback.format_exc()}")
569
- else:
570
- self.file = file
635
+ def __init__(self, file: Union[str, bytes] = None, file_id: str = None):
636
+ if file and file_id:
637
+ raise ValueError(
638
+ "Either file or file_id should be provided, not both")
639
+ elif not file and not file_id:
640
+ raise ValueError("Either file or file_id must be provided")
571
641
 
572
- def __del__(self):
573
- if hasattr(self, 'file') and hasattr(self.file, 'close'):
574
- self.file.close()
642
+ self.file = file
643
+ self.file_id = file_id
644
+
645
+ @property
646
+ def file_type(self) -> str:
647
+ if self.file_id:
648
+ return "id"
649
+ if isinstance(self.file, bytes):
650
+ return "bytes"
651
+ if self.file.startswith(('http://', 'https://')):
652
+ return "url"
653
+ return "path"
654
+
655
+ def __str__(self) -> str:
656
+ if self.file_id:
657
+ return self.file_id
658
+ return str(self.file)
659
+
660
+
661
+ class InputMediaAudio(InputMedia):
662
+ """Represents an audio file to be sent"""
663
+ type = 'audio'
664
+
665
+ def __init__(self, media: str, caption: str = None, duration: int = None,
666
+ performer: str = None, title: str = None):
667
+ super().__init__(media, caption)
668
+ self.duration = duration
669
+ self.performer = performer
670
+ self.title = title
575
671
 
576
672
  @property
577
- def to_bytes(self):
578
- if hasattr(self.file, 'read'):
579
- return self.file.read()
580
- return self.file
673
+ def media_dict(self) -> dict:
674
+ media_dict = super().media_dict
675
+ if self.duration:
676
+ media_dict['duration'] = self.duration
677
+ if self.performer:
678
+ media_dict['performer'] = self.performer
679
+ if self.title:
680
+ media_dict['title'] = self.title
681
+ return media_dict
682
+
683
+
684
+ class InputMediaDocument(InputMedia):
685
+ """Represents a document to be sent"""
686
+ type = 'document'
581
687
 
582
688
 
583
689
  class CallbackQuery:
@@ -857,8 +963,8 @@ class Chat:
857
963
  photo_url,
858
964
  reply_to_message,
859
965
  reply_markup)
860
-
861
- def send_action(self, action: str, how_many_times = 1) -> bool:
966
+
967
+ def send_action(self, action: str, how_many_times=1) -> bool:
862
968
  """Send a chat action"""
863
969
  return self.client.send_chat_action(self.id, action, how_many_times)
864
970
 
@@ -1259,6 +1365,7 @@ class User:
1259
1365
  photo_url,
1260
1366
  reply_to_message,
1261
1367
  reply_markup)
1368
+
1262
1369
  def send_action(self, action: str, how_many_times: int = 1):
1263
1370
  """Send a chat action to this user"""
1264
1371
  return self.client.send_chat_action(self.id, action, how_many_times)
@@ -1286,33 +1393,65 @@ class Message:
1286
1393
  'chat', {})})
1287
1394
  self.text = result.get('text')
1288
1395
  self.caption = result.get('caption')
1289
- self.document = Document(result.get('document'))
1290
- self.photo = Document(result.get('photo'))
1291
- self.video = Document(result.get('video'))
1292
- self.audio = Document(result.get('audio'))
1293
- self.voice = Voice(result.get('voice'))
1294
- self.animation = result.get('animation')
1295
- self.contact = Contact(result.get('contact'))
1296
- self.location = Location(result.get('location'))
1396
+
1397
+ # Media handling
1398
+ self.document = Document(
1399
+ result.get(
1400
+ 'document',
1401
+ {})) if result.get('document') else None
1402
+ # Handle photo array properly
1403
+ photos = result.get('photo', [])
1404
+ self.photo = [Document(photo) for photo in photos] if photos else None
1405
+ self.largest_photo = Document(photos[-1]) if photos else None
1406
+
1407
+ self.video = Document(
1408
+ result.get('video')) if result.get('video') else None
1409
+ self.audio = Document(
1410
+ result.get('audio')) if result.get('audio') else None
1411
+ self.voice = Voice(
1412
+ result.get('voice')) if result.get('voice') else None
1413
+ self.animation = Document(
1414
+ result.get('animation')) if result.get('animation') else None
1415
+ self.sticker = Document(
1416
+ result.get('sticker')) if result.get('sticker') else None
1417
+ self.video_note = Document(
1418
+ result.get('video_note')) if result.get('video_note') else None
1419
+
1420
+ self.media_group_id = result.get('media_group_id')
1421
+ self.has_media = any([self.document,
1422
+ self.photo,
1423
+ self.video,
1424
+ self.audio,
1425
+ self.voice,
1426
+ self.animation,
1427
+ self.sticker,
1428
+ self.video_note])
1429
+
1430
+ self.contact = Contact(
1431
+ result.get('contact')) if result.get('contact') else None
1432
+ self.location = Location(
1433
+ result.get('location')) if result.get('location') else None
1297
1434
  self.forward_from = User(client, {'ok': True, 'result': result.get(
1298
1435
  'forward_from', {})}) if result.get('forward_from') else None
1299
1436
  self.forward_from_message_id = result.get('forward_from_message_id')
1300
- self.invoice = Invoice(result.get('invoice'))
1437
+ self.invoice = Invoice(
1438
+ result.get('invoice')) if result.get('invoice') else None
1301
1439
  self.reply_to_message = Message(client, {'ok': True, 'result': result.get(
1302
1440
  'reply_to_message', {})}) if result.get('reply_to_message') else None
1303
1441
  self.reply = self.reply_message
1304
1442
  self.send = lambda text, parse_mode=None, reply_markup=None: self.client.send_message(
1305
1443
  self.chat.id, text, parse_mode, reply_markup, reply_to_message=self)
1306
-
1444
+
1307
1445
  self.command = None
1308
1446
  self.args = None
1309
- txt = self.text.split(' ')
1310
-
1311
- self.command = txt[0]
1312
- self.has_slash_command = self.command.startswith('/')
1313
- self.args = txt[1:]
1314
-
1315
- self.start = self.command == '/start'
1447
+ txt = self.text.split(' ') if self.text else []
1448
+
1449
+ self.command = txt[0] if txt else None
1450
+ self.has_slash_command = self.command.startswith(
1451
+ '/') if self.text else None
1452
+ self.args = txt[1:] if self.text else None
1453
+
1454
+ self.start = self.command == '/start' if self.text else None
1316
1455
 
1317
1456
  def edit(self,
1318
1457
  text: str,
@@ -1523,6 +1662,7 @@ class Client:
1523
1662
  self.database_name = database_name
1524
1663
  self.auto_log_start_message = auto_log_start_message
1525
1664
  self._base_url = f"{session}/bot{token}"
1665
+ self._file_url = f"{session}/file/bot{token}"
1526
1666
  self._session = requests.Session()
1527
1667
  self._message_handler = None
1528
1668
  self._message_edit_handler = None
@@ -1601,6 +1741,17 @@ class Client:
1601
1741
  data = self._make_request('GET', 'getMe')
1602
1742
  return User(self, data)
1603
1743
 
1744
+ def get_file(self, file_id: str) -> bytes:
1745
+ """Get file information from Bale API"""
1746
+ data = {
1747
+ 'file_id': file_id
1748
+ }
1749
+ response = self._make_request('POST', 'getFile', json=data)
1750
+ file_path = response['result']['file_path']
1751
+ url = f"{self._file_url}/{file_path}"
1752
+ file_response = self._session.get(url)
1753
+ return file_response.content
1754
+
1604
1755
  def set_webhook(self, url: str, certificate: Optional[str] = None,
1605
1756
  max_connections: Optional[int] = None) -> bool:
1606
1757
  """Set webhook for getting updates"""
@@ -1984,16 +2135,21 @@ class Client:
1984
2135
  'reply_markup': reply_markup.keyboard if reply_markup else None}
1985
2136
  response = self._make_request('POST', 'sendInvoice', json=data)
1986
2137
  return Message(self, response)
1987
-
1988
- def send_chat_action(self, chat: Union[int, str, 'Chat'], action: str, how_many_times: int = 1) -> bool:
2138
+
2139
+ def send_chat_action(self,
2140
+ chat: Union[int,
2141
+ str,
2142
+ 'Chat'],
2143
+ action: str,
2144
+ how_many_times: int = 1) -> bool:
1989
2145
  """Send a chat action"""
1990
2146
  if not chat:
1991
2147
  raise ValueError("Chat ID cannot be empty")
1992
-
2148
+
1993
2149
  data = {
1994
- 'chat_id': str(chat) if isinstance(chat, (int, str)) else str(chat.id),
1995
- 'action': action
1996
- }
2150
+ 'chat_id': str(chat) if isinstance(
2151
+ chat, (int, str)) else str(
2152
+ chat.id), 'action': action}
1997
2153
  res = []
1998
2154
  for _ in range(how_many_times):
1999
2155
  response = self._make_request('POST', 'sendChatAction', json=data)
@@ -2127,7 +2283,8 @@ class Client:
2127
2283
  """Handle different types of messages"""
2128
2284
  msg_data = update.get('message', {})
2129
2285
 
2130
- if 'new_chat_members' in msg_data and hasattr(self, '_member_join_handler'):
2286
+ if 'new_chat_members' in msg_data and hasattr(
2287
+ self, '_member_join_handler'):
2131
2288
  chat, user = msg_data['chat'], msg_data['new_chat_members'][0]
2132
2289
  self._create_thread(
2133
2290
  self._member_join_handler,
@@ -2137,7 +2294,8 @@ class Client:
2137
2294
  )
2138
2295
  return
2139
2296
 
2140
- if 'left_chat_member' in msg_data and hasattr(self, '_member_leave_handler'):
2297
+ if 'left_chat_member' in msg_data and hasattr(
2298
+ self, '_member_leave_handler'):
2141
2299
  chat, user = msg_data['chat'], msg_data['left_chat_member']
2142
2300
  self._create_thread(
2143
2301
  self._member_leave_handler,
@@ -2151,17 +2309,23 @@ class Client:
2151
2309
  text = msg_data['text']
2152
2310
  for command, handler in self._text_handlers.items():
2153
2311
  if text.startswith(command):
2154
- conds = conditions(self, message.author, message, None, message.chat)
2312
+ conds = conditions(
2313
+ self, message.author, message, None, message.chat)
2155
2314
  params = inspect.signature(handler).parameters
2156
2315
  args = (message, conds) if len(params) > 1 else (message,)
2157
2316
  self._create_thread(handler, *args)
2158
2317
  return
2159
2318
 
2160
2319
  if self._message_handler:
2161
- conds = conditions(self, message.author, message, None, message.chat)
2320
+ conds = conditions(
2321
+ self,
2322
+ message.author,
2323
+ message,
2324
+ None,
2325
+ message.chat)
2162
2326
  params = inspect.signature(self._message_handler).parameters
2163
- args = ((message, update, conds) if len(params) > 2 else
2164
- (message, update) if len(params) > 1 else (message,))
2327
+ args = ((message, update, conds) if len(params) > 2 else
2328
+ (message, update) if len(params) > 1 else (message,))
2165
2329
  self._create_thread(self._message_handler, *args)
2166
2330
 
2167
2331
  def _handle_update(self, update):
@@ -2176,17 +2340,21 @@ class Client:
2176
2340
 
2177
2341
  for update_type, (cls, handler) in message_types.items():
2178
2342
  if update_type in update:
2179
- message = cls(self, {'ok': True, 'result': update[update_type]})
2343
+ message = cls(
2344
+ self, {
2345
+ 'ok': True, 'result': update[update_type]})
2180
2346
  handler(message, update)
2181
2347
  return
2182
2348
 
2183
2349
  if 'callback_query' in update and self._callback_handler:
2184
- obj = CallbackQuery(self, {'ok': True, 'result': update['callback_query']})
2350
+ obj = CallbackQuery(
2351
+ self, {
2352
+ 'ok': True, 'result': update['callback_query']})
2185
2353
  params = inspect.signature(self._callback_handler).parameters
2186
2354
  conds = conditions(self, obj.author, None, obj, obj.chat)
2187
2355
  args = (obj, update, conds) if len(params) > 2 else (obj, update)
2188
2356
  self._create_thread(self._callback_handler, *args)
2189
-
2357
+
2190
2358
  def _handle_tick_events(self, current_time):
2191
2359
  """Handle periodic tick events"""
2192
2360
  if hasattr(self, '_tick_handlers'):
@@ -2198,7 +2366,7 @@ class Client:
2198
2366
  def run(self, debug=False):
2199
2367
  """Start p-olling for new messages"""
2200
2368
  try:
2201
- self.get_me()
2369
+ self.user = User(self, {"ok": True, "result": self.get_me()})
2202
2370
  except BaseException:
2203
2371
  raise BaleTokenNotFoundError("token not found")
2204
2372
 
@@ -2222,7 +2390,7 @@ class Client:
2222
2390
  print("Source file changed, restarting...")
2223
2391
  python = sys.executable
2224
2392
  os.execl(python, python, *sys.argv)
2225
-
2393
+
2226
2394
  updates = self.get_updates(offset=offset, timeout=30)
2227
2395
  for update in updates:
2228
2396
  update_id = update['update_id']
@@ -2231,7 +2399,8 @@ class Client:
2231
2399
  offset = update_id + 1
2232
2400
  past_updates.add(update_id)
2233
2401
  if len(past_updates) > 100:
2234
- past_updates = set(sorted(list(past_updates))[-50:])
2402
+ past_updates = set(
2403
+ sorted(list(past_updates))[-50:])
2235
2404
 
2236
2405
  current_time = time.time()
2237
2406
  self._handle_tick_events(current_time)
@@ -2250,9 +2419,10 @@ class Client:
2250
2419
  return False
2251
2420
 
2252
2421
  def get_updates(self, offset: Optional[int] = None,
2253
- limit: Optional[int] = None,
2254
- timeout: Optional[int] = None) -> List[Dict[str, Any]]:
2255
- params = {k: v for k, v in locals().items() if k != 'self' and v is not None}
2422
+ limit: Optional[int] = None,
2423
+ timeout: Optional[int] = None) -> List[Dict[str, Any]]:
2424
+ params = {k: v for k, v in locals().items() if k !=
2425
+ 'self' and v is not None}
2256
2426
  response = self._make_request('GET', 'getUpdates', params=params)
2257
2427
  return response.get('result', [])
2258
2428
 
@@ -2264,6 +2434,6 @@ class Client:
2264
2434
  self._threads.clear()
2265
2435
  if hasattr(self, '_close_handler'):
2266
2436
  self._close_handler()
2267
-
2437
+
2268
2438
  def create_ref_link(self, data: str):
2269
- return f"https://ble.ir/{self.get_me().username}?start={data}"
2439
+ return f"https://ble.ir/{self.get_me().username}?start={data}"
@@ -1,5 +0,0 @@
1
- pyrobale.py,sha256=Nb4HcO5aydJbc6Ofwz60PtZpLAh0Obx_IT4YydUeFgw,84787
2
- pyrobale-0.2.8.dist-info/METADATA,sha256=DjHaCavEk_o0UZI_eLqTRzDzHSTV7V85LI3ifOWOrT0,43668
3
- pyrobale-0.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4
- pyrobale-0.2.8.dist-info/licenses/LICENSE,sha256=XQsvifC5HE85232_XgRHpeQ35MfeL2YcpU4yPiAgmW4,35269
5
- pyrobale-0.2.8.dist-info/RECORD,,