sticker-convert 2.14.0.0__py3-none-any.whl → 2.15.1.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 (37) hide show
  1. sticker_convert/auth/__init__.py +0 -0
  2. sticker_convert/auth/auth_base.py +19 -0
  3. sticker_convert/{utils/auth/get_discord_auth.py → auth/auth_discord.py} +40 -13
  4. sticker_convert/{utils/auth/get_kakao_auth_android_login.py → auth/auth_kakao_android_login.py} +80 -84
  5. sticker_convert/{utils/auth/get_kakao_auth_desktop_login.py → auth/auth_kakao_desktop_login.py} +72 -30
  6. sticker_convert/{utils/auth/get_kakao_auth_desktop_memdump.py → auth/auth_kakao_desktop_memdump.py} +31 -24
  7. sticker_convert/{utils/auth/get_line_auth.py → auth/auth_line.py} +21 -6
  8. sticker_convert/{utils/auth/get_signal_auth.py → auth/auth_signal.py} +18 -20
  9. sticker_convert/auth/auth_telethon.py +151 -0
  10. sticker_convert/{utils/auth/get_viber_auth.py → auth/auth_viber.py} +19 -11
  11. sticker_convert/{utils/auth → auth}/telegram_api.py +4 -13
  12. sticker_convert/cli.py +44 -70
  13. sticker_convert/downloaders/download_line.py +2 -2
  14. sticker_convert/downloaders/download_telegram.py +1 -1
  15. sticker_convert/gui.py +15 -100
  16. sticker_convert/gui_components/frames/comp_frame.py +12 -4
  17. sticker_convert/gui_components/frames/config_frame.py +14 -6
  18. sticker_convert/gui_components/frames/control_frame.py +1 -1
  19. sticker_convert/gui_components/frames/cred_frame.py +6 -8
  20. sticker_convert/gui_components/windows/advanced_compression_window.py +3 -4
  21. sticker_convert/gui_components/windows/base_window.py +7 -2
  22. sticker_convert/gui_components/windows/discord_get_auth_window.py +3 -7
  23. sticker_convert/gui_components/windows/kakao_get_auth_window.py +79 -51
  24. sticker_convert/gui_components/windows/line_get_auth_window.py +5 -14
  25. sticker_convert/gui_components/windows/signal_get_auth_window.py +4 -12
  26. sticker_convert/gui_components/windows/viber_get_auth_window.py +8 -11
  27. sticker_convert/job.py +16 -32
  28. sticker_convert/uploaders/upload_telegram.py +1 -1
  29. sticker_convert/utils/callback.py +238 -6
  30. sticker_convert/version.py +1 -1
  31. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/METADATA +6 -6
  32. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/RECORD +36 -34
  33. sticker_convert/utils/auth/telethon_setup.py +0 -97
  34. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/WHEEL +0 -0
  35. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/entry_points.txt +0 -0
  36. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/licenses/LICENSE +0 -0
  37. {sticker_convert-2.14.0.0.dist-info → sticker_convert-2.15.1.0.dist-info}/top_level.txt +0 -0
File without changes
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ from typing import Optional
3
+
4
+ from sticker_convert.job_option import CredOption
5
+ from sticker_convert.utils.callback import CallbackCli, CallbackProtocol
6
+
7
+
8
+ class AuthBase:
9
+ def __init__(
10
+ self,
11
+ opt_cred: CredOption,
12
+ cb: Optional[CallbackProtocol] = None,
13
+ ) -> None:
14
+ self.opt_cred = opt_cred
15
+ self.cb: CallbackProtocol
16
+ if cb is None:
17
+ self.cb = CallbackCli()
18
+ else:
19
+ self.cb = cb
@@ -4,23 +4,27 @@ import os
4
4
  import platform
5
5
  import shutil
6
6
  import time
7
- from typing import Callable, Optional, Tuple
7
+ from pathlib import Path
8
+ from typing import Any, Optional, Tuple
8
9
  from urllib.parse import urlparse
9
10
 
11
+ from sticker_convert.auth.auth_base import AuthBase
10
12
  from sticker_convert.definitions import CONFIG_DIR
11
13
  from sticker_convert.utils.chrome_remotedebug import CRD
