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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. sticker_convert/cli.py +26 -13
  2. sticker_convert/downloaders/download_base.py +20 -23
  3. sticker_convert/downloaders/download_discord.py +91 -0
  4. sticker_convert/downloaders/download_kakao.py +3 -4
  5. sticker_convert/downloaders/download_line.py +3 -4
  6. sticker_convert/downloaders/download_signal.py +3 -4
  7. sticker_convert/downloaders/download_telegram.py +3 -4
  8. sticker_convert/downloaders/download_viber.py +3 -4
  9. sticker_convert/gui.py +15 -3
  10. sticker_convert/gui_components/frames/cred_frame.py +22 -1
  11. sticker_convert/gui_components/windows/discord_get_auth_window.py +82 -0
  12. sticker_convert/gui_components/windows/signal_get_auth_window.py +39 -85
  13. sticker_convert/job.py +11 -1
  14. sticker_convert/job_option.py +2 -0
  15. sticker_convert/resources/compression.json +94 -0
  16. sticker_convert/resources/help.json +2 -1
  17. sticker_convert/resources/input.json +20 -0
  18. sticker_convert/uploaders/upload_signal.py +5 -4
  19. sticker_convert/uploaders/upload_telegram.py +11 -10
  20. sticker_convert/utils/auth/get_discord_auth.py +115 -0
  21. sticker_convert/utils/auth/get_signal_auth.py +115 -114
  22. sticker_convert/utils/auth/get_viber_auth.py +10 -214
  23. sticker_convert/utils/chrome_remotedebug.py +152 -0
  24. sticker_convert/utils/emoji.py +16 -0
  25. sticker_convert/utils/files/run_bin.py +1 -1
  26. sticker_convert/utils/media/codec_info.py +45 -60
  27. sticker_convert/utils/process.py +187 -0
  28. sticker_convert/utils/url_detect.py +3 -0
  29. sticker_convert/version.py +1 -1
  30. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/METADATA +40 -42
  31. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/RECORD +35 -29
  32. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/LICENSE +0 -0
  33. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/WHEEL +0 -0
  34. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/entry_points.txt +0 -0
  35. {sticker_convert-2.9.4.dist-info → sticker_convert-2.10.0.dist-info}/top_level.txt +0 -0
@@ -3,127 +3,128 @@ import json
3
3
  import os
4
4
  import platform
5
5
  import shutil
6
- from pathlib import Path
7
- from typing import Optional, Tuple
6
+ import time
7
+ import webbrowser
8
+ from typing import Callable, Optional, Tuple, cast
8
9
 
9
- from sqlcipher3 import dbapi2 as sqlite3
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
10
13
 
11
14
 
12
15
  class GetSignalAuth:
