sticker-convert 2.10.8__py3-none-any.whl → 2.11.0__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.
Files changed (32) hide show
  1. sticker_convert/cli.py +39 -1
  2. sticker_convert/converter.py +10 -6
  3. sticker_convert/downloaders/download_base.py +37 -17
  4. sticker_convert/downloaders/download_discord.py +6 -6
  5. sticker_convert/downloaders/download_kakao.py +31 -13
  6. sticker_convert/downloaders/download_line.py +6 -6
  7. sticker_convert/downloaders/download_signal.py +10 -8
  8. sticker_convert/downloaders/download_telegram.py +22 -96
  9. sticker_convert/downloaders/download_viber.py +8 -6
  10. sticker_convert/gui.py +12 -0
  11. sticker_convert/gui_components/frames/cred_frame.py +38 -13
  12. sticker_convert/job.py +84 -63
  13. sticker_convert/job_option.py +6 -0
  14. sticker_convert/resources/compression.json +2 -2
  15. sticker_convert/resources/help.json +3 -2
  16. sticker_convert/resources/input.json +10 -0
  17. sticker_convert/resources/output.json +16 -0
  18. sticker_convert/uploaders/compress_wastickers.py +8 -6
  19. sticker_convert/uploaders/upload_signal.py +10 -6
  20. sticker_convert/uploaders/upload_telegram.py +178 -231
  21. sticker_convert/uploaders/upload_viber.py +12 -8
  22. sticker_convert/uploaders/xcode_imessage.py +8 -6
  23. sticker_convert/utils/auth/telegram_api.py +668 -0
  24. sticker_convert/utils/auth/telethon_setup.py +79 -0
  25. sticker_convert/utils/url_detect.py +1 -1
  26. sticker_convert/version.py +1 -1
  27. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/METADATA +54 -36
  28. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/RECORD +32 -30
  29. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/LICENSE +0 -0
  30. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/WHEEL +0 -0
  31. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/entry_points.txt +0 -0
  32. {sticker_convert-2.10.8.dist-info → sticker_convert-2.11.0.dist-info}/top_level.txt +0 -0
@@ -44,7 +44,7 @@ class DownloadViber(DownloadBase):
44
44
 
45
45
  return title, zip_url
46
46
 
47
- def decompress(self, zip_file: bytes) -> None:
47
+ def decompress(self, zip_file: bytes) -> int:
48
48
  with zipfile.ZipFile(BytesIO(zip_file)) as zf:
49
49
  self.cb.put("Unzipping...")
50
50
 
@@ -69,19 +69,21 @@ class DownloadViber(DownloadBase):
69
69
 
70
70
  self.cb.put("update_bar")
71
71
 
72
- def download_stickers_viber(self) -> bool:
72
+ return len(zf_files)
73
+
74
+ def download_stickers_viber(self) -> Tuple[int, int]:
73
75
  pack_info = self.get_pack_info(self.url)
74
76
  if pack_info is None:
75
77
  self.cb.put("Download failed: Cannot get pack info")
76
- return False
78
+ return 0, 0
77
79
  title, zip_url = pack_info
78
80
 
79
81
  zip_file = self.download_file(zip_url)
80
- self.decompress(zip_file)
82
+ count = self.decompress(zip_file)
81
83
 
82
84
  MetadataHandler.set_metadata(self.out_dir, title=title)
83
85
 
84
- return True
86
+ return count, count
85
87
 
86
88
  @staticmethod
87
89
  def start(
@@ -89,6 +91,6 @@ class DownloadViber(DownloadBase):
89
91
  opt_cred: Optional[CredOption],
90
92
  cb: CallbackProtocol,
91
93
  cb_return: CallbackReturn,
92
- ) -> bool:
94
+ ) -> Tuple[int, int]:
93
95
  downloader = DownloadViber(opt_input, opt_cred, cb, cb_return)
94
96
  return downloader.download_stickers_viber()
sticker_convert/gui.py CHANGED
@@ -154,6 +154,8 @@ class GUI(Window):
154
154
  self.signal_password_var = StringVar(self)
155
155
  self.telegram_token_var = StringVar(self)
156
156
  self.telegram_userid_var = StringVar(self)
