sticker-convert 2.9.4__py3-none-any.whl → 2.10.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 (35) hide show
  1. sticker_convert/cli.py +26 -13
  2. sticker_convert/downloaders/download_base.py +20 -23
  3. sticker_convert/downloaders/download_discord.py +91 -0
  4. sticker_convert/downloaders/download_kakao.py +3 -4
  5. sticker_convert/downloaders/download_line.py +3 -4
  6. sticker_convert/downloaders/download_signal.py +3 -4
  7. sticker_convert/downloaders/download_telegram.py +3 -4
  8. sticker_convert/downloaders/download_viber.py +3 -4
  9. sticker_convert/gui.py +15 -3
  10. sticker_convert/gui_components/frames/cred_frame.py +22 -1
  11. sticker_convert/gui_components/windows/discord_get_auth_window.py +82 -0
  12. sticker_convert/gui_components/windows/signal_get_auth_window.py +39 -85
  13. sticker_convert/job.py +11 -1
  14. sticker_convert/job_option.py +2 -0
  15. sticker_convert/resources/compression.json +94 -0
  16. sticker_convert/resources/help.json +2 -1
  17. sticker_convert/resources/input.json +20 -0
  18. sticker_convert/uploaders/upload_signal.py +5 -4
  19. sticker_convert/uploaders/upload_telegram.py +11 -10
  20. sticker_convert/utils/auth/get_discord_auth.py +115 -0
  21. sticker_convert/utils/auth/get_signal_auth.py +115 -114
  22. sticker_convert/utils/auth/get_viber_auth.py +10 -214
  23. sticker_convert/utils/chrome_remotedebug.py +152 -0
  24. sticker_convert/utils/emoji.py +16 -0
  25. sticker_convert/utils/files/run_bin.py +1 -1
  26. sticker_convert/utils/media/codec_info.py +45 -60
  27. sticker_convert/utils/process.py +187 -0
  28. sticker_convert/utils/url_detect.py +3 -0
  29. sticker_convert/version.py +1 -1
  30. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/METADATA +40 -42
  31. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/RECORD +35 -29
  32. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/LICENSE +0 -0
  33. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/WHEEL +0 -0
  34. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/entry_points.txt +0 -0
  35. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/top_level.txt +0 -0
sticker_convert/cli.py CHANGED
@@ -14,6 +14,7 @@ from mergedeep import merge # type: ignore
14
14
  from sticker_convert.definitions import CONFIG_DIR, DEFAULT_DIR
15
15
  from sticker_convert.job import Job
16
16
  from sticker_convert.job_option import CompOption, CredOption, InputOption, OutputOption
17
+ from sticker_convert.utils.auth.get_discord_auth import GetDiscordAuth
17
18
  from sticker_convert.utils.auth.get_kakao_auth import GetKakaoAuth
18
19
  from sticker_convert.utils.auth.get_line_auth import GetLineAuth
19
20
  from sticker_convert.utils.auth.get_signal_auth import GetSignalAuth
@@ -164,7 +165,12 @@ class CLI:
164
165
  )
165
166
 
166
167
  parser_cred = parser.add_argument_group("Credentials options")
167
- flags_cred_bool = ("signal_get_auth", "kakao_get_auth", "line_get_auth")
168
+ flags_cred_bool = (
169
+ "signal_get_auth",
170
+ "kakao_get_auth",
171
+ "line_get_auth",
172
+ "discord_get_auth",
173
+ )
168
174
  for k, v in self.help["cred"].items():
169
175
  keyword_args = {}
170
176
  if k in flags_cred_bool:
@@ -220,6 +226,8 @@ class CLI:
220
226
  "telegram": args.download_telegram,
221
227
  "kakao": args.download_kakao,
222
228
  "viber": args.download_viber,
229
+ "discord": args.download_discord,
230
+ "discord_emoji": args.download_discord_emoji,
223
231
  }
224
232
 
225
233
  download_option = "local"
