warp-beacon 2.6.46__py3-none-any.whl → 2.6.48__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,2 +1,2 @@
1
- __version__ = "2.6.46"
1
+ __version__ = "2.6.48"
2
2
 
@@ -8,7 +8,6 @@ import logging
8
8
  import html
9
9
 
10
10
  import uvloop
11
- #from contextlib import ExitStack
12
11
 
13
12
  from pyrogram import Client, filters
14
13
  from pyrogram.enums import ParseMode, ChatType
@@ -29,7 +28,7 @@ from warp_beacon.jobs import Origin
29
28
  from warp_beacon.telegram.utils import Utils
30
29
  from warp_beacon.telegram.caption_shortener import CaptionShortner
31
30
  from warp_beacon.scheduler.scheduler import IGScheduler
32
- from warp_beacon.telegram.progress_file_reader import ProgressFileReader
31
+ from warp_beacon.telegram.edit_message import EditMessage
33
32
 
34
33
  class Bot(object):
35
34
  should_exit = None
@@ -42,22 +41,19 @@ class Bot(object):
42
41
  placeholder = None
43
42
  scheduler = None
44
43
  me = None
44
+ edit_message = None
45
45
 
46
46
  def __init__(self, tg_bot_name: str, tg_token: str, tg_api_id: str, tg_api_hash: str) -> None:
47
47
  # Enable logging
48
48
  logging.basicConfig(
49
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
49
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
50
+ level=logging.INFO
50
51
  )
51
52
 
52
53
  logging.getLogger("pyrogram").setLevel(logging.ERROR)
53
-
54
54
  logging.info("Starting Warp Beacon version '%s' ...", __version__)
55
-
56
- db_connect = DBClient()
57
- self.storage = Storage(db_connect)
58
-
55
+ self.storage = Storage(DBClient())
59
56
  self.should_exit = asyncio.Event()
60
-
61
57
  workers_amount = min(32, os.cpu_count() + 4)
62
58
 
63
59
  uvloop.install()
@@ -71,6 +67,8 @@ class Bot(object):
71
67
  workers=int(os.environ.get("TG_WORKERS_POOL_SIZE", default=workers_amount))
72
68
  )
73
69
 
70
+ self.editor = EditMessage(self.client)
71
+
74
72
  self.uploader = AsyncUploader(
75
73
  storage=self.storage,
76
74
  admin_message_callback=self.send_text_to_admin,
@@ -84,7 +82,6 @@ class Bot(object):
84
82
  )
85
83
 
86
84
  self.scheduler = IGScheduler(self.downloader)
87
-
88
85
  self.handlers = Handlers(self)
89
86
 
90
87
  self.client.add_handler(MessageHandler(self.handlers.start, filters.command("start")))
@@ -227,7 +224,7 @@ class Bot(object):
227
224
 
228
225
  return caption
229
226
 
230
- def build_tg_args(self, job: UploadJob, file_io: ProgressFileReader = None) -> dict:
227
+ def build_tg_args(self, job: UploadJob) -> dict:
231
228
  args = {}
232
229
  if job.media_type == JobType.VIDEO:
233
230
  if job.tg_file_id:
@@ -243,7 +240,7 @@ class Bot(object):
243
240
  else:
244
241
  if job.placeholder_message_id:
245
242
  args["media"] = InputMediaVideo(
246
- media=file_io,
243
+ media=job.local_media_path,
247
244
  supports_streaming=True,
248
245
  width=job.media_info["width"],
249
246
  height=job.media_info["height"],
@@ -252,7 +249,7 @@ class Bot(object):
252
249
  caption=self.build_signature_caption(job)
253
250
  )
254
251
  else:
255
- args["video"] = file_io
252
+ args["video"] = job.local_media_path
256
253
  args["supports_streaming"] = True
257
254
  args["width"] = job.media_info["width"]
258
255
  args["height"] = job.media_info["height"]
@@ -274,11 +271,11 @@ class Bot(object):
274
271
  else:
275
272
  if job.placeholder_message_id:
276
273
  args["media"] = InputMediaPhoto(
277
- media=file_io,
274
+ media=job.local_media_path,
278
275
  caption=self.build_signature_caption(job)
279
276
  )
