sticker-convert 2.10.8__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.
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()