12
- from sticker_convert.utils.process import killall
14
+ from sticker_convert.utils.process import find_pid_by_name, killall
13
15
 
16
+ OK_MSG = "Got token successfully:\ntoken={token}"
17
+ FAIL_MSG = "Failed to get token"
14
18
 
15
- class GetDiscordAuth:
16
- def __init__(self, cb_msg: Callable[..., None] = print) -> None:
19
+
20
+ class AuthDiscord(AuthBase):
21
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
22
+ super().__init__(*args, **kwargs)
17
23
  chromedriver_download_dir = CONFIG_DIR / "bin"
18
24
  os.makedirs(chromedriver_download_dir, exist_ok=True)
19
25
 
20
26
  self.chromedriver_download_dir = chromedriver_download_dir
21
27
 
22
- self.cb_msg = cb_msg
23
-
24
28
  def get_discord_bin_path(self) -> Optional[str]:
25
29
  discord_bin: Optional[str]
26
30
  if platform.system() == "Windows":
@@ -42,6 +46,8 @@ class GetDiscordAuth:
42
46
  for discord_dir, discord_bin in discord_win_dirs:
43
47
  app_dir: Optional[str] = None
44
48
  chrome_path: Optional[str] = None
49
+ if os.path.isdir(discord_dir) is False:
50
+ continue
45
51
  for i in [j for j in os.listdir(discord_dir) if j.startswith("app-")]:
46
52
  app_dir = os.path.join(discord_dir, i)
47
53
  chrome_path = os.path.join(app_dir, discord_bin)
@@ -67,6 +73,9 @@ class GetDiscordAuth:
67
73
  return None
68
74
 
69
75
  def get_cred(self) -> Tuple[Optional[str], str]:
76
+ msg = "Getting Discord authorization token..."
77
+ self.cb.put(("msg_dynamic", (msg,), None))
78
+
70
79
  using_discord_app = False
71
80
  chrome_path = self.get_discord_bin_path()
72
81
  if chrome_path is not None:
@@ -74,14 +83,26 @@ class GetDiscordAuth:
74
83
  else:
75
84
  chrome_path = CRD.get_chromium_path()
76
85
  if chrome_path is None:
86
+ self.cb.put(("msg_dynamic", (None,), None))
77
87
  return (
78
88
  None,
79
89
  "Please install Discord Desktop or Chrome/Chromium and try again",
80
90
  )
81
91
 
82
92
  token = None
83
- if using_discord_app:
84
- killall("discord")
93
+
94
+ if find_pid_by_name(Path(chrome_path).name):
95
+ response = self.cb.put(
96
+ (
97
+ "ask_bool",
98
+ (f"All {Path(chrome_path).name} will be closed. Continue?",),
99
+ None,
100
+ )
101
+ )
102
+ if response is True:
103
+ killall(Path(chrome_path).name.lower())
104
+ else:
105
+ return None, FAIL_MSG
85
106
 
86
107
  crd = CRD(chrome_path)
87
108
  while True:
@@ -98,9 +119,14 @@ class GetDiscordAuth:
98
119
 
99
120
  while True:
100
121
  try:
101
- r = crd.exec_js(
102
- "(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();"
103
- )
122
+ if using_discord_app:
123
+ r = crd.exec_js(
124
+ "(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();"
125
+ )
126
+ else:
127
+ r = crd.exec_js(
128
+ "const iframe=document.createElement('iframe');JSON.parse(document.body.appendChild(iframe).contentWindow.localStorage.token);"
129
+ )
104
130
  except RuntimeError:
105
131
  break
