atomicshop 2.12.11__py3-none-any.whl → 2.12.13__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.
- atomicshop/__init__.py +1 -1
- atomicshop/diff_check.py +77 -2
- atomicshop/filesystem.py +25 -4
- atomicshop/monitor/change_monitor.py +11 -4
- atomicshop/monitor/checks/dns.py +5 -0
- {atomicshop-2.12.11.dist-info → atomicshop-2.12.13.dist-info}/METADATA +1 -1
- {atomicshop-2.12.11.dist-info → atomicshop-2.12.13.dist-info}/RECORD +10 -10
- {atomicshop-2.12.11.dist-info → atomicshop-2.12.13.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.12.11.dist-info → atomicshop-2.12.13.dist-info}/WHEEL +0 -0
- {atomicshop-2.12.11.dist-info → atomicshop-2.12.13.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/diff_check.py
CHANGED
|
@@ -2,7 +2,9 @@ import datetime
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Union, Literal
|
|
4
4
|
import json
|
|
5
|
+
import queue
|
|
5
6
|
|
|
7
|
+
from . import filesystem, datetimes
|
|
6
8
|
from .file_io import file_io, jsons
|
|
7
9
|
from .print_api import print_api
|
|
8
10
|
from .basics import list_of_dicts, dicts
|
|
@@ -39,7 +41,8 @@ class DiffChecker:
|
|
|
39
41
|
input_file_rotation_cycle_hours: Union[
|
|
40
42
|
float,
|
|
41
43
|
Literal['midnight'],
|
|
42
|
-
None] = None
|
|
44
|
+
None] = None,
|
|
45
|
+
enable_statistics_queue: bool = False
|
|
43
46
|
):
|
|
44
47
|
"""
|
|
45
48
|
:param check_object: any, object to check if it changed.
|
|
@@ -83,9 +86,30 @@ class DiffChecker:
|
|
|
83
86
|
compared only to itself, and if it changes, it will be updated.
|
|
84
87
|
None: Nothing will be done, you will get an exception.
|
|
85
88
|
:param input_file_rotation_cycle_hours:
|
|
86
|
-
float, the amount of hours the input file will be rotated.
|
|
89
|
+
float, the amount of hours the input file will be rotated in the 'hit_statistics' operation type.
|
|
87
90
|
str, (only 'midnight' is valid), the input file will be rotated daily at midnight.
|
|
88
91
|
This is valid only for the 'hit_statistics' operation type.
|
|
92
|
+
:param enable_statistics_queue: boolean, if True, the statistics queue will be enabled for the 'hit_statistics'
|
|
93
|
+
operation type. You can use this queue to process the statistics in another thread.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
diff_checker = DiffChecker(
|
|
97
|
+
check_object_display_name='List of Dicts',
|
|
98
|
+
input_file_path='D:\\input\\list_of_dicts.json',
|
|
99
|
+
input_file_write_only=True,
|
|
100
|
+
return_first_cycle=True,
|
|
101
|
+
operation_type='hit_statistics',
|
|
102
|
+
input_file_rotation_cycle_hours='midnight',
|
|
103
|
+
enable_statistics_queue=True)
|
|
104
|
+
|
|
105
|
+
def process_statistics_queue():
|
|
106
|
+
while True:
|
|
107
|
+
statistics = diff_checker.statistics_queue.get()
|
|
108
|
+
print(statistics)
|
|
109
|
+
|
|
110
|
+
threading.Thread(target=process_statistics_queue).start()
|
|
111
|
+
|
|
112
|
+
<... Your checking operation for the object ...>
|
|
89
113
|
|
|
90
114
|
--------------------------------------------------
|
|
91
115
|
|
|
@@ -167,6 +191,14 @@ class DiffChecker:
|
|
|
167
191
|
if input_file_rotation_cycle_hours and operation_type != 'hit_statistics':
|
|
168
192
|
raise ValueError("[input_file_rotation_cycle] can be specified only for 'hit_statistics' operation type.")
|
|
169
193
|
|
|
194
|
+
if enable_statistics_queue and operation_type != 'hit_statistics':
|
|
195
|
+
raise ValueError("[enable_statistics_queue] can be specified only for 'hit_statistics' operation type.")
|
|
196
|
+
|
|
197
|
+
if input_file_rotation_cycle_hours and not isinstance(input_file_rotation_cycle_hours, float) and \
|
|
198
|
+
not isinstance(input_file_rotation_cycle_hours, int) and \
|
|
199
|
+
input_file_rotation_cycle_hours != 'midnight':
|
|
200
|
+
raise ValueError("[input_file_rotation_cycle] must be float, int or 'midnight' str.")
|
|
201
|
+
|
|
170
202
|
self.check_object = check_object
|
|
171
203
|
self.check_object_display_name = check_object_display_name
|
|
172
204
|
self.aggregation: bool = aggregation
|
|
@@ -175,6 +207,7 @@ class DiffChecker:
|
|
|
175
207
|
self.return_first_cycle: bool = return_first_cycle
|
|
176
208
|
self.operation_type = operation_type
|
|
177
209
|
self.input_file_rotation_cycle = input_file_rotation_cycle_hours
|
|
210
|
+
self.enable_statistics_queue = enable_statistics_queue
|
|
178
211
|
|
|
179
212
|
if not self.check_object_display_name:
|
|
180
213
|
self.check_object_display_name = self.check_object
|
|
@@ -184,6 +217,19 @@ class DiffChecker:
|
|
|
184
217
|
# The format the file will be saved as (not used as extension): txt, json.
|
|
185
218
|
self.save_as: str = str()
|
|
186
219
|
|
|
220
|
+
# If the input file rotation cycle is set and the statistics queue is enabled, we will create the queue.
|
|
221
|
+
if self.input_file_rotation_cycle and self.enable_statistics_queue:
|
|
222
|
+
# You can use this queue to process the statistics in another thread.
|
|
223
|
+
self.statistics_queue = queue.Queue()
|
|
224
|
+
else:
|
|
225
|
+
self.statistics_queue = None
|
|
226
|
+
|
|
227
|
+
# If the input file rotation cycle is set to midnight, we will store the previous day as today.
|
|
228
|
+
if self.input_file_rotation_cycle == 'midnight':
|
|
229
|
+
self.previous_day = datetime.datetime.now().strftime('%d')
|
|
230
|
+
else:
|
|
231
|
+
self.previous_day = None
|
|
232
|
+
|
|
187
233
|
def check_string(self, print_kwargs: dict = None):
|
|
188
234
|
"""
|
|
189
235
|
The function will check file content for change by hashing it and comparing the hash.
|
|
@@ -297,6 +343,35 @@ class DiffChecker:
|
|
|
297
343
|
return result, message
|
|
298
344
|
|
|
299
345
|
def _hit_statistics_only_handling(self, current_content, result, message, sort_by_keys, print_kwargs: dict = None):
|
|
346
|
+
if self.input_file_rotation_cycle == 'midnight':
|
|
347
|
+
# If the current time is midnight, we will rotate the file.
|
|
348
|
+
# Get current date.
|
|
349
|
+
current_date = datetime.datetime.now().strftime('%d')
|
|
350
|
+
# If current date is different from previous date it means it is a new day, rotate the file.
|
|
351
|
+
if current_date != self.previous_day:
|
|
352
|
+
input_file_statistics = None
|
|
353
|
+
try:
|
|
354
|
+
# Read the latest statistics from the input file.
|
|
355
|
+
input_file_statistics = jsons.read_json_file(self.input_file_path)
|
|
356
|
+
# Basically, this means that the input statistics file doesn't exist yet, and no events hit
|
|
357
|
+
# yet and new day has come, so it doesn't matter, since there are no statistics to rotate the file.
|
|
358
|
+
except FileNotFoundError:
|
|
359
|
+
pass
|
|
360
|
+
|
|
361
|
+
if input_file_statistics:
|
|
362
|
+
# Rename the file.
|
|
363
|
+
filesystem.backup_file(
|
|
364
|
+
self.input_file_path, str(Path(self.input_file_path).parent), timestamp_as_prefix=False)
|
|
365
|
+
# Update the previous date.
|
|
366
|
+
self.previous_day = current_date
|
|
367
|
+
|
|
368
|
+
previous_day_date_object = (datetime.datetime.now() - datetime.timedelta(days=1)).date()
|
|
369
|
+
# Put the statistics in the queue to be processed.
|
|
370
|
+
if self.statistics_queue:
|
|
371
|
+
self.statistics_queue.put((input_file_statistics, previous_day_date_object))
|
|
372
|
+
else:
|
|
373
|
+
raise NotImplementedError("This feature is not implemented yet.")
|
|
374
|
+
|
|
300
375
|
# Convert the dictionary entry to string, since we will use it as a key in the dictionary.
|
|
301
376
|
current_entry = json.dumps(current_content[0])
|
|
302
377
|
|
atomicshop/filesystem.py
CHANGED
|
@@ -1391,23 +1391,44 @@ def backup_folder(directory_path: str, backup_directory: str) -> None:
|
|
|
1391
1391
|
move_folder(directory_path, backup_directory_path)
|
|
1392
1392
|
|
|
1393
1393
|
|
|
1394
|
-
def backup_file(file_path: str, backup_directory: str) -> None:
|
|
1394
|
+
def backup_file(file_path: str, backup_directory: str, timestamp_as_prefix: bool = False) -> None:
|
|
1395
1395
|
"""
|
|
1396
1396
|
Backup the specified file.
|
|
1397
1397
|
|
|
1398
1398
|
:param file_path: The file path to backup.
|
|
1399
1399
|
:param backup_directory: The directory to backup the file to.
|
|
1400
|
-
|
|
1400
|
+
:param timestamp_as_prefix: boolean, if
|
|
1401
|
+
True, then the timestamp will be added as a prefix to the file name.
|
|
1402
|
+
False, then the timestamp will be added as a suffix to the file name.
|
|
1403
|
+
-----------------------------------------
|
|
1401
1404
|
Example:
|
|
1402
|
-
backup_file(
|
|
1405
|
+
backup_file(
|
|
1406
|
+
file_path='C:\\Users\\user1\\Downloads\\file.txt',
|
|
1407
|
+
backup_directory='C:\\Users\\user1\\Downloads\\backup',
|
|
1408
|
+
timestamp_as_prefix=True
|
|
1409
|
+
)
|
|
1403
1410
|
|
|
1404
1411
|
Backed up file will be moved to 'C:\\Users\\user1\\Downloads\\backup' with timestamp in the name.
|
|
1405
1412
|
Final path will look like: 'C:\\Users\\user1\\Downloads\\backup\\20231003-120000-000000_file.txt'
|
|
1413
|
+
---------------------------------------------
|
|
1414
|
+
Example when timestamp_as_prefix is False:
|
|
1415
|
+
backup_file(
|
|
1416
|
+
file_path='C:\\Users\\user1\\Downloads\\file.txt',
|
|
1417
|
+
backup_directory='C:\\Users\\user1\\Downloads\\backup',
|
|
1418
|
+
timestamp_as_prefix=False
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
Backed up file will be moved to 'C:\\Users\\user1\\Downloads\\backup' with timestamp in the name.
|
|
1422
|
+
Final path will look like: 'C:\\Users\\user1\\Downloads\\backup\\file_20231003-120000-000000.txt'
|
|
1406
1423
|
"""
|
|
1407
1424
|
|
|
1408
1425
|
if check_file_existence(file_path):
|
|
1409
1426
|
timestamp: str = datetimes.TimeFormats().get_current_formatted_time_filename_stamp(True)
|
|
1410
1427
|
file_name_no_extension = Path(file_path).stem
|
|
1411
1428
|
file_extension = Path(file_path).suffix
|
|
1412
|
-
|
|
1429
|
+
if timestamp_as_prefix:
|
|
1430
|
+
file_name: str = f"{timestamp}_{file_name_no_extension}{file_extension}"
|
|
1431
|
+
else:
|
|
1432
|
+
file_name: str = f"{file_name_no_extension}_{timestamp}{file_extension}"
|
|
1433
|
+
backup_file_path: str = str(Path(backup_directory) / file_name)
|
|
1413
1434
|
move_file(file_path, backup_file_path)
|
|
@@ -35,7 +35,8 @@ class ChangeMonitor:
|
|
|
35
35
|
input_file_rotation_cycle_hours: Union[
|
|
36
36
|
float,
|
|
37
37
|
Literal['midnight'],
|
|
38
|
-
None] = None
|
|
38
|
+
None] = None,
|
|
39
|
+
enable_statistics_queue: bool = False
|
|
39
40
|
):
|
|
40
41
|
"""
|
|
41
42
|
:param object_type: string, type of object to check. The type must be one of the following:
|
|
@@ -81,9 +82,10 @@ class ChangeMonitor:
|
|
|
81
82
|
'all_objects': disable the DiffChecker features, meaning any new entries will be emitted as is.
|
|
82
83
|
None: will use the default operation type, based on the object type.
|
|
83
84
|
:param input_file_rotation_cycle_hours:
|
|
84
|
-
float, the amount of hours the input file will be rotated.
|
|
85
|
+
float, the amount of hours the input file will be rotated in the 'hit_statistics' operation type.
|
|
85
86
|
str, (only 'midnight' is valid), the input file will be rotated daily at midnight.
|
|
86
87
|
This is valid only for the 'hit_statistics' operation type.
|
|
88
|
+
:param enable_statistics_queue: boolean, if True, the statistics queue will be enabled.
|
|
87
89
|
|
|
88
90
|
If 'input_file_directory' is not specified, the 'input_file_name' is not specified, and
|
|
89
91
|
'generate_input_file_name' is False, then the input file will not be used and the object will be stored
|
|
@@ -127,6 +129,7 @@ class ChangeMonitor:
|
|
|
127
129
|
self.store_original_object: bool = store_original_object
|
|
128
130
|
self.operation_type = operation_type
|
|
129
131
|
self.input_file_rotation_cycle_hours = input_file_rotation_cycle_hours
|
|
132
|
+
self.enable_statistics_queue = enable_statistics_queue
|
|
130
133
|
|
|
131
134
|
# === EOF Initialize Main variables ================================
|
|
132
135
|
# === Initialize Secondary variables ===============================
|
|
@@ -141,7 +144,8 @@ class ChangeMonitor:
|
|
|
141
144
|
DiffChecker(
|
|
142
145
|
input_file_write_only=self.input_file_write_only,
|
|
143
146
|
operation_type=self.operation_type,
|
|
144
|
-
input_file_rotation_cycle_hours=self.input_file_rotation_cycle_hours
|
|
147
|
+
input_file_rotation_cycle_hours=self.input_file_rotation_cycle_hours,
|
|
148
|
+
enable_statistics_queue=self.enable_statistics_queue
|
|
145
149
|
)
|
|
146
150
|
)
|
|
147
151
|
# Else, if 'check_object_list' is None, create a DiffChecker object only once.
|
|
@@ -150,7 +154,8 @@ class ChangeMonitor:
|
|
|
150
154
|
DiffChecker(
|
|
151
155
|
input_file_write_only=self.input_file_write_only,
|
|
152
156
|
operation_type=self.operation_type,
|
|
153
|
-
input_file_rotation_cycle_hours=self.input_file_rotation_cycle_hours
|
|
157
|
+
input_file_rotation_cycle_hours=self.input_file_rotation_cycle_hours,
|
|
158
|
+
enable_statistics_queue=self.enable_statistics_queue
|
|
154
159
|
)
|
|
155
160
|
)
|
|
156
161
|
|
|
@@ -172,6 +177,8 @@ class ChangeMonitor:
|
|
|
172
177
|
self.fetch_engine = None
|
|
173
178
|
self.thread_looper = scheduling.ThreadLooper()
|
|
174
179
|
|
|
180
|
+
self.statistics_queue = None
|
|
181
|
+
|
|
175
182
|
def _set_input_file_path(self, check_object_index: int = 0):
|
|
176
183
|
if self.first_cycle:
|
|
177
184
|
# If 'input_file_directory' and 'input_file_name' are specified, we'll use a filename to store.
|
atomicshop/monitor/checks/dns.py
CHANGED
|
@@ -30,6 +30,11 @@ def _execute_cycle(change_monitor_instance, print_kwargs: dict = None):
|
|
|
30
30
|
if not change_monitor_instance.operation_type:
|
|
31
31
|
change_monitor_instance.diff_check_list[0].operation_type = 'new_objects'
|
|
32
32
|
|
|
33
|
+
# Since the 'operation_type' is 'hit_statistics', we'll set the 'statistics_queue' to the 'statistics_queue' of
|
|
34
|
+
# the 'diff_check_list[0]'.
|
|
35
|
+
if change_monitor_instance.diff_check_list[0].operation_type == 'hit_statistics':
|
|
36
|
+
change_monitor_instance.statistics_queue = change_monitor_instance.diff_check_list[0].statistics_queue
|
|
37
|
+
|
|
33
38
|
if change_monitor_instance.generate_input_file_name:
|
|
34
39
|
original_name = 'known_domains'
|
|
35
40
|
# Make path for 'input_file_name'.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=IAlzgmgRqFnCgrTcw-XPXKSojTHB6ZNg8wtkPn2glI8,124
|
|
2
2
|
atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
|
|
3
3
|
atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
|
|
4
4
|
atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
|
|
@@ -9,12 +9,12 @@ atomicshop/config_init.py,sha256=z2RXD_mw9nQlAOpuGry1h9QT-2LhNscXgGAktN3dCVQ,249
|
|
|
9
9
|
atomicshop/console_output.py,sha256=AOSJjrRryE97PAGtgDL03IBtWSi02aNol8noDnW3k6M,4667
|
|
10
10
|
atomicshop/console_user_response.py,sha256=31HIy9QGXa7f-GVR8MzJauQ79E_ZqAeagF3Ks4GGdDU,3234
|
|
11
11
|
atomicshop/datetimes.py,sha256=olsL01S5tkXk4WPzucxujqgLOh198BLgJntDnGYukRU,15533
|
|
12
|
-
atomicshop/diff_check.py,sha256=
|
|
12
|
+
atomicshop/diff_check.py,sha256=YRe4BKLFhJrOtG56-45VsviR4buxZ2nSykNZGHfH1K4,24248
|
|
13
13
|
atomicshop/dns.py,sha256=bNZOo5jVPzq7OT2qCPukXoK3zb1oOsyaelUwQEyK1SA,2500
|
|
14
14
|
atomicshop/domains.py,sha256=Rxu6JhhMqFZRcoFs69IoEd1PtYca0lMCG6F1AomP7z4,3197
|
|
15
15
|
atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
|
|
16
16
|
atomicshop/file_types.py,sha256=-0jzQMRlmU1AP9DARjk-HJm1tVE22E6ngP2mRblyEjY,763
|
|
17
|
-
atomicshop/filesystem.py,sha256=
|
|
17
|
+
atomicshop/filesystem.py,sha256=202ue2LkjI1KdaxvB_RHV-2eIczy2-caZGLO4PSePik,53887
|
|
18
18
|
atomicshop/functions.py,sha256=pK8hoCE9z61PtWCxQJsda7YAphrLH1wxU5x-1QJP-sY,499
|
|
19
19
|
atomicshop/hashing.py,sha256=Le8qGFyt3_wX-zGTeQShz7L2HL_b6nVv9PnawjglyHo,3474
|
|
20
20
|
atomicshop/http_parse.py,sha256=nrf2rZcprLqtW8HVrV7TCZ1iTBcWRRy-mXIlAOzcaJs,9703
|
|
@@ -132,9 +132,9 @@ atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256
|
|
|
132
132
|
atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=KENDVf9OwXD9gwSh4B1XxACCe7iHYjrvnW1t6F64wdE,695
|
|
133
133
|
atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
|
|
134
134
|
atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
|
-
atomicshop/monitor/change_monitor.py,sha256=
|
|
135
|
+
atomicshop/monitor/change_monitor.py,sha256=NgadgPynRohII4m5oawWfQXTTj5lUx8gj38Tuk1sWOg,12247
|
|
136
136
|
atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
137
|
-
atomicshop/monitor/checks/dns.py,sha256=
|
|
137
|
+
atomicshop/monitor/checks/dns.py,sha256=QOjvIQZndETomgTuY1PcEbUJkZkRtr6GcRV3Tfbu89c,4866
|
|
138
138
|
atomicshop/monitor/checks/hash.py,sha256=A6bJ7F5Qv_brdEh3sGhOyfviab2dsnvbXUufyBk5C1U,1951
|
|
139
139
|
atomicshop/monitor/checks/network.py,sha256=I9f3KyNnlx97E8igGZXpVJl4MlUp9iU6aSbILCKqbA0,3820
|
|
140
140
|
atomicshop/monitor/checks/process_running.py,sha256=948Sify4P2KFTE1ZrLHKLwd1B1HOgWmC11x3b6MCvz0,1892
|
|
@@ -251,8 +251,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
251
251
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
252
252
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
253
253
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
254
|
-
atomicshop-2.12.
|
|
255
|
-
atomicshop-2.12.
|
|
256
|
-
atomicshop-2.12.
|
|
257
|
-
atomicshop-2.12.
|
|
258
|
-
atomicshop-2.12.
|
|
254
|
+
atomicshop-2.12.13.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
255
|
+
atomicshop-2.12.13.dist-info/METADATA,sha256=QiNXQOpYBd9zGkvMzfTeQveN68Bo67-LIMjKVLsS7xE,10479
|
|
256
|
+
atomicshop-2.12.13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
257
|
+
atomicshop-2.12.13.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
258
|
+
atomicshop-2.12.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|