sticker-convert 2.10.9__py3-none-any.whl → 2.11.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. sticker_convert/cli.py +46 -8
  2. sticker_convert/converter.py +13 -11
  3. sticker_convert/downloaders/download_base.py +5 -5
  4. sticker_convert/downloaders/download_signal.py +4 -2
  5. sticker_convert/downloaders/download_telegram.py +19 -103
  6. sticker_convert/gui.py +12 -0
  7. sticker_convert/gui_components/frames/cred_frame.py +38 -13
  8. sticker_convert/job.py +8 -8
  9. sticker_convert/job_option.py +6 -0
  10. sticker_convert/resources/compression.json +2 -2
  11. sticker_convert/resources/help.json +3 -2
  12. sticker_convert/resources/input.json +10 -0
  13. sticker_convert/resources/output.json +16 -0
  14. sticker_convert/uploaders/compress_wastickers.py +76 -56
  15. sticker_convert/uploaders/upload_telegram.py +170 -234
  16. sticker_convert/uploaders/upload_viber.py +18 -19
  17. sticker_convert/utils/auth/telegram_api.py +670 -0
  18. sticker_convert/utils/auth/telethon_setup.py +79 -0
  19. sticker_convert/utils/files/metadata_handler.py +3 -3
  20. sticker_convert/utils/url_detect.py +1 -1
  21. sticker_convert/version.py +1 -1
  22. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/METADATA +53 -35
  23. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/RECORD +27 -25
  24. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/WHEEL +1 -1
  25. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/LICENSE +0 -0
  26. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/entry_points.txt +0 -0
  27. {sticker_convert-2.10.9.dist-info → sticker_convert-2.11.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,670 @@
1
+ #!/usr/bin/env python3
2
+ import re
3
+ import time
4
+ from collections import defaultdict
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Protocol, Tuple, Union, cast
7
+
8
+ import anyio
9
+ from telegram import InputSticker, PhotoSize, Sticker
10
+ from telegram import StickerSet as TGStickerSet
11
+ from telegram.error import BadRequest, TelegramError
12
+ from telegram.ext import AIORateLimiter, ApplicationBuilder
13
+ from telethon.errors.rpcerrorlist import StickersetInvalidError # type: ignore
14
+ from telethon.functions import messages # type: ignore
15
+ from telethon.tl.types.messages import StickerSet as TLStickerSet # type: ignore
16
+ from telethon.types import DocumentAttributeFilename, InputStickerSetShortName, InputStickerSetThumb, Message, TypeDocument # type: ignore
17
+
18
+ from sticker_convert.job_option import CredOption
19
+ from sticker_convert.utils.auth.telethon_setup import TelethonSetup
20
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
21
+
22
+ # sticker_path: Path, sticker_bytes: bytes, emoji_list: List[str], sticker_format: str
23
+ TelegramSticker = Tuple[Path, bytes, List[str], str]
24
+
25
+
26
+ class TelegramAPI(Protocol):
27
+ async def setup(
28
+ self,
29
+ opt_cred: CredOption,
30
+ is_upload: bool,
31
+ cb: CallbackProtocol,
32
+ cb_return: CallbackReturn,
33
+ ) -> bool: ...
34
+ async def exit(self) -> None: ...
35
+ async def set_upload_pack_type(self, is_emoji: bool) -> None: ...
36
+ async def set_upload_pack_short_name(self, pack_title: str) -> str: ...
37
+ async def check_pack_exist(self) -> bool: ...
38
+ async def pack_del(self) -> bool: ...
39
+ async def pack_new(
40
+ self, stickers_list: List[TelegramSticker], sticker_type: str
41
+ ) -> Tuple[int, int]: ...
42
+ async def pack_add(
43
+ self, stickers_list: List[TelegramSticker], sticker_type: str
44
+ ) -> Tuple[int, int]: ...
45
+ async def pack_thumbnail(self, thumbnail: TelegramSticker) -> bool: ...
46
+ async def get_pack_url(self) -> str: ...
47
+ async def pack_dl(
48
+ self, pack_short_name: str, out_dir: Path
49
+ ) -> Tuple[Dict[str, bool], Dict[str, str]]: ...
50
+
51
+
52
+ class BotAPI(TelegramAPI):
53
+ async def setup(
54
+ self,
55
+ opt_cred: CredOption,
56
+ is_upload: bool,
57
+ cb: CallbackProtocol,
58
+ cb_return: CallbackReturn,
59
+ ) -> bool:
60
+ self.timeout = 30
61
+ self.cb = cb
62
+
63
+ if is_upload and not (opt_cred.telegram_token and opt_cred.telegram_userid):
64
+ self.cb.put("Token and userid required for uploading to telegram")
65
+ return False
66
+ elif is_upload is False and not opt_cred.telegram_token:
67
+ self.cb.put("Token required for downloading from telegram")
68
+ return False
69
+
70
+ if opt_cred.telegram_userid.isnumeric():
71
+ self.telegram_userid = int(opt_cred.telegram_userid)
72
+ else:
73
+ self.cb.put("Invalid userid, should contain numbers only")
74
+ return False
75
+
76
+ self.application = ( # type: ignore
77
+ ApplicationBuilder()
78
+ .token(opt_cred.telegram_token)
79
+ .rate_limiter(AIORateLimiter(overall_max_rate=4, max_retries=3))
80
+ .connect_timeout(self.timeout)
81
+ .pool_timeout(self.timeout)
82
+ .read_timeout(self.timeout)
83
+ .write_timeout(self.timeout)
84
+ .build()
85
+ )
86
+ await self.application.initialize()
87
+
88
+ return True
89
+
90
+ async def exit(self) -> None:
91
+ await self.application.shutdown()
92
+
93
+ async def set_upload_pack_short_name(self, pack_title: str) -> str:
94
+ self.pack_title = pack_title
95
+ bot_name = self.application.bot.name
96
+ self.pack_short_name = (
97
+ pack_title.replace(" ", "_") + "_by_" + bot_name.replace("@", "")
98
+ )
99
+ self.pack_short_name = re.sub(
100
+ "[^0-9a-zA-Z]+", "_", self.pack_short_name
101
+ ) # name used in url, only alphanum and underscore only
102
+ return self.pack_short_name
103
+
104
+ async def set_upload_pack_type(self, is_emoji: bool) -> None:
105
+ self.is_emoji = is_emoji
106
+
107
+ async def check_pack_exist(self) -> bool:
108
+ sticker_set: Any = None
109
+ try:
110
+ sticker_set = await self.application.bot.get_sticker_set(
111
+ self.pack_short_name,
112
+ read_timeout=30,
113
+ write_timeout=30,
114
+ connect_timeout=30,
115
+ pool_timeout=30,
116
+ )
117
+ except TelegramError:
118
+ pass
119
+
120
+ if sticker_set is not None:
121
+ return True
122
+ return False
123
+
124
+ async def pack_del(self) -> bool:
125
+ try:
126
+ await self.application.bot.delete_sticker_set(self.pack_short_name)
127
+ except BadRequest as e:
128
+ msg = f"Cannot delete sticker set {self.pack_short_name} due to {e}"
129
+ if str(e) == "Stickerpack_not_found":
130
+ msg += "\nHint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
131
+ self.cb.put(msg)
132
+ return False
133
+ except TelegramError as e:
134
+ self.cb.put(f"Cannot delete sticker set {self.pack_short_name} due to {e}")
135
+ return False
136
+ return True
137
+
138
+ async def pack_new(
139
+ self, stickers_list: List[TelegramSticker], sticker_type: str
140
+ ) -> Tuple[int, int]:
141
+ init_input_stickers: List[InputSticker] = []
142
+ for i in stickers_list[:50]:
143
+ init_input_stickers.append(
144
+ InputSticker(
145
+ sticker=i[1],
146
+ emoji_list=i[2],
147
+ format=i[3],
148
+ )
149
+ )
150
+
151
+ try:
152
+ self.cb.put(
153
+ f"Creating pack and bulk uploading {len(init_input_stickers)} stickers of {self.pack_short_name}"
154
+ )
155
+ await self.application.bot.create_new_sticker_set(
156
+ self.telegram_userid,
157
+ self.pack_short_name,
158
+ self.pack_title,
159
+ init_input_stickers,
160
+ sticker_type,
161
+ )
162
+ self.cb.put(
163
+ f"Created pack and bulk uploaded {len(init_input_stickers)} stickers of {self.pack_short_name}"
164
+ )
165
+ _, success_add = await self.pack_add(stickers_list[50:], sticker_type)
166
+ return len(stickers_list), len(init_input_stickers) + success_add
167
+ except TelegramError as e:
168
+ self.cb.put(
169
+ f"Cannot create pack and bulk upload {len(init_input_stickers)} stickers of {self.pack_short_name} due to {e}"
170
+ )
171
+ return len(stickers_list), 0
172
+
173
+ async def pack_add(
174
+ self, stickers_list: List[TelegramSticker], sticker_type: str
175
+ ) -> Tuple[int, int]:
176
+ stickers_ok = 0
177
+ self.cb.put(
178
+ (
179
+ "bar",
180
+ None,
181
+ {
182
+ "set_progress_mode": "determinate",
183
+ "steps": len(stickers_list),
184
+ },
185
+ )
186
+ )
187
+ for i in stickers_list:
188
+ input_sticker = InputSticker(
189
+ sticker=i[1],
190
+ emoji_list=i[2],
191
+ format=i[3],
192
+ )
193
+ try:
194
+ # We could use tg.start_soon() here
195
+ # But this would disrupt the order of stickers
196
+ await self.application.bot.add_sticker_to_set(
197
+ self.telegram_userid,
198
+ self.pack_short_name,
199
+ input_sticker,
200
+ )
201
+ self.cb.put(f"Uploaded sticker {i[0]} of {self.pack_short_name}")
202
+ stickers_ok += 1
203
+ except BadRequest as e:
204
+ self.cb.put(
205
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {e}"
206
+ )
207
+ if str(e) == "Stickerpack_not_found":
208
+ self.cb.put(
209
+ "Hint: You might had deleted and recreated pack too quickly. Wait about 3 minutes and try again."
210
+ )
211
+ except TelegramError as e:
212
+ self.cb.put(
213
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {e}"
214
+ )
215
+ self.cb.put("update_bar")
216
+
217
+ self.cb.put(("bar", None, {"set_progress_mode": "indeterminate"}))
218
+ return len(stickers_list), stickers_ok
219
+
220
+ async def pack_thumbnail(self, thumbnail: TelegramSticker) -> bool:
221
+ try:
222
+ self.cb.put(f"Uploading cover (thumbnail) of pack {self.pack_short_name}")
223
+ await self.application.bot.set_sticker_set_thumbnail(
224
+ self.pack_short_name,
225
+ self.telegram_userid,
226
+ thumbnail[3],
227
+ thumbnail[1],
228
+ )
229
+ self.cb.put(f"Uploaded cover (thumbnail) of pack {self.pack_short_name}")
230
+ return True
231
+ except TelegramError as e:
232
+ self.cb.put(
233
+ f"Cannot upload cover (thumbnail) of pack {self.pack_short_name} due to {e}"
234
+ )
235
+ return False
236
+
237
+ async def get_pack_url(self) -> str:
238
+ if self.is_emoji:
239
+ return f"https://t.me/addemoji/{self.pack_short_name}"
240
+ else:
241
+ return f"https://t.me/addstickers/{self.pack_short_name}"
242
+
243
+ async def _download_sticker(
244
+ self,
245
+ sticker: Union[PhotoSize, Sticker],
246
+ f_id: str,
247
+ out_dir: Path,
248
+ results: Dict[str, bool],
249
+ emoji_dict: Dict[str, str],
250
+ ) -> None:
251
+ try:
252
+ sticker_file = await sticker.get_file(
253
+ read_timeout=self.timeout,
254
+ write_timeout=self.timeout,
255
+ connect_timeout=self.timeout,
256
+ pool_timeout=self.timeout,
257
+ )
258
+ except TelegramError as e:
259
+ self.cb.put(f"Failed to download {f_id}: {str(e)}")
260
+ results[f_id] = False
261
+ return
262
+ fpath = sticker_file.file_path
263
+ assert fpath is not None
264
+ ext = Path(fpath).suffix
265
+ f_name = f_id + ext
266
+ f_path = Path(out_dir, f_name)
267
+ await sticker_file.download_to_drive(
268
+ custom_path=f_path,
269
+ read_timeout=self.timeout,
270
+ write_timeout=self.timeout,
271
+ connect_timeout=self.timeout,
272
+ pool_timeout=self.timeout,
273
+ )
274
+ if isinstance(sticker, Sticker) and sticker.emoji is not None:
275
+ emoji_dict[f_id] = sticker.emoji
276
+ self.cb.put(f"Downloaded {f_name}")
277
+ results[f_id] = True
278
+ if f_id != "cover":
279
+ self.cb.put("update_bar")
280
+
281
+ async def pack_dl(
282
+ self, pack_short_name: str, out_dir: Path
283
+ ) -> Tuple[Dict[str, bool], Dict[str, str]]:
284
+ results: Dict[str, bool] = {}
285
+ emoji_dict: Dict[str, str] = {}
286
+
287
+ try:
288
+ sticker_set: TGStickerSet = await self.application.bot.get_sticker_set(
289
+ pack_short_name,
290
+ read_timeout=self.timeout,
291
+ write_timeout=self.timeout,
292
+ connect_timeout=self.timeout,
293
+ pool_timeout=self.timeout,
294
+ )
295
+ except TelegramError as e:
296
+ self.cb.put(
297
+ f"Failed to download telegram sticker set {pack_short_name} due to: {e}"
298
+ )
299
+ return results, emoji_dict
300
+
301
+ self.cb.put(
302
+ (
303
+ "bar",
304
+ None,
305
+ {
306
+ "set_progress_mode": "determinate",
307
+ "steps": len(sticker_set.stickers),
308
+ },
309
+ )
310
+ )
311
+
312
+ async with anyio.create_task_group() as tg:
313
+ for num, sticker in enumerate(sticker_set.stickers):
314
+ f_id = str(num).zfill(3)
315
+ tg.start_soon(
316
+ self._download_sticker, sticker, f_id, out_dir, results, emoji_dict
317
+ )
318
+
319
+ if sticker_set.thumbnail is not None:
320
+ results_thumb: Dict[str, bool] = {}
321
+ tg.start_soon(
322
+ self._download_sticker,
323
+ sticker_set.thumbnail,
324
+ "cover",
325
+ out_dir,
326
+ results_thumb,
327
+ emoji_dict,
328
+ )
329
+
330
+ return results, emoji_dict
331
+
332
+
333
+ class TelethonAPI(TelegramAPI):
334
+ async def setup(
335
+ self,
336
+ opt_cred: CredOption,
337
+ is_upload: bool,
338
+ cb: CallbackProtocol,
339
+ cb_return: CallbackReturn,
340
+ ) -> bool:
341
+ self.opt_cred = opt_cred
342
+ self.cb = cb
343
+ self.cb_return = cb_return
344
+
345
+ success, self.client, _, _ = await TelethonSetup(
346
+ self.opt_cred, self.cb_ask_str
347
+ ).start_async()
348
+
349
+ return success
350
+
351
+ async def exit(self) -> None:
352
+ self.client.disconnect()
353
+
354
+ def cb_ask_str(
355
+ self, msg: Optional[str] = None, initialvalue: Optional[str] = None
356
+ ) -> str:
357
+ self.cb.put(("ask_str", (msg,), None))
358
+ response = self.cb_return.get_response()
359
+
360
+ assert isinstance(response, str)
361
+ return response
362
+
363
+ async def set_upload_pack_short_name(self, pack_title: str) -> str:
364
+ self.pack_title = pack_title
365
+ self.pack_short_name = re.sub(
366
+ "[^0-9a-zA-Z]+", "_", pack_title
367
+ ) # name used in url, only alphanum and underscore only
368
+ return self.pack_short_name
369
+
370
+ async def set_upload_pack_type(self, is_emoji: bool) -> None:
371
+ self.is_emoji = is_emoji
372
+
373
+ async def check_pack_exist(self) -> bool:
374
+ try:
375
+ await self.client(
376
+ messages.GetStickerSetRequest(
377
+ InputStickerSetShortName(self.pack_short_name), 0
378
+ )
379
+ )
380
+ except StickersetInvalidError:
381
+ return False
382
+
383
+ return True
384
+
385
+ async def _send_and_recv(self, msg: Union[str, Path]) -> str:
386
+ if isinstance(msg, str):
387
+ sent_message = await self.client.send_message("Stickers", msg)
388
+ else:
389
+ sent_message = cast(Message, await self.client.send_file("Stickers", msg)) # type: ignore
390
+
391
+ for _ in range(5):
392
+ # https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
393
+ # In a single chat, avoid sending more than one message per second.
394
+ time.sleep(1)
395
+ last_message = cast(
396
+ List[Message],
397
+ await self.client.get_messages("Stickers", 1), # type: ignore
398
+ )[0]
399
+ if sent_message.id != last_message.id:
400
+ return last_message.message
401
+
402
+ return "timeout"
403
+
404
+ async def pack_del(self) -> bool:
405
+ msg_fail = "Cannot delete pack of {} due to {}"
406
+ if self.is_emoji:
407
+ repl = await self._send_and_recv("/delemoji")
408
+ else:
409
+ repl = await self._send_and_recv("/delpack")
410
+ if repl != "Choose the sticker set you want to delete.":
411
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
412
+ return False
413
+ repl = await self._send_and_recv(self.pack_short_name)
414
+ if "Yes, I am totally sure." not in repl:
415
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
416
+ return False
417
+ repl = await self._send_and_recv("Yes, I am totally sure.")
418
+ if "Done!" not in repl:
419
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
420
+ return False
421
+
422
+ return True
423
+
424
+ async def pack_new(
425
+ self, stickers_list: List[TelegramSticker], sticker_type: str
426
+ ) -> Tuple[int, int]:
427
+ stickers_ok = 0
428
+ if self.is_emoji:
429
+ repl = await self._send_and_recv("/newemojipack")
430
+ elif stickers_list[0][3] == "static":
431
+ repl = await self._send_and_recv("/newsticker")
432
+ elif stickers_list[0][3] == "video":
433
+ repl = await self._send_and_recv("/newvideo")
434
+ elif stickers_list[0][3] == "animated":
435
+ repl = await self._send_and_recv("/newanimated")
436
+ else:
437
+ self.cb.put(
438
+ f"Cannot upload any sticker to {self.pack_short_name} due to invalid sticker format {stickers_list[0][3]}"
439
+ )
440
+ return len(stickers_list), 0
441
+ if "Yay!" not in repl:
442
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
443
+ return len(stickers_list), 0
444
+
445
+ if self.is_emoji:
446
+ repl = await self._send_and_recv(
447
+ f"{stickers_list[0][3].capitalize()} emoji"
448
+ )
449
+ if "Yay!" not in repl:
450
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
451
+ return len(stickers_list), 0
452
+
453
+ repl = await self._send_and_recv(self.pack_title)
454
+ if "Alright!" not in repl:
455
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
456
+ return len(stickers_list), 0
457
+ self.cb.put(
458
+ (
459
+ "bar",
460
+ None,
461
+ {
462
+ "set_progress_mode": "determinate",
463
+ "steps": len(stickers_list),
464
+ },
465
+ )
466
+ )
467
+ for i in stickers_list:
468
+ repl = await self._send_and_recv(i[0])
469
+ if "Thanks!" not in repl:
470
+ self.cb.put(
471
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
472
+ )
473
+ self.cb.put("update_bar")
474
+ continue
475
+ repl = await self._send_and_recv("".join(i[2]))
476
+ if "Congratulations." not in repl:
477
+ self.cb.put(
478
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
479
+ )
480
+ self.cb.put("update_bar")
481
+ continue
482
+ stickers_ok += 1
483
+ self.cb.put("update_bar")
484
+ repl = await self._send_and_recv("/publish")
485
+ if "icon" not in repl:
486
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
487
+ return len(stickers_list), 0
488
+ repl = await self._send_and_recv("/skip")
489
+ if "Please provide a short name" not in repl:
490
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
491
+ return len(stickers_list), 0
492
+ repl = await self._send_and_recv(self.pack_short_name)
493
+ if "Kaboom!" not in repl:
494
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
495
+ return len(stickers_list), 0
496
+
497
+ self.cb.put(("bar", None, {"set_progress_mode": "indeterminate"}))
498
+
499
+ return len(stickers_list), stickers_ok
500
+
501
+ async def pack_add(
502
+ self, stickers_list: List[TelegramSticker], sticker_type: str
503
+ ) -> Tuple[int, int]:
504
+ stickers_ok = 0
505
+ if self.is_emoji:
506
+ repl = await self._send_and_recv("/addemoji")
507
+ else:
508
+ repl = await self._send_and_recv("/addsticker")
509
+ if "Choose" not in repl:
510
+ self.cb.put(
511
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
512
+ )
513
+ return len(stickers_list), 0
514
+ repl = await self._send_and_recv(self.pack_short_name)
515
+ if "Alright!" not in repl:
516
+ self.cb.put(
517
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
518
+ )
519
+ return len(stickers_list), 0
520
+
521
+ self.cb.put(
522
+ (
523
+ "bar",
524
+ None,
525
+ {
526
+ "set_progress_mode": "determinate",
527
+ "steps": len(stickers_list),
528
+ },
529
+ )
530
+ )
531
+ for i in stickers_list:
532
+ repl = await self._send_and_recv(i[0])
533
+ if "Thanks!" not in repl:
534
+ self.cb.put(
535
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
536
+ )
537
+ self.cb.put("update_bar")
538
+ continue
539
+ repl = await self._send_and_recv("".join(i[2]))
540
+ if "There we go." not in repl:
541
+ self.cb.put(
542
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
543
+ )
544
+ self.cb.put("update_bar")
545
+ continue
546
+ self.cb.put("update_bar")
547
+ stickers_ok += 1
548
+
549
+ self.cb.put(("bar", None, {"set_progress_mode": "indeterminate"}))
550
+
551
+ repl = await self._send_and_recv("/done")
552
+ if "OK" not in repl:
553
+ self.cb.put(
554
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
555
+ )
556
+ return len(stickers_list), 0
557
+
558
+ return len(stickers_list), stickers_ok
559
+
560
+ async def pack_thumbnail(self, thumbnail: TelegramSticker) -> bool:
561
+ repl = await self._send_and_recv("/setpackicon")
562
+ if "OK" not in repl:
563
+ self.cb.put(
564
+ f"Cannot set pack icon for {self.pack_short_name} due to {repl}"
565
+ )
566
+ return False
567
+ repl = await self._send_and_recv(thumbnail[0])
568
+ if "Enjoy!" not in repl:
569
+ self.cb.put(
570
+ f"Cannot set pack icon for {self.pack_short_name} due to {repl}"
571
+ )
572
+ return False
573
+ return True
574
+
575
+ async def get_pack_url(self) -> str:
576
+ if self.is_emoji:
577
+ return f"https://t.me/addemoji/{self.pack_short_name}"
578
+ else:
579
+ return f"https://t.me/addstickers/{self.pack_short_name}"
580
+
581
+ async def _download_sticker(
582
+ self,
583
+ sticker: TypeDocument,
584
+ f_id: str,
585
+ out_dir: Path,
586
+ id_to_emoji: Dict[int, str],
587
+ emoji_dict: Dict[str, str],
588
+ results: Dict[str, bool],
589
+ ) -> None:
590
+ fpath_attr = [
591
+ attr
592
+ for attr in sticker.attributes # type: ignore
593
+ if isinstance(attr, DocumentAttributeFilename)
594
+ ]
595
+ assert len(fpath_attr) > 0
596
+ fpath = fpath_attr[0].file_name
597
+ ext = Path(fpath).suffix
598
+ f_name = f_id + ext
599
+ f_path = Path(out_dir, f_name)
600
+
601
+ try:
602
+ await self.client.download_media(sticker, file=f_path) # type: ignore
603
+ except Exception as e:
604
+ self.cb.put(f"Failed to download {f_id}: {str(e)}")
605
+ results[f_id] = False
606
+ return
607
+
608
+ emoji_dict[f_id] = id_to_emoji[sticker.id]
609
+ self.cb.put(f"Downloaded {f_name}")
610
+ results[f_id] = True
611
+ self.cb.put("update_bar")
612
+
613
+ async def pack_dl(
614
+ self, pack_short_name: str, out_dir: Path
615
+ ) -> Tuple[Dict[str, bool], Dict[str, str]]:
616
+ results: Dict[str, bool] = {}
617
+ emoji_dict: Dict[str, str] = {}
618
+ id_to_emoji: Dict[int, str] = defaultdict(str)
619
+
620
+ sticker_set = cast(
621
+ TLStickerSet,
622
+ await self.client(
623
+ messages.GetStickerSetRequest(
624
+ InputStickerSetShortName(pack_short_name), 0
625
+ )
626
+ ),
627
+ )
628
+
629
+ self.cb.put(
630
+ (
631
+ "bar",
632
+ None,
633
+ {
634
+ "set_progress_mode": "determinate",
635
+ "steps": len(sticker_set.documents),
636
+ },
637
+ )
638
+ )
639
+
640
+ for pack in sticker_set.packs:
641
+ for document_id in pack.documents:
642
+ id_to_emoji[document_id] += pack.emoticon
643
+
644
+ ext = ""
645
+ async with anyio.create_task_group() as tg:
646
+ for num, sticker in enumerate(sticker_set.documents):
647
+ f_id = str(num).zfill(3)
648
+ tg.start_soon(
649
+ self._download_sticker,
650
+ sticker,
651
+ f_id,
652
+ out_dir,
653
+ id_to_emoji,
654
+ emoji_dict,
655
+ results,
656
+ )
657
+
658
+ if sticker_set.set.thumb_version and ext:
659
+ try:
660
+ await self.client.download_file( # type: ignore
661
+ InputStickerSetThumb(
662
+ InputStickerSetShortName(pack_short_name),
663
+ thumb_version=sticker_set.set.thumb_version,
664
+ ),
665
+ f"cover{ext}",
666
+ )
667
+ except Exception as e:
668
+ self.cb.put(f"Failed to download cover{ext}: {str(e)}")
669
+
670
+ return results, emoji_dict