warp-beacon 2.6.46__tar.gz → 2.6.48__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {warp_beacon-2.6.46/warp_beacon.egg-info → warp_beacon-2.6.48}/PKG-INFO +1 -1
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/setup.py +2 -0
- warp_beacon-2.6.48/warp_beacon/__version__.py +2 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/bot.py +31 -43
- warp_beacon-2.6.48/warp_beacon/telegram/edit_message.py +141 -0
- warp_beacon-2.6.48/warp_beacon/telegram/progress_bar.py +66 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/progress_file_reader.py +8 -12
- {warp_beacon-2.6.46 → warp_beacon-2.6.48/warp_beacon.egg-info}/PKG-INFO +1 -1
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon.egg-info/SOURCES.txt +2 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon.egg-info/top_level.txt +2 -0
- warp_beacon-2.6.46/warp_beacon/__version__.py +0 -2
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/LICENSE +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/MANIFEST.in +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/README.md +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/assets/placeholder.gif +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/etc/.gitignore +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/etc/accounts.json +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/etc/proxies.json +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/etc/warp_beacon.conf +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/etc/warp_beacon.service +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/pyproject.toml +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/setup.cfg +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/compress/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/compress/video.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/jobs/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/jobs/abstract.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/jobs/download_job.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/jobs/types.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/jobs/upload_job.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/mediainfo/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/mediainfo/abstract.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/mediainfo/audio.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/mediainfo/silencer.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/mediainfo/video.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scheduler/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scheduler/instagram_human.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scheduler/scheduler.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/abstract.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/account_selector.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/exceptions.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/fail_handler.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/instagram/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/instagram/captcha.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/instagram/instagram.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/link_resolver.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/utils.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/youtube/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/youtube/abstract.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/youtube/music.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/youtube/shorts.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/scraper/youtube/youtube.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/storage/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/storage/mongo.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/caption_shortener.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/handlers.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/placeholder_message.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/telegram/utils.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/uploader/__init__.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/warp_beacon.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon/yt_auth.py +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon.egg-info/dependency_links.txt +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon.egg-info/entry_points.txt +0 -0
- {warp_beacon-2.6.46 → warp_beacon-2.6.48}/warp_beacon.egg-info/requires.txt +0 -0
@@ -66,7 +66,9 @@ setup(
|
|
66
66
|
"warp_beacon/telegram/handlers",
|
67
67
|
"warp_beacon/telegram/utils",
|
68
68
|
"warp_beacon/telegram/caption_shortener",
|
69
|
+
"warp_beacon/telegram/progress_bar",
|
69
70
|
"warp_beacon/telegram/progress_file_reader",
|
71
|
+
"warp_beacon/telegram/edit_message",
|
70
72
|
"warp_beacon/jobs/abstract",
|
71
73
|
"warp_beacon/jobs/download_job",
|
72
74
|
"warp_beacon/jobs/upload_job",
|
@@ -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.
|
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",
|
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
|
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=
|
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"] =
|
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=
|
274
|
+
media=job.local_media_path,
|
278
275
|
caption=self.build_signature_caption(job)
|
279
276
|
)
|
280
277
|
else:
|
281
|
-
args["photo"] =
|
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=
|
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"] =
|
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=
|
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"] =
|
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=
|
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=
|
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,
|
409
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
447
|
-
|
448
|
-
|
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 =
|
463
|
-
|
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.
|
6
|
+
class ProgressFileReader(io.FileIO):
|
7
7
|
def __init__(self, file_path: str, callback: Optional[Callable[[str, int, int], None]]) -> None:
|
8
|
-
|
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.
|
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.
|
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()
|
@@ -55,8 +55,10 @@ warp_beacon/storage/mongo.py
|
|
55
55
|
warp_beacon/telegram/__init__.py
|
56
56
|
warp_beacon/telegram/bot.py
|
57
57
|
warp_beacon/telegram/caption_shortener.py
|
58
|
+
warp_beacon/telegram/edit_message.py
|
58
59
|
warp_beacon/telegram/handlers.py
|
59
60
|
warp_beacon/telegram/placeholder_message.py
|
61
|
+
warp_beacon/telegram/progress_bar.py
|
60
62
|
warp_beacon/telegram/progress_file_reader.py
|
61
63
|
warp_beacon/telegram/utils.py
|
62
64
|
warp_beacon/uploader/__init__.py
|
@@ -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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|