280
277
  else:
281
- args["photo"] = file_io
278
+ args["photo"] = job.local_media_path
282
279
  args["caption"] = self.build_signature_caption(job)
283
280
 
284
281
  args["file_name"] = os.path.basename(job.local_media_path)
@@ -294,7 +291,7 @@ class Bot(object):
294
291
  else:
295
292
  if job.placeholder_message_id:
296
293
  args["media"] = InputMediaAudio(
297
- media=file_io,
294
+ media=job.local_media_path,
298
295
  performer=job.media_info["performer"],
299
296
  thumb=job.media_info["thumb"],
300
297
  duration=round(job.media_info["duration"]),
@@ -302,7 +299,7 @@ class Bot(object):
302
299
  caption=self.build_signature_caption(job)
303
300
  )
304
301
  else:
305
- args["audio"] = file_io
302
+ args["audio"] = job.local_media_path
306
303
  args["performer"] = job.media_info["performer"]
307
304
  args["thumb"] = job.media_info["thumb"]
308
305
  args["duration"] = round(job.media_info["duration"])
@@ -323,7 +320,7 @@ class Bot(object):
323
320
  else:
324
321
  if job.placeholder_message_id:
325
322
  args["media"] = InputMediaAnimation(
326
- media=file_io,
323
+ media=job.local_media_path,
327
324
  thumb=job.media_info["thumb"],
328
325
  duration=round(job.media_info["duration"]),
329
326
  width=job.media_info["width"],
@@ -331,7 +328,7 @@ class Bot(object):
331
328
  caption=self.build_signature_caption(job)
332
329
  )
333
330
  else:
334
- args["animation"] = file_io
331
+ args["animation"] = job.local_media_path
335
332
  args["width"] = job.media_info["width"]
336
333
  args["height"] = job.media_info["height"]
337
334
  args["duration"] = round(job.media_info["duration"])
@@ -364,7 +361,7 @@ class Bot(object):
364
361
  for j in chunk:
365
362
  if j.media_type == JobType.VIDEO:
366
363
  vid = InputMediaVideo(
367
- media=ProgressFileReader(j.local_media_path, self.progress_callback),
364
+ media=j.local_media_path,
368
365
  supports_streaming=True,
369
366
  width=j.media_info["width"],
370
367
  height=j.media_info["height"],
@@ -375,7 +372,7 @@ class Bot(object):
375
372
  tg_chunk.append(vid)
376
373
  elif j.media_type == JobType.IMAGE:
377
374
  photo = InputMediaPhoto(
378
- media=ProgressFileReader(j.local_media_path, self.progress_callback),
375
+ media=j.local_media_path,
379
376
  caption=self.build_signature_caption(job)
380
377
  )
381
378
  tg_chunk.append(photo)
@@ -405,8 +402,12 @@ class Bot(object):
405
402
 
406
403
  return args
407
404
 
408
- def progress_callback(self, filename: str, current: int, total: int) -> None:
409
- logging.info("[%s] Uploaded %.1f%%", filename, current * 100 / total)
405
+ async def progress_callback(self, current: int, total: int, chat_id: int | str, message_id: int, label: str) -> None:
406
+ percents = int(current * 100 / total)
407
+ if (percents % 40) > 0 or percents == 100:
408
+ p = round(percents)
409
+ await self.client.edit_message_caption(chat_id, message_id, f"<b>{p}% <code>{label}</code> uploaded</b>", ParseMode.HTML)
410
+ logging.info("[%s] Uploaded to Telegram %d%%", label, p)
410
411
 
411
412
  async def upload_job(self, job: UploadJob) -> list[str]:
412
413
  tg_file_ids = []
@@ -421,8 +422,7 @@ class Bot(object):
421
422
  await Utils.ensure_me_loaded(self.client)
422
423
  if job.placeholder_message_id:
423
424
  try:
424
- with ProgressFileReader(job.local_media_path, self.progress_callback) as f:
425
- reply_message = await self.client.edit_message_media(**self.build_tg_args(job, f))
425
+ reply_message = await self.editor.edit(**self.build_tg_args(job))
426
426
  except MessageIdInvalid:
427
427
  logging.warning("Placeholder message not found. Looks like placeholder message was deleted by administrator.")
428
428
  job.placeholder_message_id = None
@@ -435,41 +435,29 @@ class Bot(object):
435
435
  JobType.ANIMATION: self.client.send_animation
436
436
  }
