sticker-convert 2.13.3.0__py3-none-any.whl → 2.17.0.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 (93) hide show
  1. sticker_convert/__main__.py +24 -27
  2. sticker_convert/auth/__init__.py +0 -0
  3. sticker_convert/auth/auth_base.py +19 -0
  4. sticker_convert/{utils/auth/get_discord_auth.py → auth/auth_discord.py} +149 -118
  5. sticker_convert/{utils/auth/get_kakao_auth.py → auth/auth_kakao_android_login.py} +331 -330
  6. sticker_convert/auth/auth_kakao_desktop_login.py +327 -0
  7. sticker_convert/{utils/auth/get_kakao_desktop_auth.py → auth/auth_kakao_desktop_memdump.py} +281 -263
  8. sticker_convert/{utils/auth/get_line_auth.py → auth/auth_line.py} +98 -80
  9. sticker_convert/{utils/auth/get_signal_auth.py → auth/auth_signal.py} +139 -135
  10. sticker_convert/auth/auth_telethon.py +161 -0
  11. sticker_convert/{utils/auth/get_viber_auth.py → auth/auth_viber.py} +250 -235
  12. sticker_convert/{utils/auth → auth}/telegram_api.py +736 -675
  13. sticker_convert/cli.py +623 -608
  14. sticker_convert/converter.py +1093 -1084
  15. sticker_convert/definitions.py +4 -0
  16. sticker_convert/downloaders/download_band.py +111 -110
  17. sticker_convert/downloaders/download_base.py +171 -166
  18. sticker_convert/downloaders/download_discord.py +92 -91
  19. sticker_convert/downloaders/download_kakao.py +417 -404
  20. sticker_convert/downloaders/download_line.py +484 -475
  21. sticker_convert/downloaders/download_ogq.py +80 -79
  22. sticker_convert/downloaders/download_signal.py +108 -105
  23. sticker_convert/downloaders/download_telegram.py +56 -55
  24. sticker_convert/downloaders/download_viber.py +121 -120
  25. sticker_convert/gui.py +788 -873
  26. sticker_convert/gui_components/frames/comp_frame.py +180 -166
  27. sticker_convert/gui_components/frames/config_frame.py +156 -113
  28. sticker_convert/gui_components/frames/control_frame.py +32 -30
  29. sticker_convert/gui_components/frames/cred_frame.py +232 -233
  30. sticker_convert/gui_components/frames/input_frame.py +139 -137
  31. sticker_convert/gui_components/frames/output_frame.py +112 -110
  32. sticker_convert/gui_components/frames/right_clicker.py +25 -23
  33. sticker_convert/gui_components/windows/advanced_compression_window.py +757 -757
  34. sticker_convert/gui_components/windows/base_window.py +7 -2
  35. sticker_convert/gui_components/windows/discord_get_auth_window.py +79 -82
  36. sticker_convert/gui_components/windows/kakao_get_auth_window.py +511 -321
  37. sticker_convert/gui_components/windows/line_get_auth_window.py +94 -102
  38. sticker_convert/gui_components/windows/signal_get_auth_window.py +84 -89
  39. sticker_convert/gui_components/windows/viber_get_auth_window.py +168 -168
  40. sticker_convert/ios-message-stickers-template/.github/FUNDING.yml +3 -3
  41. sticker_convert/ios-message-stickers-template/README.md +10 -10
  42. sticker_convert/ios-message-stickers-template/stickers/Info.plist +43 -43
  43. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Info.plist +31 -31
  44. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Contents.json +6 -6
  45. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Contents.json +20 -20
  46. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 1.sticker/Contents.json +9 -9
  47. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 2.sticker/Contents.json +9 -9
  48. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/Sticker Pack.stickerpack/Sticker 3.sticker/Contents.json +9 -9
  49. sticker_convert/ios-message-stickers-template/stickers StickerPackExtension/Stickers.xcstickers/iMessage App Icon.stickersiconset/Contents.json +91 -91
  50. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.pbxproj +364 -364
  51. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -7
  52. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -8
  53. sticker_convert/ios-message-stickers-template/stickers.xcodeproj/xcuserdata/niklaspeterson.xcuserdatad/xcschemes/xcschememanagement.plist +14 -14
  54. sticker_convert/job.py +166 -130
  55. sticker_convert/job_option.py +1 -0
  56. sticker_convert/locales/en_US/LC_MESSAGES/base.mo +0 -0
  57. sticker_convert/locales/ja_JP/LC_MESSAGES/base.mo +0 -0
  58. sticker_convert/locales/zh_CN/LC_MESSAGES/base.mo +0 -0
  59. sticker_convert/locales/zh_TW/LC_MESSAGES/base.mo +0 -0
  60. sticker_convert/py.typed +0 -0
  61. sticker_convert/resources/NotoColorEmoji.ttf +0 -0
  62. sticker_convert/resources/help.ja_JP.json +88 -0
  63. sticker_convert/resources/help.json +10 -7
  64. sticker_convert/resources/help.zh_CN.json +88 -0
  65. sticker_convert/resources/help.zh_TW.json +88 -0
  66. sticker_convert/resources/input.ja_JP.json +74 -0
  67. sticker_convert/resources/input.json +121 -121
  68. sticker_convert/resources/input.zh_CN.json +74 -0
  69. sticker_convert/resources/input.zh_TW.json +74 -0
  70. sticker_convert/resources/output.ja_JP.json +38 -0
  71. sticker_convert/resources/output.zh_CN.json +38 -0
  72. sticker_convert/resources/output.zh_TW.json +38 -0
  73. sticker_convert/uploaders/compress_wastickers.py +186 -177
  74. sticker_convert/uploaders/upload_base.py +44 -35
  75. sticker_convert/uploaders/upload_signal.py +218 -203
  76. sticker_convert/uploaders/upload_telegram.py +353 -338
  77. sticker_convert/uploaders/upload_viber.py +178 -169
  78. sticker_convert/uploaders/xcode_imessage.py +295 -286
  79. sticker_convert/utils/callback.py +238 -6
  80. sticker_convert/utils/emoji.py +16 -4
  81. sticker_convert/utils/files/json_resources_loader.py +24 -19
  82. sticker_convert/utils/files/metadata_handler.py +3 -3
  83. sticker_convert/utils/translate.py +108 -0
  84. sticker_convert/utils/url_detect.py +40 -37
  85. sticker_convert/version.py +1 -1
  86. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/METADATA +89 -74
  87. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/RECORD +91 -74
  88. sticker_convert/utils/auth/telethon_setup.py +0 -97
  89. sticker_convert/utils/singletons.py +0 -18
  90. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/WHEEL +0 -0
  91. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/entry_points.txt +0 -0
  92. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/licenses/LICENSE +0 -0
  93. {sticker_convert-2.13.3.0.dist-info → sticker_convert-2.17.0.0.dist-info}/top_level.txt +0 -0
