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

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,668 @@
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(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
+ last_message = cast(
393
+ List[Message],
394
+ await self.client.get_messages("Stickers", 1), # type: ignore
395
+ )[0]
396
+ if sent_message.id != last_message.id:
397
+ return last_message.message
398
+ time.sleep(1)
399
+
400
+ return "timeout"
401
+
402
+ async def pack_del(self) -> bool:
403
+ msg_fail = "Cannot delete pack of {} due to {}"
404
+ if self.is_emoji:
405
+ repl = await self._send_and_recv("/delemoji")
406
+ else:
407
+ repl = await self._send_and_recv("/delpack")
408
+ if repl != "Choose the sticker set you want to delete.":
409
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
410
+ return False
411
+ repl = await self._send_and_recv(self.pack_short_name)
412
+ if "Yes, I am totally sure." not in repl:
413
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
414
+ return False
415
+ repl = await self._send_and_recv("Yes, I am totally sure.")
416
+ if "Done!" not in repl:
417
+ self.cb.put(msg_fail.format(self.pack_short_name, repl))
418
+ return False
419
+
420
+ return True
421
+
422
+ async def pack_new(
423
+ self, stickers_list: List[TelegramSticker], sticker_type: str
424
+ ) -> Tuple[int, int]:
425
+ stickers_ok = 0
426
+ if self.is_emoji:
427
+ repl = await self._send_and_recv("/newemojipack")
428
+ elif stickers_list[0][3] == "static":
429
+ repl = await self._send_and_recv("/newsticker")
430
+ elif stickers_list[0][3] == "video":
431
+ repl = await self._send_and_recv("/newvideo")
432
+ elif stickers_list[0][3] == "animated":
433
+ repl = await self._send_and_recv("/newanimated")
434
+ else:
435
+ self.cb.put(
436
+ f"Cannot upload any sticker to {self.pack_short_name} due to invalid sticker format {stickers_list[0][3]}"
437
+ )
438
+ return len(stickers_list), 0
439
+ if "Yay!" not in repl:
440
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
441
+ return len(stickers_list), 0
442
+
443
+ if self.is_emoji:
444
+ repl = await self._send_and_recv(
445
+ f"{stickers_list[0][3].capitalize()} emoji"
446
+ )
447
+ if "Yay!" not in repl:
448
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
449
+ return len(stickers_list), 0
450
+
451
+ repl = await self._send_and_recv(self.pack_title)
452
+ if "Alright!" not in repl:
453
+ self.cb.put(f"Cannot upload any sticker due to {repl}")
454
+ return len(stickers_list), 0
455
+ self.cb.put(
456
+ (
457
+ "bar",
458
+ None,
459
+ {
460
+ "set_progress_mode": "determinate",
461
+ "steps": len(stickers_list),
462
+ },
463
+ )
464
+ )
465
+ for i in stickers_list:
466
+ repl = await self._send_and_recv(i[0])
467
+ if "Thanks!" not in repl:
468
+ self.cb.put(
469
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
470
+ )
471
+ self.cb.put("update_bar")
472
+ continue
473
+ repl = await self._send_and_recv("".join(i[2]))
474
+ if "Congratulations." not in repl:
475
+ self.cb.put(
476
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
477
+ )
478
+ self.cb.put("update_bar")
479
+ continue
480
+ stickers_ok += 1
481
+ self.cb.put("update_bar")
482
+ repl = await self._send_and_recv("/publish")
483
+ if "icon" not in repl:
484
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
485
+ return len(stickers_list), 0
486
+ repl = await self._send_and_recv("/skip")
487
+ if "Please provide a short name" not in repl:
488
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
489
+ return len(stickers_list), 0
490
+ repl = await self._send_and_recv(self.pack_short_name)
491
+ if "Kaboom!" not in repl:
492
+ self.cb.put(f"Cannot upload pack {self.pack_short_name} due to {repl}")
493
+ return len(stickers_list), 0
494
+
495
+ self.cb.put(("bar", None, {"set_progress_mode": "indeterminate"}))
496
+
497
+ return len(stickers_list), stickers_ok
498
+
499
+ async def pack_add(
500
+ self, stickers_list: List[TelegramSticker], sticker_type: str
501
+ ) -> Tuple[int, int]:
502
+ stickers_ok = 0
503
+ if self.is_emoji:
504
+ repl = await self._send_and_recv("/addemoji")
505
+ else:
506
+ repl = await self._send_and_recv("/addsticker")
507
+ if "Choose" not in repl:
508
+ self.cb.put(
509
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
510
+ )
511
+ return len(stickers_list), 0
512
+ repl = await self._send_and_recv(self.pack_short_name)
513
+ if "Alright!" not in repl:
514
+ self.cb.put(
515
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
516
+ )
517
+ return len(stickers_list), 0
518
+
519
+ self.cb.put(
520
+ (
521
+ "bar",
522
+ None,
523
+ {
524
+ "set_progress_mode": "determinate",
525
+ "steps": len(stickers_list),
526
+ },
527
+ )
528
+ )
529
+ for i in stickers_list:
530
+ repl = await self._send_and_recv(i[0])
531
+ if "Thanks!" not in repl:
532
+ self.cb.put(
533
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
534
+ )
535
+ self.cb.put("update_bar")
536
+ continue
537
+ repl = await self._send_and_recv("".join(i[2]))
538
+ if "There we go." not in repl:
539
+ self.cb.put(
540
+ f"Cannot upload sticker {i[0]} of {self.pack_short_name} due to {repl}"
541
+ )
542
+ self.cb.put("update_bar")
543
+ continue
544
+ self.cb.put("update_bar")
545
+ stickers_ok += 1
546
+
547
+ self.cb.put(("bar", None, {"set_progress_mode": "indeterminate"}))
548
+
549
+ repl = await self._send_and_recv("/done")
550
+ if "OK" not in repl:
551
+ self.cb.put(
552
+ f"Cannot upload any sticker to {self.pack_short_name} due to {repl}"
553
+ )
554
+ return len(stickers_list), 0
555
+
556
+ return len(stickers_list), stickers_ok
557
+
558
+ async def pack_thumbnail(self, thumbnail: TelegramSticker) -> bool:
559
+ repl = await self._send_and_recv("/setpackicon")
560
+ if "OK" not in repl:
561
+ self.cb.put(
562
+ f"Cannot set pack icon for {self.pack_short_name} due to {repl}"
563
+ )
564
+ return False
565
+ repl = await self._send_and_recv(thumbnail[0])
566
+ if "Enjoy!" not in repl:
567
+ self.cb.put(
568
+ f"Cannot set pack icon for {self.pack_short_name} due to {repl}"
569
+ )
570
+ return False
571
+ return True
572
+
573
+ async def get_pack_url(self) -> str:
574
+ if self.is_emoji:
575
+ return f"https://t.me/addemoji/{self.pack_short_name}"
576
+ else:
577
+ return f"https://t.me/addstickers/{self.pack_short_name}"
578
+
579
+ async def _download_sticker(
580
+ self,
581
+ sticker: TypeDocument,
582
+ f_id: str,
583
+ out_dir: Path,
584
+ id_to_emoji: Dict[int, str],
585
+ emoji_dict: Dict[str, str],
586
+ results: Dict[str, bool],
587
+ ) -> None:
588
+ fpath_attr = [
589
+ attr
590
+ for attr in sticker.attributes # type: ignore
591
+ if isinstance(attr, DocumentAttributeFilename)
592
+ ]
593
+ assert len(fpath_attr) > 0
594
+ fpath = fpath_attr[0].file_name
595
+ ext = Path(fpath).suffix
596
+ f_name = f_id + ext
597
+ f_path = Path(out_dir, f_name)
598
+
599
+ try:
600
+ await self.client.download_media(sticker, file=f_path) # type: ignore
601
+ except Exception as e:
602
+ self.cb.put(f"Failed to download {f_id}: {str(e)}")
603
+ results[f_id] = False
604
+ return
605
+
606
+ emoji_dict[f_id] = id_to_emoji[sticker.id]
607
+ self.cb.put(f"Downloaded {f_name}")
608
+ results[f_id] = True
609
+ self.cb.put("update_bar")
610
+
611
+ async def pack_dl(
612
+ self, pack_short_name: str, out_dir: Path
613
+ ) -> Tuple[Dict[str, bool], Dict[str, str]]:
614
+ results: Dict[str, bool] = {}
615
+ emoji_dict: Dict[str, str] = {}
616
+ id_to_emoji: Dict[int, str] = defaultdict(str)
617
+
618
+ sticker_set = cast(
619
+ TLStickerSet,
620
+ await self.client(
621
+ messages.GetStickerSetRequest(
622
+ InputStickerSetShortName(pack_short_name), 0
623
+ )
624
+ ),
625
+ )
626
+
627
+ self.cb.put(
628
+ (
629
+ "bar",
630
+ None,
631
+ {
632
+ "set_progress_mode": "determinate",
633
+ "steps": len(sticker_set.documents),
634
+ },
635
+ )
636
+ )
637
+
638
+ for pack in sticker_set.packs:
639
+ for document_id in pack.documents:
640
+ id_to_emoji[document_id] += pack.emoticon
641
+
642
+ ext = ""
643
+ async with anyio.create_task_group() as tg:
644
+ for num, sticker in enumerate(sticker_set.documents):
645
+ f_id = str(num).zfill(3)
646
+ tg.start_soon(
647
+ self._download_sticker,
648
+ sticker,
649
+ f_id,
650
+ out_dir,
651
+ id_to_emoji,
652
+ emoji_dict,
653
+ results,
654
+ )
655
+
656
+ if sticker_set.set.thumb_version and ext:
657
+ try:
658
+ await self.client.download_file( # type: ignore
659
+ InputStickerSetThumb(
660
+ InputStickerSetShortName(pack_short_name),
661
+ thumb_version=sticker_set.set.thumb_version,
662
+ ),
663
+ f"cover{ext}",
664
+ )
665
+ except Exception as e:
666
+ self.cb.put(f"Failed to download cover{ext}: {str(e)}")
667
+
668
+ return results, emoji_dict