sticker-convert 2.9.3__py3-none-any.whl → 2.10.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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.3.dist-info → sticker_convert-2.10.0.dist-info}/METADATA +42 -44
  31. {sticker_convert-2.9.3.dist-info → sticker_convert-2.10.0.dist-info}/RECORD +35 -29
  32. {sticker_convert-2.9.3.dist-info → sticker_convert-2.10.0.dist-info}/WHEEL +1 -1
  33. {sticker_convert-2.9.3.dist-info → sticker_convert-2.10.0.dist-info}/LICENSE +0 -0
  34. {sticker_convert-2.9.3.dist-info → sticker_convert-2.10.0.dist-info}/entry_points.txt +0 -0
  35. {sticker_convert-2.9.3.dist-info → sticker_convert-2.10.0.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  from functools import partial
3
- from pathlib import Path
4
- from subprocess import Popen
5
- from tkinter import filedialog
3
+ from threading import Thread
6
4
  from typing import Any
7
5
 
8
- from ttkbootstrap import Button, Entry, Frame, Label # type: ignore
6
+ from ttkbootstrap import Button, Frame, Label # type: ignore
9
7
 
10
8
  from sticker_convert.gui_components.gui_utils import GUIUtils
11
9
  from sticker_convert.gui_components.windows.base_window import BaseWindow
@@ -13,8 +11,8 @@ from sticker_convert.utils.auth.get_signal_auth import GetSignalAuth
13
11
 
14
12
 
15
13
  class SignalGetAuthWindow(BaseWindow):
16
- def __init__(self, *args: Any, **kwargs: Any) -> None:
17
- super().__init__(*args, **kwargs)
14
+ def __init__(self, *args: Any, **kwargs: Any):
15
+ super(SignalGetAuthWindow, self).__init__(*args, **kwargs)
18
16
 
19
17
  self.title("Get Signal uuid and password")
20
18
 
@@ -22,79 +20,57 @@ class SignalGetAuthWindow(BaseWindow):
22
20
  self.cb_ask_str_signal = partial(self.gui.cb_ask_str, parent=self)
23
21
 
24
22
  self.frame_info = Frame(self.scrollable_frame)
25
- self.frame_btns = Frame(self.scrollable_frame)
26
- self.frame_config = Frame(self.scrollable_frame)
23
+ self.frame_start_btn = Frame(self.scrollable_frame)
27
24
 
28
25
  self.frame_info.grid(column=0, row=0, sticky="news", padx=3, pady=3)
29
- self.frame_btns.grid(column=0, row=1, sticky="news", padx=3, pady=3)
30
- self.frame_config.grid(column=0, row=2, sticky="news", padx=3, pady=3)
26
+ self.frame_start_btn.grid(column=0, row=1, sticky="news", padx=3, pady=3)
31
27
 
32
28
  # Info frame
33
- self.explanation_lbl = Label(
29
+ self.explanation1_lbl = Label(
34
30
  self.frame_info,
35
- text="Please install Signal Desktop and login first.",
31
+ text="Please install Signal Desktop",
36
32
  justify="left",
37
33
  anchor="w",
38
34
  )
39
-
40
- self.explanation_lbl.grid(column=0, row=0, sticky="w", padx=3, pady=3)
41
-
42
- # Start button frame
43
- self.launch_btn = Button(
44
- self.frame_btns,
45
- text="Launch Signal Desktop",
46
- command=self.cb_launch_signal,
47
- bootstyle="secondary", # type: ignore
48
- )
49
-
50
- self.get_cred_btn = Button(
51
- self.frame_btns,
52
- text="Get uuid and password",
53
- command=self.cb_get_cred,
54
- bootstyle="default", # type: ignore
35
+ self.explanation2_lbl = Label(
36
+ self.frame_info,
37
+ text="After installation, you need to login to Signal Desktop",
38
+ justify="left",
39
+ anchor="w",
55
40
  )
56
-
57
- self.launch_btn.pack()
58
- self.get_cred_btn.pack()
59
-
60
- # Config frame
61
- self.setdir_lbl = Label(
62
- self.frame_config,
63
- text=self.gui.help["cred"]["signal_data_dir"],
41
+ self.explanation3_lbl = Label(
42
+ self.frame_info,
43
+ text="uuid and password will be automatically fetched",
64
44
  justify="left",
65
45
  anchor="w",
66
46
  )
67
47
 
68
- self.setdir_entry = Entry(
69
- self.frame_config,
70
- textvariable=self.gui.signal_data_dir_var,
71
- width=32,
48
+ self.explanation1_lbl.grid(
49
+ column=0, row=0, columnspan=3, sticky="w", padx=3, pady=3
72
50
  )
73
- self.setdir_btn = Button(
74
- self.frame_config,
75
- text="Choose",
76
- command=self.cb_setdir,
77
- width=8,
78
- bootstyle="secondary", # type: ignore
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
79
56
  )
80
57
 
81
- self.setdir_lbl.grid(column=0, row=0, columnspan=2, sticky="w", padx=3, pady=3)
82
- self.setdir_entry.grid(column=0, row=1, sticky="w", padx=3, pady=3)
83
- self.setdir_btn.grid(column=1, row=1, sticky="e", padx=3, pady=3)
58
+ # Start button frame
59
+ self.login_btn = Button(
60
+ self.frame_start_btn, text="Get uuid and password", command=self.cb_login
61
+ )
84
62
 
85
- GUIUtils.finalize_window(self)
63
+ self.login_btn.pack()
86
64
 
87
- def cb_get_cred(self) -> None:
88
- m = GetSignalAuth()
65
+ GUIUtils.finalize_window(self)
89
66
 
90
- signal_bin_path = None
91
- signal_user_data_dir = None
92
- if self.gui.signal_data_dir_var.get():
93
- signal_bin_path = "(User specified)"
94
- signal_user_data_dir = self.gui.signal_data_dir_var.get()
67
+ def cb_login(self):
68
+ Thread(target=self.cb_login_thread, daemon=True).start()
95
69
 
96
- uuid, password, msg = m.get_cred(signal_bin_path, signal_user_data_dir)
70
+ def cb_login_thread(self, *args: Any):
71
+ m = GetSignalAuth(cb_msg=self.gui.cb_msg, cb_ask_str=self.cb_ask_str_signal)
97
72
 
73
+ uuid, password = m.get_cred()
98
74
  if uuid and password:
99
75
  if not self.gui.creds.get("signal"):
100
76
  self.gui.creds["signal"] = {}
@@ -103,33 +79,11 @@ class SignalGetAuthWindow(BaseWindow):
103
79
  self.gui.signal_uuid_var.set(uuid)
104
80
  self.gui.signal_password_var.set(password)
105
81
 
82
+ self.cb_msg_block_signal(
83
+ f"Got uuid and password successfully:\nuuid={uuid}\npassword={password}"
84
+ )
106
85
  self.gui.save_creds()
107
86
  self.gui.highlight_fields()
87
+ return
108
88
 
109
- self.cb_msg_block_signal(msg)
110
-
111
- def cb_launch_signal(self) -> None:
112
- m = GetSignalAuth()
113
- signal_bin_path, signal_user_data_dir = m.get_signal_desktop()
114
-
115
- if self.gui.signal_data_dir_var.get():
116
- signal_user_data_dir = self.gui.signal_data_dir_var.get()
117
-
118
- if signal_bin_path:
119
- Popen(
120
- [
121
- signal_bin_path,
122
- "--no-sandbox",
123
- f"--user-data-dir={signal_user_data_dir}",
124
- ]
125
- )
126
- else:
127
- self.cb_msg_block_signal("Error: Signal Desktop not installed.")
128
-
129
- def cb_setdir(self) -> None:
130
- orig_input_dir = self.gui.signal_data_dir_var.get()
131
- if not Path(orig_input_dir).is_dir():
132
- orig_input_dir = ""
133
- input_dir = filedialog.askdirectory(initialdir=orig_input_dir)
134
- if input_dir:
135
- self.gui.signal_data_dir_var.set(input_dir)
89
+ self.cb_msg_block_signal("Failed to get uuid and password")
sticker_convert/job.py CHANGED
@@ -12,6 +12,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
12
12
  from urllib.parse import urlparse
13
13
 
14
14
  from sticker_convert.converter import StickerConvert
15
+ from sticker_convert.downloaders.download_discord import DownloadDiscord
15
16
  from sticker_convert.downloaders.download_kakao import DownloadKakao
16
17
  from sticker_convert.downloaders.download_line import DownloadLine
17
18
  from sticker_convert.downloaders.download_signal import DownloadSignal
@@ -295,6 +296,12 @@ class Job:
295
296
  )
296
297
  error_msg += save_to_local_tip
297
298
 
299
+ if (
300
+ self.opt_input.option.startswith("discord")
301
+ and not self.opt_cred.discord_token
302
+ ):
303
+ error_msg += "[X] Downloading from Discord requires token.\n"
304
+
298
305
  if self.opt_output.option == "telegram" and not self.opt_cred.telegram_userid:
299
306
  error_msg += "[X] Uploading to telegram requires user_id \n"
300
307
  error_msg += " (From real account, not bot account).\n"
@@ -536,6 +543,9 @@ class Job:
536
543
  if self.opt_input.option == "viber":
537
544
  downloaders.append(DownloadViber.start)
538
545
 
546
+ if self.opt_input.option.startswith("discord"):
547
+ downloaders.append(DownloadDiscord.start)
548
+
539
549
  if len(downloaders) > 0:
540
550
  self.executor.cb("Downloading...")
541
551
  else:
@@ -547,7 +557,7 @@ class Job:
547
557
  for downloader in downloaders:
548
558
  self.executor.add_work(
549
559
  work_func=downloader,
550
- work_args=(self.opt_input.url, self.opt_input.dir, self.opt_cred),
560
+ work_args=(self.opt_input, self.opt_cred),
551
561
  )
552
562
 
553
563
  self.executor.join_workers()
@@ -223,6 +223,7 @@ class CredOption(BaseOption):
223
223
  kakao_phone_number: str = ""
224
224
  line_cookies: str = ""
225
225
  viber_auth: str = ""
226
+ discord_token: str = ""
226
227
 
227
228
  def to_dict(self) -> Dict[Any, Any]:
228
229
  return {
@@ -237,4 +238,5 @@ class CredOption(BaseOption):
237
238
  },
238
239
  "line": {"cookies": self.line_cookies},
239
240
  "viber": {"auth": self.viber_auth},
241
+ "discord": {"token": self.discord_token},
240
242
  }
@@ -375,6 +375,100 @@
375
375
  "quantize_method": "imagequant",
376
376
  "default_emoji": "😀"
377
377
  },
378
+ "discord": {
379
+ "size_max": {
380
+ "img": 512000,
381
+ "vid": 512000
382
+ },
383
+ "format": {
384
+ "img": ".png",
385
+ "vid": ".png"
386
+ },
387
+ "fps": {
388
+ "min": 1,
389
+ "max": 60,
390
+ "power": -0.3
391
+ },
392
+ "res": {
393
+ "w": {
394
+ "min": 320,
395
+ "max": 320
396
+ },
397
+ "h": {
398
+ "min": 320,
399
+ "max": 320
400
+ },
401
+ "power": 1
402
+ },
403
+ "quality": {
404
+ "min": 10,
405
+ "max": 95,
406
+ "power": 5
407
+ },
408
+ "color": {
409
+ "min": 32,
410
+ "max": 257,
411
+ "power": 3
412
+ },
413
+ "duration": {
414
+ "min": 0,
415
+ "max": 5000
416
+ },
417
+ "padding_percent": 0,
418
+ "bg_color": "",
419
+ "steps": 16,
420
+ "fake_vid": false,
421
+ "scale_filter": "bicubic",
422
+ "quantize_method": "imagequant",
423
+ "default_emoji": "😀"
424
+ },
425
+ "discord_emoji": {
426
+ "size_max": {
427
+ "img": 256000,
428
+ "vid": 256000
429
+ },
430
+ "format": {
431
+ "img": ".png",
432
+ "vid": ".gif"
433
+ },
434
+ "fps": {
435
+ "min": 1,
436
+ "max": 60,
437
+ "power": -0.3
438
+ },
439
+ "res": {
440
+ "w": {
441
+ "min": 32,
442
+ "max": 128
443
+ },
444
+ "h": {
445
+ "min": 32,
446
+ "max": 128
447
+ },
448
+ "power": 1
449
+ },
450
+ "quality": {
451
+ "min": 10,
452
+ "max": 95,
453
+ "power": 5
454
+ },
455
+ "color": {
456
+ "min": 32,
457
+ "max": 257,
458
+ "power": 3
459
+ },
460
+ "duration": {
461
+ "min": 0,
462
+ "max": 5000
463
+ },
464
+ "padding_percent": 0,
465
+ "bg_color": "",
466
+ "steps": 16,
467
+ "fake_vid": false,
468
+ "scale_filter": "bicubic",
469
+ "quantize_method": "imagequant",
470
+ "default_emoji": "😀"
471
+ },
378
472
  "imessage_small": {
379
473
  "size_max": {
380
474
  "img": 500000,
@@ -58,7 +58,6 @@
58
58
  "signal_uuid": "Set Signal uuid. Required for uploading Signal stickers.",
59
59
  "signal_password": "Set Signal password. Required for uploading Signal stickers.",
60
60
  "signal_get_auth": "Generate Signal uuid and password.",
61
- "signal_data_dir": "Optionally specify Signal data directory\nfor getting uuid and password. Useful for portable Signal.",
62
61
  "telegram_token": "Set Telegram token. Required for uploading and downloading Telegram stickers.",
63
62
  "telegram_userid": "Set telegram user_id (From real account, not bot account). Required for uploading Telegram stickers.",
64
63
  "kakao_auth_token": "Set Kakao auth_token. Required for downloading animated stickers from https://e.kakao.com/t/xxxxx",
@@ -72,6 +71,8 @@
72
71
  "viber_auth": "Set Viber authentication data.\nRequired for uploading Viber stickers.",
73
72
  "viber_get_auth": "Generate Viber authentication data.",
74
73
  "viber_bin_path": "Specify location of Viber Desktop application.\nUseful for portable installation.",
74
+ "discord_get_auth": "Get Discord token.",
75
+ "discord_token": "Set Discord token. Required for downloading Discord stickers and emojis.",
75
76
  "save_cred": "Save Signal and Telegram credentials."
76
77
  }
77
78
  }
@@ -59,6 +59,26 @@
59
59
  "author": false
60
60
  }
61
61
  },