@@ -456,6 +464,9 @@ class CLI:
456
464
  viber_auth=args.viber_auth
457
465
  if args.viber_auth
458
466
  else creds.get("viber", {}).get("auth"),
467
+ discord_token=args.discord_token
468
+ if args.discord_token
469
+ else creds.get("discord", {}).get("token"),
459
470
  )
460
471
 
461
472
  if args.kakao_get_auth:
@@ -473,23 +484,16 @@ class CLI:
473
484
  self.cb.msg(f"Got auth_token successfully: {auth_token}")
474
485
 
475
486
  if args.signal_get_auth:
476
- get_signal_auth = GetSignalAuth()
477
-
478
- signal_bin_path = None
479
- signal_user_data_dir = None
480
- if args.signal_data_dir:
481
- signal_bin_path = "(User specified)"
482
- signal_user_data_dir = args.signal_data_dir
483
-
484
- uuid, password, msg = get_signal_auth.get_cred(
485
- signal_bin_path, signal_user_data_dir
486
- )
487
+ m = GetSignalAuth(cb_msg=self.cb.msg, cb_ask_str=self.cb.ask_str)
487
488
 
489
+ uuid, password = m.get_cred()
488
490
  if uuid and password:
489
491
  opt_cred.signal_uuid = uuid
490
492
  opt_cred.signal_password = password
491
493
 
492
- self.cb.msg(msg)
494
+ self.cb.msg(f"Got uuid and password successfully: {uuid}, {password}")
495
+
496
+ self.cb.msg("Failed to get uuid and password")
493
497
 
494
498
  if args.line_get_auth:
495
499
  get_line_auth = GetLineAuth()
@@ -519,6 +523,15 @@ class CLI:
519
523
 
520
524
  self.cb.msg(msg)
521
525
 
526
+ if args.discord_get_auth:
527
+ get_discord_auth = GetDiscordAuth(self.cb.msg)
528
+ discord_token, msg = get_discord_auth.get_cred()
529
+
530
+ if discord_token:
531
+ opt_cred.discord_token = discord_token
532
+
533
+ self.cb.msg(msg)
534
+
522
535
  if args.save_cred:
523
536
  creds_path = CONFIG_DIR / "creds.json"
524
537
  JsonManager.save_json(creds_path, opt_cred.to_dict())
@@ -6,23 +6,24 @@ from pathlib import Path
6
6
  from typing import Any, List, Optional, Tuple
7
7
 
8
8
  import anyio
9
+ import httpx
9
10
  import requests
10
11
 
11
- from sticker_convert.job_option import CredOption
12
+ from sticker_convert.job_option import CredOption, InputOption
12
13
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
13
14
 
14
15
 
15
16
  class DownloadBase:
16
17
  def __init__(
17
18
  self,
18
- url: str,
19
- out_dir: Path,
19
+ opt_input: InputOption,
20
20
  opt_cred: Optional[CredOption],
21
21
  cb: CallbackProtocol,
22
22
  cb_return: CallbackReturn,
23
23
  ) -> None:
24
- self.url = url
25
- self.out_dir = out_dir
24
+ self.url = opt_input.url
25
+ self.out_dir = opt_input.dir
26
+ self.input_option = opt_input.option
26
27
  self.opt_cred = opt_cred
27
28
  self.cb = cb
28
29
  self.cb_return = cb_return
@@ -42,36 +43,32 @@ class DownloadBase:
42
43
  ("bar", None, {"set_progress_mode": "determinate", "steps": len(targets)})
43
44
  )
44
45
 
45
- async with anyio.create_task_group() as tg:
46
- for url, dest in targets:
47
- tg.start_soon(self.download_file_async, url, dest, retries, **kwargs)
46
+ async with httpx.AsyncClient() as client:
47
+ async with anyio.create_task_group() as tg:
48
+ for url, dest in targets:
49
+ tg.start_soon(
50
+ self.download_file_async, client, url, dest, retries, **kwargs
51
+ )
48
52
 