13
- def get_signal_desktop(self) -> Tuple[Optional[str], Optional[str]]:
14
- if platform.system() == "Windows":
15
- signal_bin_path_prod = os.path.expandvars(
16
- "%localappdata%/Programs/signal-desktop/Signal.exe"
17
- )
18
- signal_bin_path_beta = os.path.expandvars(
19
- "%localappdata%/Programs/signal-desktop-beta/Signal Beta.exe"
20
- )
21
- signal_user_data_dir_prod = os.path.abspath(
22
- os.path.expandvars("%appdata%/Signal")
16
+ def __init__(
17
+ self,
18
+ cb_msg: Callable[..., None] = print,
19
+ cb_ask_str: Callable[..., str] = input,
20
+ ):
21
+ chromedriver_download_dir = CONFIG_DIR / "bin"
22
+ os.makedirs(chromedriver_download_dir, exist_ok=True)
23
+
24
+ self.chromedriver_download_dir = chromedriver_download_dir
25
+
26
+ self.cb_ask_str = cb_ask_str
27
+ self.cb_msg = cb_msg
28
+
29
+ def download_signal_desktop(self):
30
+ download_url = "https://signal.org/en/download/"
31
+
32
+ webbrowser.open(download_url)
33
+
34
+ self.cb_msg(download_url)
35
+
36
+ prompt = "Signal Desktop not detected.\n"
37
+ prompt += "Download and install Signal Desktop version\n"
38
+ prompt += "After installation, quit Signal Desktop before continuing"
39
+ if self.cb_ask_str != input:
40
+ self.cb_ask_str(
41
+ prompt, initialvalue=download_url, cli_show_initialvalue=False
23
42
  )
24
- signal_user_data_dir_beta = os.path.abspath(
25
- os.path.expandvars("%appdata%/Signal Beta")
43
+ else:
44
+ self.cb_msg(prompt)
45
+
46
+ def get_signal_bin_path(self) -> Optional[str]:
47
+ signal_paths: Tuple[Optional[str], ...]
48
+ if platform.system() == "Windows":
49
+ signal_paths = (
50
+ os.path.expandvars("%localappdata%/Programs/signal-desktop/Signal.exe"),
51
+ os.path.expandvars(
52
+ "%localappdata%/Programs/signal-desktop-beta/Signal Beta.exe"
53
+ ),
26
54
  )
27
55
  elif platform.system() == "Darwin":
28
- signal_bin_path_prod = "/Applications/Signal.app/Contents/MacOS/Signal"
29
- signal_bin_path_beta = (
30
- "/Applications/Signal Beta.app/Contents/MacOS/Signal Beta"
56
+ signal_paths = (
57
+ "/Applications/Signal.app/Contents/MacOS/Signal",
58
+ "/Applications/Signal Beta.app/Contents/MacOS/Signal Beta",
31
59
  )
32
- signal_user_data_dir_prod = os.path.expanduser(
33
- "~/Library/Application Support/Signal"
34
- )
35
- signal_user_data_dir_beta = os.path.expanduser(
36
- "~/Library/Application Support/Signal Beta"
60
+ else:
61
+ signal_paths = (
62
+ shutil.which("signal-desktop"),
63
+ shutil.which("signal-desktop-beta"),
37
64
  )
65
+
66
+ for signal_path in signal_paths:
67
+ if signal_path is not None and os.path.isfile(signal_path):
68
+ return signal_path
69
+ return None
70
+
71
+ def get_cred(self) -> Tuple[Optional[str], Optional[str]]:
72
+ signal_bin_path = self.get_signal_bin_path()
73
+ if signal_bin_path is None:
74
+ self.download_signal_desktop()
75
+ return None, None
76
+
77
+ if platform.system() == "Windows":
78
+ killall("signal")
38
79
  else:
39
- prod_which = shutil.which("signal-desktop")
40
- if prod_which is None:
41
- signal_bin_path_prod = "signal-desktop"
42
- else:
43
- signal_bin_path_prod = prod_which
44
- beta_which = shutil.which("signal-desktop-beta")
45
- if beta_which is None:
46
- signal_bin_path_beta = "signal-desktop-beta"
47
- else:
48
- signal_bin_path_beta = beta_which
49
- signal_user_data_dir_prod = os.path.expanduser("~/.config/Signal")
50
- signal_user_data_dir_beta = os.path.expanduser("~/.config/Signal Beta")
51
-
52
- if Path(signal_bin_path_prod).is_file():
53
- return signal_bin_path_prod, signal_user_data_dir_prod
54
- if Path(signal_bin_path_beta).is_file():
55
- return signal_bin_path_beta, signal_user_data_dir_beta
56
-
57
- return None, None
58
-
59
- def get_cred(
60
- self,
61
- signal_bin_path: Optional[str] = None,
62
- signal_user_data_dir: Optional[str] = None,
63
- ) -> Tuple[Optional[str], Optional[str], str]:
64
- if not (signal_bin_path and signal_user_data_dir):
65
- signal_bin_path, signal_user_data_dir = self.get_signal_desktop()
66
-
67
- if not (signal_bin_path and signal_user_data_dir):
68
- msg = "Signal Desktop not detected.\n"
69
- msg += "Download and install Signal Desktop,\n"
70
- msg += "then login to Signal Desktop and try again."
71
-
72
- return None, None, msg
73
-
74
- signal_config = Path(signal_user_data_dir, "config.json")
75
-
76
- if not signal_config.is_file():
77
- msg = "Signal Desktop installed,\n"
78
- msg += "but it's config file not found.\n"
79
- msg += "Please login to Signal Desktop and try again.\n"
80
- msg += "\n"
81
- msg += f"{signal_bin_path=}\n"
82
- msg += f"{signal_user_data_dir=}\n"
83
- return None, None, msg
84
-
85
- with open(signal_config, encoding="utf-8") as f:
86
- config = json.load(f)
87
- key = config.get("key")
88
- db_key = f"x'{key}'"
89
-
90
- signal_database = Path(signal_user_data_dir, "sql/db.sqlite")
91
-
92
- if not signal_database.is_file():
93
- msg = "Signal Desktop installed,\n"
94
- msg += "but database file not found.\n"
95
- msg += "Please login to Signal Desktop and try again.\n"
96
- msg += "\n"
97
- msg += f"{signal_bin_path=}\n"
98
- msg += f"{signal_user_data_dir=}\n"
99
- return None, None, msg
100
-
101
- db_conn = sqlite3.connect(signal_database.as_posix())
102
- db_cursor = db_conn.cursor()
103
- db_cursor.execute(f'PRAGMA key="{db_key}"')
104
-
105
- uuid_id = None
106
- result = db_cursor.execute("SELECT * FROM items WHERE id='uuid_id'").fetchone()
107
- if result:
108
- uuid_id = json.loads(result[1])["value"]
109
-
110
- password = None
111
- result = db_cursor.execute("SELECT * FROM items WHERE id='password'").fetchone()
112
- if result:
113
- password = json.loads(result[1])["value"]
114
-
115
- db_conn.close()
116
-
117
- if uuid_id and password:
118
- msg = "Got uuid and password successfully:\n"
119
- msg += f"{uuid_id=}\n"
120
- msg += f"{password=}"
121
- return uuid_id, password, msg
122
-
123
- msg = "Signal Desktop installed and Database found,\n"
124
- msg += "but uuid and password not found.\n"
125
- msg += "Please login to Signal Desktop and try again.\n"
126
- msg += "\n"
127
- msg += f"{signal_bin_path=}\n"
128
- msg += f"{signal_user_data_dir=}\n"
129
- return None, None, msg
80
+ killall("signal-desktop")
81
+
82
+ crd = CRD(signal_bin_path)
83
+ crd.connect()
84
+ # crd.runtime_enable()
85
+ # crd.reload()
86
+ # while True:
87
+ # r = crd.ws.recv()
88
+ # data = json.loads(r)
89
+ # if data.get("method") == "Runtime.executionContextCreated":
90
+ # print(data)
91
+ # if (data.get("method") == "Runtime.executionContextCreated" and
92
+ # data["params"]["context"]["name"] == "Electron Isolated Context"
93
+ # ):
94
+ # context_id = data["params"]["context"]["id"]
95
+ # break
96
+ # crd.runtime_disable()
97
+ context_id = 2
98
+
99
+ uuid, password = None, None
100
+ while True:
101
+ try:
102
+ r = crd.exec_js(
103
+ "window.reduxStore.getState().items.uuid_id", context_id
104
+ )
105
+ except RuntimeError:
106
+ break
107
+ if (
108
+ json.loads(r).get("result", {}).get("result", {}).get("type", "")
109
+ == "string"
110
+ ):
111
+ uuid = cast(str, json.loads(r)["result"]["result"]["value"])
112
+ break
113
+ time.sleep(1)
114
+ while True:
115
+ try:
116
+ r = crd.exec_js(
117
+ "window.reduxStore.getState().items.password", context_id
118
+ )
119
+ except RuntimeError:
120
+ break
121
+ if (
122
+ json.loads(r).get("result", {}).get("result", {}).get("type", "")
123
+ == "string"
124
+ ):
125
+ password = cast(str, json.loads(r)["result"]["result"]["value"])
126
+ break
127
+ time.sleep(1)
128
+
129
+ crd.close()
130
+ return uuid, password
@@ -3,14 +3,14 @@ import importlib.util
3
3
  import os
4
4
  import platform
5
5
  import shutil
6
- import signal
7
6
  import subprocess
8
7
  import time
8
+ from functools import partial
9
9
  from getpass import getpass
10
10
  from pathlib import Path
11
11
  from typing import Callable, List, Optional, Tuple, cast
12
12
 
13
- from sticker_convert.definitions import ROOT_DIR
13
+ from sticker_convert.utils.process import check_admin, find_pid_by_name, get_mem, killall
14
14
 
15
15
  MSG_NO_BIN = """Viber Desktop not detected.
16
16
  Download and install Viber Desktop,
@@ -23,102 +23,10 @@ MSG_SIP_ENABLED = """You need to disable SIP:
23
23
  2. Launch Terminal from the Utilities menu
24
24
  3. Run the command `csrutil disable`
25
25
  4. Restart your computer"""
26
- MSG_NO_PGREP = "pgrep command or psutil python package is necessary"
27
26
  MSG_LAUNCH_FAIL = "Failed to launch Viber"
28
27
  MSG_PERMISSION_ERROR = "Failed to read Viber process memory"
29
28
 
30
29
 
31
- def check_admin_windows() -> bool:
32
- username = os.getenv("username")
33
- if username is None:
34
- return False
35
-
36
- s = subprocess.run(
37
- ["net", "user", username],
38
- capture_output=True,
39
- text=True,
40
- ).stdout
41
-
42
- return True if "*Administrators" in s else False
43
-
44
-
45
- def check_admin_linux() -> bool:
46
- s = subprocess.run(
47
- ["sudo", "-l"],
48
- capture_output=True,
49
- text=True,
50
- ).stdout
51
-
52
- return True if "may run the following commands" in s else False
53
-
54
-
55
- def killall(name: str) -> bool:
56
- result = False
57
-
58
- while True:
59
- pid = find_pid_by_name(name)
60
- if pid is not None:
61
- os.kill(pid, signal.SIGTERM)
62
- result = True
63
- else:
64
- break
65
-
66
- return result
67
-
68
-
69
- def find_pid_by_name(name: str) -> Optional[int]:
70
- if platform.system() == "Windows":
71
- s = subprocess.run(
72
- ["powershell", "-c", "Get-Process", f"*{name}*"],
73
- capture_output=True,
74
- text=True,
75
- ).stdout
76
-
77
- for line in s.split("\n"):
78
- if name in line.lower():
79
- info = name.split()
80
- pid = info[5]
81
- if pid.isnumeric():
82
- return int(pid)
83
-
84
- return None
85
- else:
86
- if platform.system() == "Darwin":
87
- pattern = "Viber"
88
- else:
89
- pattern = ".*[Vv]iber.*"
90
- pid = (
91
- subprocess.run(["pgrep", pattern], capture_output=True, text=True)
92
- .stdout.split("\n")[0]
93
- .strip()
94
- )
95
- if pid == "" or pid.isnumeric() is False:
96
- return None
97
- else:
98
- return int(pid)
99
-
100
-
101
- if importlib.util.find_spec("psutil"):
102
- import psutil
103
-
104
- def killall(name: str) -> bool:
105
- result = False
106
-
107
- for proc in psutil.process_iter(): # type: ignore
108
- if name in proc.name().lower():
109
- proc.kill()
110
- result = True
111
-
112
- return result
113
-
114
- def find_pid_by_name(name: str) -> Optional[int]:
115
- for proc in psutil.process_iter(): # type: ignore
116
- if name in proc.name().lower():
117
- return proc.pid
118
-
119
- return None
120
-
121
-
122
30
  class GetViberAuth:
123
31
  def __init__(self, cb_ask_str: Callable[..., str] = input):
124
32
  self.cb_ask_str = cb_ask_str
@@ -137,113 +45,6 @@ class GetViberAuth:
137
45
 
138
46
  return find_pid_by_name("viber")
139
47
 
140
- def get_mem_windows(self, viber_pid: int) -> Tuple[Optional[bytes], str]:
141
- from pathlib import WindowsPath
142
-
143
- memdump_ps_path = str(WindowsPath(ROOT_DIR / "resources/memdump_windows.ps1"))
144
- arglist = (
145
- f'-NoProfile -ExecutionPolicy Bypass -File "{memdump_ps_path}" {viber_pid}'
146
- )
147
- dump_fpath = os.path.expandvars(f"%temp%/memdump.bin.{viber_pid}")
148
-
149
- cmd = [
150
- "powershell",
151
- "-NoProfile",
152
- "-ExecutionPolicy",
153
- "Bypass",
154
- "-Command",
155
- f"Start-Process -Verb RunAs powershell -ArgumentList '{arglist}'",
156
- ]
157
-
158
- subprocess.run(cmd, capture_output=True, text=True)
159
-
160
- while True:
161
- try:
162
- with open(dump_fpath, "rb") as f:
163
- s = f.read()
164
- if len(s) != 0:
165
- break
166
- time.sleep(1)
167
- except (FileNotFoundError, PermissionError):
168
- pass
169
-
170
- while True:
171
- try:
172
- os.remove(dump_fpath)
173
- break
174
- except PermissionError:
175
- pass
176
-
177
- return s, ""
178
-
179
- def get_mem_linux(self, viber_pid: int) -> Tuple[Optional[bytes], str]:
180
- memdump_sh_path = (ROOT_DIR / "resources/memdump_linux.sh").as_posix()
181
-
182
- s = subprocess.run(
183
- [
184
- memdump_sh_path,
185
- str(viber_pid),
186
- ],
187
- capture_output=True,
188
- ).stdout
189
-
190
- if len(s) > 1000:
191
- pass
192
- elif shutil.which("pkexec") and os.getenv("DISPLAY"):
193
- s = subprocess.run(
194
- [
195
- "pkexec",
196
- memdump_sh_path,
197
- str(viber_pid),
198
- ],
199
- capture_output=True,
200
- ).stdout
201
- else:
202
- prompt = "Enter sudo password: "
203
- if self.cb_ask_str != input:
204
- sudo_password = self.cb_ask_str(
205
- prompt, initialvalue="", cli_show_initialvalue=False
206
- )
207
- else:
208
- sudo_password = getpass(prompt)
209
- sudo_password_pipe = subprocess.Popen(
210
- ("echo", sudo_password), stdout=subprocess.PIPE
211
- )
212
- s = subprocess.run(
213
- [
214
- "sudo",
215
- "-S",
216
- memdump_sh_path,
217
- str(viber_pid),
218
- ],
219
- capture_output=True,
220
- stdin=sudo_password_pipe.stdout,
221
- ).stdout
222
-
223
- return s, ""
224
-
225
- def get_mem_darwin(self, viber_pid: int) -> Tuple[Optional[bytes], str]:
226
- subprocess.run(
227
- [
228
- "lldb",
229
- "--attach-pid",
230
- str(viber_pid),
231
- "-o",
232
- "process save-core /tmp/viber.dmp",
233
- "-o",
234
- "quit",
235
- ],
236
- stdout=subprocess.DEVNULL,
237
- stderr=subprocess.DEVNULL,
238
- )
239
-
240
- with open("/tmp/viber.dmp", "rb") as f:
241
- s = f.read()
242
-
243
- os.remove("/tmp/viber.dmp")
244
-
245
- return s, ""
246
-
247
48
  def get_auth_by_pme(
248
49
  self, viber_bin_path: str, relaunch: bool = True
249
50
  ) -> Tuple[Optional[str], str]:
@@ -315,12 +116,6 @@ class GetViberAuth:
315
116
 
316
117
  if "enabled" in csrutil_status:
317
118
  return None, MSG_SIP_ENABLED
318
- elif (
319
- platform.system() != "Windows"
320
- and shutil.which("pgrep") is None
321
- and importlib.find_loader("psutil") is None
322
- ):
323
- return None, MSG_NO_PGREP
324
119
 
325
120
  if relaunch:
326
121
  viber_pid = self.relaunch_viber(viber_bin_path)
@@ -329,12 +124,13 @@ class GetViberAuth:
329
124
  if viber_pid is None:
330
125
  return None, MSG_LAUNCH_FAIL
331
126
 
332
- if platform.system() == "Windows":
333
- s, msg = self.get_mem_windows(viber_pid)
334
- elif platform.system() == "Darwin":
335
- s, msg = self.get_mem_darwin(viber_pid)
127
+ if self.cb_ask_str == input:
128
+ pw_func = getpass
336
129
  else:
337
- s, msg = self.get_mem_linux(viber_pid)
130
+ pw_func = partial(
131
+ self.cb_ask_str, initialvalue="", cli_show_initialvalue=False
132
+ )
133
+ s, msg = get_mem(viber_pid, pw_func)
338
134
 
339
135
  if s is None:
340
136
  return None, msg
@@ -421,14 +217,14 @@ class GetViberAuth:
421
217
  methods.append(self.get_auth_by_dump)
422
218
  if pme_present:
423
219
  methods.append(self.get_auth_by_pme)
424
- if check_admin_windows() is False:
220
+ if check_admin() is False:
425
221
  methods.reverse()
426
222
  else:
427
223
  if not os.path.isfile("/.dockerenv"):
428
224
  methods.append(self.get_auth_by_dump)
429
225
  if pme_present:
430
226
  methods.append(self.get_auth_by_pme)
431
- if check_admin_linux() is False:
227
+ if check_admin() is False:
432
228
  methods.reverse()
433
229
 
434
230
  for method in methods:
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import socket
7
+ import subprocess
8
+ import time
9
+ from typing import Any, Dict, Optional, Union, cast
10
+
11
+ import requests
12
+ import websocket
13
+
14
+ # References
15
+ # https://github.com/yeongbin-jo/python-chromedriver-autoinstaller/blob/master/chromedriver_autoinstaller/utils.py
16
+ # https://chromedevtools.github.io/devtools-protocol/
17
+
18
+
19
+ def get_free_port() -> int:
20
+ with socket.socket() as sock:
21
+ sock.bind(("", 0))
22
+ port = sock.getsockname()[1]
23
+ return port
24
+
25
+
26
+ class CRD:
27
+ def __init__(self, chrome_bin: str, port: Optional[int] = None):
28
+ if port is None:
29
+ port = get_free_port()
30
+ self.port = port
31
+ self.chrome_proc = subprocess.Popen(
32
+ [
33
+ chrome_bin,
34
+ "--no-sandbox",
35
+ f"--remote-debugging-port={port}",
36
+ f"--remote-allow-origins=http://localhost:{port}",
37
+ ]
38
+ )
39
+
40
+ @staticmethod
41
+ def get_chrome_path() -> Optional[str]:
42
+ chrome_bin: Optional[str]
43
+ if platform.system() == "Darwin":
44
+ chrome_bin = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
45
+
46
+ if os.path.isfile(chrome_bin) is False:
47
+ return None
48
+
49
+ return chrome_bin
50
+
51
+ elif platform.system() == "Windows":
52
+ chrome_x64 = f"{os.environ.get('PROGRAMW6432') or os.environ.get('PROGRAMFILES')}\\Google\\Chrome\\Application"
53
+ chrome_x86 = (
54
+ f"{os.environ.get('PROGRAMFILES(X86)')}\\Google\\Chrome\\Application"
55
+ )
56
+
57
+ chrome_dir = (
58
+ chrome_x64
59
+ if os.path.isdir(chrome_x64)
60
+ else chrome_x86
61
+ if os.path.isdir(chrome_x86)
62
+ else None
63
+ )
64
+
65
+ if chrome_dir is None:
66
+ return None
67
+
68
+ return chrome_dir + "\\chrome.exe"
69
+
70
+ else:
71
+ for executable in (
72
+ "google-chrome",
73
+ "google-chrome-stable",
74
+ "google-chrome-beta",
75
+ "google-chrome-dev",
76
+ "chromium-browser",
77
+ "chromium",
78
+ ):
79
+ chrome_bin = shutil.which(executable)
80
+ if chrome_bin is not None:
81
+ return chrome_bin
82
+ return None
83
+
84
+ def connect(self):
85
+ self.cmd_id = 1
86
+ r = None
87
+ for _ in range(3):
88
+ try:
89
+ r = requests.get(f"http://localhost:{self.port}/json")
90
+ break
91
+ except requests.exceptions.ConnectionError:
92
+ time.sleep(1)
93
+
94
+ if r is None:
95
+ raise RuntimeError("Cannot connect to chrome debugging port")
96
+
97
+ targets = json.loads(r.text)
98
+ self.ws = websocket.create_connection(targets[0]["webSocketDebuggerUrl"]) # type: ignore
99
+
100
+ def send_cmd(self, command: Dict[Any, Any]) -> Union[str, bytes]:
101
+ if command.get("id") is None:
102
+ command["id"] = self.cmd_id
103
+ for _ in range(3):
104
+ try:
105
+ self.ws.send(json.dumps(command))
106
+ r = self.ws.recv()
107
+ self.cmd_id += 1
108
+ return r
109
+ except BrokenPipeError:
110
+ self.connect()
111
+
112
+ raise RuntimeError("Websocket keep disconnecting")
113
+
114
+ def exec_js(self, js: str, context_id: Optional[int] = None):
115
+ command: Dict[str, Any] = {
116
+ "id": self.cmd_id,
117
+ "method": "Runtime.evaluate",
118
+ "params": {"expression": js},
119
+ }
120
+ if context_id is not None:
121
+ command["params"]["contextId"] = context_id
122
+ return self.send_cmd(command)
123
+
124
+ def get_curr_url(self) -> str:
125
+ r = self.exec_js("window.location.href")
126
+ return cast(str, json.loads(r)["result"]["result"]["value"])
127
+
128
+ def navigate(self, url: str):
129
+ command = {"id": self.cmd_id, "method": "Page.navigate", "params": {"url": url}}
130
+ self.send_cmd(command)
131
+
132
+ def runtime_enable(self):
133
+ command = {
134
+ "method": "Runtime.enable",
135
+ }
136
+ self.send_cmd(command)
137
+
138
+ def runtime_disable(self):
139
+ command = {
140
+ "method": "Runtime.disable",
141
+ }
142
+ self.send_cmd(command)
143
+
144
+ def reload(self):
145
+ command = {
146
+ "method": "Page.reload",
147
+ }
148
+ self.send_cmd(command)
149
+
150
+ def close(self):
151
+ self.ws.close()
152
+ self.chrome_proc.kill()
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python3
2
+ from typing import List
3
+
4
+ from sticker_convert.utils.files.json_resources_loader import EMOJI_JSON
5
+
6
+
7
+ def get_emoji_list() -> List[str]:
8
+ return [i["emoji"] for i in EMOJI_JSON]
9
+
10
+
11
+ EMOJI_LIST = get_emoji_list()
12
+
13
+
14
+ # https://stackoverflow.com/a/43146653
15
+ def extract_emojis(s: str) -> str:
16
+ return "".join(set(c for c in s if c in EMOJI_LIST))