@@ -1,330 +1,331 @@
1
- #!/usr/bin/env python3
2
- import json
3
- import secrets
4
- import uuid
5
- from typing import Any, Callable, Dict, Optional
6
- from urllib.parse import parse_qs, urlparse
7
-
8
- import requests
9
- from bs4 import BeautifulSoup
10
-
11
- from sticker_convert.job_option import CredOption
12
-
13
-
14
- class GetKakaoAuth:
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
-
27
- self.cb_msg = cb_msg
28
- self.cb_msg_block = cb_msg_block
29
- self.cb_ask_str = cb_ask_str
30
-
31
- self.device_uuid = secrets.token_hex(32)
32
- self.device_ssaid = secrets.token_hex(20)
33
- self.uuid_c = str(uuid.uuid4())
34
- self.device_info = (
35
- f"android/30; uuid={self.device_uuid}; ssaid={self.device_ssaid}; "
36
- + "model=SDK_GPHONE_X86_64; screen_resolution=1080x1920; sim=310260/1/us; onestore=false; uvc3=null"
37
- )
38
- self.app_platform = "android"
39
- self.app_version_number = self.get_version()
40
- self.app_language = "en"
41
- self.app_version = (
42
- f"{self.app_platform}/{self.app_version_number}/{self.app_language}"
43
- )
44
-
45
- self.headers = {
46
- "Host": "katalk.kakao.com",
47
- "Accept-Language": "en",
48
- "User-Agent": f"KT/{self.app_version_number} An/11 en",
49
- "Device-Info": self.device_info,
50
- "A": self.app_version,
51
- "C": self.uuid_c,
52
- "Content-Type": "application/json",
53
- "Connection": "close",
54
- }
55
-
56
- def get_version(self) -> str:
57
- # It is difficult to get app version number from Google Play
58
- r = requests.get(
59
- "https://apkpure.net/kakaotalk-messenger/com.kakao.talk/versions"
60
- )
61
- soup = BeautifulSoup(r.text, "html.parser")
62
- for li in soup.find_all("li"):
63
- a = li.find("a", class_="dt-version-icon")
64
- if a is None:
65
- continue
66
- if "/kakaotalk-messenger/com.kakao.talk/download/" in a.get("href", ""):
67
- return a.get("href").split("/")[-1]
68
- return "25.2.1"
69
-
70
- def login(self) -> bool:
71
- self.cb_msg("Logging in")
72
-
73
- json_data = {
74
- "id": self.username,
75
- "password": self.password,
76
- }
77
-
78
- response = requests.post(
79
- "https://katalk.kakao.com/android/account2/login",
80
- headers=self.headers,
81
- json=json_data,
82
- )
83
-
84
- response_json = json.loads(response.text)
85
-
86
- if response_json["status"] != 0:
87
- self.cb_msg_block(f"Failed at login: {response.text}")
88
- return False
89
-
90
- self.headers["Ss"] = response.headers["Set-SS"]
91
- self.country_dicts = response_json["viewData"]["countries"]["all"]
92
-
93
- return True
94
-
95
- def get_country_iso(self) -> bool:
96
- self.country_iso = None
97
- for country_dict in self.country_dicts:
98
- if country_dict["code"] == self.country_code:
99
- self.country_iso = country_dict["iso"]
100
-
101
- if not self.country_iso:
102
- self.cb_msg_block("Invalid country code")
103
- return False
104
-
105
- return True
106
-
107
- def enter_phone(self) -> bool:
108
- self.cb_msg("Submitting phone number")
109
-
110
- json_data: Dict[str, Any] = {
111
- "countryCode": self.country_code,
112
- "countryIso": self.country_iso,
113
- "phoneNumber": self.phone_number,
114
- "termCodes": [],
115
- "simPhoneNumber": f"+{self.country_code}{self.phone_number}",
116
- }
117
-
118
- response = requests.post(
119
- "https://katalk.kakao.com/android/account2/phone-number",
120
- headers=self.headers,
121
- json=json_data,
122
- )
123
-
124
- response_json = json.loads(response.text)
125
-
126
- if response_json["status"] != 0:
127
- self.cb_msg_block(f"Failed at entering phone number: {response.text}")
128
- return False
129
-
130
- self.verify_method = response_json["view"]
131
-
132
- if self.verify_method == "passcode":
133
- return self.verify_receive_sms()
134
- if self.verify_method == "mo-send":
135
- dest_number = response_json["viewData"]["moNumber"]
136
- msg = response_json["viewData"]["moMessage"]
137
- 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")
143
-
144
- response = requests.post(
145
- "https://katalk.kakao.com/android/account2/mo-sent", headers=self.headers
146
- )
147
-
148
- response_json = json.loads(response.text)
149
-
150
- if response_json["status"] != 0:
151
- self.cb_msg_block(f"Failed at confirm sending SMS: {response.text}")
152
- return False
153
-
154
- 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)
160
-
161
- response = requests.post(
162
- "https://katalk.kakao.com/android/account2/mo-confirm", headers=self.headers
163
- )
164
-
165
- response_json = json.loads(response.text)
166
-
167
- if response_json["status"] != 0:
168
- self.cb_msg_block(f"Failed at verifying SMS sent: {response.text}")
169
- return False
170
-
171
- 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
174
-
175
- self.confirm_url = response_json.get("viewData", {}).get("url")
176
-
177
- return True
178
-
179
- def verify_receive_sms(self) -> bool:
180
- self.cb_msg("Verification by receiving SMS")
181
-
182
- passcode = self.cb_ask_str("Enter passcode received from SMS:")
183
-
184
- json_data = {
185
- "passcode": passcode,
186
- }
187
-
188
- response = requests.post(
189
- "https://katalk.kakao.com/android/account2/passcode",
190
- headers=self.headers,
191
- json=json_data,
192
- )
193
-
194
- response_json = json.loads(response.text)
195
-
196
- if response_json["status"] != 0:
197
- self.cb_msg_block(f"Failed at verifying passcode: {response.text}")
198
- return False
199
-
200
- self.confirm_url = response_json.get("viewData", {}).get("url")
201
-
202
- return True
203
-
204
- def confirm_device_change(self) -> bool:
205
- self.cb_msg("Confirm device change")
206
-
207
- confirm_url_parsed = urlparse(self.confirm_url) # type: ignore
208
- confirm_url_qs = parse_qs(confirm_url_parsed.query) # type: ignore
209
- session_token: str = confirm_url_qs["sessionToken"][0] # type: ignore
210
-
211
- headers_browser = {
212
- "Host": "katalk.kakao.com",
213
- "Accept": "*/*",
214
- "X-Requested-With": "XMLHttpRequest",
215
- "User-Agent": "Mozilla/5.0 (Linux; Android 11; Android SDK built for x86 Build/RSR1.210210.001.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36;KAKAOTALK 2410030",
216
- "Content-Type": "application/json",
217
- "Origin": "https://katalk.kakao.com",
218
- "Sec-Fetch-Site": "same-origin",
219
- "Sec-Fetch-Mode": "cors",
220
- "Sec-Fetch-Dest": "empty",
221
- "Referer": self.confirm_url,
222
- "Accept-Language": "en-US,en;q=0.9",
223
- "Connection": "close",
224
- }
225
-
226
- json_data: Dict[str, str] = {
227
- "decision": "continue",
228
- "lang": self.app_language,
229
- "sessionToken": session_token,
230
- "appVersion": self.app_version_number,
231
- }
232
-
233
- response = requests.post(
234
- "https://katalk.kakao.com/android/account2/confirm-device-change",
235
- headers=headers_browser,
236
- json=json_data,
237
- )
238
-
239
- response_json = json.loads(response.text)
240
-
241
- if response_json["status"] != 0:
242
- self.cb_msg_block(f"Failed at confirm device change: {response.text}")
243
- return False
244
-
245
- return True
246
-
247
- def passcode_callback(self) -> bool:
248
- self.cb_msg("Passcode callback")
249
-
250
- response = requests.get(
251
- "https://katalk.kakao.com/android/account2/passcode/callback",
252
- headers=self.headers,
253
- )
254
-
255
- response_json = json.loads(response.text)
256
-
257
- if response_json["status"] != 0:
258
- self.cb_msg_block(f"Failed at passcode callback: {response.text}")
259
- return False
260
-
261
- return True
262
-
263
- def skip_restoration(self) -> bool:
264
- self.cb_msg("Skip restoration")
265
-
266
- response = requests.post(
267
- "https://katalk.kakao.com/android/account2/skip-restoration",
268
- headers=self.headers,
269
- )
270
- response_json = json.loads(response.text)
271
-
272
- if response_json["status"] != 0:
273
- self.cb_msg_block(f"Failed at skip restoration: {response.text}")
274
- return False
275
-
276
- self.nickname = response_json.get("viewData", {}).get("nickname")
277
-
278
- if self.nickname is None:
279
- self.cb_msg_block(f"Failed at passcode callback: {response.text}")
280
- return False
281
-
282
- return True
283
-
284
- def get_profile(self) -> bool:
285
- self.cb_msg("Get profile")
286
-
287
- json_data = {
288
- "nickname": self.nickname,
289
- "profileImageFlag": 1,
290
- "friendAutomation": True,
291
- }
292
-
293
- response = requests.post(
294
- "https://katalk.kakao.com/android/account2/profile",
295
- headers=self.headers,
296
- json=json_data,
297
- )
298
-
299
- response_json = json.loads(response.text)
300
-
301
- if response_json["status"] != 0:
302
- self.cb_msg_block(f"Failed at get profile: {response.text}")
303
- return False
304
-
305
- self.access_token = response_json["signupData"]["oauth2Token"]["accessToken"]
306
-
307
- return True
308
-
309
- def get_cred(self) -> Optional[str]:
310
- self.cb_msg("Get authorization token")
311
-
312
- authorization_token = None
313
-
314
- steps = (
315
- self.login,
316
- self.get_country_iso,
317
- self.enter_phone,
318
- self.confirm_device_change,
319
- self.passcode_callback,
320
- self.skip_restoration,
321
- self.get_profile,
322
- )
323
-
324
- for step in steps:
325
- success = step()
326
- if not success:
327
- return None
328
-
329
- authorization_token = self.access_token + "-" + self.device_uuid
330
- return authorization_token
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import secrets
4
+ import uuid
5
+ from typing import Any, Dict, Optional, Tuple
6
+ from urllib.parse import parse_qs, urlparse
7
+
8
+ import requests
9
+ from bs4 import BeautifulSoup
10
+
11
+ from sticker_convert.auth.auth_base import AuthBase
12
+ from sticker_convert.utils.translate import I
13
+
14
+
15
+ class AuthKakaoAndroidLogin(AuthBase):
16
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
17
+ self.OK_MSG = I("Got auth_token successfully:\nauth_token={}\n")
18
+
19
+ super().__init__(*args, **kwargs)
20
+ self.username = self.opt_cred.kakao_username
21
+ self.password = self.opt_cred.kakao_password
22
+ self.country_code = self.opt_cred.kakao_country_code
23
+ self.phone_number = self.opt_cred.kakao_phone_number
24
+
25
+ self.device_uuid = secrets.token_hex(32)
26
+ self.device_ssaid = secrets.token_hex(20)
27
+ self.uuid_c = str(uuid.uuid4())
28
+ self.device_info = (
29
+ f"android/30; uuid={self.device_uuid}; ssaid={self.device_ssaid}; "
30
+ + "model=SDK_GPHONE_X86_64; screen_resolution=1080x1920; sim=310260/1/us; onestore=false; uvc3=null"
31
+ )
32
+ self.app_platform = "android"
33
+ self.app_version_number = self.get_version()
34
+ self.app_language = "en"
35
+ self.app_version = (
36
+ f"{self.app_platform}/{self.app_version_number}/{self.app_language}"
37
+ )
38
+
39
+ self.headers = {
40
+ "Host": "katalk.kakao.com",
41
+ "Accept-Language": "en",
42
+ "User-Agent": f"KT/{self.app_version_number} An/11 en",
43
+ "Device-Info": self.device_info,
44
+ "A": self.app_version,
45
+ "C": self.uuid_c,
46
+ "Content-Type": "application/json",
47
+ "Connection": "close",
48
+ }
49
+
50
+ def get_version(self) -> str:
51
+ # It is difficult to get app version number from Google Play
52
+ r = requests.get(
53
+ "https://apkpure.net/kakaotalk-messenger/com.kakao.talk/versions"
54
+ )
55
+ soup = BeautifulSoup(r.text, "html.parser")
56
+ for li in soup.find_all("li"):
57
+ a = li.find("a", class_="dt-version-icon")
58
+ if a is None:
59
+ continue
60
+ if "/kakaotalk-messenger/com.kakao.talk/download/" in a.get("href", ""):
61
+ return a.get("href").split("/")[-1]
62
+ return "25.2.1"
63
+
64
+ def login(self) -> Tuple[bool, str]:
65
+ msg = I("Getting Kakao authorization token by Android login: Logging in")
66
+ self.cb.put(("msg_dynamic", (msg,), None))
67
+
68
+ json_data = {
69
+ "id": self.username,
70
+ "password": self.password,
71
+ }
72
+
73
+ response = requests.post(
74
+ "https://katalk.kakao.com/android/account2/login",
75
+ headers=self.headers,
76
+ json=json_data,
77
+ )
78
+
79
+ response_json = json.loads(response.text)
80
+
81
+ self.cb.put(("msg_dynamic", (None,), None))
82
+ if response_json["status"] != 0:
83
+ return False, f"Failed at login: {response.text}"
84
+
85
+ self.headers["Ss"] = response.headers["Set-SS"]
86
+ self.country_dicts = response_json["viewData"]["countries"]["all"]
87
+
88
+ return True, ""
89
+
90
+ def get_country_iso(self) -> Tuple[bool, str]:
91
+ self.country_iso = None
92
+ for country_dict in self.country_dicts:
93
+ if country_dict["code"] == self.country_code:
94
+ self.country_iso = country_dict["iso"]
95
+
96
+ if not self.country_iso:
97
+ return False, "Invalid country code"
98
+
99
+ return True, ""
100
+
101
+ def enter_phone(self) -> Tuple[bool, str]:
102
+ msg = I(
103
+ "Getting Kakao authorization token by Android login: Submitting phone number"
104
+ )
105
+ self.cb.put(("msg_dynamic", (msg,), None))
106
+
107
+ json_data: Dict[str, Any] = {
108
+ "countryCode": self.country_code,
109
+ "countryIso": self.country_iso,
110
+ "phoneNumber": self.phone_number,
111
+ "termCodes": [],
112
+ "simPhoneNumber": f"+{self.country_code}{self.phone_number}",
113
+ }
114
+
115
+ response = requests.post(
116
+ "https://katalk.kakao.com/android/account2/phone-number",
117
+ headers=self.headers,
118
+ json=json_data,
119
+ )
120
+
121
+ response_json = json.loads(response.text)
122
+
123
+ self.cb.put(("msg_dynamic", (None,), None))
124
+ if response_json["status"] != 0:
125
+ return False, f"Failed at entering phone number: {response.text}"
126
+
127
+ self.verify_method = response_json["view"]
128
+
129
+ if self.verify_method == "passcode":
130
+ return self.verify_receive_sms()
131
+ if self.verify_method == "mo-send":
132
+ dest_number = response_json["viewData"]["moNumber"]
133
+ msg = response_json["viewData"]["moMessage"]
134
+ return self.verify_send_sms(dest_number, msg)
135
+ return False, f"Unknown verification method: {response.text}"
136
+
137
+ def verify_send_sms(self, dest_number: str, verify_msg: str) -> Tuple[bool, str]:
138
+ response = requests.post(
139
+ "https://katalk.kakao.com/android/account2/mo-sent", headers=self.headers
140
+ )
141
+
142
+ response_json = json.loads(response.text)
143
+
144
+ if response_json["status"] != 0:
145
+ return False, f"Failed at confirm sending SMS: {response.text}"
146
+
147
+ prompt = I("Send this SMS message to number {} then press enter:").format(
148
+ dest_number
149
+ )
150
+ self.cb.put(
151
+ (
152
+ "ask_str",
153
+ None,
154
+ {
155
+ "msg": prompt,
156
+ "initialvalue": verify_msg,
157
+ "cli_show_initialvalue": False,
158
+ },
159
+ )
160
+ )
161
+
162
+ response = requests.post(
163
+ "https://katalk.kakao.com/android/account2/mo-confirm", headers=self.headers
164
+ )
165
+
166
+ response_json = json.loads(response.text)
167
+
168
+ if response_json["status"] != 0:
169
+ return False, f"Failed at verifying SMS sent: {response.text}"
170
+
171
+ if response_json.get("reason") or "error" in response_json.get("message", ""):
172
+ return False, f"Failed at verifying SMS sent: {response.text}"
173
+
174
+ self.confirm_url = response_json.get("viewData", {}).get("url")
175
+
176
+ return True, ""
177
+
178
+ def verify_receive_sms(self) -> Tuple[bool, str]:
179
+ msg = I("Enter passcode received from SMS:")
180
+ passcode = self.cb.put(("ask_str", (msg,), None))
181
+
182
+ json_data = {
183
+ "passcode": passcode,
184
+ }
185
+
186
+ response = requests.post(
187
+ "https://katalk.kakao.com/android/account2/passcode",
188
+ headers=self.headers,
189
+ json=json_data,
190
+ )
191
+
192
+ response_json = json.loads(response.text)
193
+
194
+ self.cb.put(("msg_dynamic", (None,), None))
195
+ if response_json["status"] != 0:
196
+ return False, f"Failed at verifying passcode: {response.text}"
197
+
198
+ self.confirm_url = response_json.get("viewData", {}).get("url")
199
+
200
+ return True, ""
201
+
202
+ def confirm_device_change(self) -> Tuple[bool, str]:
203
+ msg = I(
204
+ "Getting Kakao authorization token by Android login: Confirm device change"
205
+ )
206
+ self.cb.put(("msg_dynamic", (msg,), None))
207
+
208
+ confirm_url_parsed = urlparse(self.confirm_url) # type: ignore
209
+ confirm_url_qs = parse_qs(confirm_url_parsed.query) # type: ignore
210
+ session_token: str = confirm_url_qs["sessionToken"][0] # type: ignore
211
+
212
+ headers_browser = {
213
+ "Host": "katalk.kakao.com",
214
+ "Accept": "*/*",
215
+ "X-Requested-With": "XMLHttpRequest",
216
+ "User-Agent": "Mozilla/5.0 (Linux; Android 11; Android SDK built for x86 Build/RSR1.210210.001.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36;KAKAOTALK 2410030",
217
+ "Content-Type": "application/json",
218
+ "Origin": "https://katalk.kakao.com",
219
+ "Sec-Fetch-Site": "same-origin",
220
+ "Sec-Fetch-Mode": "cors",
221
+ "Sec-Fetch-Dest": "empty",
222
+ "Referer": self.confirm_url,
223
+ "Accept-Language": "en-US,en;q=0.9",
224
+ "Connection": "close",
225
+ }
226
+
227
+ json_data: Dict[str, str] = {
228
+ "decision": "continue",
229
+ "lang": self.app_language,
230
+ "sessionToken": session_token,
231
+ "appVersion": self.app_version_number,
232
+ }
233
+
234
+ response = requests.post(
235
+ "https://katalk.kakao.com/android/account2/confirm-device-change",
236
+ headers=headers_browser,
237
+ json=json_data,
238
+ )
239
+
240
+ response_json = json.loads(response.text)
241
+
242
+ self.cb.put(("msg_dynamic", (None,), None))
243
+ if response_json["status"] != 0:
244
+ return False, f"Failed at confirm device change: {response.text}"
245
+
246
+ return True, ""
247
+
248
+ def passcode_callback(self) -> Tuple[bool, str]:
249
+ msg = I("Getting Kakao authorization token by Android login: Passcode callback")
250
+ self.cb.put(("msg_dynamic", (msg,), None))
251
+
252
+ response = requests.get(
253
+ "https://katalk.kakao.com/android/account2/passcode/callback",
254
+ headers=self.headers,
255
+ )
256
+
257
+ response_json = json.loads(response.text)
258
+
259
+ self.cb.put(("msg_dynamic", (None,), None))
260
+ if response_json["status"] != 0:
261
+ return False, f"Failed at passcode callback: {response.text}"
262
+
263
+ return True, ""
264
+
265
+ def skip_restoration(self) -> Tuple[bool, str]:
266
+ msg = I("Getting Kakao authorization token by Android login: Skip restoration")
267
+ self.cb.put(("msg_dynamic", (msg,), None))
268
+
269
+ response = requests.post(
270
+ "https://katalk.kakao.com/android/account2/skip-restoration",
271
+ headers=self.headers,
272
+ )
273
+ response_json = json.loads(response.text)
274
+
275
+ if response_json["status"] != 0:
276
+ return False, f"Failed at skip restoration: {response.text}"
277
+
278
+ self.nickname = response_json.get("viewData", {}).get("nickname")
279
+
280
+ self.cb.put(("msg_dynamic", (None,), None))
281
+ if self.nickname is None:
282
+ return False, f"Failed at passcode callback: {response.text}"
283
+
284
+ return True, ""
285
+
286
+ def get_profile(self) -> Tuple[bool, str]:
287
+ msg = I("Getting Kakao authorization token by Android login: Get profile")
288
+ self.cb.put(("msg_dynamic", (msg,), None))
289
+
290
+ json_data = {
291
+ "nickname": self.nickname,
292
+ "profileImageFlag": 1,
293
+ "friendAutomation": True,
294
+ }
295
+
296
+ response = requests.post(
297
+ "https://katalk.kakao.com/android/account2/profile",
298
+ headers=self.headers,
299
+ json=json_data,
300
+ )
301
+
302
+ response_json = json.loads(response.text)
303
+
304
+ self.cb.put(("msg_dynamic", (None,), None))
305
+ if response_json["status"] != 0:
306
+ return False, f"Failed at get profile: {response.text}"
307
+
308
+ self.access_token = response_json["signupData"]["oauth2Token"]["accessToken"]
309
+
310
+ return True, ""
311
+
312
+ def get_cred(self) -> Tuple[Optional[str], str]:
313
+ auth_token = None
314
+
315
+ steps = (
316
+ self.login,
317
+ self.get_country_iso,
318
+ self.enter_phone,
319
+ self.confirm_device_change,
320
+ self.passcode_callback,
321
+ self.skip_restoration,
322
+ self.get_profile,
323
+ )
324
+
325
+ for step in steps:
326
+ success, msg = step()
327
+ if not success:
328
+ return None, msg
329
+
330
+ auth_token = self.access_token + "-" + self.device_uuid
331
+ return auth_token, self.OK_MSG.format(auth_token)