62
+ "discord": {
63
+ "full_name": "Download from Discord",
64
+ "help": "Download discord stickers from a channel URL / ID as input",
65
+ "example": "Example: https://discord.com/channels/169256939211980800/@home\nOR 169256939211980800",
66
+ "address_lbls": "URL address / ID",
67
+ "metadata_provides": {
68
+ "title": true,
69
+ "author": true
70
+ }
71
+ },
72
+ "discord_emoji": {
73
+ "full_name": "Download from Discord (emoji)",
74
+ "help": "Download discord emojis from a channel URL / ID as input",
75
+ "example": "Example: https://discord.com/channels/169256939211980800/@home\nOR 169256939211980800",
76
+ "address_lbls": "URL address / ID",
77
+ "metadata_provides": {
78
+ "title": true,
79
+ "author": true
80
+ }
81
+ },
62
82
  "local": {
63
83
  "full_name": "From local directory",
64
84
  "help": "Load files from local directory on computer",
@@ -12,6 +12,7 @@ from sticker_convert.converter import StickerConvert
12
12
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
13
13
  from sticker_convert.uploaders.upload_base import UploadBase
14
14
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
15
+ from sticker_convert.utils.emoji import extract_emojis
15
16
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
16
17
  from sticker_convert.utils.media.codec_info import CodecInfo
17
18
  from sticker_convert.utils.media.format_verify import FormatVerify
@@ -55,12 +56,12 @@ class UploadSignal(UploadBase):
55
56
  sticker = Sticker()
56
57
  sticker.id = pack.nb_stickers
57
58
 
58
- emoji = emoji_dict.get(Path(src).stem, None)
59
- if not emoji:
59
+ emoji = extract_emojis(emoji_dict.get(Path(src).stem, ""))
60
+ if emoji == "":
60
61
  self.cb.put(
61
- f"Warning: Cannot find emoji for file {Path(src).name}, skip uploading this file..."
62
+ f"Warning: Cannot find emoji for file {Path(src).name}, using default emoji..."
62
63
  )
63
- continue
64
+ emoji = self.opt_comp.default_emoji
64
65
  sticker.emoji = emoji[:1]
65
66
 
66
67
  if Path(src).suffix == ".webp":
@@ -13,6 +13,7 @@ from sticker_convert.converter import StickerConvert
13
13
  from sticker_convert.job_option import CompOption, CredOption, OutputOption
14
14
  from sticker_convert.uploaders.upload_base import UploadBase
15
15
  from sticker_convert.utils.callback import CallbackProtocol, CallbackReturn
16
+ from sticker_convert.utils.emoji import extract_emojis
16
17
  from sticker_convert.utils.files.metadata_handler import MetadataHandler
17
18
  from sticker_convert.utils.media.format_verify import FormatVerify
18
19
 
@@ -158,18 +159,18 @@ class UploadTelegram(UploadBase):
158
159
  for count, src in enumerate(stickers):
159
160
  self.cb.put(f"Verifying {src} for uploading to telegram")
160
161
 
161
- emoji = emoji_dict.get(Path(src).stem, None)
162
- if emoji:
163
- if len(emoji) > 20:
164
- self.cb.put(
165
- f"Warning: {len(emoji)} emoji for file {Path(src).name}, exceeding limit of 20, keep first 20 only..."
166
- )
167
- emoji_list = [*emoji][:20]
168
- else:
162
+ emoji = extract_emojis(emoji_dict.get(Path(src).stem, ""))
163
+ if emoji == "":
164
+ self.cb.put(
165
+ f"Warning: Cannot find emoji for file {Path(src).name}, using default emoji..."
166
+ )
167
+ emoji_list = [self.opt_comp.default_emoji]
168
+
169
+ if len(emoji) > 20:
169
170
  self.cb.put(
170
- f"Warning: Cannot find emoji for file {Path(src).name}, skip uploading this file..."
171
+ f"Warning: {len(emoji)} emoji for file {Path(src).name}, exceeding limit of 20, keep first 20 only..."
171
172
  )
172
- continue
173
+ emoji_list = [*emoji][:20]
173
174
 
174
175
  ext = Path(src).suffix
175
176
  if ext == ".tgs":
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import time
7
+ from typing import Callable, Optional, Tuple
8
+ from urllib.parse import urlparse
9
+
10
+ from sticker_convert.definitions import CONFIG_DIR
11
+ from sticker_convert.utils.chrome_remotedebug import CRD
12
+ from sticker_convert.utils.process import killall
13
+
14
+
15
+ class GetDiscordAuth:
16
+ def __init__(self, cb_msg: Callable[..., None] = print):
17
+ chromedriver_download_dir = CONFIG_DIR / "bin"
18
+ os.makedirs(chromedriver_download_dir, exist_ok=True)
19
+
20
+ self.chromedriver_download_dir = chromedriver_download_dir
21
+
22
+ self.cb_msg = cb_msg
23
+
24
+ def get_discord_bin_path(self) -> Optional[str]:
25
+ discord_bin: Optional[str]
26
+ if platform.system() == "Windows":
27
+ discord_win_dirs: Tuple[Tuple[str, str], ...]
28
+ discord_win_dirs = (
29
+ (
30
+ os.path.expandvars("%localappdata%/Discord"),
31
+ "Discord.exe",
32
+ ),
33
+ (
34
+ os.path.expandvars("%localappdata%/DiscordCanary"),
35
+ "DiscordCanary.exe",
36
+ ),
37
+ (
38
+ os.path.expandvars("%localappdata%/DiscordPTB"),
39
+ "DiscordPTB.exe",
40
+ ),
41
+ )
42
+ for discord_dir, discord_bin in discord_win_dirs:
43
+ app_dir: Optional[str] = None
44
+ chrome_path: Optional[str] = None
45
+ for i in [j for j in os.listdir(discord_dir) if j.startswith("app-")]:
46
+ app_dir = os.path.join(discord_dir, i)
47
+ chrome_path = os.path.join(app_dir, discord_bin)
48
+ if os.path.isfile(chrome_path):
49
+ return chrome_path
50
+ else:
51
+ discord_dirs: Tuple[Optional[str], ...]
52
+ if platform.system() == "Darwin":
53
+ discord_dirs = (
54
+ "/Applications/Discord.app/Contents/MacOS/Discord",
55
+ "/Applications/Discord Canary.app/Contents/MacOS/Discord Canary",
56
+ "/Applications/Discord PTB.app/Contents/MacOS/Discord PTB",
57
+ )
58
+ else:
59
+ discord_dirs = (
60
+ shutil.which("discord"),
61
+ shutil.which("discord-canary"),
62
+ shutil.which("discord-ptb"),
63
+ )
64
+ for discord_bin in discord_dirs:
65
+ if discord_bin is not None and os.path.isfile(discord_bin):
66
+ return discord_bin
67
+ return None
68
+
69
+ def get_cred(self) -> Tuple[Optional[str], str]:
70
+ using_discord_app = False
71
+ chrome_path = self.get_discord_bin_path()
72
+ if chrome_path is not None:
73
+ using_discord_app = True
74
+ else:
75
+ chrome_path = CRD.get_chrome_path()
76
+ if chrome_path is None:
77
+ return None, "Please install Discord Desktop or Chrome and try again"
78
+
79
+ token = None
80
+ if using_discord_app:
81
+ killall("discord")
82
+
83
+ crd = CRD(chrome_path)
84
+ while True:
85
+ crd.connect()
86
+ if using_discord_app is False:
87
+ crd.navigate("https://discord.com/channels/@me")
88
+ break
89
+ else:
90
+ curr_url = crd.get_curr_url()
91
+ netloc = urlparse(curr_url).netloc
92
+ if netloc in ("discordapp.com", "discord.com"):
93
+ break
94
+ time.sleep(1)
95
+
96
+ while True:
97
+ try:
98
+ r = crd.exec_js(
99
+ "(webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken();"
100
+ )
101
+ except RuntimeError:
102
+ break
103
+ if (
104
+ json.loads(r).get("result", {}).get("result", {}).get("type", "")
105
+ == "string"
106
+ ):
107
+ token = json.loads(r)["result"]["result"]["value"]
108
+ break
109
+ time.sleep(1)
110
+ crd.close()
111
+
112
+ if token is None:
113
+ return None, "Failed to get token"
114
+
115
+ return token, f"Got token successfully:\ntoken={token}"