157
+ self.telethon_api_id_var = IntVar(self)
158
+ self.telethon_api_hash_var = StringVar(self)
157
159
  self.kakao_auth_token_var = StringVar(self)
158
160
  self.kakao_username_var = StringVar(self)
159
161
  self.kakao_password_var = StringVar(self)
@@ -406,6 +408,10 @@ class GUI(Window):
406
408
  self.signal_password_var.set(self.creds.get("signal", {}).get("password", ""))
407
409
  self.telegram_token_var.set(self.creds.get("telegram", {}).get("token", ""))
408
410
  self.telegram_userid_var.set(self.creds.get("telegram", {}).get("userid", ""))
411
+ self.telethon_api_id_var.set(self.creds.get("telethon", {}).get("api_id", 0))
412
+ self.telethon_api_hash_var.set(
413
+ self.creds.get("telethon", {}).get("api_hash", "")
414
+ )
409
415
  self.kakao_auth_token_var.set(self.creds.get("kakao", {}).get("auth_token", ""))
410
416
  self.kakao_username_var.set(self.creds.get("kakao", {}).get("username", ""))
411
417
  self.kakao_password_var.set(self.creds.get("kakao", {}).get("password", ""))
@@ -451,6 +457,10 @@ class GUI(Window):
451
457
  selection = self.comp_preset_var.get()
452
458
  if selection == "auto":
453
459
  output_option = self.get_output_name()
460
+ if "telegram_emoji" in output_option:
461
+ return "telegram_emoji"
462
+ if "telegram" in output_option:
463
+ return "telegram"
454
464
  if output_option == "imessage":
455
465
  return "imessage_small"
456
466
  if output_option == "local":
@@ -572,6 +582,8 @@ class GUI(Window):
572
582
  signal_password=self.signal_password_var.get(),
573
583
  telegram_token=self.telegram_token_var.get(),
574
584
  telegram_userid=self.telegram_userid_var.get(),
585
+ telethon_api_id=self.telethon_api_id_var.get(),
586
+ telethon_api_hash=self.telethon_api_hash_var.get(),
575
587
  kakao_auth_token=self.kakao_auth_token_var.get(),
576
588
  kakao_username=self.kakao_username_var.get(),
577
589
  kakao_password=self.kakao_password_var.get(),
@@ -10,6 +10,7 @@ from sticker_convert.gui_components.windows.kakao_get_auth_window import KakaoGe
10
10
  from sticker_convert.gui_components.windows.line_get_auth_window import LineGetAuthWindow
11
11
  from sticker_convert.gui_components.windows.signal_get_auth_window import SignalGetAuthWindow
12
12
  from sticker_convert.gui_components.windows.viber_get_auth_window import ViberGetAuthWindow
13
+ from sticker_convert.utils.auth.telethon_setup import TelethonSetup
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from sticker_convert.gui import GUI # type: ignore
@@ -77,6 +78,16 @@ class CredFrame(LabelFrame):
77
78
  )
78
79
  self.telegram_userid_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
79
80
 
81
+ self.telethon_auth_lbl = Label(
82
+ self, text="Telethon authorization", justify="left", anchor="w"
83
+ )
84
+ self.telethon_auth_btn = Button(
85
+ self,
86
+ text="Generate",
87
+ command=self.cb_telethon_get_auth,
88
+ bootstyle="secondary", # type: ignore
89
+ )
90
+
80
91
  self.kakao_auth_token_lbl = Label(
81
92
  self, text="Kakao auth_token", justify="left", anchor="w"
82
93
  )
@@ -157,19 +168,21 @@ class CredFrame(LabelFrame):
157
168
  self.telegram_userid_entry.grid(
158
169
  column=1, row=4, columnspan=2, sticky="w", padx=3, pady=3
159
170
  )
