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,263 +1,281 @@
1
- #!/usr/bin/env python3
2
- import os
3
- import platform
4
- import re
5
- import subprocess
6
- import time
7
- from functools import partial
8
- from getpass import getpass
9
- from pathlib import Path
10
- from typing import Callable, Optional, Tuple, Union, cast
11
-
12
- from sticker_convert.utils.process import find_pid_by_name, get_mem, killall
13
-
14
- MSG_NO_BIN = """Kakao Desktop not detected.
15
- Download and install Kakao Desktop,
16
- then login to Kakao Desktop and try again."""
17
- MSG_NO_AUTH = """Kakao Desktop installed,
18
- but kakao_auth not found.
19
- Please login to Kakao Desktop and try again."""
20
- MSG_SIP_ENABLED = """You need to disable SIP:
21
- 1. Restart computer in Recovery mode
22
- 2. Launch Terminal from the Utilities menu
23
- 3. Run the command `csrutil disable`
24
- 4. Restart your computer"""
25
- MSG_LAUNCH_FAIL = "Failed to launch Kakao"
26
- MSG_PERMISSION_ERROR = "Failed to read Kakao process memory"
27
-
28
-
29
- class GetKakaoDesktopAuth:
30
- def __init__(self, cb_ask_str: Callable[..., str] = input) -> None:
31
- self.cb_ask_str = cb_ask_str
32
-
33
- def launch_kakao(self, kakao_bin_path: str) -> None:
34
- if platform.system() == "Windows":
35
- subprocess.Popen([kakao_bin_path])
36
- elif platform.system() == "Darwin":
37
- subprocess.Popen(["open", kakao_bin_path])
38
- else:
39
- subprocess.Popen(["wine", kakao_bin_path])
40
-
41
- def relaunch_kakao(self, kakao_bin_path: str) -> Optional[int]:
42
- killed = killall("kakaotalk")
43
- if killed:
44
- time.sleep(5)
45
-
46
- self.launch_kakao(kakao_bin_path)
47
- time.sleep(20)
48
-
49
- return find_pid_by_name("kakaotalk")
50
-
51
- def get_auth_by_pme(
52
- self, kakao_bin_path: str, relaunch: bool = True
53
- ) -> Tuple[Optional[str], str]:
54
- from PyMemoryEditor import OpenProcess # type: ignore
55
-
56
- auth_token = None
57
-
58
- if relaunch:
59
- kakao_pid = self.relaunch_kakao(kakao_bin_path)
60
- else:
61
- kakao_pid = find_pid_by_name("kakaotalk")
62
- if kakao_pid is None:
63
- return None, MSG_LAUNCH_FAIL
64
-
65
- try:
66
- with OpenProcess(pid=int(kakao_pid)) as process:
67
- for address in process.search_by_value( # type: ignore
68
- str, 15, "authorization: "
69
- ):
70
- auth_token_addr = cast(int, address) + 15
71
- auth_token_bytes = process.read_process_memory(
72
- auth_token_addr, bytes, 200
73
- )
74
- auth_token_term = auth_token_bytes.find(b"\x00")
75
- if auth_token_term == -1:
76
- continue
77
- auth_token_candidate = auth_token_bytes[:auth_token_term].decode(
78
- encoding="ascii"
79
- )
80
- if len(auth_token_candidate) > 150:
81
- auth_token = auth_token_candidate
82
- break
83
- except PermissionError:
84
- return None, MSG_PERMISSION_ERROR
85
-
86
- if auth_token is None:
87
- return None, MSG_NO_AUTH
88
- else:
89
- msg = "(Note: auth_token will be invalid if you quit Kakao Desktop)"
90
- msg += "Got auth_token successfully:\n"
91
- msg += f"{auth_token=}\n"
92
-
93
- return auth_token, msg
94
-
95
- def get_auth_by_dump(
96
- self, kakao_bin_path: str, relaunch: bool = True
97
- ) -> Tuple[Optional[str], str]:
98
- auth_token = None
99
- kakao_pid: Union[str, int, None]
100
- if platform.system() == "Windows":
101
- is_wine = False
102
- if relaunch:
103
- kakao_pid = self.relaunch_kakao(kakao_bin_path)
104
- else:
105
- kakao_pid = find_pid_by_name("kakaotalk")
106
- if kakao_pid is None:
107
- return None, MSG_LAUNCH_FAIL
108
- else:
109
- is_wine = True
110
- kakao_pid = "KakaoTalk.exe"
111
- if relaunch and self.relaunch_kakao(kakao_bin_path) is None:
112
- return None, MSG_LAUNCH_FAIL
113
-
114
- if self.cb_ask_str == input:
115
- pw_func = getpass
116
- else:
117
- pw_func = partial(
118
- self.cb_ask_str, initialvalue="", cli_show_initialvalue=False
119
- )
120
- s = get_mem(kakao_pid, pw_func, is_wine)
121
-
122
- if s is None:
123
- return None, "Failed to dump memory"
124
-
125
- auth_token = None
126
- for i in re.finditer(b"authorization: ", s):
127
- auth_token_addr = i.start() + 15
128
-
129
- auth_token_bytes = s[auth_token_addr : auth_token_addr + 200]
130
- auth_token_term = auth_token_bytes.find(b"\x00")
131
- if auth_token_term == -1:
132
- return None, MSG_NO_AUTH
133
- auth_token_candidate = auth_token_bytes[:auth_token_term].decode(
134
- encoding="ascii"
135
- )
136
- if len(auth_token_candidate) > 150:
137
- auth_token = auth_token_candidate
138
- break
139
-
140
- if auth_token is None:
141
- return None, MSG_NO_AUTH
142
- else:
143
- msg = "Got auth_token successfully:\n"
144
- msg += f"{auth_token=}\n"
145
-
146
- return auth_token, msg
147
-
148
- def get_auth_darwin(self, kakao_bin_path: str) -> Tuple[Optional[str], str]:
149
- csrutil_status = subprocess.run(
150
- ["csrutil", "status"], capture_output=True, text=True
151
- ).stdout
152
-
153
- if "enabled" in csrutil_status:
154
- return None, MSG_SIP_ENABLED
155
-
156
- killall("kakaotalk")
157
-
158
- subprocess.run(
159
- [
160
- "lldb",
161
- kakao_bin_path,
162
- "-o",
163
- "b ptrace",
164
- "-o",
165
- "r",
166
- "-o",
167
- "thread return",
168
- "-o",
169
- "con",
170
- "-o",
171
- "process save-core /tmp/memdump.kakaotalk.dmp",
172
- "-o",
173
- "con",
174
- "-o",
175
- "quit",
176
- ],
177
- stdout=subprocess.DEVNULL,
178
- stderr=subprocess.DEVNULL,
179
- )
180
-
181
- with open("/tmp/memdump.kakaotalk.dmp", "rb") as f:
182
- mem = f.read()
183
-
184
- os.remove("/tmp/memdump.kakaotalk.dmp")
185
-
186
- auth_token = None
187
- for i in re.finditer(b"]mac/", mem):
188
- auth_token_term = i.start()
189
-
190
- auth_token_bytes = mem[auth_token_term - 200 : auth_token_term]
191
- auth_token_start = auth_token_bytes.find(b"application/json_\x10\x8a") + 19
192
- if auth_token_start == -1:
193
- continue
194
- try:
195
- auth_token_candidate = auth_token_bytes[auth_token_start:].decode(
196
- encoding="ascii"
197
- )
198
- except UnicodeDecodeError:
199
- continue
200
-
201
- if 150 > len(auth_token_candidate) > 100:
202
- auth_token = auth_token_candidate
203
- break
204
-
205
- if auth_token is None:
206
- return None, MSG_NO_AUTH
207
- else:
208
- msg = "Got auth_token successfully:\n"
209
- msg += f"{auth_token=}\n"
210
-
211
- return auth_token, msg
212
-
213
- def get_kakao_desktop(self) -> Optional[str]:
214
- if platform.system() == "Windows":
215
- kakao_bin_path = os.path.expandvars(
216
- "%programfiles(x86)%\\Kakao\\KakaoTalk\\KakaoTalk.exe"
217
- )
218
- if Path(kakao_bin_path).exists() is False:
219
- kakao_bin_path = os.path.expandvars(
220
- "%programfiles%\\Kakao\\KakaoTalk\\KakaoTalk.exe"
221
- )
222
- elif platform.system() == "Darwin":
223
- kakao_bin_path = "/Applications/KakaoTalk.app"
224
- else:
225
- wineprefix = os.environ.get("WINEPREFIX")
226
- if not (wineprefix and Path(wineprefix).exists()):
227
- wineprefix = os.path.expanduser("~/.wine")
228
- kakao_bin_path = f"{wineprefix}/drive_c/Program Files (x86)/Kakao/KakaoTalk/KakaoTalk.exe"
229
-
230
- if Path(kakao_bin_path).exists():
231
- return kakao_bin_path
232
-
233
- return None
234
-
235
- def get_cred(
236
- self,
237
- kakao_bin_path: Optional[str] = None,
238
- ) -> Tuple[Optional[str], str]:
239
- # get_auth_by_dump()
240
- # + Fast
241
- # - Requires downloading procdump, or builtin method that needs admin
242
-
243
- # get_auth_by_pme()
244
- # + No admin (If have permission to read process)
245
- # - Slow
246
- # - Cannot run on macOS
247
-
248
- if not kakao_bin_path:
249
- kakao_bin_path = self.get_kakao_desktop()
250
-
251
- if not kakao_bin_path:
252
- return None, MSG_NO_BIN
253
-
254
- if platform.system() == "Windows":
255
- kakao_auth, msg = self.get_auth_by_dump(kakao_bin_path)
256
- if kakao_auth is None:
257
- kakao_auth, msg = self.get_auth_by_pme(kakao_bin_path, False)
258
- elif platform.system() == "Darwin":
259
- kakao_auth, msg = self.get_auth_darwin(kakao_bin_path)
260
- else:
261
- kakao_auth, msg = self.get_auth_by_dump(kakao_bin_path)
262
-
263
- return kakao_auth, msg
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import platform
4
+ import re
5
+ import subprocess
6
+ import time
7
+ from pathlib import Path
8
+ from typing import Any, Optional, Tuple, Union, cast
9
+
10
+ from sticker_convert.auth.auth_base import AuthBase
11
+ from sticker_convert.utils.process import find_pid_by_name, get_mem, killall
12
+ from sticker_convert.utils.translate import I
13
+
14
+
15
+ class AuthKakaoDesktopMemdump(AuthBase):
16
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
17
+ self.MSG_NO_BIN = I(
18
+ "Kakao Desktop not detected.\n"
19
+ "Download and install Kakao Desktop,\n"
20
+ "then login to Kakao Desktop and try again."
21
+ )
22
+ self.MSG_NO_AUTH = I(
23
+ "Kakao Desktop installed,\n"
24
+ "but kakao_auth not found.\n"
25
+ "Please login to Kakao Desktop and try again."
26
+ )
27
+ self.MSG_SIP_ENABLED = I(
28
+ "You need to disable SIP:\n"
29
+ "1. Restart computer in Recovery mode\n"
30
+ "2. Launch Terminal from the Utilities menu\n"
31
+ "3. Run the command `csrutil disable`\n"
32
+ "4. Restart your computer"
33
+ )
34
+ self.MSG_LAUNCH_FAIL = I("Failed to launch Kakao")
35
+ self.MSG_PERMISSION_ERROR = I("Failed to read Kakao process memory")
36
+
37
+ super().__init__(*args, **kwargs)
38
+
39
+ def launch_kakao(self, kakao_bin_path: str) -> None:
40
+ if platform.system() == "Windows":
41
+ subprocess.Popen([kakao_bin_path])
42
+ elif platform.system() == "Darwin":
43
+ subprocess.Popen(["open", kakao_bin_path])
44
+ else:
45
+ subprocess.Popen(["wine", kakao_bin_path])
46
+
47
+ def relaunch_kakao(self, kakao_bin_path: str) -> Optional[int]:
48
+ killed = killall("kakaotalk")
49
+ if killed:
50
+ time.sleep(5)
51
+
52
+ self.launch_kakao(kakao_bin_path)
53
+ time.sleep(20)
54
+
55
+ return find_pid_by_name("kakaotalk")
56
+
57
+ def get_auth_by_pme(
58
+ self, kakao_bin_path: str, relaunch: bool = True
59
+ ) -> Tuple[Optional[str], str]:
60
+ from PyMemoryEditor import OpenProcess # type: ignore
61
+
62
+ auth_token = None
63
+
64
+ if relaunch:
65
+ kakao_pid = self.relaunch_kakao(kakao_bin_path)
66
+ else:
67
+ kakao_pid = find_pid_by_name("kakaotalk")
68
+ if kakao_pid is None:
69
+ return None, self.MSG_LAUNCH_FAIL
70
+
71
+ try:
72
+ with OpenProcess(pid=int(kakao_pid)) as process:
73
+ for address in process.search_by_value( # type: ignore
74
+ str, 15, "authorization: "
75
+ ):
76
+ auth_token_addr = cast(int, address) + 15
77
+ auth_token_bytes = process.read_process_memory(
78
+ auth_token_addr, bytes, 200
79
+ )
80
+ auth_token_term = auth_token_bytes.find(b"\x00")
81
+ if auth_token_term == -1:
82
+ continue
83
+ auth_token_candidate = auth_token_bytes[:auth_token_term].decode(
84
+ encoding="ascii"
85
+ )
86
+ if len(auth_token_candidate) > 150:
87
+ auth_token = auth_token_candidate
88
+ break
89
+ except PermissionError:
90
+ return None, self.MSG_PERMISSION_ERROR
91
+
92
+ if auth_token is None:
93
+ return None, self.MSG_NO_AUTH
94
+ else:
95
+ msg = "(Note: auth_token will be invalid if you quit Kakao Desktop)"
96
+ msg += "Got auth_token successfully:\n"
97
+ msg += f"{auth_token=}\n"
98
+
99
+ return auth_token, msg
100
+
101
+ def get_auth_by_dump(
102
+ self, kakao_bin_path: str, relaunch: bool = True
103
+ ) -> Tuple[Optional[str], str]:
104
+ auth_token = None
105
+ kakao_pid: Union[str, int, None]
106
+ if platform.system() == "Windows":
107
+ is_wine = False
108
+ if relaunch:
109
+ kakao_pid = self.relaunch_kakao(kakao_bin_path)
110
+ else:
111
+ kakao_pid = find_pid_by_name("kakaotalk")
112
+ if kakao_pid is None:
113
+ return None, self.MSG_LAUNCH_FAIL
114
+ else:
115
+ is_wine = True
116
+ kakao_pid = "KakaoTalk.exe"
117
+ if relaunch and self.relaunch_kakao(kakao_bin_path) is None:
118
+ return None, self.MSG_LAUNCH_FAIL
119
+
120
+ def pw_func(msg: str) -> str:
121
+ return self.cb.put(
122
+ (
123
+ "ask_str",
124
+ None,
125
+ {
126
+ "message": msg,
127
+ "password": True,
128
+ },
129
+ )
130
+ )
131
+
132
+ s = get_mem(kakao_pid, pw_func, is_wine)
133
+
134
+ if s is None:
135
+ return None, "Failed to dump memory"
136
+
137
+ auth_token = None
138
+ for i in re.finditer(b"authorization: ", s):
139
+ auth_token_addr = i.start() + 15
140
+
141
+ auth_token_bytes = s[auth_token_addr : auth_token_addr + 200]
142
+ auth_token_term = auth_token_bytes.find(b"\x00")
143
+ if auth_token_term == -1:
144
+ continue
145
+ auth_token_candidate = auth_token_bytes[:auth_token_term].decode(
146
+ encoding="ascii"
147
+ )
148
+ if len(auth_token_candidate) > 150:
149
+ auth_token = auth_token_candidate
150
+ break
151
+
152
+ if auth_token is None:
153
+ return None, self.MSG_NO_AUTH
154
+ else:
155
+ msg = "Got auth_token successfully:\n"
156
+ msg += f"{auth_token=}\n"
157
+
158
+ return auth_token, msg
159
+
160
+ def get_auth_darwin(self, kakao_bin_path: str) -> Tuple[Optional[str], str]:
161
+ csrutil_status = subprocess.run(
162
+ ["csrutil", "status"], capture_output=True, text=True
163
+ ).stdout
164
+
165
+ if "enabled" in csrutil_status:
166
+ return None, self.MSG_SIP_ENABLED
167
+
168
+ killall("kakaotalk")
169
+
170
+ subprocess.run(
171
+ [
172
+ "lldb",
173
+ kakao_bin_path,
174
+ "-o",
175
+ "b ptrace",
176
+ "-o",
177
+ "r",
178
+ "-o",
179
+ "thread return",
180
+ "-o",
181
+ "con",
182
+ "-o",
183
+ "process save-core /tmp/memdump.kakaotalk.dmp",
184
+ "-o",
185
+ "con",
186
+ "-o",
187
+ "quit",
188
+ ],
189
+ stdout=subprocess.DEVNULL,
190
+ stderr=subprocess.DEVNULL,
191
+ )
192
+
193
+ with open("/tmp/memdump.kakaotalk.dmp", "rb") as f:
194
+ mem = f.read()
195
+
196
+ os.remove("/tmp/memdump.kakaotalk.dmp")
197
+
198
+ auth_token = None
199
+ for i in re.finditer(b"]mac/", mem):
200
+ auth_token_term = i.start()
201
+
202
+ auth_token_bytes = mem[auth_token_term - 200 : auth_token_term]
203
+ auth_token_start = auth_token_bytes.find(b"application/json_\x10\x8a") + 19
204
+ if auth_token_start == -1:
205
+ continue
206
+ try:
207
+ auth_token_candidate = auth_token_bytes[auth_token_start:].decode(
208
+ encoding="ascii"
209
+ )
210
+ except UnicodeDecodeError:
211
+ continue
212
+
213
+ if 150 > len(auth_token_candidate) > 100:
214
+ auth_token = auth_token_candidate
215
+ break
216
+
217
+ if auth_token is None:
218
+ return None, self.MSG_NO_AUTH
219
+ else:
220
+ msg = "Got auth_token successfully:\n"
221
+ msg += f"{auth_token=}\n"
222
+
223
+ return auth_token, msg
224
+
225
+ def get_kakao_desktop(self) -> Optional[str]:
226
+ if platform.system() == "Windows":
227
+ kakao_bin_path = os.path.expandvars(
228
+ "%programfiles(x86)%\\Kakao\\KakaoTalk\\KakaoTalk.exe"
229
+ )
230
+ if Path(kakao_bin_path).exists() is False:
231
+ kakao_bin_path = os.path.expandvars(
232
+ "%programfiles%\\Kakao\\KakaoTalk\\KakaoTalk.exe"
233
+ )
234
+ elif platform.system() == "Darwin":
235
+ kakao_bin_path = "/Applications/KakaoTalk.app"
236
+ else:
237
+ wineprefix = os.environ.get("WINEPREFIX")
238
+ if not (wineprefix and Path(wineprefix).exists()):
239
+ wineprefix = os.path.expanduser("~/.wine")
240
+ kakao_bin_path = f"{wineprefix}/drive_c/Program Files (x86)/Kakao/KakaoTalk/KakaoTalk.exe"
241
+
242
+ if Path(kakao_bin_path).exists():
243
+ return kakao_bin_path
244
+
245
+ return None
246
+
247
+ def get_cred(
248
+ self,
249
+ kakao_bin_path: Optional[str] = None,
250
+ ) -> Tuple[Optional[str], str]:
251
+ # get_auth_by_dump()
252
+ # + Fast
253
+ # - Requires downloading procdump, or builtin method that needs admin
254
+
255
+ # get_auth_by_pme()
256
+ # + No admin (If have permission to read process)
257
+ # - Slow
258
+ # - Cannot run on macOS
259
+
260
+ msg = I(
261
+ "Getting Kakao authorization token by Desktop memdump...\n(This may take a minute)"
262
+ )
263
+ self.cb.put(("msg_dynamic", (msg,), None))
264
+ if not kakao_bin_path:
265
+ kakao_bin_path = self.get_kakao_desktop()
266
+
267
+ if not kakao_bin_path:
268
+ return None, self.MSG_NO_BIN
269
+
270
+ if platform.system() == "Windows":
271
+ kakao_auth, msg = self.get_auth_by_dump(kakao_bin_path)
272
+ if kakao_auth is None:
273
+ kakao_auth, msg = self.get_auth_by_pme(kakao_bin_path, False)
274
+ elif platform.system() == "Darwin":
275
+ kakao_auth, msg = self.get_auth_darwin(kakao_bin_path)
276
+ else:
277
+ kakao_auth, msg = self.get_auth_by_dump(kakao_bin_path)
278
+
279
+ self.cb.put(("msg_dynamic", (None,), None))
280
+
281
+ return kakao_auth, msg