49
53
  async def download_file_async(
50
54
  self,
55
+ client: httpx.AsyncClient,
51
56
  url: str,
52
57
  dest: Path,
53
58
  retries: int = 3,
54
59
  **kwargs: Any,
55
60
  ) -> None:
61
+ self.cb.put(f"Downloading {url}")
56
62
  for retry in range(retries):
57
- try:
58
- response = requests.get(url, allow_redirects=True, **kwargs)
59
-
60
- if not response.ok:
61
- self.cb.put("update_bar")
62
- raise requests.exceptions.RequestException(
63
- f"Error {response.status_code}"
64
- )
63
+ response = await client.get(url, follow_redirects=True, **kwargs)
65
64
 
66
- self.cb.put(f"Downloading {url}")
67
- with open(dest, "wb+") as f:
68
- f.write(response.content)
65
+ if response.is_success:
66
+ async with await anyio.open_file(dest, "wb+") as f:
67
+ await f.write(response.content)
69
68
  self.cb.put(f"Downloaded {url}")
70
- break
71
-
72
- except requests.exceptions.RequestException as e:
69
+ else:
73
70
  self.cb.put(
74
- f"Cannot download {url} (tried {retry+1}/{retries} times): {e}"
71
+ f"Error {response.status_code}: {url} (tried {retry+1}/{retries} times)"
75
72
  )
76
73
 
77
74
  self.cb.put("update_bar")
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ from pathlib import Path
4
+ from typing import Dict, List, Optional, Tuple, cast
5
+ from urllib.parse import urlparse
6
+
7
+ import requests
8
+
9
+ from sticker_convert.downloaders.download_base import DownloadBase
10
+ from sticker_convert.job_option import CredOption, InputOption
11
+ from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
12
+ from sticker_convert.utils.emoji import extract_emojis
13
+ from sticker_convert.utils.files.metadata_handler import MetadataHandler
14
+
15
+ # References:
16
+ # https://github.com/ThaTiemsz/Discord-Emoji-Downloader/blob/master/assets/app.js
17
+ # https://github.com/zgibberish/discord-emoji-scraper/blob/main/emoji_scraper.py
18
+
19
+
20
+ class DownloadDiscord(DownloadBase):
21
+ # def __init__(self, *args: Any, **kwargs: Any) -> None:
22
+ # super().__init__(*args, **kwargs)
23
+
24
+ def download_stickers_discord(self) -> bool:
25
+ if self.opt_cred is None or self.opt_cred.discord_token == "":
26
+ self.cb.put("Error: Downloading from Discord requires token")
27
+ return False
28
+
29
+ gid: Optional[str] = None
30
+ if self.url.isnumeric():
31
+ gid = self.url
32
+ else:
33
+ url_parsed = urlparse(self.url)
34
+ if url_parsed.netloc == "discord.com":
35
+ gid = url_parsed.path.split("/")[2]
36
+
37
+ if gid is None or gid.isnumeric() is False:
38
+ self.cb.put("Error: Invalid url")
39
+ return False
40
+
41
+ headers = {
42
+ "Authorization": self.opt_cred.discord_token,
43
+ }
44
+
45
+ r = requests.get(f"https://discord.com/api/v10/guilds/{gid}", headers=headers)
46
+ r_json = json.loads(r.text)
47
+
48
+ if self.input_option == "discord_emoji":
49
+ stickers = r_json["emojis"]
50
+ else:
51
+ stickers = r_json["stickers"]
52
+
53
+ targets: List[Tuple[str, Path]] = []
54
+ emoji_dict: Dict[str, str] = {}
55
+ for i, sticker in enumerate(stickers):
56
+ f_id = str(i).zfill(3)
57
+ sticker_id = sticker["id"]
58
+ if self.input_option == "discord_emoji":
59
+ f_ext = ".gif" if sticker["animated"] else ".png"
60
+ sticker_url = f"https://cdn.discordapp.com/emojis/{sticker_id}{f_ext}?size=4096&quality=lossless"
61
+ else:
62
+ # https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types
63
+ format_type = cast(int, sticker["format_type"])
64
+ f_ext = [".png", ".png", ".json", ".gif"][format_type - 1]
65
+ sticker_url = f"https://cdn.discordapp.com/stickers/{sticker_id}{f_ext}?size=4096&quality=lossless"
66
+ emoji_dict[f_id] = extract_emojis(sticker["tags"])
67
+ f_name = f_id + f_ext
68
+ f_path = Path(self.out_dir, f_name)
69
+ targets.append((sticker_url, f_path))
70
+
71
+ self.download_multiple_files(targets)
72
+
73
+ server_name = r_json["name"]
74
+ MetadataHandler.set_metadata(
75
+ self.out_dir,
76
+ title=server_name,
77
+ author=server_name,
78
+ emoji_dict=emoji_dict if self.input_option == "discord" else None,
79
+ )
80
+
81
+ return True
82
+
83
+ @staticmethod
84
+ def start(
85
+ opt_input: InputOption,
86
+ opt_cred: Optional[CredOption],
87
+ cb: CallbackProtocol,
88
+ cb_return: CallbackReturn,
89
+ ) -> bool:
90
+ downloader = DownloadDiscord(opt_input, opt_cred, cb, cb_return)
91
+ return downloader.download_stickers_discord()
@@ -12,7 +12,7 @@ from bs4 import BeautifulSoup
12
12
  from py_mini_racer import MiniRacer
