atomicshop 2.11.47__py3-none-any.whl → 3.10.5__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
+
from atomicshop import filesystem
|
|
3
4
|
from atomicshop.wrappers.factw.fact_extractor import get_extractor
|
|
4
5
|
from atomicshop.wrappers.factw import config_install
|
|
5
|
-
from atomicshop import
|
|
6
|
+
from atomicshop.permissions import ubuntu_permissions
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def main():
|
|
9
10
|
get_extractor.get_extractor_script()
|
|
10
11
|
fact_extractor_executable_path: str = (
|
|
11
12
|
filesystem.get_working_directory() + os.sep + config_install.FACT_EXTRACTOR_FILE_NAME)
|
|
12
|
-
|
|
13
|
+
ubuntu_permissions.set_executable(fact_extractor_executable_path)
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
if __name__ == '__main__':
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
REM Install WIndows 10 SDK before running.
|
|
2
|
+
REM Execute this cmd in:
|
|
3
|
+
REM Start => All Apps => Visual Studio 2022 => x64 Native Tools Command Prompt for VS 2022
|
|
4
|
+
REM Run the prompt as admin.
|
|
5
|
+
REM "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\Hostx64\x64\cl.exe" /LD /EHsc c:\compile\process_list.cpp /Fo:c:\compile /Fe:c:\compile\process_list.dll /I c:\compile\inc\ /link /LIBPATH:c:\compile\lib\ Advapi32.lib ntdll.lib Psapi.lib
|
|
6
|
+
cl /LD /EHsc c:\compile\process_list.cpp /Fo:c:\compile /Fe:c:\compile\process_list.dll /I c:\compile\inc\ /link /LIBPATH:c:\compile\lib\ Advapi32.lib ntdll.lib Psapi.lib
|
|
7
|
+
pause
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
#include <tchar.h>
|
|
4
4
|
#include <iostream>
|
|
5
5
|
#include <tlhelp32.h>
|
|
6
|
+
#include <atomic>
|
|
7
|
+
|
|
8
|
+
std::atomic<bool> cancelRequested(false);
|
|
6
9
|
|
|
7
10
|
typedef LONG NTSTATUS;
|
|
8
11
|
typedef NTSTATUS (*PFN_NT_QUERY_INFORMATION_PROCESS)(HANDLE, UINT, PVOID, ULONG, PULONG);
|
|
@@ -121,6 +124,10 @@ bool GetProcessCommandLine(DWORD dwPid, wchar_t** ppszCmdLine, wchar_t* szProces
|
|
|
121
124
|
|
|
122
125
|
typedef void(*CallbackFunc)(DWORD pid, wchar_t* process_name, wchar_t* cmdline);
|
|
123
126
|
|
|
127
|
+
extern "C" __declspec(dllexport) void RequestCancellation() {
|
|
128
|
+
cancelRequested.store(true);
|
|
129
|
+
}
|
|
130
|
+
|
|
124
131
|
extern "C" __declspec(dllexport) void GetProcessDetails(CallbackFunc callback) {
|
|
125
132
|
if (!EnableDebugPrivilege()) {
|
|
126
133
|
std::wcout << L"Failed to enable debug privilege." << std::endl;
|
|
@@ -156,7 +163,7 @@ extern "C" __declspec(dllexport) void GetProcessDetails(CallbackFunc callback) {
|
|
|
156
163
|
} else {
|
|
157
164
|
callback(pe32.th32ProcessID, szProcessName, NULL);
|
|
158
165
|
}
|
|
159
|
-
} while (Process32NextW(hSnap, &pe32));
|
|
166
|
+
} while (Process32NextW(hSnap, &pe32) && !cancelRequested.load());
|
|
160
167
|
|
|
161
168
|
CloseHandle(hSnap);
|
|
162
169
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import psutil
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _make_parser():
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
description="List all local TCP source ports currently in use that have a command line."
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
'-a', '--show-all', action='store_true',
|
|
15
|
+
help='Show all the TCP ports and not only these that have command line.')
|
|
16
|
+
|
|
17
|
+
return parser
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main(
|
|
21
|
+
show_all: bool = False
|
|
22
|
+
) -> int:
|
|
23
|
+
# Use a set so we only print each port once
|
|
24
|
+
source_ports = set()
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
# Get all TCP connections (any state)
|
|
28
|
+
# If you only want established ones, use: kind='tcp' and filter by c.status
|
|
29
|
+
connections = psutil.net_connections(kind='tcp')
|
|
30
|
+
except psutil.AccessDenied:
|
|
31
|
+
print("Access denied when reading connections. Try running as admin/root.")
|
|
32
|
+
return 1
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print(f"Error getting connections: {e}")
|
|
35
|
+
return 1
|
|
36
|
+
|
|
37
|
+
# Cache whether each PID has a non-empty cmdline to avoid repeated lookups
|
|
38
|
+
pid_has_cmdline: dict[int, bool] = {}
|
|
39
|
+
|
|
40
|
+
for c in connections:
|
|
41
|
+
# c.laddr is the local (source) address; may be empty for some entries
|
|
42
|
+
if not c.laddr:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
# psutil uses a namedtuple for laddr: (ip, port)
|
|
46
|
+
try:
|
|
47
|
+
port = c.laddr.port # works on modern psutil (namedtuple)
|
|
48
|
+
except AttributeError:
|
|
49
|
+
# fallback if it's a plain tuple
|
|
50
|
+
port = c.laddr[1]
|
|
51
|
+
|
|
52
|
+
if not show_all:
|
|
53
|
+
pid = c.pid
|
|
54
|
+
if pid is None:
|
|
55
|
+
# No associated process => treat as "no command line"
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
if pid not in pid_has_cmdline:
|
|
59
|
+
try:
|
|
60
|
+
proc = psutil.Process(pid)
|
|
61
|
+
cmdline = proc.cmdline()
|
|
62
|
+
# Non-empty list and at least one non-empty arg
|
|
63
|
+
has_cmd = bool(cmdline and any(arg.strip() for arg in cmdline))
|
|
64
|
+
pid_has_cmdline[pid] = has_cmd
|
|
65
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
66
|
+
# If we can't read it, treat as no command line
|
|
67
|
+
pid_has_cmdline[pid] = False
|
|
68
|
+
|
|
69
|
+
if not pid_has_cmdline[pid]:
|
|
70
|
+
# Skip ports whose owning process has an empty/unavailable cmdline
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
# Either show_all is True, or the owning process has a non-empty cmdline
|
|
74
|
+
source_ports.add(port)
|
|
75
|
+
|
|
76
|
+
# Print ports sorted for readability
|
|
77
|
+
for port in sorted(source_ports):
|
|
78
|
+
print(port)
|
|
79
|
+
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
arg_parser = _make_parser()
|
|
84
|
+
args = arg_parser.parse_args()
|
|
85
|
+
sys.exit(main(**vars(args)))
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import subprocess
|
|
5
|
+
import re
|
|
6
|
+
import base64
|
|
7
|
+
import textwrap
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_ca_installed(issuer_name: str) -> tuple[bool, str]:
|
|
11
|
+
result = subprocess.run(
|
|
12
|
+
['certutil', '-store', 'Root', issuer_name],
|
|
13
|
+
capture_output=True,
|
|
14
|
+
text=True,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if 'Object was not found' in result.stdout:
|
|
18
|
+
return False, ''
|
|
19
|
+
|
|
20
|
+
if result.returncode == 0:
|
|
21
|
+
return True, ''
|
|
22
|
+
else:
|
|
23
|
+
message: str = (f"stdout: {result.stdout}\n"
|
|
24
|
+
f"stderr: {result.stderr}\n")
|
|
25
|
+
return False, message
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def remove_ca_certificate(issuer_name: str) -> int:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
['certutil', '-delstore', 'Root', issuer_name],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if result.returncode != 0:
|
|
36
|
+
print(f"Error removing certificate: {result.stderr}", file=sys.stderr)
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
return 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def install_ca_certificate(certificate_string: str) -> int:
|
|
43
|
+
pem = certificate_string or ""
|
|
44
|
+
|
|
45
|
+
# Extract one or more CERTIFICATE blocks
|
|
46
|
+
blocks = re.findall(
|
|
47
|
+
r"-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----",
|
|
48
|
+
pem,
|
|
49
|
+
flags=re.DOTALL,
|
|
50
|
+
)
|
|
51
|
+
if not blocks:
|
|
52
|
+
print("Error installing certificate: no PEM CERTIFICATE block found.", file=sys.stderr)
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
def normalize_pem(block: str) -> str:
|
|
56
|
+
m = re.search(
|
|
57
|
+
r"-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----",
|
|
58
|
+
block,
|
|
59
|
+
flags=re.DOTALL,
|
|
60
|
+
)
|
|
61
|
+
if not m:
|
|
62
|
+
raise ValueError("Invalid PEM block.")
|
|
63
|
+
|
|
64
|
+
# Remove everything except Base64 alphabet and padding
|
|
65
|
+
b64 = re.sub(r"[^A-Za-z0-9+/=]", "", m.group(1))
|
|
66
|
+
|
|
67
|
+
# Strict decode/encode round-trip to ensure it is valid DER
|
|
68
|
+
der = base64.b64decode(b64, validate=True)
|
|
69
|
+
b64_clean = base64.b64encode(der).decode("ascii")
|
|
70
|
+
wrapped = "\r\n".join(textwrap.wrap(b64_clean, 64))
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
"-----BEGIN CERTIFICATE-----\r\n"
|
|
74
|
+
+ wrapped
|
|
75
|
+
+ "\r\n-----END CERTIFICATE-----\r\n"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
for block in blocks:
|
|
79
|
+
tmp_path = None
|
|
80
|
+
try:
|
|
81
|
+
normalized = normalize_pem(block)
|
|
82
|
+
|
|
83
|
+
with tempfile.NamedTemporaryFile(
|
|
84
|
+
mode="w",
|
|
85
|
+
suffix=".cer",
|
|
86
|
+
delete=False, # certutil needs a real path on Windows
|
|
87
|
+
encoding="ascii",
|
|
88
|
+
newline="",
|
|
89
|
+
) as f:
|
|
90
|
+
f.write(normalized)
|
|
91
|
+
tmp_path = f.name
|
|
92
|
+
|
|
93
|
+
result = subprocess.run(
|
|
94
|
+
["certutil", "-f", "-addstore", "Root", tmp_path],
|
|
95
|
+
text=True,
|
|
96
|
+
capture_output=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if result.returncode != 0:
|
|
100
|
+
# Optional: try to get a more descriptive parse error
|
|
101
|
+
dump = subprocess.run(
|
|
102
|
+
["certutil", "-dump", tmp_path],
|
|
103
|
+
text=True,
|
|
104
|
+
capture_output=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
print(
|
|
108
|
+
"Error installing certificate:\n"
|
|
109
|
+
f"stdout: {result.stdout}\n"
|
|
110
|
+
f"stderr: {result.stderr}\n"
|
|
111
|
+
"certutil -dump output:\n"
|
|
112
|
+
f"{dump.stdout}\n"
|
|
113
|
+
f"{dump.stderr}",
|
|
114
|
+
file=sys.stderr,
|
|
115
|
+
)
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f"Error installing certificate: {e}", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
finally:
|
|
123
|
+
if tmp_path:
|
|
124
|
+
try:
|
|
125
|
+
os.remove(tmp_path)
|
|
126
|
+
except OSError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def main() -> int:
|
|
133
|
+
if len(sys.argv) < 3:
|
|
134
|
+
print("Usage: install_ca_certificate.py <Issuer Name> <crt cert string>", file=sys.stderr)
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
certificate_string_base64: str = sys.argv[2]
|
|
138
|
+
certificate_string = base64.b64decode(certificate_string_base64).decode("utf-8")
|
|
139
|
+
if not "-----BEGIN CERTIFICATE-----" in certificate_string:
|
|
140
|
+
print("Error: Certificate string must be in PEM format.", file=sys.stderr)
|
|
141
|
+
print(certificate_string, file=sys.stderr)
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
issuer_name: str = sys.argv[1]
|
|
145
|
+
is_installed, message = is_ca_installed(issuer_name)
|
|
146
|
+
if not is_installed and message:
|
|
147
|
+
print(f"Error checking certificate installation: {message}", file=sys.stderr)
|
|
148
|
+
return 1
|
|
149
|
+
|
|
150
|
+
if is_installed:
|
|
151
|
+
rc: int = remove_ca_certificate(issuer_name)
|
|
152
|
+
if rc != 0:
|
|
153
|
+
return rc
|
|
154
|
+
|
|
155
|
+
rc: int = install_ca_certificate(certificate_string)
|
|
156
|
+
if rc != 0:
|
|
157
|
+
return rc
|
|
158
|
+
|
|
159
|
+
is_installed, message = is_ca_installed(issuer_name)
|
|
160
|
+
if not is_installed and message:
|
|
161
|
+
print(f"Error checking certificate installation: {message}", file=sys.stderr)
|
|
162
|
+
return 1
|
|
163
|
+
|
|
164
|
+
if not is_installed:
|
|
165
|
+
print("Error: Certificate installation failed.", file=sys.stderr)
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
return 0
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == '__main__':
|
|
172
|
+
sys.exit(main())
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Print the command line of the process that owns a given TCP/UDP source port.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python get_cmdline_from_port.py <port> [kind]
|
|
7
|
+
|
|
8
|
+
<port> is required (e.g. 54321)
|
|
9
|
+
[kind] is optional: 'tcp', 'udp', or 'inet' (default: 'inet')
|
|
10
|
+
|
|
11
|
+
Requires: psutil
|
|
12
|
+
|
|
13
|
+
===========================
|
|
14
|
+
|
|
15
|
+
In Ubuntu, you can check available TCP connections and processes with:
|
|
16
|
+
ss -tnpa
|
|
17
|
+
In Windows, you can check available TCP connections and processes with (only PIDs, no command lines):
|
|
18
|
+
netstat -ano
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
import shlex
|
|
23
|
+
import psutil
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def find_cmdline_by_port(
|
|
27
|
+
port: int,
|
|
28
|
+
# kind: str = "inet"
|
|
29
|
+
kind: str = "tcp"
|
|
30
|
+
) -> str | None:
|
|
31
|
+
"""
|
|
32
|
+
Return the command line (joined string) of the first process whose local
|
|
33
|
+
port == `port`, for connections of type `kind` ('tcp', 'udp', 'inet', etc).
|
|
34
|
+
|
|
35
|
+
'tcp' is much more specific and faster than 'inet' (which includes both TCP and UDP).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# Single system-wide call; 'inet' = IPv4 + IPv6 only (no unix sockets)
|
|
39
|
+
# Use 'tcp' if you know it's TCP only – slightly faster.
|
|
40
|
+
try:
|
|
41
|
+
conns = psutil.net_connections(kind=kind)
|
|
42
|
+
except psutil.Error:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
for conn in conns:
|
|
46
|
+
# Some entries have no local address or PID (e.g. kernel sockets)
|
|
47
|
+
laddr = conn.laddr
|
|
48
|
+
if not laddr:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
# laddr is a namedtuple (ip, port) for AF_INET / AF_INET6
|
|
52
|
+
try:
|
|
53
|
+
local_port = laddr.port
|
|
54
|
+
except AttributeError:
|
|
55
|
+
# Older psutil: laddr is a simple tuple (ip, port)
|
|
56
|
+
if len(laddr) < 2:
|
|
57
|
+
continue
|
|
58
|
+
local_port = laddr[1]
|
|
59
|
+
|
|
60
|
+
if local_port != port:
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
pid = conn.pid
|
|
64
|
+
if pid is None or pid == 0:
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
proc = psutil.Process(pid)
|
|
69
|
+
cmdline_list = proc.cmdline()
|
|
70
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
if cmdline_list:
|
|
74
|
+
result = shlex.join(cmdline_list)
|
|
75
|
+
else:
|
|
76
|
+
# Fallback to process name if cmdline missing
|
|
77
|
+
try:
|
|
78
|
+
result = proc.name()
|
|
79
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
# If result is just a PID string, also fallback to name
|
|
83
|
+
if result.isnumeric():
|
|
84
|
+
try:
|
|
85
|
+
result = proc.name()
|
|
86
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def main() -> None:
|
|
95
|
+
if len(sys.argv) < 2:
|
|
96
|
+
print("Usage: get_cmdline_from_port.py <port> [kind]", file=sys.stderr)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
port = int(sys.argv[1])
|
|
101
|
+
except ValueError:
|
|
102
|
+
print("Port must be an integer", file=sys.stderr)
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
|
|
105
|
+
kind = sys.argv[2] if len(sys.argv) >= 3 else "tcp"
|
|
106
|
+
|
|
107
|
+
result = find_cmdline_by_port(port, kind)
|
|
108
|
+
if result is not None:
|
|
109
|
+
print(result)
|
|
110
|
+
sys.exit(0)
|
|
111
|
+
else:
|
|
112
|
+
# Print nothing, or a message – up to you.
|
|
113
|
+
# Empty output is often nicer for scripts that just parse stdout.
|
|
114
|
+
# print(f"No process found with local port {port}", file=sys.stderr)
|
|
115
|
+
sys.exit(2)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
main()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import socket
|
|
3
|
+
from typing import Union
|
|
4
|
+
import ipaddress
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
import psutil
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_ip_address(string_value: str) -> bool:
|
|
11
|
+
try:
|
|
12
|
+
ipaddress.IPv4Address(string_value)
|
|
13
|
+
return True
|
|
14
|
+
except (ipaddress.AddressValueError, ValueError):
|
|
15
|
+
return False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_default_internet_ipv4(target: str = "8.8.8.8") -> str:
|
|
19
|
+
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
20
|
+
s.connect((target, 80)) # no packet sent; OS just chooses a route
|
|
21
|
+
return s.getsockname()[0] # local address of that route
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_default_connection_name() -> Union[dict, None]:
|
|
25
|
+
"""
|
|
26
|
+
Function to get the default network interface.
|
|
27
|
+
:return: dict[interface_name: details] or None.
|
|
28
|
+
"""
|
|
29
|
+
# Get all interfaces.
|
|
30
|
+
interfaces: dict = psutil.net_if_addrs()
|
|
31
|
+
default_ip_address: str = get_default_internet_ipv4()
|
|
32
|
+
|
|
33
|
+
for interface, details in interfaces.items():
|
|
34
|
+
for address in details:
|
|
35
|
+
# Check if the address is an IPv4 address (AF_INET) and not a loopback (127.0.0.1)
|
|
36
|
+
if address.family == socket.AF_INET and not address.address.startswith('127.'):
|
|
37
|
+
# Check if the address is the default IP address.
|
|
38
|
+
if address.address == default_ip_address:
|
|
39
|
+
return {interface: details}
|
|
40
|
+
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_default_interface_name() -> str:
|
|
45
|
+
default_connection_name_dict: dict = get_default_connection_name()
|
|
46
|
+
if not default_connection_name_dict:
|
|
47
|
+
return ""
|
|
48
|
+
# Get the first key from the dictionary.
|
|
49
|
+
connection_name: str = list(default_connection_name_dict.keys())[0]
|
|
50
|
+
return connection_name
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def set_default_gateway_ipv4(gateway_ipv4: str) -> str | None:
|
|
54
|
+
interface_name: str = get_default_interface_name()
|
|
55
|
+
if not interface_name:
|
|
56
|
+
return "Could not determine the default network interface name."
|
|
57
|
+
|
|
58
|
+
# Set the default gateway using 'netsh' command.
|
|
59
|
+
command: str = f'netsh interface ipv4 set dns name="{interface_name}" static {gateway_ipv4} primary'
|
|
60
|
+
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
61
|
+
stdout: str = result.stdout.decode().strip()
|
|
62
|
+
stderr: str = result.stderr.decode().strip()
|
|
63
|
+
|
|
64
|
+
if result.returncode != 0:
|
|
65
|
+
return (f"stdout: {stdout}\n"
|
|
66
|
+
f"stderr: {stderr}")
|
|
67
|
+
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main() -> int:
|
|
72
|
+
if len(sys.argv) < 2:
|
|
73
|
+
print("Usage: set_default_dns_gateway.py <IPv4>", file=sys.stderr)
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
dns_ipv4: str = sys.argv[1]
|
|
77
|
+
if not is_ip_address(dns_ipv4):
|
|
78
|
+
print("Invalid IPv4 address", file=sys.stderr)
|
|
79
|
+
return 1
|
|
80
|
+
|
|
81
|
+
error_message: str | None = set_default_gateway_ipv4(dns_ipv4)
|
|
82
|
+
if error_message:
|
|
83
|
+
print(f"Failed to set default DNS gateway:\n{error_message}", file=sys.stderr)
|
|
84
|
+
return 1
|
|
85
|
+
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == '__main__':
|
|
90
|
+
sys.exit(main())
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from atomicshop.file_io import tomls
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_args():
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
description='Update the config.toml file.\n'
|
|
11
|
+
'This script will update the current config.toml file with the keys from the target config.toml file.\n'
|
|
12
|
+
'If the key is not present in the current config.toml file, it will be added.\n'
|
|
13
|
+
'If the key is present in the current config.toml file, its value will be left unchanged.\n')
|
|
14
|
+
parser.add_argument('current_config_file', type=str, help="Path to the current config.toml file that will be updated.")
|
|
15
|
+
parser.add_argument('target_config_file', type=str, help="Path to the target config.toml file that we'll get the updates from.")
|
|
16
|
+
parser.add_argument('-n', '--new_file_path', type=str, help="(OPTIONAL) Path to the new config.toml file that will be created with the updates.", default=None)
|
|
17
|
+
return parser.parse_args()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main():
|
|
21
|
+
args = parse_args()
|
|
22
|
+
|
|
23
|
+
if os.path.isfile(args.current_config_file) is False:
|
|
24
|
+
print(f"Error: The current config file '{args.current_config_file}' does not exist.")
|
|
25
|
+
return 1
|
|
26
|
+
if os.path.isfile(args.target_config_file) is False:
|
|
27
|
+
print(f"Error: The target config file '{args.target_config_file}' does not exist.")
|
|
28
|
+
return 1
|
|
29
|
+
|
|
30
|
+
tomls.update_toml_file_with_new_config(
|
|
31
|
+
main_config_file_path=args.current_config_file,
|
|
32
|
+
changes_config_file_path=args.target_config_file,
|
|
33
|
+
new_config_file_path=args.new_file_path)
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
sys.exit(main())
|
|
@@ -5,7 +5,7 @@ from .print_api import print_api
|
|
|
5
5
|
from .basics.lists import remove_duplicates
|
|
6
6
|
from .datetimes import convert_single_digit_to_zero_padded, create_date_range_for_year, \
|
|
7
7
|
create_date_range_for_year_month
|
|
8
|
-
from .file_io
|
|
8
|
+
from .file_io import csvs
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AppointmentManager:
|
|
@@ -38,7 +38,8 @@ class AppointmentManager:
|
|
|
38
38
|
def read_latest_date_csv(self):
|
|
39
39
|
try:
|
|
40
40
|
# Read the csv to list of dicts.
|
|
41
|
-
csv_list, _ =
|
|
41
|
+
csv_list, _ = csvs.read_csv_to_list_of_dicts_by_header(
|
|
42
|
+
file_path=self.latest_date_to_check_filepath, raise_exception=True)
|
|
42
43
|
# It has only 1 line, so get it to dict.
|
|
43
44
|
latest_date_dict = csv_list[0]
|
|
44
45
|
|
|
@@ -100,7 +101,8 @@ class BlacklistEngine:
|
|
|
100
101
|
def read_blacklist_csv(self) -> None:
|
|
101
102
|
try:
|
|
102
103
|
# Read the csv to list of dicts.
|
|
103
|
-
csv_list, _ =
|
|
104
|
+
csv_list, _ = csvs.read_csv_to_list_of_dicts_by_header(
|
|
105
|
+
file_path=self.blacklist_dates_filepath, raise_exception=True)
|
|
104
106
|
|
|
105
107
|
daterange = None
|
|
106
108
|
# Iterate through all the rows.
|
|
@@ -23,7 +23,8 @@ def get_colors_basic_dict(color):
|
|
|
23
23
|
'yellow': ColorsBasic.YELLOW,
|
|
24
24
|
'blue': ColorsBasic.BLUE,
|
|
25
25
|
'header': ColorsBasic.HEADER,
|
|
26
|
-
'cyan': ColorsBasic.CYAN
|
|
26
|
+
'cyan': ColorsBasic.CYAN,
|
|
27
|
+
'orange': ColorsBasic.ORANGE
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
return colors_basic_dict[color]
|
|
@@ -48,6 +49,7 @@ class ColorsBasic:
|
|
|
48
49
|
BLUE = '\033[94m'
|
|
49
50
|
HEADER = '\033[95m'
|
|
50
51
|
CYAN = '\033[96m'
|
|
52
|
+
ORANGE = '\033[38;2;255;165;0m'
|
|
51
53
|
END = ANSI_END
|
|
52
54
|
|
|
53
55
|
|
|
@@ -36,6 +36,8 @@ class ArgparseWrapper:
|
|
|
36
36
|
# formatter_class=RawTextHelpFormatter: shows raw text and not the default argparse text parsing.
|
|
37
37
|
self.parser = argparse.ArgumentParser(description=self.description_full,
|
|
38
38
|
usage=self.usage_variable,
|
|
39
|
+
# usage=argparse.SUPPRESS # Remove usage line from the beginning.
|
|
40
|
+
# add_help=False, # Remove help line from the end.
|
|
39
41
|
formatter_class=RawTextHelpFormatter)
|
|
40
42
|
|
|
41
43
|
def add_arguments(self) -> None:
|