437
437
  try:
438
- with ProgressFileReader(job.local_media_path, self.progress_callback) as f:
439
- reply_message = await send_funcs[job.media_type](**self.build_tg_args(job, f))
438
+ reply_message = await send_funcs[job.media_type](**self.build_tg_args(job))
440
439
  except ValueError as e:
441
440
  err_text = str(e)
442
441
  if "Expected" in err_text:
443
442
  logging.warning("Expectations exceeded reality.")
444
443
  logging.warning(err_text)
445
444
  expectation, reality = Utils.parse_expected_patronum_error(err_text)
446
- with ProgressFileReader(job.local_media_path, self.progress_callback) as f:
447
- job_args = self.build_tg_args(job, f)
448
- job_args[reality.value.lower()] = job_args.pop(expectation.value.lower())
449
- reply_message = await send_funcs[reality](**job_args)
445
+ job_args = self.build_tg_args(job)
446
+ job_args[reality.value.lower()] = job_args.pop(expectation.value.lower())
447
+ reply_message = await send_funcs[reality](**job_args)
450
448
 
451
449
  tg_file_id = Utils.extract_file_id(reply_message)
452
450
  tg_file_ids.append(tg_file_id)
453
451
  job.tg_file_id = tg_file_id
454
452
  logging.info("Uploaded media file with type '%s' tg_file_id is '%s'", job.media_type.value, job.tg_file_id)
455
453
  elif job.media_type == JobType.COLLECTION:
456
- #uploaded_files = await self.upload_with_progress(job)
457
454
  col_job_args = self.build_tg_args(job)
458
455
  sent_messages = []
459
456
  snd_grp_options = {"chat_id": job.chat_id, "reply_to_message_id": job.message_id}
460
457
  for i, media_chunk in enumerate(col_job_args["media"]):
461
458
  snd_grp_options["media"] = media_chunk
462
- messages = None
463
- try:
464
- messages = await self.client.send_media_group(**snd_grp_options)
465
- finally:
466
- try:
467
- for med in snd_grp_options["media"]:
468
- med.media.close()
469
- except AttributeError:
470
- pass
471
- if messages:
472
- sent_messages += messages
459
+ messages = await self.client.send_media_group(**snd_grp_options)
460
+ sent_messages += messages
473
461
  if job.media_collection:
474
462
  for j, _ in enumerate(media_chunk):
475
463
  tg_file_id = Utils.extract_file_id(messages[j])