13
13
 
14
14
  from sticker_convert.downloaders.download_base import DownloadBase
15
- from sticker_convert.job_option import CredOption
15
+ from sticker_convert.job_option import CredOption, InputOption
16
16
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
17
17
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
18
18
  from sticker_convert.utils.media.decrypt_kakao import DecryptKakao
@@ -326,11 +326,10 @@ class DownloadKakao(DownloadBase):
326
326
 
327
327
  @staticmethod
328
328
  def start(
329
- url: str,
330
- out_dir: Path,
329
+ opt_input: InputOption,
331
330
  opt_cred: Optional[CredOption],
332
331
  cb: CallbackProtocol,
333
332
  cb_return: CallbackReturn,
334
333
  ) -> bool:
335
- downloader = DownloadKakao(url, out_dir, opt_cred, cb, cb_return)
334
+ downloader = DownloadKakao(opt_input, opt_cred, cb, cb_return)
336
335
  return downloader.download_stickers_kakao()
@@ -15,7 +15,7 @@ from bs4 import BeautifulSoup
15
15
  from PIL import Image
16
16
 
17
17
  from sticker_convert.downloaders.download_base import DownloadBase
18
- from sticker_convert.job_option import CredOption
18
+ from sticker_convert.job_option import CredOption, InputOption
19
19
  from sticker_convert.utils.auth.get_line_auth import GetLineAuth
20
20
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
21
21
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
@@ -462,11 +462,10 @@ class DownloadLine(DownloadBase):
462
462
 
463
463
  @staticmethod
464
464
  def start(
465
- url: str,
466
- out_dir: Path,
465
+ opt_input: InputOption,
467
466
  opt_cred: Optional[CredOption],
468
467
  cb: CallbackProtocol,
469
468
  cb_return: CallbackReturn,
470
469
  ) -> bool:
471
- downloader = DownloadLine(url, out_dir, opt_cred, cb, cb_return)
470
+ downloader = DownloadLine(opt_input, opt_cred, cb, cb_return)
472
471
  return downloader.download_stickers_line()
@@ -8,7 +8,7 @@ from signalstickers_client.models import StickerPack
8
8
  from signalstickers_client.stickersclient import StickersClient
9
9
 
10
10
  from sticker_convert.downloaders.download_base import DownloadBase
11
- from sticker_convert.job_option import CredOption
11
+ from sticker_convert.job_option import CredOption, InputOption
12
12
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
13
13
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
14
14
  from sticker_convert.utils.media.codec_info import CodecInfo