160
- self.kakao_auth_token_lbl.grid(column=0, row=5, sticky="w", padx=3, pady=3)
161
- self.kakao_auth_token_entry.grid(column=1, row=5, sticky="w", padx=3, pady=3)
162
- self.kakao_get_auth_btn.grid(column=2, row=5, sticky="e", padx=3, pady=3)
163
- self.line_cookies_lbl.grid(column=0, row=6, sticky="w", padx=3, pady=3)
164
- self.line_cookies_entry.grid(column=1, row=6, sticky="w", padx=3, pady=3)
165
- self.line_get_auth_btn.grid(column=2, row=6, sticky="e", padx=3, pady=3)
166
- self.viber_auth_lbl.grid(column=0, row=7, sticky="w", padx=3, pady=3)
167
- self.viber_auth_entry.grid(column=1, row=7, sticky="w", padx=3, pady=3)
168
- self.viber_get_auth_btn.grid(column=2, row=7, sticky="e", padx=3, pady=3)
169
- self.discord_token_lbl.grid(column=0, row=8, sticky="w", padx=3, pady=3)
170
- self.discord_token_entry.grid(column=1, row=8, sticky="w", padx=3, pady=3)
171
- self.discord_get_auth_btn.grid(column=2, row=8, sticky="e", padx=3, pady=3)
172
- self.help_btn.grid(column=2, row=9, sticky="e", padx=3, pady=3)
171
+ self.telethon_auth_lbl.grid(column=0, row=5, sticky="w", padx=3, pady=3)
172
+ self.telethon_auth_btn.grid(column=2, row=5, sticky="e", padx=3, pady=3)
173
+ self.kakao_auth_token_lbl.grid(column=0, row=6, sticky="w", padx=3, pady=3)
174
+ self.kakao_auth_token_entry.grid(column=1, row=6, sticky="w", padx=3, pady=3)
175
+ self.kakao_get_auth_btn.grid(column=2, row=6, sticky="e", padx=3, pady=3)
176
+ self.line_cookies_lbl.grid(column=0, row=7, sticky="w", padx=3, pady=3)
177
+ self.line_cookies_entry.grid(column=1, row=7, sticky="w", padx=3, pady=3)
178
+ self.line_get_auth_btn.grid(column=2, row=7, sticky="e", padx=3, pady=3)
179
+ self.viber_auth_lbl.grid(column=0, row=8, sticky="w", padx=3, pady=3)
180
+ self.viber_auth_entry.grid(column=1, row=8, sticky="w", padx=3, pady=3)
181
+ self.viber_get_auth_btn.grid(column=2, row=8, sticky="e", padx=3, pady=3)
182
+ self.discord_token_lbl.grid(column=0, row=9, sticky="w", padx=3, pady=3)
183
+ self.discord_token_entry.grid(column=1, row=9, sticky="w", padx=3, pady=3)
184
+ self.discord_get_auth_btn.grid(column=2, row=9, sticky="e", padx=3, pady=3)
185
+ self.help_btn.grid(column=2, row=10, sticky="e", padx=3, pady=3)
173
186
 
174
187
  def cb_cred_help(self, *_: Any) -> None:
175
188
  faq_site = "https://github.com/laggykiller/sticker-convert#faq"
@@ -177,6 +190,18 @@ class CredFrame(LabelFrame):
177
190
  if not success:
178
191
  self.gui.cb_ask_str("You can get help from:", initialvalue=faq_site)
179
192
 
193
+ def cb_telethon_get_auth(self, *_: Any) -> None:
194
+ success, _client, api_id, api_hash = TelethonSetup(
195
+ self.gui.get_opt_cred(), self.gui.cb_ask_str
196
+ ).start()
197
+ if success:
198
+ self.gui.telethon_api_id_var.set(api_id)
199
+ self.gui.telethon_api_hash_var.set(api_hash)
200
+ self.gui.save_creds()
201
+ self.gui.cb_msg_block("Telethon setup successful")
202
+ else:
203
+ self.gui.cb_msg_block("Telethon setup failed")
204
+
180
205
  def cb_kakao_get_auth(self, *_: Any) -> None:
181
206
  KakaoGetAuthWindow(self.gui)
182
207
 
sticker_convert/job.py CHANGED
@@ -216,7 +216,6 @@ class Job:
216
216
  self.cb_ask_bool = cb_ask_bool
217
217
  self.cb_ask_str = cb_ask_str
218
218
 
219
- self.compress_fails: List[str] = []
220
219
  self.out_urls: List[str] = []
221
220
 
