atomicshop 3.3.28__py3-none-any.whl → 3.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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

Files changed (99) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  3. atomicshop/a_mains/install_ca_certificate.py +172 -0
  4. atomicshop/a_mains/process_from_port.py +119 -0
  5. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  6. atomicshop/basics/strings.py +1 -1
  7. atomicshop/certificates.py +2 -2
  8. atomicshop/dns.py +26 -28
  9. atomicshop/etws/traces/trace_tcp.py +1 -2
  10. atomicshop/mitm/centered_settings.py +133 -0
  11. atomicshop/mitm/config_static.py +18 -43
  12. atomicshop/mitm/connection_thread_worker.py +376 -162
  13. atomicshop/mitm/engines/__parent/recorder___parent.py +1 -1
  14. atomicshop/mitm/engines/__parent/requester___parent.py +1 -1
  15. atomicshop/mitm/engines/__parent/responder___parent.py +15 -2
  16. atomicshop/mitm/engines/create_module_template.py +1 -2
  17. atomicshop/mitm/import_config.py +79 -88
  18. atomicshop/mitm/initialize_engines.py +1 -2
  19. atomicshop/mitm/message.py +5 -4
  20. atomicshop/mitm/mitm_main.py +222 -121
  21. atomicshop/mitm/recs_files.py +61 -5
  22. atomicshop/mitm/ssh_tester.py +82 -0
  23. atomicshop/networks.py +108 -93
  24. atomicshop/package_mains_processor.py +84 -0
  25. atomicshop/permissions/ubuntu_permissions.py +47 -0
  26. atomicshop/print_api.py +3 -5
  27. atomicshop/python_functions.py +23 -108
  28. atomicshop/speech_recognize.py +8 -0
  29. atomicshop/ssh_remote.py +115 -51
  30. atomicshop/web.py +20 -7
  31. atomicshop/web_apis/google_llm.py +22 -14
  32. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  33. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -1
  34. atomicshop/wrappers/dockerw/dockerw.py +2 -2
  35. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +5 -5
  36. atomicshop/wrappers/githubw.py +175 -63
  37. atomicshop/wrappers/loggingw/handlers.py +1 -1
  38. atomicshop/wrappers/loggingw/loggingw.py +17 -1
  39. atomicshop/wrappers/netshw.py +124 -3
  40. atomicshop/wrappers/playwrightw/scenarios.py +1 -1
  41. atomicshop/wrappers/powershell_networking.py +80 -0
  42. atomicshop/wrappers/psutilw/psutil_networks.py +9 -0
  43. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  44. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  45. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  46. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +12 -27
  47. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +15 -9
  48. atomicshop/wrappers/socketw/certificator.py +19 -9
  49. atomicshop/wrappers/socketw/creator.py +30 -7
  50. atomicshop/wrappers/socketw/dns_server.py +6 -6
  51. atomicshop/wrappers/socketw/exception_wrapper.py +3 -3
  52. atomicshop/wrappers/socketw/process_getter.py +86 -0
  53. atomicshop/wrappers/socketw/receiver.py +29 -9
  54. atomicshop/wrappers/socketw/sender.py +10 -9
  55. atomicshop/wrappers/socketw/sni.py +23 -6
  56. atomicshop/wrappers/socketw/{base.py → socket_base.py} +33 -1
  57. atomicshop/wrappers/socketw/socket_client.py +6 -8
  58. atomicshop/wrappers/socketw/socket_wrapper.py +82 -21
  59. atomicshop/wrappers/socketw/ssl_base.py +6 -2
  60. atomicshop/wrappers/win_auditw.py +189 -0
  61. {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/METADATA +25 -30
  62. {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/RECORD +74 -88
  63. atomicshop/_basics_temp.py +0 -101
  64. atomicshop/a_installs/ubuntu/docker_rootless.py +0 -11
  65. atomicshop/a_installs/ubuntu/docker_sudo.py +0 -11
  66. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  67. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  68. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  69. atomicshop/addons/package_setup/Setup.cmd +0 -7
  70. atomicshop/archiver/__init__.py +0 -0
  71. atomicshop/archiver/_search_in_zip.py +0 -189
  72. atomicshop/archiver/search_in_archive.py +0 -284
  73. atomicshop/archiver/sevenz_app_w.py +0 -86
  74. atomicshop/archiver/sevenzs.py +0 -73
  75. atomicshop/archiver/shutils.py +0 -34
  76. atomicshop/archiver/zips.py +0 -353
  77. atomicshop/file_types.py +0 -24
  78. atomicshop/pbtkmultifile_argparse.py +0 -88
  79. atomicshop/script_as_string_processor.py +0 -42
  80. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  81. atomicshop/ssh_scripts/process_from_port.py +0 -27
  82. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  83. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  84. atomicshop/wrappers/dockerw/install_docker.py +0 -449
  85. atomicshop/wrappers/ffmpegw.py +0 -125
  86. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  87. atomicshop/wrappers/socketw/get_process.py +0 -123
  88. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  89. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  90. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  91. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  92. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  93. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  94. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  95. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  96. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  97. {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/WHEEL +0 -0
  98. {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/licenses/LICENSE.txt +0 -0
  99. {atomicshop-3.3.28.dist-info → atomicshop-3.10.0.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ from typing import Union
4
4
  from .print_api import print_api
5
5
 
6
6
 
7
- def get_current_python_version_string() -> str:
7
+ def get_python_version_string() -> str:
8
8
  """
9
9
  Function gets version MAJOR.MINOR.MICRO from 'sys.version_info' object and returns it as a string.
10
10
  :return: python MAJOR.MINOR.MICRO version string.
@@ -13,120 +13,35 @@ def get_current_python_version_string() -> str:
13
13
  return f'{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}'
14
14
 
15
15
 
16
- # noinspection PyUnusedLocal
17
- def check_if_version_object_is_tuple_or_string(version_object: any,
18
- **kwargs) -> tuple:
19
- """
20
- Function checks if 'version_object' that was passed is tuple or string.
21
- If it's tuple then returns it back. If it's string, converts to tuple then returns it.
22
- If the object is none of the above, returns None.
23
-
24
- :param version_object: Can be string ('3.10') or tuple of integers ((3, 10)).
25
- :return:
26
- """
27
- # Check if tuple was passed.
28
- if isinstance(version_object, tuple):
29
- return version_object
30
- else:
31
- # Then check if a string was passed.
32
- if isinstance(version_object, str):
33
- # The check will be against tuple of integers, so we'll convert a string to tuple of integers.
34
- return tuple(map(int, version_object.split('.')))
35
- else:
36
- message = f'[*] Function: [check_if_version_object_is_tuple_or_string]\n' \
37
- f'[*] [version_object] object passed is not tuple or string.\n' \
38
- f'[*] Object type: {type(version_object)}\n' \
39
- f'[*] Object content {version_object}\n' \
40
- f'Exiting...'
41
- print_api(message, error_type=True, logger_method='critical', **kwargs)
42
-
43
- return None
44
-
45
-
46
- # noinspection PyUnusedLocal
47
16
  def check_python_version_compliance(
48
- minimum_version: Union[str, tuple] = None,
49
- maximum_version: Union[str, tuple] = None,
50
- minor_version: Union[str, tuple] = None,
51
- **kwargs
52
- ) -> bool:
17
+ min_ver: tuple = None,
18
+ max_ver: tuple = None,
19
+ ) -> str | None:
53
20
  """
54
21
  Python version check. Should be executed before importing external libraries, since they depend on Python version.
55
22
 
56
- :param minimum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
57
- :param maximum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
23
+ :param min_ver: tuple of integers (3, 10).
24
+ :param max_ver: tuple of integers (3, 10).
58
25
  If maximum version is not specified, it will be considered as all versions above the minimum are compliant.
59
- :param minor_version: Can be string ('3.10') or tuple of integers ((3, 10)).
60
- If minor version is specified, it will be considered as all the versions with the same major, minor version
61
- are compliant. Example: if minor_version is '3.10', then all the versions with '3.10.x' are compliant.
62
- :return:
26
+ :return: If version is not compliant, returns string with error message. Otherwise, returns None.
63
27
  """
64
28
 
65
- if not minimum_version and not maximum_version and not minor_version:
29
+ if not min_ver and not max_ver:
66
30
  raise ValueError("At least one of the version parameters should be passed.")
67
31
 
68
- if minor_version and (minimum_version or maximum_version):
69
- raise ValueError("Minor version should be passed alone.")
70
-
71
- # Check objects for string or tuple and get the tuple.
72
- if minimum_version:
73
- minimum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(minimum_version, **kwargs)
74
- else:
75
- minimum_version_scheme = tuple()
76
- # If 'maximum_version' object was passed, check it for string or tuple and get the tuple.
77
- if maximum_version:
78
- maximum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(maximum_version, **kwargs)
79
- else:
80
- maximum_version_scheme = tuple()
81
-
82
- # If 'minor_version' object was passed, check it for string or tuple and get the tuple.
83
- if minor_version:
84
- minor_version_scheme: tuple = check_if_version_object_is_tuple_or_string(minor_version, **kwargs)
32
+ current_version_info: tuple = sys.version_info[:3]
33
+ if min_ver and not max_ver:
34
+ if current_version_info < min_ver:
35
+ return f'Python version {".".join(map(str, min_ver))} or higher is required. '\
36
+ f'Current version is {".".join(map(str, current_version_info))}.'
37
+ elif max_ver and not min_ver:
38
+ if current_version_info > max_ver:
39
+ return f'Python version up to {".".join(map(str, max_ver))} is required. '\
40
+ f'Current version is {".".join(map(str, current_version_info))}.'
41
+ elif min_ver and max_ver:
42
+ if not (min_ver <= current_version_info <= max_ver):
43
+ return f'Python version between {".".join(map(str, min_ver))} and '\
44
+ f'{".".join(map(str, max_ver))} is required. '\
45
+ f'Current version is {".".join(map(str, current_version_info))}.'
85
46
  else:
86
- minor_version_scheme = tuple()
87
-
88
- # Get current python version.
89
- python_version_full: str = get_current_python_version_string()
90
-
91
- message = f"[*] Current Python Version: {python_version_full}"
92
- print_api(message, logger_method='info', **kwargs)
93
-
94
- if minor_version_scheme:
95
- # Check if current python version is later or equals to the minimum required version.
96
- if sys.version_info[:2] != minor_version_scheme:
97
- message = f"[!!!] YOU NEED TO INSTALL ANY PYTHON " \
98
- f"[{'.'.join(str(i) for i in minor_version_scheme)}] version, " \
99
- f"to work properly."
100
- print_api(message, error_type=True, logger_method='critical', **kwargs)
101
-
102
- return False
103
- elif minimum_version_scheme and maximum_version_scheme:
104
- if not sys.version_info >= minimum_version_scheme or not sys.version_info < maximum_version_scheme:
105
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
106
- f"[{'.'.join(str(i) for i in minimum_version_scheme)}], " \
107
- f"AND EARLIER THAN [{'.'.join(str(i) for i in maximum_version_scheme)}], " \
108
- f"to work properly."
109
- print_api(message, error_type=True, logger_method='critical', **kwargs)
110
-
111
- return False
112
- elif minimum_version_scheme and not maximum_version_scheme:
113
- if not sys.version_info >= minimum_version_scheme:
114
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
115
- f"[{'.'.join(str(i) for i in minimum_version_scheme)}], " \
116
- f"to work properly."
117
- print_api(message, error_type=True, logger_method='critical', **kwargs)
118
-
119
- return False
120
- elif not minimum_version_scheme and maximum_version_scheme:
121
- if not sys.version_info < maximum_version_scheme:
122
- message = f"[!!!] YOU NEED TO INSTALL EARLIER THAN PYTHON " \
123
- f"[{'.'.join(str(i) for i in maximum_version_scheme)}], " \
124
- f"to work properly."
125
- print_api(message, error_type=True, logger_method='critical', **kwargs)
126
-
127
- return False
128
-
129
- message = "[*] Version Check PASSED."
130
- print_api(message, logger_method='info', **kwargs)
131
-
132
- return True
47
+ return None
@@ -1,3 +1,11 @@
1
+ # TODO: Change manual wrapper to:
2
+ # from ffmpy import FFmpeg
3
+ # ff = FFmpeg(
4
+ # inputs={'input.mp4': None},
5
+ # outputs={'output.avi': None}
6
+ # )
7
+ # ff.run()
8
+
1
9
  from .wrappers.ffmpegw import FFmpegWrapper
2
10
  from .tempfiles import TempFile
3
11
  from .web import download
atomicshop/ssh_remote.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import sys
2
- import base64
3
2
  import logging
4
3
  from pathlib import Path
4
+ import shlex
5
5
 
6
6
  try:
7
7
  import paramiko
@@ -11,7 +11,12 @@ except ImportError as exception_object:
11
11
 
12
12
  from .print_api import print_api
13
13
  from .wrappers.loggingw import loggingw
14
- from .wrappers.socketw import base
14
+ from .wrappers.socketw import socket_base
15
+
16
+
17
+ class SSHRemoteWrapperNoPythonFound(Exception):
18
+ """Raised when no usable Python 3 interpreter found on remote host."""
19
+ pass
15
20
 
16
21
 
17
22
  class SSHRemote:
@@ -100,17 +105,23 @@ class SSHRemote:
100
105
  # Initializing paramiko SSHClient class
101
106
  self.ssh_client = paramiko.SSHClient()
102
107
 
108
+ # Variable to store detected python command on remote (python3 / python).
109
+ self.python_cmd: str | None = None
110
+
103
111
  if logger:
104
112
  # Create child logger for the provided logger with the module's name.
105
113
  self.logger: logging.Logger = loggingw.get_logger_with_level(f'{logger.name}.{Path(__file__).stem}')
106
114
  else:
107
115
  self.logger: logging.Logger = logger
108
116
 
109
- def connect(self):
117
+ def connect(
118
+ self,
119
+ timeout: int = 60
120
+ ):
110
121
  error: str = str()
111
122
 
112
123
  # Get all local interfaces IPv4 addresses.
113
- local_interfaces_ipv4 = base.get_local_network_interfaces_ip_address("ipv4", True)
124
+ local_interfaces_ipv4 = socket_base.get_local_network_interfaces_ip_address("ipv4", True)
114
125
  # Check if the target IP address is in the list of local interfaces.
115
126
  if self.ip_address in local_interfaces_ipv4:
116
127
  # If it is, we don't need to connect to it via SSH, it means that we want to connect to ourselves.
@@ -130,7 +141,7 @@ class SSHRemote:
130
141
  self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
131
142
 
132
143
  # Executing SSH connection to client.
133
- self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=60)
144
+ self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=timeout)
134
145
 
135
146
  return error
136
147
 
@@ -151,17 +162,50 @@ class SSHRemote:
151
162
  if "ModuleNotFoundError: No module named" in line:
152
163
  function_result = f"Python library is not installed - {line}"
153
164
  break
165
+ else:
166
+ function_result = console_output_string
154
167
 
155
168
  return function_result
156
169
 
157
- def remote_execution(self, script_string: str) -> tuple[str, str]:
170
+ def remote_execution(
171
+ self,
172
+ command: str,
173
+ script_string: str = None
174
+ ) -> tuple[str, str]:
175
+ """
176
+ Function to execute any command over SSH.
177
+
178
+ :param command: command to execute over SSH.
179
+ :param script_string: string representation of script to execute as input.
180
+ Example:
181
+ command = "python - 56734"
182
+ script_string = "import sys;print(f'sys.argv[0]')"
183
+
184
+ Under ssh in terminal it would execute like this:
185
+ ssh User@HostIpv4
186
+
187
+ python - 56734 << EOF
188
+ import sys;print(f'sys.argv[0]')
189
+ EOF
190
+
191
+ Or as onliner:
192
+ ssh User@HostIpv4 "python - 56734 << EOF import sys;print(f'sys.argv[0]') EOF"
193
+
194
+ or using a specific file path:
195
+ ssh User@HostIpv4 "python - 56734" < /path/to/script.py
196
+
197
+ :return: SSH console output, Error output
198
+ """
158
199
  output_result: str = str()
159
200
  error_result: str = str()
160
201
 
161
202
  # Execute the command over SSH remotely.
162
- # Don't put debugging break point over the next line in PyCharm. For some reason it gets stuck.
163
- # Put the point right after that.
164
- stdin, stdout, stderr = self.ssh_client.exec_command(command=script_string, timeout=30)
203
+ stdin, stdout, stderr = self.ssh_client.exec_command(command=command, timeout=30)
204
+
205
+ # Writing the script string into stdin buffer.
206
+ if script_string:
207
+ stdin.write(script_string)
208
+ stdin.channel.shutdown_write()
165
209
 
166
210
  # Reading the buffer of stdout.
167
211
  output_lines: list = stdout.readlines()
@@ -182,53 +226,68 @@ class SSHRemote:
182
226
 
183
227
  return output_result, error_result
184
228
 
185
- def remote_execution_python(self, script_string: str):
229
+ def _detect_remote_python_cmd_name(self) -> str:
186
230
  """
187
- Function to execute python script over SSH.
231
+ Try 'python3' then 'python' on the remote, return the one that is Python 3.
232
+ Raises if neither works.
233
+ """
234
+ for candidate in ("python3", "python"):
235
+ # Use a simple version check that works on both Windows and Linux
236
+ cmd = f'{candidate} -c "import sys; print(sys.version_info[0])"'
237
+ stdin, stdout, stderr = self.ssh_client.exec_command(cmd, timeout=5)
238
+
239
+ out = stdout.read().decode().strip()
240
+ exit_status = stdout.channel.recv_exit_status()
188
241
 
189
- Example:
190
- network.logger.info("Initializing SSH connection to get the calling process.")
191
-
192
- # Initializing SSHRemote class.
193
- ssh_client = SSHRemote(ip_address=client_message.client_ip, username=username, password=password)
194
- # Making actual SSH Connection to the computer.
195
- ssh_connection_error = ssh_client.connect()
196
- # If there's an exception / error during connection.
197
- if ssh_connection_error:
198
- # Put the error in the process name value.
199
- client_message.process_name = ssh_connection_error
200
- else:
201
- # If no error, then initialize the variables for python script execution over SSH.
202
- remote_output = remote_error = None
203
-
204
- # Put source port variable inside the string script.
205
- script_string: str = \
206
- put_variable_into_string_script(ssh_script_port_by_process_string, client_message.source_port)
207
-
208
- # Execute the python script on remote computer over SSH.
209
- remote_output, remote_error = ssh_client.remote_execution_python(script_string)
210
-
211
- # If there was an error during execution, put it in process name.
212
- if remote_error:
213
- client_message.process_name = remote_error
214
- # If there was no error during execution, put the output of the ssh to process name.
215
- else:
216
- client_message.process_name = remote_output
217
- network.logger.info(f"Remote SSH: Client executing Command Line: {client_message.process_name}")
242
+ if exit_status == 0 and out == "3":
243
+ print_api(f"Detected remote Python 3 interpreter (once per client port): {candidate}", logger=self.logger)
244
+ return candidate
245
+
246
+ raise SSHRemoteWrapperNoPythonFound("No usable Python 3 interpreter found on remote host")
247
+
248
+ def _get_python_cmd(self) -> str:
249
+ if self.python_cmd is None:
250
+ self.python_cmd = self._detect_remote_python_cmd_name()
251
+ return self.python_cmd
252
+
253
+ def remote_execution_python(
254
+ self,
255
+ script_string: str,
256
+ script_arg_values: tuple = None,
257
+ script_kwargs: dict = None,
258
+ ):
259
+ """
260
+ Function to execute python script over SSH.
218
261
 
219
262
  :param script_string: string representation of python script.
263
+ :param script_arg_values: values arguments to pass to the script. Example for first argument: 56734
264
+ :param script_kwargs: keyword arguments to pass to the script.
265
+ Example: {'-r': None}
266
+ Interpreted as: -r
267
+ Example: {'-f': 'value'}
268
+ Interpreted as: -f value
269
+ Example: {'--arg': value}
270
+ Interpreted as: --arg value
271
+
220
272
  :return: SSH console output, Error output
221
273
  """
222
274
  # Defining variables.
223
275
  error_result: str | None = None
224
276
 
225
- encoded_base64_string = base64.b64encode(script_string.encode('ascii'))
226
- command_string: str = fr'python -c "import base64;exec(base64.b64decode({encoded_base64_string}))"'
277
+ python_cmd = self._get_python_cmd()
278
+ command: str = f"{python_cmd} -"
227
279
 
228
- # remote_output, remote_error = ssh_client.remote_execution('ipconfig')
229
- # remote_output, remote_error = ssh_client.remote_execution("python -c print('Hello')")
230
- # remote_output, remote_error = ssh_client.remote_execution("python -c import psutil")
231
- remote_output, remote_error = self.remote_execution(command_string)
280
+ if script_arg_values:
281
+ for arg in script_arg_values:
282
+ command += " " + shlex.quote(str(arg))
283
+
284
+ if script_kwargs:
285
+ for key, value in script_kwargs.items():
286
+ command += f" {shlex.quote(str(key))}"
287
+ if value is not None:
288
+ command += " " + shlex.quote(str(value))
289
+
290
+ remote_output, remote_error = self.remote_execution(command=command, script_string=script_string)
232
291
 
233
292
  # If there was an error during remote execution
234
293
  if remote_error:
@@ -238,10 +297,15 @@ class SSHRemote:
238
297
  if console_check:
239
298
  # 'execution_error' variable will be that full error.
240
299
  error_result = console_check
300
+ else:
301
+ error_result = remote_error
241
302
 
242
303
  return remote_output, error_result
243
304
 
244
- def connect_get_client_commandline(self, script_string):
305
+ def connect_get_client_commandline(
306
+ self,
307
+ port: int,
308
+ script_string: str):
245
309
  # Defining locals.
246
310
  execution_output: str | None = None
247
311
 
@@ -249,18 +313,18 @@ class SSHRemote:
249
313
  execution_error = self.connect()
250
314
  # if there was an error, try to connect again.
251
315
  if execution_error:
252
- self.logger.info("Retrying SSH Connection Initialization.")
316
+ print_api("Retrying SSH Connection Initialization.", logger=self.logger, logger_method='info')
253
317
  execution_error = self.connect()
254
318
 
255
319
  # If there was still an error, we won't be executing the script. And the error will be passed to
256
320
  # 'process_name'.
257
321
  if not execution_error:
258
- self.logger.info("Executing SSH command to acquire the calling process.")
322
+ print_api("Executing SSH command to acquire the calling process.", logger=self.logger, logger_method='info')
259
323
 
260
- execution_output, execution_error = self.remote_execution_python(script_string)
324
+ execution_output, execution_error = self.remote_execution_python(script_string=script_string, script_arg_values=(str(port),))
261
325
 
262
326
  # Closing SSH connection at this stage.
263
327
  self.close()
264
- self.logger.info("Acquired. Closed SSH connection.")
328
+ print_api("Acquired. Closed SSH connection.", logger=self.logger, logger_method='info')
265
329
 
266
330
  return execution_output, execution_error
atomicshop/web.py CHANGED
@@ -7,8 +7,8 @@ import http.client
7
7
 
8
8
  # noinspection PyPackageRequirements
9
9
  import certifi
10
+ from dkarchiver.arch_wrappers import zips
10
11
 
11
- from .archiver import zips
12
12
  from .urls import url_parser
13
13
  from .file_io import file_io
14
14
  from .wrappers.playwrightw import scenarios
@@ -18,10 +18,12 @@ from . import filesystem, print_api
18
18
  # https://www.useragents.me/
19
19
  # https://user-agents.net/
20
20
  USER_AGENTS = {
21
- 'Chrome_111.0.0_Windows_10-11_x64':
21
+ 'Chrome 111.0.0 Windows_10/11 x64':
22
22
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
23
- 'Chrome 132.0.0, Windows 10/11':
24
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36'
23
+ 'Chrome 132.0.0 Windows 10/11 x64':
24
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
25
+ 'Chrome 142.0.0 Windows 10/11 x64':
26
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'
25
27
  }
26
28
 
27
29
 
@@ -75,7 +77,7 @@ def get_page_bytes(
75
77
  raise ValueError('ERROR: [user_agent] specified and [chrome_user_agent] usage is [True]. Choose one.')
76
78
 
77
79
  if chrome_user_agent:
78
- user_agent = USER_AGENTS['Chrome_111.0.0_Windows_10-11_x64']
80
+ user_agent = USER_AGENTS['Chrome 142.0.0 Windows 10/11 x64']
79
81
 
80
82
  if user_agent:
81
83
  # Create a 'Request' object with the URL and user agent.
@@ -159,6 +161,7 @@ def download(
159
161
  target_directory: str = None,
160
162
  file_name: str = None,
161
163
  headers: dict = None,
164
+ overwrite: bool = False,
162
165
  # use_certifi_ca_repository: bool = False,
163
166
  **kwargs
164
167
  ) -> str | None:
@@ -174,6 +177,9 @@ def download(
174
177
  :param file_name: string, file name (example: file.zip) that you want the downloaded file to be saved as.
175
178
  If not specified, the default filename from 'file_url' will be used.
176
179
  :param headers: dictionary, HTTP headers to use when downloading the file.
180
+ :param overwrite: boolean, if True, the file will be overwritten if it already exists.
181
+ If False, the file will not be overwritten and the function will return None if the file already exists.
182
+ Default is False.
177
183
  :param use_certifi_ca_repository: boolean, if True, the certifi CA store will be used for SSL context
178
184
  instead of the system's default CA store.
179
185
  :return: string, full file path of downloaded file. If download failed, 'None' will be returned.
@@ -201,6 +207,13 @@ def download(
201
207
  # Build full path to file.
202
208
  file_path: str = f'{target_directory}{os.sep}{file_name}'
203
209
 
210
+ if os.path.exists(file_path):
211
+ if overwrite:
212
+ print_api.print_api(f'File already exists: {file_path}. Overwriting...', **kwargs)
213
+ else:
214
+ print_api.print_api(f'File already exists: {file_path}. Skipping download.', **kwargs)
215
+ return file_path
216
+
204
217
  print_api.print_api(f'Downloading: {file_url}', **kwargs)
205
218
  print_api.print_api(f'To: {file_path}', **kwargs)
206
219
 
@@ -301,7 +314,7 @@ def download_and_extract_file(
301
314
  Default is empty. If it is empty, then the filename will be extracted from 'file_url'.
302
315
  :param target_directory: string, target directory where to save the file.
303
316
  :param archive_remove_first_directory: boolean, sets if archive extract function will extract the archive without
304
- first directory in the archive. Check reference in the 'extract_archive_with_zipfile' function.
317
+ first directory in the archive. Check reference in the 'dkarchiver.arch_wrappers.zips.extract_archive_with_zipfile' function.
305
318
  :param headers: dictionary, HTTP headers to use when downloading the file.
306
319
  :return:
307
320
  """
@@ -313,7 +326,7 @@ def download_and_extract_file(
313
326
  # Extract the archive and remove the first directory.
314
327
  zips.extract_archive_with_zipfile(
315
328
  archive_path=f'{file_path}', extract_directory=target_directory,
316
- remove_first_directory=archive_remove_first_directory, **kwargs)
329
+ remove_first_directory=archive_remove_first_directory)
317
330
 
318
331
  # Remove the archive file.
319
332
  filesystem.remove_file(file_path=f'{file_path}', **kwargs)
@@ -1,7 +1,7 @@
1
- import os
2
1
  from typing import Literal
3
2
 
4
- import google.generativeai as genai
3
+ from google import genai
4
+ from google.genai.types import GenerateContentConfig
5
5
 
6
6
  from . import google_custom_search
7
7
  from ..wrappers.playwrightw import scenarios
@@ -11,6 +11,14 @@ from .. import urls
11
11
  class GoogleCustomSearchError(Exception):
12
12
  pass
13
13
 
14
+ class GoogleLLMNoContentError(Exception):
15
+ pass
16
+
17
+
18
+ """
19
+ Rate Limits and Quotas: https://ai.google.dev/gemini-api/docs/rate-limits
20
+ """
21
+
14
22
 
15
23
  class GoogleLLM:
16
24
  def __init__(
@@ -26,13 +34,10 @@ class GoogleLLM:
26
34
  :param search_engine_id: str, the search engine ID for the Google Custom Search API.
27
35
  """
28
36
 
29
- self.genai = genai
37
+ self.client = genai.Client(api_key=llm_api_key)
30
38
  self.search_api_key: str = search_api_key
31
39
  self.search_engine_id: str = search_engine_id
32
40
 
33
- os.environ["API_KEY"] = llm_api_key
34
- genai.configure(api_key=os.environ["API_KEY"])
35
-
36
41
  def get_current_models(
37
42
  self,
38
43
  full_info: bool = False
@@ -43,7 +48,7 @@ class GoogleLLM:
43
48
  :param full_info: bool, if True, returns the full information about the models, otherwise only the names for API usage.
44
49
  """
45
50
  result_list: list = []
46
- for model in self.genai.list_models():
51
+ for model in self.client.models.list():
47
52
  if full_info:
48
53
  result_list.append(model)
49
54
  else:
@@ -68,7 +73,7 @@ class GoogleLLM:
68
73
  temperature: float = 0,
69
74
  # max_output_tokens: int = 4096,
70
75
  # model_name: str = 'gemini-2.0-flash-thinking-exp-01-21'
71
- model_name: str = 'models/gemini-2.5-pro-preview-03-25'
76
+ model_name: str = 'gemini-2.5-pro'
72
77
  ) -> str:
73
78
  """
74
79
  Function to get the answer to a question by searching Google Custom Console API and processing the content using Gemini API.
@@ -112,6 +117,9 @@ class GoogleLLM:
112
117
  urls=links[:number_of_top_links], number_of_characters_per_link=number_of_characters_per_link,
113
118
  text_fetch_method=text_fetch_method)
114
119
 
120
+ if not contents:
121
+ raise GoogleLLMNoContentError("No content was fetched from the provided URL(s).")
122
+
115
123
  combined_content = ""
116
124
  for content in contents:
117
125
  combined_content += f'{content}\n\n\n\n================================================================'
@@ -126,12 +134,12 @@ class GoogleLLM:
126
134
  gemini_response = self.ask_gemini(final_question, temperature, model_name)
127
135
  return gemini_response
128
136
 
129
- @staticmethod
130
137
  def ask_gemini(
138
+ self,
131
139
  question: str,
132
140
  temperature: float,
133
141
  # max_output_tokens: int,
134
- model_name: str = 'gemini-2.0-flash-thinking-exp-01-21'
142
+ model_name: str = 'gemini-2.5-pro'
135
143
  ) -> str:
136
144
  """
137
145
  Function to ask the Gemini API a question and get the response.
@@ -161,8 +169,8 @@ class GoogleLLM:
161
169
  # "max_output_tokens": max_output_tokens,
162
170
  }
163
171
 
164
- # model = genai.GenerativeModel('gemini-1.5-pro-latest',
165
- # noinspection PyTypeChecker
166
- model = genai.GenerativeModel(model_name, generation_config=model_config)
167
- response = model.generate_content(question)
172
+ response = self.client.models.generate_content(
173
+ model=model_name,
174
+ contents=question,
175
+ config=GenerateContentConfig(**model_config))
168
176
  return response.text
@@ -1,8 +1,9 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
 
4
+ from dkarchiver.arch_wrappers import sevenz_app_w
5
+
4
6
  from . import tables
5
- from ....archiver import sevenz_app_w
6
7
 
7
8
 
8
9
  def resolve_directory_path(directory_info, directory_key):
@@ -1,11 +1,12 @@
1
1
  import os
2
2
  import argparse
3
3
 
4
+ from dkarchiver.arch_wrappers import sevenz_app_w
5
+
4
6
  from .base import msi
5
7
  from . import base, tables, cabs
6
8
  from ... import olefilew
7
9
  from ....print_api import print_api
8
- from ....archiver import sevenz_app_w
9
10
 
10
11
 
11
12
  # Directory names.
@@ -68,8 +68,8 @@ def change_image_content(
68
68
  dockerw.change_image_content(
69
69
  image_id_or_name="your_docker_image_id_or_name",
70
70
  list_of_commands=[
71
- "apt-get update",
72
- "apt-get install -y python3"
71
+ "apt update",
72
+ "apt install -y python3"
73
73
  ]
74
74
  )
75
75
  ----------------------