@@ -82,11 +82,10 @@ class DownloadSignal(DownloadBase):
82
82
 
83
83
  @staticmethod
84
84
  def start(
85
- url: str,
86
- out_dir: Path,
85
+ opt_input: InputOption,
87
86
  opt_cred: Optional[CredOption],
88
87
  cb: CallbackProtocol,
89
88
  cb_return: CallbackReturn,
90
89
  ) -> bool:
91
- downloader = DownloadSignal(url, out_dir, opt_cred, cb, cb_return)
90
+ downloader = DownloadSignal(opt_input, opt_cred, cb, cb_return)
92
91
  return downloader.download_stickers_signal()
@@ -9,7 +9,7 @@ from telegram.error import TelegramError
9
9
  from telegram.ext import AIORateLimiter, ApplicationBuilder
10
10
 
11
11
  from sticker_convert.downloaders.download_base import DownloadBase
12
- from sticker_convert.job_option import CredOption
12
+ from sticker_convert.job_option import CredOption, InputOption
13
13
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
14
14
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
15
15
 
@@ -120,11 +120,10 @@ class DownloadTelegram(DownloadBase):
120
120
 
121
121
  @staticmethod
122
122
  def start(
123
- url: str,
124
- out_dir: Path,
123
+ opt_input: InputOption,
125
124
  opt_cred: Optional[CredOption],
126
125
  cb: CallbackProtocol,
127
126
  cb_return: CallbackReturn,
128
127
  ) -> bool:
129
- downloader = DownloadTelegram(url, out_dir, opt_cred, cb, cb_return)
128
+ downloader = DownloadTelegram(opt_input, opt_cred, cb, cb_return)
130
129
  return downloader.download_stickers_telegram()
@@ -10,7 +10,7 @@ import requests
10
10
  from bs4 import BeautifulSoup
11
11
 
12
12
  from sticker_convert.downloaders.download_base import DownloadBase
13
- from sticker_convert.job_option import CredOption
13
+ from sticker_convert.job_option import CredOption, InputOption
14
14
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
15
15
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
16
16
 
@@ -85,11 +85,10 @@ class DownloadViber(DownloadBase):
85
85
 
86
86
  @staticmethod
87
87
  def start(
88
- url: str,
89
- out_dir: Path,
88
+ opt_input: InputOption,
90
89
  opt_cred: Optional[CredOption],
91
90
  cb: CallbackProtocol,
92
91
  cb_return: CallbackReturn,
93
92
  ) -> bool:
94
- downloader = DownloadViber(url, out_dir, opt_cred, cb, cb_return)
93
+ downloader = DownloadViber(opt_input, opt_cred, cb, cb_return)
95
94
  return downloader.download_stickers_viber()
sticker_convert/gui.py CHANGED
@@ -150,7 +150,6 @@ class GUI(Window):
150
150
  # Credentials
151
151
  self.signal_uuid_var = StringVar(self)
152
152
  self.signal_password_var = StringVar(self)
153
- self.signal_data_dir_var = StringVar(self)
154
153
  self.telegram_token_var = StringVar(self)
155
154
  self.telegram_userid_var = StringVar(self)
156
155
  self.kakao_auth_token_var = StringVar(self)
@@ -161,6 +160,7 @@ class GUI(Window):
161
160
  self.line_cookies_var = StringVar(self)
162
161
  self.viber_auth_var = StringVar(self)
163
162
  self.viber_bin_path_var = StringVar(self)
163
+ self.discord_token_var = StringVar(self)
164
164
 
165
165
  # Config
166
166
  self.settings_save_cred_var = BooleanVar()
@@ -388,6 +388,7 @@ class GUI(Window):
388
388
  )
389
389
  self.line_cookies_var.set(self.creds.get("line", {}).get("cookies", ""))
390
390
  self.viber_auth_var.set(self.creds.get("viber", {}).get("auth", ""))