@@ -0,0 +1,141 @@
1
+ from pyrogram.client import Client
2
+ from pyrogram.types import InputMedia, InputMediaAudio, InputMediaPhoto, InputMediaVideo, InputMediaAnimation, InlineKeyboardMarkup
3
+ from pyrogram import raw
4
+ from pyrogram import types
5
+
6
+ #from warp_beacon.telegram.bot import Bot
7
+ from warp_beacon.telegram.progress_bar import ProgressBar
8
+
9
+ class EditMessage(object):
10
+ def __init__(self, client: Client) -> None:
11
+ self.client = client
12
+
13
+ def get_wrapped_video(self, raw_file: raw.base.InputFile, raw_thumb: raw.base.InputFile, media: InputMediaVideo, file_name: str = None) -> raw.types.InputMediaUploadedDocument:
14
+ return raw.types.InputMediaUploadedDocument(
15
+ file=raw_file,
16
+ mime_type=self.client.guess_mime_type(media.media) or "video/mp4",
17
+ thumb=raw_thumb,
18
+ spoiler=media.has_spoiler,
19
+ attributes=[
20
+ raw.types.DocumentAttributeVideo(
21
+ duration=media.duration,
22
+ w=media.width,
23
+ h=media.height,
24
+ supports_streaming=media.supports_streaming
25
+ ),
26
+ raw.types.DocumentAttributeFilename(
27
+ file_name=file_name
28
+ )
29
+ ]
30
+ )
31
+
32
+ def get_wrapped_photo(self, raw_file: raw.base.InputFile, media: InputMediaPhoto) -> raw.types.InputMediaUploadedPhoto:
33
+ return raw.types.InputMediaUploadedPhoto(
34
+ file=raw_file,
35
+ spoiler=media.has_spoiler
36
+ )
37
+
38
+ def get_wrapped_audio(self, raw_file: raw.base.InputFile, raw_thumb: raw.base.InputFile, media: InputMediaAudio, file_name: str = None) -> raw.types.InputMediaUploadedDocument:
39
+ return raw.types.InputMediaUploadedDocument(
40
+ mime_type=self.client.guess_mime_type(media.media) or "audio/mpeg",
41
+ thumb=raw_thumb,
42
+ file=raw_file,
43
+ attributes=[
44
+ raw.types.DocumentAttributeAudio(
45
+ duration=media.duration,
46
+ performer=media.performer,
47
+ title=media.title
48
+ ),
49
+ raw.types.DocumentAttributeFilename(
50
+ file_name=file_name
51
+ )
52
+ ]
53
+ )
54
+
55
+ def get_wrapped_animation(self, raw_file: raw.base.InputFile, raw_thumb: raw.base.InputFile, media: InputMediaVideo, file_name: str = None) -> raw.types.InputMediaUploadedDocument:
56
+ return raw.types.InputMediaUploadedDocument(
57
+ mime_type=self.client.guess_mime_type(media.media) or "video/mp4",
58
+ thumb=raw_thumb,
59
+ spoiler=media.has_spoiler,
60
+ file=raw_file,
61
+ attributes=[
62
+ raw.types.DocumentAttributeVideo(
63
+ supports_streaming=True,
64
+ duration=media.duration,
65
+ w=media.width,
66
+ h=media.height
67
+ ),
68
+ raw.types.DocumentAttributeFilename(
69
+ file_name=file_name
70
+ ),
71
+ raw.types.DocumentAttributeAnimated()
72
+ ]
73
+ )
74
+
75
+ #async def upload_with_progress(self,
76
+ # media: InputMedia | InputMediaAudio | InputMediaPhoto | InputMediaVideo | InputMediaAnimation,
77
+ # chat_id: int | str,
78
+ # message_id: int,
79
+ # file_name: str
80
+ #) -> raw.base.InputFile:
81
+ # progress_bar = ProgressBar(self.client)
82
+ # progress_id = f"{chat_id}:{message_id}:{file_name}"
83
+ # self.progress_bars[progress_id] = progress_bar
84
+ # raw_file = await self.client.save_file(path=media.media, progress=progress_bar.progress_callback, progress_args=(chat_id, message_id, file_name,))
85
+ # del self.progress_bars[progress_id]
86
+ # return raw_file
87
+
88
+ async def edit(self,
89
+ chat_id: int | str,
90
+ message_id: int,
91
+ media: InputMedia | InputMediaAudio | InputMediaPhoto | InputMediaVideo | InputMediaAnimation,
92
+ reply_markup: InlineKeyboardMarkup = None,
93
+ file_name: str = None
94
+ ) -> None:
95
+ progress_bar = ProgressBar(self.client)
96
+ raw_file = await self.client.save_file(path=media.media, progress=progress_bar.progress_callback, progress_args=(chat_id, message_id, file_name,))
97
+
98
+ caption = media.caption
99
+ parse_mode = media.parse_mode
100
+
101
+ message, entities = None, None
102
+
103
+ if caption is not None:
104
+ message, entities = (await self.client.parser.parse(caption, parse_mode)).values()
105
+
106
+ raw_media = None
107
+ if isinstance(media, types.InputMediaVideo):
108
+ progress_bar_thumb = ProgressBar(self.client)
109
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
110
+ raw_media = self.get_wrapped_video(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
111
+ elif isinstance(media, types.InputMediaPhoto):
112
+ raw_media = self.get_wrapped_photo(raw_file=raw_file, media=media)
113
+ elif isinstance(media, types.InputMediaAudio):
114
+ progress_bar_thumb = ProgressBar(self.client)
115
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
116
+ raw_media = self.get_wrapped_audio(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
117
+ elif isinstance(media, types.InputMediaAnimation):
118
+ progress_bar_thumb = ProgressBar(self.client)
119
+ raw_file_thumb = await self.client.save_file(path=media.thumb, progress=progress_bar_thumb.progress_callback, progress_args=(chat_id, message_id, "thumbnail",))
120
+ raw_media = self.get_wrapped_animation(raw_file=raw_file, raw_thumb=raw_file_thumb, media=media, file_name=file_name)
121
+
122
+ peer = await self.client.resolve_peer(chat_id)
123
+
124
+ r = await self.client.invoke(
125
+ raw.functions.messages.EditMessage(
126
+ peer=peer,
127
+ id=message_id,
128
+ media=raw_media,
129
+ reply_markup=await reply_markup.write(self.client) if reply_markup else None,
130
+ message=message,
131
+ entities=entities
132
+ )
133
+ )
134
+
135
+ for i in r.updates:
136
+ if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)):
137
+ return await types.Message._parse(
138
+ self.client, i.message,
139
+ {i.id: i for i in r.users},
140
+ {i.id: i for i in r.chats}
141
+ )
@@ -0,0 +1,66 @@
1
+ import logging
2
+
3
+ import hashlib
4
+
5
+ from pyrogram.enums import ParseMode
6
+ from pyrogram.errors.exceptions.bad_request_400 import MessageNotModified
7
+ from pyrogram import Client
8
+
9
+ class ProgressBar(object):
10
+ def __init__(self, client: Client) -> None:
11
+ self._next_threshold = 20
12
+ self.client = client
13
+
14
+ def make_progress_bar(self, current: int, total: int, length: int = 10) -> str:
15
+ """
16
+ Returns string
17
+ [████▌────] 55.0%
18
+ length — amount of characters in bar
19
+ """
20
+ # fraction of completed job from 0.0 to 1.0
21
+ frac = current / total if total else 0
22
+ # how much "filled" cells
23
+ filled = int(frac * length)
24
+ # part between whole cells, optional may withdraw half
25
+ half_block = ''
26
+ if (frac * length) - filled >= 0.5:
27
+ half_block = '▌' # or '▏', '▍' and etc.
28
+ # building bar
29
+ pbar = '█' * filled + half_block + '─' * (length - filled - len(half_block))
30
+ percent = frac * 100
31
+ return f"[{pbar}] {round(percent)}%"
32
+
33
+ def make_emoji_progress_bar(self, current: int, total: int, length: int = 10) -> str:
34
+ """
35
+ Returns string:
36
+ [🟩🟩🟩⬜️⬜️⬜️⬜️⬜️⬜️⬜️] 30.0%
37
+ length — common number of emoji cells
38
+ """
39
+ frac = (current / total) if total else 0
40
+ filled_count = int(frac * length)
41
+ empty_count = length - filled_count
42
+ pbar = "🟩" * filled_count + "⬜️" * empty_count
43
+ percent = frac * 100
44
+ return f"[{pbar}] {round(percent)}%"
45
+
46
+ async def progress_callback(self, current: int, total: int, chat_id: int | str, message_id: int, label: str) -> None:
47
+ percent = current * 100 / total
48
+ if percent >= self._next_threshold:
49
+ #pbar = self.make_progress_bar(percent, 100, 25)
50
+ pbar = self.make_emoji_progress_bar(percent, 100, 14)
51
+ logging.info("[%s] Uploaded to Telegram %d%%", label, percent)
52
+ try:
53
+ await self.client.edit_message_caption(chat_id, message_id, f"{pbar}<br><b>Uploading <code>{label}</code></b>", ParseMode.HTML)
54
+ except MessageNotModified:
55
+ logging.warning("bad_request_400.MessageNotModified")
56
+ except Exception as e:
57
+ logging.warning("An error occurred while updating progress bar")
58
+ logging.exception(e)
59
+ self._next_threshold += 20
60
+
61
+ @staticmethod
62
+ def make_hash(chat_id: str | int, message_id: int, algorithm: str = 'sha256') -> str:
63
+ s = f"{chat_id}:{message_id}"
64
+ # md5, sha1, sha256
65
+ h = hashlib.new(algorithm, s.encode('utf-8'))
66
+ return h.hexdigest()
@@ -3,27 +3,27 @@ import os
3
3
  from types import TracebackType