222
221
  self.executor = Executor(
@@ -236,19 +235,21 @@ class Job:
236
235
 
237
236
  self.executor.cb("msg", kwargs={"cls": True})
238
237
 
239
- tasks = (
238
+ tasks: Tuple[Callable[..., Tuple[bool, Optional[str]]], ...] = (
240
239
  self.verify_input,
241
240
  self.cleanup,
242
241
  self.download,
243
242
  self.compress,
244
243
  self.export,
245
- self.report,
246
244
  )
247
245
 
248
246
  code = 0
247
+ summaries: List[str] = []
249
248
  for task in tasks:
250
249
  self.executor.cb("bar", kwargs={"set_progress_mode": "indeterminate"})
251
- success = task()
250
+ success, summary = task()
251
+ if summary is not None:
252
+ summaries.append(summary)
252
253
 
253
254
  if self.executor.is_cancel_job.value == 1: # type: ignore
254
255
  code = 2
@@ -258,6 +259,22 @@ class Job:
258
259
  self.executor.cb("An error occured during this run.")
259
260
  break
260
261
 
262
+ msg = "##########\n"
263
+ msg += "Summary:\n"
264
+ msg += "##########\n"
265
+
266
+ msg += "\n"
267
+ msg += "\n".join(summaries)
268
+ msg += "\n"
269
+
270
+ if self.out_urls:
271
+ msg += "Export results:\n"
272
+ msg += "\n".join(self.out_urls)
273
+ else:
274
+ msg += "Export result: None"
275
+
276
+ self.executor.cb(msg)
277
+
261
278
  self.executor.cleanup()
262
279
 
263
280
  return code
@@ -265,7 +282,7 @@ class Job:
265
282
  def cancel(self, *_: Any, **_kwargs: Any) -> None:
266
283
  self.executor.kill_workers()
267
284
 
268
- def verify_input(self) -> bool:
285
+ def verify_input(self) -> Tuple[bool, None]:
269
286
  info_msg = ""
270
287
  error_msg = ""
271
288
 
@@ -352,7 +369,7 @@ class Job:
352
369
 
353
370
  if error_msg != "":
354
371
  self.executor.cb(error_msg)
355
- return False
372
+ return False, None
356
373
 
357
374
  # Check if preset not equal to export option
358
375
  # Only warn if the compression option is available in export preset
@@ -362,7 +379,10 @@ class Job:
362
379
  not self.opt_comp.no_compress
363
380
  and self.opt_output.option != "local"
364
381
  and self.opt_comp.preset != "custom"
365
- and self.opt_output.option not in self.opt_comp.preset
382
+ and (
383
+ self.opt_output.option not in self.opt_comp.preset
384
+ and self.opt_comp.preset not in self.opt_output.option
385
+ )
366
386
  ):
367
387
  msg = "Compression preset does not match export option\n"
368
388
  msg += "You may continue, but the files will need to be compressed again before export\n"
@@ -372,7 +392,7 @@ class Job:
372
392
  response = self.executor.cb_return.get_response()
373
393
 
374
394
  if response is False:
375
- return False
395
+ return False, None
376
396
 
377
397
  for param, value in (
378
398
  ("fps_power", self.opt_comp.fps_power),
@@ -431,7 +451,7 @@ class Job:
431
451
  response = self.executor.cb_return.get_response()
432
452
 
433
453
  if response is False:
434
- return False
454
+ return False, None
435
455
 
436
456
  # Warn about in/output directories that might contain other files
437
457
  # Directory is safe if the name is stickers_input/stickers_output, or
@@ -468,13 +488,13 @@ class Job:
468
488
  response = self.executor.cb_return.get_response()
469
489
 
470
490
  if response is False:
471
- return False
491
+ return False, None
472
492
 
473
493
  break
474
494
 
475
- return True
495
+ return True, None
476
496
 
477
- def cleanup(self) -> bool:
497
+ def cleanup(self) -> Tuple[bool, None]:
478
498
  # If input is 'From local directory', then we should keep files in input/output directory as it maybe edited by user
479
499
  # If input is not 'From local directory', then we should move files in input/output directory as new files will be downloaded
480
500
  # Output directory should be cleanup unless no_compress is true (meaning files in output directory might be edited by user)
@@ -523,10 +543,10 @@ class Job:
523
543
  new_path = Path(archive_dir, old_path.name)
524
544
  old_path.rename(new_path)
525
545
 
526
- return True
546
+ return True, None
527
547
 
528
- def download(self) -> bool:
529
- downloaders: List[Callable[..., bool]] = []
548
+ def download(self) -> Tuple[bool, str]:
549
+ downloaders: List[Callable[..., Tuple[int, int]]] = []
530
550
 
531
551
  if self.opt_input.option == "signal":
532
552
  downloaders.append(DownloadSignal.start)
@@ -534,7 +554,7 @@ class Job:
534
554
  if self.opt_input.option == "line":
535
555
  downloaders.append(DownloadLine.start)
536
556
 
537
- if self.opt_input.option == "telegram":
557
+ if self.opt_input.option.startswith("telegram"):
538
558
  downloaders.append(DownloadTelegram.start)
539
559
 
540
560
  if self.opt_input.option == "kakao":
@@ -549,8 +569,8 @@ class Job:
549
569
  if len(downloaders) > 0:
550
570
  self.executor.cb("Downloading...")
551
571
  else:
552
- self.executor.cb("Nothing to download")
553
- return True
572
+ self.executor.cb("Skipped download (No files to download)")
573
+ return True, "Download: Skipped (No files to download)"
554
574
 
555
575
  self.executor.start_workers(processes=1)
556
576
 
@@ -563,15 +583,24 @@ class Job:
563
583
  self.executor.join_workers()
564
584
 
565
585
  # Return False if any of the job returns failure
586
+ stickers_ok = 0
587
+ stickers_total = 0
588
+ success = True
566
589
  for result in self.executor.results_list:
567
- if result is False:
568
- return False
590
+ stickers_ok += result[0]
591
+ stickers_total += result[1]
592
+ success = (
593
+ success if stickers_ok == stickers_total and stickers_ok > 0 else False
594
+ )
569
595
 
570
- return True
596
+ return (
597
+ success,
598
+ f"Download: {stickers_ok}/{stickers_total} stickers success",
599
+ )
571
600
 
572
- def compress(self) -> bool:
601
+ def compress(self) -> Tuple[bool, str]:
573
602
  if self.opt_comp.no_compress is True:
574
- self.executor.cb("no_compress is set to True, skip compression")
603
+ self.executor.cb("Skipped compression (no_compress is set to True)")
575
604
  in_dir_files = [
576
605
  i
577
606
  for i in sorted(self.opt_input.dir.iterdir())
@@ -598,7 +627,7 @@ class Job:
598
627
  src_f = Path(self.opt_input.dir, i.name)
599
628
  dst_f = Path(self.opt_output.dir, i.name)
600
629
  shutil.copy(src_f, dst_f)
601
- return True
630
+ return True, "Compress: Skipped (no_compress is set to True)"
602
631
  msg = "Compressing..."
603
632
 
604
633
  input_dir = Path(self.opt_input.dir)
@@ -620,8 +649,8 @@ class Job:
620
649
 
621
650
  in_fs_count = len(in_fs)
622
651
  if in_fs_count == 0:
623
- self.executor.cb("No files to compress")
624
- return True
652
+ self.executor.cb("Skipped compression (No files to compress)")
653
+ return True, "Compress: Skipped (No files to compress)"
625
654
 
626
655
  self.executor.cb(msg)
627
656
  self.executor.cb(
@@ -640,21 +669,35 @@ class Job:
640
669
 
641
670
  self.executor.join_workers()
642
671
 
643
- # Return False if any of the job returns failure
672
+ success = True
673
+ stickers_ok = 0
674
+ stickers_total = 0
675
+ fails: List[str] = []
644
676
  for result in self.executor.results_list:
677
+ stickers_total += 1
645
678
  if result[0] is False:
646
- return False
679
+ success = False
680
+ fails.append(str(result[1]))
681
+ else:
682
+ stickers_ok += 1
647
683
 
648
- return True
684
+ msg_append = ""
685
+ if success is False:
686
+ msg_append = " (Failed: " + ", ".join(fails) + ")"
649
687
 
650
- def export(self) -> bool:
688
+ return (
689
+ success,
690
+ f"Compress: {stickers_ok}/{stickers_total} stickers success" + msg_append,
691
+ )
692
+
693
+ def export(self) -> Tuple[bool, str]:
651
694
  if self.opt_output.option == "local":
652
- self.executor.cb("Saving to local directory only, nothing to export")
653
- return True
695
+ self.executor.cb("Skipped export (Saving to local directory only)")
696
+ return True, "Export: Skipped (Saving to local directory only)"
654
697
 
655
698
  self.executor.cb("Exporting...")
656
699
 
657
- exporters: List[Callable[..., List[str]]] = []
700
+ exporters: List[Callable[..., Tuple[int, int, List[str]]]] = []
658
701
 
659
702
  if self.opt_output.option == "whatsapp":
660
703
  exporters.append(CompressWastickers.start)
@@ -662,10 +705,7 @@ class Job:
662
705
  if self.opt_output.option == "signal":
663
706
  exporters.append(UploadSignal.start)
664
707
 
665
- if self.opt_output.option == "telegram":
666
- exporters.append(UploadTelegram.start)
667
-
668
- if self.opt_output.option == "telegram_emoji":
708
+ if self.opt_output.option.startswith("telegram"):
669
709
  exporters.append(UploadTelegram.start)
670
710
 
671
711
  if self.opt_output.option == "imessage":
@@ -684,8 +724,12 @@ class Job:
684
724
 
685
725
  self.executor.join_workers()
686
726
 
727
+ stickers_ok = 0
728
+ stickers_total = 0
687
729
  for result in self.executor.results_list:
688
- self.out_urls.extend(result)
730
+ stickers_ok += result[0]
731
+ stickers_total += result[1]
732
+ self.out_urls.extend(result[2])
689
733
 
690
734
  if self.out_urls:
691
735
  with open(
@@ -694,29 +738,6 @@ class Job:
694
738
  f.write("\n".join(self.out_urls))
695
739
  else:
696
740
  self.executor.cb("An error occured while exporting stickers")
697
- return False
698
-
699
- return True
700
-
701
- def report(self) -> bool:
702
- msg = "##########\n"
703
- msg += "Summary:\n"
704
- msg += "##########\n"
705
- msg += "\n"
706
-
707
- if self.compress_fails:
708
- msg += f'Warning: Could not compress the following {len(self.compress_fails)} file{"s" if len(self.compress_fails) > 1 else ""}:\n'
709
- msg += "\n".join(self.compress_fails)
710
- msg += "\n"
711
- msg += "\nConsider adjusting compression parameters"
712
- msg += "\n"
713
-
714
- if self.out_urls:
715
- msg += "Export results:\n"
716
- msg += "\n".join(self.out_urls)
717
- else:
718
- msg += "Export result: None"
719
-
720
- self.executor.cb(msg)
741
+ return False, f"Export: {stickers_ok}/{stickers_total} stickers success"
721
742
 
722
- return True
743
+ return True, f"Export: {stickers_ok}/{stickers_total} stickers success"
@@ -214,6 +214,8 @@ class CredOption(BaseOption):
214
214
  signal_password: str = ""
215
215
  telegram_token: str = ""
216
216
  telegram_userid: str = ""
217
+ telethon_api_id: int = 0
218
+ telethon_api_hash: str = ""
217
219
  kakao_auth_token: str = ""
218
220
  kakao_username: str = ""
219
221
  kakao_password: str = ""
@@ -227,6 +229,10 @@ class CredOption(BaseOption):
227
229
  return {
228
230
  "signal": {"uuid": self.signal_uuid, "password": self.signal_password},
229
231
  "telegram": {"token": self.telegram_token, "userid": self.telegram_userid},
232
+ "telethon": {
233
+ "api_id": self.telethon_api_id,
234
+ "api_hash": self.telethon_api_hash,
235
+ },
230
236
  "kakao": {
231
237
  "auth_token": self.kakao_auth_token,
232
238
  "username": self.kakao_username,
@@ -213,8 +213,8 @@
213
213
  "power": 3
214
214
  },
215
215
  "quality": {
216
- "min": 10,
217
- "max": 95,
216
+ "min": 0,
217
+ "max": 100,
218
218
  "power": 5
219
219
  },
220
220
  "color": {
@@ -59,7 +59,8 @@
59
59
  "signal_password": "Set Signal password. Required for uploading Signal stickers.",
60
60
  "signal_get_auth": "Generate Signal uuid and password.",
61
61
  "telegram_token": "Set Telegram token. Required for uploading and downloading Telegram stickers.",
62
- "telegram_userid": "Set telegram user_id (From real account, not bot account). Required for uploading Telegram stickers.",
62
+ "telegram_userid": "Set Telegram user_id (From real account, not bot account). Required for uploading Telegram stickers.",
63
+ "telethon_setup": "Setup Telethon",
63
64
  "kakao_auth_token": "Set Kakao auth_token. Required for downloading animated stickers from https://e.kakao.com/t/xxxxx",
64
65
  "kakao_get_auth": "Generate Kakao auth_token by simulating login. Kakao username, password, country code and phone number are also required.",
65
66
  "kakao_get_auth_desktop": "Get Kakao auth_token from Kakao Desktop application.\n(Only working on Windows.)",
@@ -75,6 +76,6 @@
75
76
  "viber_bin_path": "Specify location of Viber Desktop application.\nUseful for portable installation.",
76
77
  "discord_get_auth": "Get Discord token.",
77
78
  "discord_token": "Set Discord token. Required for downloading Discord stickers and emojis.",
78
- "save_cred": "Save Signal and Telegram credentials."
79
+ "save_cred": "Save credentials."
79
80
  }
80
81
  }
@@ -29,6 +29,16 @@
29
29
  "author": false
30
30
  }
31
31
  },
32
+ "telegram_telethon": {
33
+ "full_name": "Download from Telegram with Telethon",
34
+ "help": "Download telegram stickers from a URL as input with Telethon",
35
+ "example": "Example: https://telegram.me/addstickers/xxxxx\n OR https://telegram.me/addemoji/xxxxx",
36
+ "address_lbls": "URL address",
37
+ "metadata_provides": {
38
+ "title": true,
39
+ "author": false
40
+ }
41
+ },
32
42
  "line": {
33
43
  "full_name": "Download from Line",
34
44
  "help": "Download line stickers from a URL / ID as input",
@@ -23,6 +23,22 @@
23
23
  "author": false
24
24
  }
25
25
  },
26
+ "telegram_telethon": {
27
+ "full_name": "Upload to Telegram with Telethon",
28
+ "help": "Upload to Telegram with Telethon *Not recommended, but allow link not end with _by_xxxbot*",
29
+ "metadata_requirements": {
30
+ "title": true,
31
+ "author": false
32
+ }
33
+ },
34
+ "telegram_emoji_telethon": {
35
+ "full_name": "Upload to Telegram with Telethon (Custom emoji)",
36
+ "help": "Upload to Telegram with Telethon (Custom emoji) *Not recommended, but allow link not end with _by_xxxbot*",
37
+ "metadata_requirements": {
38
+ "title": true,
39
+ "author": false
40
+ }
41
+ },
26
42
  "viber": {
27
43
  "full_name": "Upload to Viber",
28
44
  "help": "Upload to Viber",
@@ -3,7 +3,7 @@ import copy
3
3
  import shutil
4
4
  import zipfile
5
5
  from pathlib import Path
6
- from typing import Any, List
6
+ from typing import Any, List, Tuple
7
7
 
8
8
  from sticker_convert.converter import StickerConvert
9
9
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
@@ -47,7 +47,7 @@ class CompressWastickers(UploadBase):
47
47
  self.opt_comp_merged = copy.deepcopy(self.opt_comp)
48
48
  self.opt_comp_merged.merge(self.base_spec)
49
49
 
50
- def compress_wastickers(self) -> List[str]:
50
+ def compress_wastickers(self) -> Tuple[int, int, List[str]]:
51
51
  urls: List[str] = []
52
52
  title, author, _ = MetadataHandler.get_metadata(
53
53
  self.opt_output.dir,
@@ -56,10 +56,10 @@ class CompressWastickers(UploadBase):
56
56
  )
57
57
  if not title:
58
58
  self.cb.put("Title is required for compressing .wastickers")
59
- return urls
59
+ return 0, 0, urls
60
60
  if not author:
61
61
  self.cb.put("Author is required for compressing .wastickers")
62
- return urls
62
+ return 0, 0, urls
63
63
  packs = MetadataHandler.split_sticker_packs(
64
64
  self.opt_output.dir,
65
65
  title=title,
@@ -67,7 +67,9 @@ class CompressWastickers(UploadBase):
67
67
  separate_image_anim=not self.opt_comp.fake_vid,
68
68
  )
69
69
 
70
+ stickers_total = 0
70
71
  for pack_title, stickers in packs.items():
72
+ stickers_total += len(stickers)
71
73
  # Originally the Sticker Maker application name the files with int(time.time())
72
74
  with CacheStore.get_cache_store(path=self.opt_comp.cache_dir) as tempdir:
73
75
  for num, src in enumerate(stickers):
@@ -106,7 +108,7 @@ class CompressWastickers(UploadBase):
106
108
  self.cb.put((out_f))
107
109
  urls.append(out_f)
108
110
 
109
- return urls
111
+ return stickers_total, stickers_total, urls
110
112
 
111
113
  def add_metadata(self, pack_dir: Path, title: str, author: str) -> None:
112
114
  opt_comp_merged = copy.deepcopy(self.opt_comp)
@@ -150,6 +152,6 @@ class CompressWastickers(UploadBase):
150
152
  opt_cred: CredOption,
151
153
  cb: CallbackProtocol,
152
154
  cb_return: CallbackReturn,
153
- ) -> List[str]:
155
+ ) -> Tuple[int, int, List[str]]:
154
156
  exporter = CompressWastickers(opt_output, opt_comp, opt_cred, cb, cb_return)
155
157
  return exporter.compress_wastickers()
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  import copy
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List
4
+ from typing import Any, Dict, List, Tuple
5
5
 
6
6
  import anyio
7
7
  from signalstickers_client.errors import SignalException
@@ -91,15 +91,15 @@ class UploadSignal(UploadBase):
91
91
 
92
92
  pack._addsticker(sticker) # type: ignore
93
93
 
94
- def upload_stickers_signal(self) -> List[str]:
94
+ def upload_stickers_signal(self) -> Tuple[int, int, List[str]]:
95
95
  urls: List[str] = []
96
96
 
97
97
  if not self.opt_cred.signal_uuid:
98
98
  self.cb.put("uuid required for uploading to Signal")
99
- return urls
99
+ return 0, 0, urls
100
100
  if not self.opt_cred.signal_password:
101
101
  self.cb.put("password required for uploading to Signal")
102
- return urls
102
+ return 0, 0, urls
103
103
 
104
104
  title, author, emoji_dict = MetadataHandler.get_metadata(
105
105
  self.opt_output.dir,
@@ -138,7 +138,10 @@ class UploadSignal(UploadBase):
138
138
  file_per_pack=200,
139
139
  separate_image_anim=False,
140
140
  )
141
+ stickers_total = 0
142
+ stickers_ok = 0
141
143
  for pack_title, stickers in packs.items():
144
+ stickers_total += len(stickers)
142
145
  pack = LocalStickerPack()
143
146
  pack.title = pack_title
144
147
  pack.author = author
@@ -155,11 +158,12 @@ class UploadSignal(UploadBase):
155
158
  )
156
159
  self.cb.put((result))
157
160
  urls.append(result)
161
+ stickers_ok += len(stickers)
158
162
 
159
163
  except SignalException as e:
160
164
  self.cb.put(f"Failed to upload pack {pack_title} due to {repr(e)}")
161
165
 
162
- return urls
166
+ return stickers_ok, stickers_total, urls
163
167
 
164
168
  @staticmethod
165
169
  def start(
@@ -168,6 +172,6 @@ class UploadSignal(UploadBase):
168
172
  opt_cred: CredOption,
169
173
  cb: CallbackProtocol,
170
174
  cb_return: CallbackReturn,
171
- ) -> List[str]:
175
+ ) -> Tuple[int, int, List[str]]:
172
176
  exporter = UploadSignal(opt_output, opt_comp, opt_cred, cb, cb_return)
173
177
  return exporter.upload_stickers_signal()