391
+ self.discord_token_var.set(self.creds.get("discord", {}).get("token", ""))
391
392
 
392
393
  def get_input_name(self) -> str:
393
394
  return [
@@ -549,6 +550,7 @@ class GUI(Window):
549
550
  kakao_phone_number=self.kakao_phone_number_var.get(),
550
551
  line_cookies=self.line_cookies_var.get(),
551
552
  viber_auth=self.viber_auth_var.get(),
553
+ discord_token=self.discord_token_var.get(),
552
554
  )
553
555
 
554
556
  def start_process(self) -> None:
@@ -702,8 +704,13 @@ class GUI(Window):
702
704
  if not url:
703
705
  self.input_frame.address_entry.config(bootstyle="warning") # type: ignore
704
706
 
705
- elif download_option != input_option and not (
706
- input_option in ("kakao", "line") and url.isnumeric()
707
+ elif (
708
+ download_option is None
709
+ or input_option.startswith(download_option) is False
710
+ and not (
711
+ input_option in ("kakao", "line", "discord", "discord_emoji")
712
+ and url.isnumeric()
713
+ )
707
714
  ):
708
715
  self.input_frame.address_entry.config(bootstyle="danger") # type: ignore
709
716
  self.input_frame.address_tip.config(
@@ -788,6 +795,11 @@ class GUI(Window):
788
795
  else:
789
796
  self.cred_frame.kakao_auth_token_entry.config(bootstyle="default") # type: ignore
790
797
 
798
+ if input_option.startswith("discord") and not self.discord_token_var.get():
799
+ self.cred_frame.discord_token_entry.config(bootstyle="warning") # type: ignore
800
+ else:
801
+ self.cred_frame.discord_token_entry.config(bootstyle="default") # type: ignore
802
+
791
803
  # Check for Input and Compression mismatch
792
804
  if (
793
805
  not self.no_compress_var.get()
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any
5
5
  from ttkbootstrap import Button, Entry, Label, LabelFrame # type: ignore
6
6
 
7
7
  from sticker_convert.gui_components.frames.right_clicker import RightClicker
8
+ from sticker_convert.gui_components.windows.discord_get_auth_window import DiscordGetAuthWindow
8
9
  from sticker_convert.gui_components.windows.kakao_get_auth_window import KakaoGetAuthWindow
9
10
  from sticker_convert.gui_components.windows.line_get_auth_window import LineGetAuthWindow
10
11
  from sticker_convert.gui_components.windows.signal_get_auth_window import SignalGetAuthWindow
@@ -118,6 +119,20 @@ class CredFrame(LabelFrame):
118
119
  bootstyle="secondary", # type: ignore
119
120
  )
120
121
 
122
+ self.discord_token_lbl = Label(
123
+ self, text="Discord token", width=18, justify="left", anchor="w"
124
+ )
125
+ self.discord_token_entry = Entry(
126
+ self, textvariable=self.gui.discord_token_var, width=35
127
+ )
128
+ self.discord_token_entry.bind("<Button-3><ButtonRelease-3>", RightClicker)
129
+ self.discord_get_auth_btn = Button(
130
+ self,
131
+ text="Generate",
132
+ command=self.cb_discord_get_auth,
133
+ bootstyle="secondary", # type: ignore
134
+ )
135
+
121
136
  self.help_btn = Button(
122
137
  self,
123
138
  text="Get help",
@@ -151,7 +166,10 @@ class CredFrame(LabelFrame):
151
166
  self.viber_auth_lbl.grid(column=0, row=7, sticky="w", padx=3, pady=3)
152
167
  self.viber_auth_entry.grid(column=1, row=7, sticky="w", padx=3, pady=3)
153
168
  self.viber_get_auth_btn.grid(column=2, row=7, sticky="e", padx=3, pady=3)
154
- self.help_btn.grid(column=2, row=8, 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)
155
173
 
156
174
  def cb_cred_help(self, *_: Any) -> None:
157
175
  faq_site = "https://github.com/laggykiller/sticker-convert#faq"
@@ -171,6 +189,9 @@ class CredFrame(LabelFrame):
171
189
  def cb_viber_get_auth(self, *_: Any) -> None:
172
190
  ViberGetAuthWindow(self.gui)
173
191
 
192
+ def cb_discord_get_auth(self, *_: Any) -> None:
193
+ DiscordGetAuthWindow(self.gui)
194
+
174
195
  def set_states(self, state: str) -> None:
175
196
  self.signal_uuid_entry.config(state=state)
176
197
  self.signal_password_entry.config(state=state)
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ from functools import partial
3
+ from threading import Thread
4
+ from typing import Any
5
+
6
+ from ttkbootstrap import Button, Frame, Label # type: ignore
7
+
8
+ from sticker_convert.gui_components.gui_utils import GUIUtils
9
+ from sticker_convert.gui_components.windows.base_window import BaseWindow
10
+ from sticker_convert.utils.auth.get_discord_auth import GetDiscordAuth
11
+
12
+
13
+ class DiscordGetAuthWindow(BaseWindow):
14
+ def __init__(self, *args: Any, **kwargs: Any):
15
+ super(DiscordGetAuthWindow, self).__init__(*args, **kwargs)
16
+
17
+ self.title("Get Discord token")
18
+
19
+ self.cb_msg_block_discord = partial(self.gui.cb_msg_block, parent=self)
20
+ self.cb_ask_str_discord = partial(self.gui.cb_ask_str, parent=self)
21
+
22
+ self.frame_info = Frame(self.scrollable_frame)
23
+ self.frame_start_btn = Frame(self.scrollable_frame)
24
+
25
+ self.frame_info.grid(column=0, row=0, sticky="news", padx=3, pady=3)
26
+ self.frame_start_btn.grid(column=0, row=1, sticky="news", padx=3, pady=3)
27
+
28
+ # Info frame
29
+ self.explanation1_lbl = Label(
30
+ self.frame_info,
31
+ text="Please install Discord Desktop or Chrome",
32
+ justify="left",
33
+ anchor="w",
34
+ )
35
+ self.explanation2_lbl = Label(
36
+ self.frame_info,
37
+ text="After installation, you need to login to Discord",
38
+ justify="left",
39
+ anchor="w",
40
+ )
41
+ self.explanation3_lbl = Label(
42
+ self.frame_info,
43
+ text="Token will be automatically fetched",
44
+ justify="left",
45
+ anchor="w",
46
+ )
47
+
48
+ self.explanation1_lbl.grid(
49
+ column=0, row=0, columnspan=3, sticky="w", padx=3, pady=3
50
+ )
51
+ self.explanation2_lbl.grid(
52
+ column=0, row=1, columnspan=3, sticky="w", padx=3, pady=3
53
+ )
54
+ self.explanation3_lbl.grid(
55
+ column=0, row=2, columnspan=3, sticky="w", padx=3, pady=3
56
+ )
57
+
58
+ # Start button frame
59
+ self.login_btn = Button(
60
+ self.frame_start_btn, text="Get token", command=self.cb_login
61
+ )
62
+
63
+ self.login_btn.pack()
64
+
65
+ GUIUtils.finalize_window(self)
66
+
67
+ def cb_login(self):
68
+ Thread(target=self.cb_login_thread, daemon=True).start()
69
+
70
+ def cb_login_thread(self, *args: Any):
71
+ m = GetDiscordAuth(cb_msg=self.gui.cb_msg)
72
+ discord_token, msg = m.get_cred()
73
+ if discord_token:
74
+ if not self.gui.creds.get("discord"):
75
+ self.gui.creds["discord"] = {}
76
+ self.gui.creds["discord"]["token"] = discord_token
77
+ self.gui.discord_token_var.set(discord_token)
78
+
79
+ self.gui.save_creds()
80
+ self.gui.highlight_fields()
81
+
82
+ self.cb_msg_block_discord(msg)