4
4
  from typing import Optional, Callable, Type
5
5
 
6
- class ProgressFileReader(io.BufferedReader):
6
+ class ProgressFileReader(io.FileIO):
7
7
  def __init__(self, file_path: str, callback: Optional[Callable[[str, int, int], None]]) -> None:
8
- raw = open(file_path, "rb")
9
- super().__init__(raw)
10
- self._raw = raw
8
+ super().__init__(file_path, "rb")
11
9
  self.callback = callback
12
10
  self._total = os.path.getsize(file_path)
13
11
  self._read_bytes = 0
14
- self._name = os.path.basename(file_path)
12
+ self._display_name = os.path.basename(file_path)
13
+
14
+ def __fspath__(self) -> str:
15
+ return self._display_name
15
16
 
16
17
  def read(self, size: int = -1) -> bytes:
17
18
  chunk = super().read(size)
18
19
  self._read_bytes += len(chunk)
19
20
  if self.callback:
20
- self.callback(self.name, self._read_bytes, self._total)
21
+ self.callback(self._display_name, self._read_bytes, self._total)
21
22
  return chunk
22
23
 
23
24
  def close(self) -> None:
24
25
  if not self.closed:
25
26
  super().close()
26
- self._raw.close()
27
27
 
28
28
  def __enter__(self) -> "ProgressFileReader":