106
132
  if (
@@ -112,7 +138,8 @@ class GetDiscordAuth:
112
138
  time.sleep(1)
113
139
  crd.close()
114
140
 
141
+ self.cb.put(("msg_dynamic", (None,), None))
115
142
  if token is None:
116
- return None, "Failed to get token"
143
+ return None, FAIL_MSG
117
144
 
118
- return token, f"Got token successfully:\ntoken={token}"
145
+ return token, OK_MSG.format(token=token)
@@ -2,31 +2,24 @@
2
2
  import json
3
3
  import secrets
4
4
  import uuid
5
- from typing import Any, Callable, Dict, Optional
5
+ from typing import Any, Dict, Optional, Tuple
6
6
  from urllib.parse import parse_qs, urlparse
7
7
 
8
8
  import requests
9
9
  from bs4 import BeautifulSoup
10
10
 
11
- from sticker_convert.job_option import CredOption
11
+ from sticker_convert.auth.auth_base import AuthBase
12
12
 
13
+ OK_MSG = "Got auth_token successfully:\n{auth_token=}\n"
13
14
 
14
- class GetKakaoAuthAndroidLogin:
15
- def __init__(
16
- self,
17
- opt_cred: CredOption,
18
- cb_msg: Callable[..., None] = print,
19
- cb_msg_block: Callable[..., Any] = input,
20
- cb_ask_str: Callable[..., str] = input,
21
- ) -> None:
22
- self.username = opt_cred.kakao_username
23
- self.password = opt_cred.kakao_password
24
- self.country_code = opt_cred.kakao_country_code
25
- self.phone_number = opt_cred.kakao_phone_number
26
15
 
27
- self.cb_msg = cb_msg
28
- self.cb_msg_block = cb_msg_block
29
- self.cb_ask_str = cb_ask_str
16
+ class AuthKakaoAndroidLogin(AuthBase):
17
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
18
+ super().__init__(*args, **kwargs)
19
+ self.username = self.opt_cred.kakao_username
20
+ self.password = self.opt_cred.kakao_password
21
+ self.country_code = self.opt_cred.kakao_country_code
22
+ self.phone_number = self.opt_cred.kakao_phone_number
30
23
 
31
24
  self.device_uuid = secrets.token_hex(32)
32
25
  self.device_ssaid = secrets.token_hex(20)
@@ -67,8 +60,9 @@ class GetKakaoAuthAndroidLogin:
67
60
  return a.get("href").split("/")[-1]
68
61
  return "25.2.1"
69
62
 
70
- def login(self) -> bool:
71
- self.cb_msg("Logging in")
63
+ def login(self) -> Tuple[bool, str]:
64
+ msg = "Getting Kakao authorization token by Android login: Logging in"
65
+ self.cb.put(("msg_dynamic", (msg,), None))
72
66
 
73
67
  json_data = {
74
68
  "id": self.username,
@@ -83,29 +77,29 @@ class GetKakaoAuthAndroidLogin:
83
77
 
84
78
  response_json = json.loads(response.text)
85
79
 
80
+ self.cb.put(("msg_dynamic", (None,), None))
86
81
  if response_json["status"] != 0:
87
- self.cb_msg_block(f"Failed at login: {response.text}")
88
- return False
82
+ return False, f"Failed at login: {response.text}"
89
83
 
90
84
  self.headers["Ss"] = response.headers["Set-SS"]
91
85
  self.country_dicts = response_json["viewData"]["countries"]["all"]
92
86
 
93
- return True
87
+ return True, ""
94
88
 
95
- def get_country_iso(self) -> bool:
89
+ def get_country_iso(self) -> Tuple[bool, str]:
96
90
  self.country_iso = None
97
91
  for country_dict in self.country_dicts:
98
92
  if country_dict["code"] == self.country_code:
99
93
  self.country_iso = country_dict["iso"]
100
94
 
101
95
  if not self.country_iso:
102
- self.cb_msg_block("Invalid country code")
103
- return False
96
+ return False, "Invalid country code"
104
97
 
105
- return True
98
+ return True, ""
106
99
 
107
- def enter_phone(self) -> bool:
108
- self.cb_msg("Submitting phone number")
100
+ def enter_phone(self) -> Tuple[bool, str]:
101
+ msg = "Getting Kakao authorization token by Android login: Submitting phone number"
102
+ self.cb.put(("msg_dynamic", (msg,), None))
109
103
 
110
104
  json_data: Dict[str, Any] = {
111
105
  "countryCode": self.country_code,
@@ -123,9 +117,9 @@ class GetKakaoAuthAndroidLogin:
123
117
 
124
118
  response_json = json.loads(response.text)
125
119
 
120
+ self.cb.put(("msg_dynamic", (None,), None))
126
121
  if response_json["status"] != 0:
127
- self.cb_msg_block(f"Failed at entering phone number: {response.text}")
128
- return False
122
+ return False, f"Failed at entering phone number: {response.text}"
129
123
 
130
124
  self.verify_method = response_json["view"]
131
125
 
@@ -135,12 +129,9 @@ class GetKakaoAuthAndroidLogin:
135
129
  dest_number = response_json["viewData"]["moNumber"]
136
130
  msg = response_json["viewData"]["moMessage"]
137
131
  return self.verify_send_sms(dest_number, msg)
138
- self.cb_msg_block(f"Unknown verification method: {response.text}")
139
- return False
140
-
141
- def verify_send_sms(self, dest_number: str, msg: str) -> bool:
142
- self.cb_msg("Verification by sending SMS")
132
+ return False, f"Unknown verification method: {response.text}"
143
133
 
134
+ def verify_send_sms(self, dest_number: str, verify_msg: str) -> Tuple[bool, str]:
144
135
  response = requests.post(
145
136
  "https://katalk.kakao.com/android/account2/mo-sent", headers=self.headers
146
137
  )
@@ -148,15 +139,20 @@ class GetKakaoAuthAndroidLogin:
148
139
  response_json = json.loads(response.text)
149
140
 
150
141
  if response_json["status"] != 0:
151
- self.cb_msg_block(f"Failed at confirm sending SMS: {response.text}")
152
- return False
142
+ return False, f"Failed at confirm sending SMS: {response.text}"
153
143
 
154
144
  prompt = f"Send this SMS message to number {dest_number} then press enter:"
155
- self.cb_msg(msg)
156
- if self.cb_ask_str != input:
157
- self.cb_ask_str(prompt, initialvalue=msg, cli_show_initialvalue=False)
158
- else:
159
- input(prompt)
145
+ self.cb.put(
146
+ (
147
+ "ask_str",
148
+ None,
149
+ {
150
+ "msg": prompt,
151
+ "initialvalue": verify_msg,
152
+ "cli_show_initialvalue": False,
153
+ },
154
+ )
155
+ )
160
156
 
161
157
  response = requests.post(
162
158
  "https://katalk.kakao.com/android/account2/mo-confirm", headers=self.headers
@@ -165,21 +161,18 @@ class GetKakaoAuthAndroidLogin:
165
161
  response_json = json.loads(response.text)
166
162
 
167
163
  if response_json["status"] != 0:
168
- self.cb_msg_block(f"Failed at verifying SMS sent: {response.text}")
169
- return False
164
+ return False, f"Failed at verifying SMS sent: {response.text}"
170
165
 
171
166
  if response_json.get("reason") or "error" in response_json.get("message", ""):
172
- self.cb_msg_block(f"Failed at verifying SMS sent: {response.text}")
173
- return False
167
+ return False, f"Failed at verifying SMS sent: {response.text}"
174
168
 
175
169
  self.confirm_url = response_json.get("viewData", {}).get("url")
176
170
 
177
- return True
178
-
179
- def verify_receive_sms(self) -> bool:
180
- self.cb_msg("Verification by receiving SMS")
171
+ return True, ""
181
172
 
182
- passcode = self.cb_ask_str("Enter passcode received from SMS:")
173
+ def verify_receive_sms(self) -> Tuple[bool, str]:
174
+ msg = "Enter passcode received from SMS:"
175
+ passcode = self.cb.put(("ask_str", (msg,), None))
183
176
 
184
177
  json_data = {
185
178
  "passcode": passcode,
@@ -193,16 +186,19 @@ class GetKakaoAuthAndroidLogin:
193
186
 
194
187
  response_json = json.loads(response.text)
195
188
 
189
+ self.cb.put(("msg_dynamic", (None,), None))
196
190
  if response_json["status"] != 0:
197
- self.cb_msg_block(f"Failed at verifying passcode: {response.text}")
198
- return False
191
+ return False, f"Failed at verifying passcode: {response.text}"
199
192
 
200
193
  self.confirm_url = response_json.get("viewData", {}).get("url")
201
194
 
202
- return True
195
+ return True, ""
203
196
 
204
- def confirm_device_change(self) -> bool:
205
- self.cb_msg("Confirm device change")
197
+ def confirm_device_change(self) -> Tuple[bool, str]:
198
+ msg = (
199
+ "Getting Kakao authorization token by Android login: Confirm device change"
200
+ )
201
+ self.cb.put(("msg_dynamic", (msg,), None))
206
202
 
207
203
  confirm_url_parsed = urlparse(self.confirm_url) # type: ignore
208
204
  confirm_url_qs = parse_qs(confirm_url_parsed.query) # type: ignore
@@ -238,14 +234,15 @@ class GetKakaoAuthAndroidLogin:
238
234
 
239
235
  response_json = json.loads(response.text)
240
236
 
237
+ self.cb.put(("msg_dynamic", (None,), None))
241
238
  if response_json["status"] != 0:
242
- self.cb_msg_block(f"Failed at confirm device change: {response.text}")
243
- return False
239
+ return False, f"Failed at confirm device change: {response.text}"
244
240
 
245
- return True
241
+ return True, ""
246
242
 
247
- def passcode_callback(self) -> bool:
248
- self.cb_msg("Passcode callback")
243
+ def passcode_callback(self) -> Tuple[bool, str]:
244
+ msg = "Getting Kakao authorization token by Android login: Passcode callback"
245
+ self.cb.put(("msg_dynamic", (msg,), None))
249
246
 
250
247
  response = requests.get(
251
248
  "https://katalk.kakao.com/android/account2/passcode/callback",
@@ -254,14 +251,15 @@ class GetKakaoAuthAndroidLogin:
254
251
 
255
252
  response_json = json.loads(response.text)
256
253
 
254
+ self.cb.put(("msg_dynamic", (None,), None))
257
255
  if response_json["status"] != 0:
258
- self.cb_msg_block(f"Failed at passcode callback: {response.text}")
259
- return False
256
+ return False, f"Failed at passcode callback: {response.text}"
260
257
 
261
- return True
258
+ return True, ""
262
259
 
263
- def skip_restoration(self) -> bool:
264
- self.cb_msg("Skip restoration")
260
+ def skip_restoration(self) -> Tuple[bool, str]:
261
+ msg = "Getting Kakao authorization token by Android login: Skip restoration"
262
+ self.cb.put(("msg_dynamic", (msg,), None))
265
263
 
266
264
  response = requests.post(
267
265
  "https://katalk.kakao.com/android/account2/skip-restoration",
@@ -270,19 +268,19 @@ class GetKakaoAuthAndroidLogin:
270
268
  response_json = json.loads(response.text)
271
269
 
272
270
  if response_json["status"] != 0:
273
- self.cb_msg_block(f"Failed at skip restoration: {response.text}")
274
- return False
271
+ return False, f"Failed at skip restoration: {response.text}"
275
272
 
276
273
  self.nickname = response_json.get("viewData", {}).get("nickname")
277
274
 
275
+ self.cb.put(("msg_dynamic", (None,), None))
278
276
  if self.nickname is None:
279
- self.cb_msg_block(f"Failed at passcode callback: {response.text}")
280
- return False
277
+ return False, f"Failed at passcode callback: {response.text}"
281
278
 
282
- return True
279
+ return True, ""
283
280
 
284
- def get_profile(self) -> bool:
285
- self.cb_msg("Get profile")
281
+ def get_profile(self) -> Tuple[bool, str]:
282
+ msg = "Getting Kakao authorization token by Android login: Get profile"
283
+ self.cb.put(("msg_dynamic", (msg,), None))
286
284
 
287
285
  json_data = {
288
286
  "nickname": self.nickname,
@@ -298,18 +296,16 @@ class GetKakaoAuthAndroidLogin:
298
296
 
299
297
  response_json = json.loads(response.text)
300
298
 
299
+ self.cb.put(("msg_dynamic", (None,), None))
301
300
  if response_json["status"] != 0:
302
- self.cb_msg_block(f"Failed at get profile: {response.text}")
303
- return False
301
+ return False, f"Failed at get profile: {response.text}"
304
302
 
305
303
  self.access_token = response_json["signupData"]["oauth2Token"]["accessToken"]
306
304
 
307
- return True
308
-
309
- def get_cred(self) -> Optional[str]:
310
- self.cb_msg("Get authorization token")
305
+ return True, ""
311
306
 
312
- authorization_token = None
307
+ def get_cred(self) -> Tuple[Optional[str], str]:
308
+ auth_token = None
313
309
 
314
310
  steps = (
315
311
  self.login,
@@ -322,9 +318,9 @@ class GetKakaoAuthAndroidLogin:
322
318
  )
323
319
 
324
320
  for step in steps:
325
- success = step()
321
+ success, msg = step()
326
322
  if not success:
327
- return None
323
+ return None, msg
328
324
 
329
- authorization_token = self.access_token + "-" + self.device_uuid
330
- return authorization_token
325
+ auth_token = self.access_token + "-" + self.device_uuid
326
+ return auth_token, OK_MSG.format(auth_token=auth_token)
@@ -4,40 +4,36 @@ import hashlib
4
4
  import json
5
5
  import os
6
6
  import platform
7
+ import re
7
8
  import shutil
8
9
  import socket
9
10
  import subprocess
10
11
  import time
11
12
  import uuid
12
- from typing import Any, Callable, Optional, Tuple
13
+ from typing import Any, Optional, Tuple
13
14
 
14
15
  import requests
16
+ from bs4 import BeautifulSoup
15
17
  from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
16
18
 
17
- from sticker_convert.job_option import CredOption
19
+ from sticker_convert.auth.auth_base import AuthBase
18
20
 
21
+ OK_MSG = "Login successful, auth_token: {auth_token}"
19
22
 
20
- class GetKakaoAuthDesktopLogin:
21
- def __init__(
22
- self,
23
- opt_cred: CredOption,
24
- cb_msg: Callable[..., None] = print,
25
- cb_msg_block: Callable[..., Any] = input,
26
- cb_ask_str: Callable[..., str] = input,
27
- ) -> None:
28
- self.opt_cred = opt_cred
29
- self.username = opt_cred.kakao_username
30
- self.password = opt_cred.kakao_password
31
23
 
32
- self.cb_msg = cb_msg
33
- self.cb_msg_block = cb_msg_block
34
- self.cb_ask_str = cb_ask_str
24
+ class AuthKakaoDesktopLogin(AuthBase):
25
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
26
+ super().__init__(*args, **kwargs)
27
+ self.username = self.opt_cred.kakao_username
28
+ self.password = self.opt_cred.kakao_password
35
29
 
36
30
  if platform.system() == "Darwin":
37
31
  self.plat = "mac"
38
32
  user_agent_os = "Mc"
39
33
  self.os_version = platform.mac_ver()[0]
40
- version = "25.2.0"
34
+ version = self.macos_get_ver()
35
+ if version is None:
36
+ version = "25.2.0"
41
37
  else:
42
38
  self.plat = "win32"
43
39
  user_agent_os = "Wd"
@@ -45,7 +41,9 @@ class GetKakaoAuthDesktopLogin:
45
41
  self.os_version = platform.uname().version
46
42
  else:
47
43
  self.os_version = "10.0"
48
- version = "25.7.7"
44
+ version = self.windows_get_ver()
45
+ if version is None:
46
+ version = "25.8.2"
49
47
 
50
48
  user_agent = f"KT/{version} {user_agent_os}/{self.os_version} en"
51
49
  self.headers = {
@@ -59,6 +57,8 @@ class GetKakaoAuthDesktopLogin:
59
57
  }
60
58
 
61
59
  self.device_name = socket.gethostname()
60
+ if platform.system() != "Darwin":
61
+ self.device_name = self.device_name.upper()
62
62
  self.device_uuid = self.get_device_uuid()
63
63
 
64
64
  hash = hashlib.sha512(
@@ -74,6 +74,31 @@ class GetKakaoAuthDesktopLogin:
74
74
  def pkcs7_pad(self, m: str) -> str:
75
75
  return m + chr(16 - len(m) % 16) * (16 - len(m) % 16)
76
76
 
77
+ def windows_get_ver(self) -> Optional[str]:
78
+ r = requests.get(
79
+ "https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/k/Kakao/KakaoTalk"
80
+ )
81
+ rjson = json.loads(r.text)
82
+ if len(rjson) == 0:
83
+ return None
84
+ ver = rjson[-1]["name"]
85
+ return ".".join(ver.split(".")[:3])
86
+
87
+ def macos_get_ver(self) -> Optional[str]:
88
+ country = "us"
89
+ app_name = "kakaotalk-messenger"
90
+ app_id = "362057947"
91
+
92
+ url = f"https://apps.apple.com/{country}/app/{app_name}/id{app_id}"
93
+
94
+ r = requests.get(url)
95
+ soup = BeautifulSoup(r.text, "html.parser")
96
+ version_tag = soup.find("p", {"class": re.compile(".*latest__version")})
97
+ version_str = None
98
+ if version_tag is not None:
99
+ version_str = version_tag.text.replace("Version ", "")
100
+ return version_str
101
+
77
102
  def windows_get_pragma(self, use_wine: bool = False) -> Optional[str]:
78
103
  sys_uuid = None
79
104
  hdd_model = None
@@ -132,10 +157,10 @@ class GetKakaoAuthDesktopLogin:
132
157
 
133
158
  return base64.b64encode(hwid_sha1 + hwid_sha256).decode()
134
159
  else:
135
- use_wine = True if platform.system != "Windows" else False
160
+ use_wine = True if platform.system() != "Windows" else False
136
161
  pragma = self.windows_get_pragma(use_wine=use_wine)
137
162
  if pragma is None:
138
- if platform.system == "Windows":
163
+ if platform.system() == "Windows":
139
164
  if self.opt_cred.kakao_device_uuid:
140
165
  sys_uuid = self.opt_cred.kakao_device_uuid
141
166
  else:
@@ -248,28 +273,45 @@ class GetKakaoAuthDesktopLogin:
248
273
  return json.loads(response.text)
249
274
 
250
275
  def get_cred(self) -> Tuple[Optional[str], str]:
251
- self.cb_msg("Get authorization token")
276
+ msg = "Getting Kakao authorization token by desktop login..."
277
+ self.cb.put(("msg_dynamic", (msg,), None))
252
278
  rjson = self.login()
253
279
  access_token = rjson.get("access_token")
254
280
  if access_token is not None:
255
281
  auth_token = access_token + "-" + self.device_uuid
256
- return auth_token, f"Login successful, auth_token: {auth_token}"
282
+ self.cb.put(("msg_dynamic", (None,), None))
283
+ return auth_token, OK_MSG.format(auth_token=auth_token)
257
284
 
258
285
  rjson = self.generate_passcode()
259
286
  if rjson.get("status") != 0:
260
287
  return None, f"Failed to generate passcode: {rjson}"
261
288
  passcode = rjson["passcode"]
262
289
 
263
- start_time = time.time()
264
- register_success = False
265
- while time.time() - start_time < 60:
266
- self.cb_ask_str(f"Please enter passcode within 1 minute: {passcode}")
290
+ fail_reason = None
291
+ self.cb.put(("msg_dynamic", (None,), None))
292
+ while True:
267
293
  rjson = self.register_device()
268
294
  if rjson["status"] == 0:
269
- register_success = True
270
295
  break
271
- if register_success is False:
272
- return None, f"Failed to register device: {rjson}"
296
+ elif rjson["status"] == -110:
297
+ fail_reason = "Timeout"
298
+ break
299
+ elif rjson["status"] != -100:
300
+ fail_reason = str(rjson)
301
+ break
302
+ time_remaining = rjson.get("remainingSeconds")
303
+ next_req_time = rjson.get("nextRequestIntervalInSeconds")
304
+ if time_remaining is None or next_req_time is None:
305
+ fail_reason = str(rjson)
306
+ msg = f"Please enter passcode in Kakao app on mobile device within {time_remaining} seconds: {passcode}"
307
+ msg_dynamic_window_exist = self.cb.put(("msg_dynamic", (msg,), None))
308
+ if msg_dynamic_window_exist is False:
309
+ fail_reason = "Cancelled"
310
+ break
311
+ time.sleep(next_req_time)
312
+ self.cb.put(("msg_dynamic", (None,), None))
313
+ if fail_reason is not None:
314
+ return None, f"Failed to register device: {fail_reason}"
273
315
 
274
316
  rjson = self.login()
275
317
  if rjson.get("status") == -101:
@@ -279,4 +321,4 @@ class GetKakaoAuthDesktopLogin:
279
321
  return None, f"Failed to login after registering device: {rjson}"
280
322
 
281
323
  auth_token = access_token + "-" + self.device_uuid
282
- return auth_token, f"Login successful, auth_token: {auth_token}"
324
+ return auth_token, OK_MSG.format(auth_token=auth_token)