29
29
  return self
@@ -33,8 +33,4 @@ class ProgressFileReader(io.BufferedReader):
33
33
  exc_val: Optional[BaseException],
34
34
  exc_tb: Optional[TracebackType]
35
35
  ) -> None:
36
- self.close()
37
-
38
- @property
39
- def name(self) -> str:
40
- return self._name
36
+ self.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warp_beacon
3
- Version: 2.6.46
3
+ Version: 2.6.48
4
4
  Summary: Telegram bot for expanding external media links
5
5
  Home-page: https://github.com/sb0y/warp_beacon
6
6
  Author: Andrey Bagrintsev
@@ -4,7 +4,7 @@ var/warp_beacon/accounts.json,sha256=OsXdncs6h88xrF_AP6_WDCK1waGBn9SR-uYdIeK37GM
4
4
  var/warp_beacon/placeholder.gif,sha256=cE5CGJVaop4Sx21zx6j4AyoHU0ncmvQuS2o6hJfEH88,6064
5
5
  var/warp_beacon/proxies.json,sha256=VnjlQDXumOEq72ZFjbh6IqHS1TEHqn8HPYAZqWCeSIA,95
6
6
  warp_beacon/__init__.py,sha256=_rThNODmz0nDp_n4mWo_HKaNFE5jk1_7cRhHyYaencI,163
7
- warp_beacon/__version__.py,sha256=e-1kHFchpbGQJP--lXbx_a56Qd3DtFLuKUq99y8jeAw,24
7
+ warp_beacon/__version__.py,sha256=zs4BuKdJxx9o9qJ7xzVHPij03aEAmL9DN9F1bJlI6hk,24
8
8
  warp_beacon/warp_beacon.py,sha256=ED43vNzdjDUJ_9qLCbri0bjWLWEJ69BENGj9i7G6AvM,342
9
9
  warp_beacon/yt_auth.py,sha256=GUTKqYr_tzDC-07Lx_ahWXSag8EyLxXBUnQbDBIkEmk,6022
10
10
  warp_beacon/compress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -40,16 +40,18 @@ warp_beacon/scraper/youtube/youtube.py,sha256=x9v9p1coA9TvBhxjNAofGu4UBkAEdYPE2e
40
40
  warp_beacon/storage/__init__.py,sha256=0Vajd0oITKJfu2vmNx5uQSt3-L6vwIvUYWJo8HZCjco,3398
41
41
  warp_beacon/storage/mongo.py,sha256=qC4ZiO8XXvPnP0rJwz4CJx42pqFsyAjCiW10W5QdT6E,527
42
42
  warp_beacon/telegram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- warp_beacon/telegram/bot.py,sha256=NGakTkZr7CVaxhH77c0b_UssZtcDZIJd2Pcn0C8mARA,19576
43
+ warp_beacon/telegram/bot.py,sha256=GTxKEAyCzMXXMrAEw2INQED7Y4XPE0CjiTexPDNWrpA,19256
44
44
  warp_beacon/telegram/caption_shortener.py,sha256=EnguNCF52ne7y4P-iJAbI6K3sqoJqJbND_dX5Fhwkv0,1549
45
+ warp_beacon/telegram/edit_message.py,sha256=XDGBbQ3c6xzQsqwJ2ufVozgDZh3oWF-JQlk3wk78soo,5556
45
46
  warp_beacon/telegram/handlers.py,sha256=uvR6TPHSqdSxigp3wR-ewiE6t3TvVcbVLVcYGwkgD2s,9559
46
47
  warp_beacon/telegram/placeholder_message.py,sha256=wN9-BRiyrtHG-EvXtZkGJHt2CX71munQ57ITttjt0mw,6400
47
- warp_beacon/telegram/progress_file_reader.py,sha256=p74VDWXI5iv52p2AJNkCPcxqTpFS5NMgC49wUXsOaAQ,1013
48
+ warp_beacon/telegram/progress_bar.py,sha256=C0tBthrmsvVsFlP2wlfG2XiKtxODguFSWucrY-OAPJ8,2388
49
+ warp_beacon/telegram/progress_file_reader.py,sha256=e3equyNKlKs764AD-iE9QRsh3YDHTzP78Mx5tdvPPWs,969
48
50
  warp_beacon/telegram/utils.py,sha256=1Lq67aRylVJzbwSyvAgjPAGjJZFATkICvAj3TJGuJiM,4635
49
51
  warp_beacon/uploader/__init__.py,sha256=j3qcuKhpchseZLGzSsSiogqe6WdMbkK8d3I-ConhNRs,5687
50
- warp_beacon-2.6.46.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
51
- warp_beacon-2.6.46.dist-info/METADATA,sha256=ckfd93afacyBxtMvReIMo8JxeC-OGdksfVF2g7-ob90,22706
52
- warp_beacon-2.6.46.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
53
- warp_beacon-2.6.46.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
54
- warp_beacon-2.6.46.dist-info/top_level.txt,sha256=aXHYe44ttXYi5p6_BGE-9t-RbPH6gLEJDH7gYoWamN4,1229
55
- warp_beacon-2.6.46.dist-info/RECORD,,
52
+ warp_beacon-2.6.48.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
53
+ warp_beacon-2.6.48.dist-info/METADATA,sha256=AtGtWTBYvxEnprg6f_DwjkfetYR6Cm38JaM8mxOi6UI,22706
54
+ warp_beacon-2.6.48.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
55
+ warp_beacon-2.6.48.dist-info/entry_points.txt,sha256=eSB61Rb89d56WY0O-vEIQwkn18J-4CMrJcLA_R_8h3g,119
56
+ warp_beacon-2.6.48.dist-info/top_level.txt,sha256=pYstFq5qRCQuQc4fOAAc1ti5UwsYldTREx7h2dNplI4,1297
57
+ warp_beacon-2.6.48.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -34,8 +34,10 @@ warp_beacon/storage/mongo
34
34
  warp_beacon/telegram
35
35
  warp_beacon/telegram/bot
36
36
  warp_beacon/telegram/caption_shortener
37
+ warp_beacon/telegram/edit_message
37
38
  warp_beacon/telegram/handlers
38
39
  warp_beacon/telegram/placeholder_message
40
+ warp_beacon/telegram/progress_bar
39
41
  warp_beacon/telegram/progress_file_reader
40
42
  warp_beacon/telegram/utils
41
43
  warp_beacon/uploader