inital v4
This commit is contained in:
39
v4/windows_client/NotifyPulseAgent-V4.spec
Normal file
39
v4/windows_client/NotifyPulseAgent-V4.spec
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['client.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('agent_config.example.json', '.')],
|
||||
hiddenimports=['websockets', 'requests', 'winotify', 'pystray', 'PIL'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='NotifyPulseAgent-V4',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=['icon.ico'],
|
||||
)
|
||||
BIN
v4/windows_client/__pycache__/client.cpython-313.pyc
Normal file
BIN
v4/windows_client/__pycache__/client.cpython-313.pyc
Normal file
Binary file not shown.
4
v4/windows_client/agent_config.example.json
Normal file
4
v4/windows_client/agent_config.example.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"server_url": "http://192.168.178.122:8080",
|
||||
"api_token": "replace-with-your-token"
|
||||
}
|
||||
4
v4/windows_client/agent_config.json
Normal file
4
v4/windows_client/agent_config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"server_url": "http://192.168.178.122:8080",
|
||||
"api_token": "123456789"
|
||||
}
|
||||
46
v4/windows_client/build.bat
Normal file
46
v4/windows_client/build.bat
Normal file
@@ -0,0 +1,46 @@
|
||||
@echo off
|
||||
setlocal
|
||||
echo ============================================
|
||||
echo NotifyPulse Agent V4 - Build
|
||||
echo ============================================
|
||||
echo.
|
||||
|
||||
where python >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo ERROR: Python not found in PATH
|
||||
pause & exit /b 1
|
||||
)
|
||||
|
||||
set "PY=python"
|
||||
where py >nul 2>&1
|
||||
if not errorlevel 1 set "PY=py -3"
|
||||
|
||||
echo [1/2] Installing dependencies...
|
||||
%PY% -m pip install -r requirements.txt --quiet
|
||||
if errorlevel 1 ( echo FAILED & pause & exit /b 1 )
|
||||
|
||||
echo [2/2] Building executable...
|
||||
set "ICON_ARG="
|
||||
if exist "icon.ico" set "ICON_ARG=--icon icon.ico"
|
||||
|
||||
%PY% -m PyInstaller --noconfirm --onefile --windowed ^
|
||||
--name "NotifyPulseAgent-V4" ^
|
||||
%ICON_ARG% ^
|
||||
--hidden-import=websockets ^
|
||||
--hidden-import=requests ^
|
||||
--hidden-import=winotify ^
|
||||
--hidden-import=pystray ^
|
||||
--hidden-import=PIL ^
|
||||
--add-data "agent_config.example.json;." ^
|
||||
client.py
|
||||
|
||||
if errorlevel 1 (
|
||||
echo BUILD FAILED
|
||||
pause & exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================
|
||||
echo SUCCESS: dist\NotifyPulseAgent-V4.exe
|
||||
echo ============================================
|
||||
pause
|
||||
7608
v4/windows_client/build/NotifyPulseAgent-V4/Analysis-00.toc
Normal file
7608
v4/windows_client/build/NotifyPulseAgent-V4/Analysis-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
4140
v4/windows_client/build/NotifyPulseAgent-V4/EXE-00.toc
Normal file
4140
v4/windows_client/build/NotifyPulseAgent-V4/EXE-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
4117
v4/windows_client/build/NotifyPulseAgent-V4/PKG-00.toc
Normal file
4117
v4/windows_client/build/NotifyPulseAgent-V4/PKG-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
v4/windows_client/build/NotifyPulseAgent-V4/PYZ-00.pyz
Normal file
BIN
v4/windows_client/build/NotifyPulseAgent-V4/PYZ-00.pyz
Normal file
Binary file not shown.
2889
v4/windows_client/build/NotifyPulseAgent-V4/PYZ-00.toc
Normal file
2889
v4/windows_client/build/NotifyPulseAgent-V4/PYZ-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
v4/windows_client/build/NotifyPulseAgent-V4/base_library.zip
Normal file
BIN
v4/windows_client/build/NotifyPulseAgent-V4/base_library.zip
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
v4/windows_client/build/NotifyPulseAgent-V4/localpycs/struct.pyc
Normal file
BIN
v4/windows_client/build/NotifyPulseAgent-V4/localpycs/struct.pyc
Normal file
Binary file not shown.
@@ -0,0 +1,279 @@
|
||||
|
||||
This file lists modules PyInstaller was not able to find. This does not
|
||||
necessarily mean these modules are required for running your program. Both
|
||||
Python's standard library and 3rd-party Python packages often conditionally
|
||||
import optional modules, some of which may be available only on certain
|
||||
platforms.
|
||||
|
||||
Types of import:
|
||||
* top-level: imported at the top-level - look at these first
|
||||
* conditional: imported within an if-statement
|
||||
* delayed: imported within a function
|
||||
* optional: imported within a try-except-statement
|
||||
|
||||
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
|
||||
tracking down the missing module yourself. Thanks!
|
||||
|
||||
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), setuptools._distutils.util (delayed, conditional, optional), netrc (delayed, optional), getpass (delayed, optional), setuptools._vendor.backports.tarfile (optional), setuptools._distutils.archive_util (optional), http.server (delayed, optional)
|
||||
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), setuptools._vendor.backports.tarfile (optional), setuptools._distutils.archive_util (optional)
|
||||
missing module named 'collections.abc' - imported by logging (top-level), inspect (top-level), typing (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), traceback (top-level), asyncio.base_events (top-level), http.client (top-level), asyncio.coroutines (top-level), setuptools (top-level), setuptools._distutils.filelist (top-level), setuptools._distutils.util (top-level), setuptools._vendor.jaraco.functools (top-level), setuptools._vendor.more_itertools.more (top-level), setuptools._distutils._modified (top-level), setuptools._distutils.compat (top-level), setuptools._distutils.spawn (top-level), typing_extensions (top-level), setuptools._distutils.compilers.C.base (top-level), setuptools._distutils.fancy_getopt (top-level), setuptools._reqs (top-level), setuptools._vendor.jaraco.context (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._vendor.importlib_metadata (top-level), setuptools._vendor.importlib_metadata._meta (top-level), setuptools._distutils.command.bdist (top-level), setuptools._distutils.core (top-level), setuptools._distutils.cmd (top-level), setuptools._distutils.dist (top-level), configparser (top-level), setuptools._distutils.extension (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), setuptools.extension (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (conditional), setuptools.wheel (top-level), setuptools.command.egg_info (top-level), setuptools.command.bdist_egg (top-level), setuptools.command.sdist (top-level), setuptools._distutils.command.build (top-level), setuptools._distutils.command.sdist (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), requests.compat (top-level), websockets.imports (top-level), websockets.asyncio.client (top-level), websockets.client (top-level), websockets.datastructures (top-level), websockets.frames (top-level), websockets.extensions.base (top-level), websockets.http11 (top-level), websockets.headers (top-level), websockets.protocol (top-level), websockets.streams (top-level), websockets.extensions.permessage_deflate (top-level), websockets.asyncio.connection (top-level), websockets.asyncio.messages (top-level), websockets.asyncio.server (top-level), websockets.server (top-level), werkzeug.wrappers.request (top-level), werkzeug.datastructures.accept (top-level), werkzeug.datastructures.structures (top-level), markupsafe (top-level), werkzeug.datastructures.cache_control (top-level), werkzeug.datastructures.mixins (top-level), werkzeug.datastructures.auth (top-level), werkzeug.datastructures.csp (top-level), werkzeug.datastructures.etag (top-level), werkzeug.datastructures.file_storage (top-level), werkzeug.datastructures.headers (top-level), werkzeug.datastructures.range (top-level), werkzeug.middleware.shared_data (top-level), PIL.Image (top-level), PIL._typing (top-level), numpy._typing._array_like (top-level), numpy._typing._nested_sequence (conditional), numpy._typing._shape (top-level), numpy._typing._dtype_like (top-level), numpy.lib._function_base_impl (top-level), numpy.lib._npyio_impl (top-level), yaml.constructor (top-level), numpy.random._common (top-level), numpy.random._generator (top-level), numpy.random.bit_generator (top-level), numpy.random.mtrand (top-level), numpy.polynomial._polybase (top-level), xml.etree.ElementTree (top-level), PIL.TiffImagePlugin (top-level), PIL.ImageOps (top-level), PIL.ImagePalette (top-level), PIL.GimpGradientFile (conditional), PIL.ImageFilter (top-level), PIL.ImageQt (conditional), PIL.ImageMath (conditional), PIL.ImageSequence (conditional), PIL.PngImagePlugin (conditional), PIL.ImageDraw (top-level), PIL._imagingft (top-level), websockets.legacy.auth (top-level), websockets.legacy.server (top-level), websockets.legacy.protocol (top-level), websockets.legacy.framing (top-level), websockets.legacy.client (top-level), websockets.sync.client (top-level), websockets.sync.connection (top-level), websockets.sync.server (top-level), PIL.Jpeg2KImagePlugin (conditional), setuptools._distutils.command.build_ext (top-level), _pyrepl.types (top-level), _pyrepl.readline (top-level), setuptools._distutils.compilers.C.msvc (top-level)
|
||||
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), _pyrepl.unix_console (delayed, optional)
|
||||
missing module named resource - imported by posix (top-level)
|
||||
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
|
||||
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
|
||||
missing module named annotationlib - imported by typing_extensions (conditional)
|
||||
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
|
||||
missing module named fcntl - imported by subprocess (optional), _pyrepl.unix_console (top-level)
|
||||
missing module named _typeshed - imported by setuptools._distutils.dist (conditional), setuptools.command.bdist_egg (conditional), setuptools.glob (conditional), setuptools._vendor.wheel.wheelfile (conditional), setuptools.compat.py311 (conditional), numpy.random.bit_generator (top-level)
|
||||
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
|
||||
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
|
||||
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
|
||||
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
|
||||
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
|
||||
missing module named _scproxy - imported by urllib.request (conditional)
|
||||
missing module named termios - imported by getpass (optional), tty (top-level), _pyrepl.pager (delayed, optional), werkzeug._reloader (delayed, optional), _pyrepl.unix_console (top-level), _pyrepl.fancy_termios (top-level), _pyrepl.unix_eventqueue (top-level)
|
||||
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
|
||||
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
|
||||
missing module named multiprocessing.Value - imported by multiprocessing (top-level), werkzeug.debug (top-level)
|
||||
missing module named usercustomize - imported by site (delayed, optional)
|
||||
missing module named sitecustomize - imported by site (delayed, optional)
|
||||
missing module named _curses - imported by curses (top-level), curses.has_key (top-level), _pyrepl.curses (optional)
|
||||
missing module named readline - imported by code (delayed, conditional, optional), cmd (delayed, conditional, optional), pdb (delayed, conditional, optional), rlcompleter (optional), websockets.cli (delayed, optional), site (delayed, optional)
|
||||
missing module named _manylinux - imported by packaging._manylinux (delayed, optional), setuptools._vendor.packaging._manylinux (delayed, optional)
|
||||
missing module named setuptools._vendor.backports.zstd - imported by setuptools._vendor.backports (top-level), urllib3.util.request (conditional, optional), urllib3.response (conditional, optional)
|
||||
missing module named importlib_resources - imported by setuptools._vendor.jaraco.text (optional)
|
||||
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional)
|
||||
missing module named pyimod02_importers - imported by C:\Users\timoh\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
|
||||
missing module named '_typeshed.wsgi' - imported by werkzeug._internal (conditional), werkzeug.exceptions (conditional), werkzeug.http (conditional), werkzeug.wsgi (conditional), werkzeug.utils (conditional), werkzeug.wrappers.response (conditional), werkzeug.test (conditional), werkzeug.datastructures.headers (conditional), werkzeug.formparser (conditional), werkzeug.wrappers.request (conditional), werkzeug.serving (conditional), werkzeug.debug (conditional), werkzeug.middleware.shared_data (conditional), werkzeug.routing.exceptions (conditional), werkzeug.routing.map (conditional)
|
||||
missing module named 'watchdog.observers' - imported by werkzeug._reloader (delayed)
|
||||
missing module named 'watchdog.events' - imported by werkzeug._reloader (delayed)
|
||||
missing module named watchdog - imported by werkzeug._reloader (delayed)
|
||||
missing module named cryptography - imported by urllib3.contrib.pyopenssl (top-level), requests (conditional, optional), werkzeug.serving (delayed, optional)
|
||||
missing module named 'cryptography.x509' - imported by urllib3.contrib.pyopenssl (delayed, optional), werkzeug.serving (delayed, conditional, optional)
|
||||
missing module named 'cryptography.hazmat' - imported by werkzeug.serving (delayed, conditional, optional)
|
||||
missing module named 'python_socks.sync' - imported by websockets.sync.client (optional)
|
||||
missing module named python_socks - imported by websockets.asyncio.client (optional), websockets.sync.client (optional)
|
||||
missing module named 'python_socks.async_' - imported by websockets.asyncio.client (optional)
|
||||
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional)
|
||||
missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||
missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||
missing module named 'numpy_distutils.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
|
||||
missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional)
|
||||
missing module named psutil - imported by numpy.testing._private.utils (delayed, optional)
|
||||
missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional)
|
||||
missing module named numpy.random.RandomState - imported by numpy.random (top-level), numpy.random._generator (top-level)
|
||||
missing module named pyodide_js - imported by threadpoolctl (delayed, optional)
|
||||
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.vstack - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.vecmat - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.vecdot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.ushort - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.unsignedinteger - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ulonglong - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ulong - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.uintp - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.uintc - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.uint64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.uint32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.uint16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.uint - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ubyte - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.trunc - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.true_divide - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._function_base_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.trace - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.timedelta64 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.tensordot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.tanh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.tan - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.swapaxes - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.sum - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.subtract - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.str_ - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.square - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.sqrt - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.spacing - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.sort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.sinh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.signbit - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.sign - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.result_type - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.remainder - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.reciprocal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.radians - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.rad2deg - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.prod - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.power - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.positive - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.pi - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.outer - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.ones - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.object_ - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.number - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.not_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.nextafter - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.newaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.negative - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ndarray - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy.lib._utils_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.multiply - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.moveaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.modf - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.mod - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.minimum - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.maximum - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.max - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.matvec - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.matrix_transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.matmul - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.longlong - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.longdouble - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.long - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logical_xor - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logical_or - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logical_not - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logical_and - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logaddexp2 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.logaddexp - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.log10 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.log2 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.log1p - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.log - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.linspace - imported by numpy._core (top-level), numpy.lib._index_tricks_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.less_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.less - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.left_shift - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ldexp - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.lcm - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.isscalar - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.isnat - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
|
||||
missing module named numpy._core.isnan - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.isfinite - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.intp - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.integer - imported by numpy._core (conditional), numpy (conditional), numpy.fft._helper (top-level)
|
||||
missing module named numpy._core.intc - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.int64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.int32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.int16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.int8 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.inf - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.inexact - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.iinfo - imported by numpy._core (top-level), numpy.lib._twodim_base_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.hypot - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.hstack - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.heaviside - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.half - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.greater_equal - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.greater - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.gcd - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.frompyfunc - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.frexp - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.fmod - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.fmin - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.fmax - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.floor_divide - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.floor - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.floating - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.float_power - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.float32 - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.float16 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.finfo - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.fabs - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.expm1 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.exp2 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.exp - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.euler_gamma - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.errstate - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.equal - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.empty_like - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.empty - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
|
||||
missing module named numpy._core.e - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.double - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.dot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.divmod - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.divide - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.diagonal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.degrees - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.deg2rad - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.datetime64 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.csingle - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.cross - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.count_nonzero - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.cosh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.cos - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.copysign - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.conjugate - imported by numpy._core (conditional), numpy (conditional), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.conj - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.complexfloating - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.complex64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
|
||||
missing module named numpy._core.clongdouble - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.character - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.ceil - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.cdouble - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.cbrt - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bytes_ - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.byte - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bool_ - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bitwise_xor - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bitwise_or - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bitwise_count - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.bitwise_and - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.atleast_3d - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.atleast_2d - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.atleast_1d - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.asarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._array_utils_impl (top-level), numpy (conditional), numpy.fft._helper (top-level), numpy.fft._pocketfft (top-level)
|
||||
missing module named numpy._core.asanyarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.array_repr - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
|
||||
missing module named numpy._core.array2string - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.array - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
|
||||
missing module named numpy._core.argsort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.arctanh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arctan2 - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arctan - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arcsinh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arcsin - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arccosh - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arccos - imported by numpy._core (conditional), numpy (conditional)
|
||||
missing module named numpy._core.arange - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
|
||||
missing module named numpy._core.amin - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.amax - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._core.all - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
|
||||
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
|
||||
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
|
||||
missing module named olefile - imported by PIL.FpxImagePlugin (top-level), PIL.MicImagePlugin (top-level)
|
||||
missing module named defusedxml - imported by PIL.Image (optional)
|
||||
missing module named 'gi.repository' - imported by pystray._appindicator (top-level), pystray._util.gtk (top-level), pystray._util.notify_dbus (top-level), pystray._gtk (top-level)
|
||||
missing module named gi - imported by pystray._appindicator (top-level), pystray._util.gtk (top-level), pystray._util.notify_dbus (top-level), pystray._gtk (top-level)
|
||||
runtime module named six.moves - imported by pystray._base (top-level), pystray._win32 (top-level), pystray._xorg (top-level)
|
||||
missing module named StringIO - imported by six (conditional)
|
||||
missing module named 'Xlib.XK' - imported by pystray._xorg (top-level)
|
||||
missing module named 'Xlib.threaded' - imported by pystray._xorg (top-level)
|
||||
missing module named Xlib - imported by pystray._xorg (top-level)
|
||||
missing module named PyObjCTools - imported by pystray._darwin (top-level)
|
||||
missing module named objc - imported by pystray._darwin (top-level)
|
||||
missing module named Foundation - imported by pystray._darwin (top-level)
|
||||
missing module named AppKit - imported by pystray._darwin (top-level)
|
||||
missing module named simplejson - imported by requests.compat (conditional, optional)
|
||||
missing module named dummy_threading - imported by requests.cookies (optional)
|
||||
missing module named compression - imported by urllib3.util.request (conditional, optional), urllib3.response (conditional, optional)
|
||||
missing module named 'h2.events' - imported by urllib3.http2.connection (top-level)
|
||||
missing module named 'h2.connection' - imported by urllib3.http2.connection (top-level)
|
||||
missing module named h2 - imported by urllib3.http2.connection (top-level)
|
||||
missing module named brotli - imported by urllib3.util.request (optional), urllib3.response (optional)
|
||||
missing module named brotlicffi - imported by urllib3.util.request (optional), urllib3.response (optional)
|
||||
missing module named socks - imported by urllib3.contrib.socks (optional)
|
||||
missing module named 'OpenSSL.crypto' - imported by urllib3.contrib.pyopenssl (delayed, conditional)
|
||||
missing module named OpenSSL - imported by urllib3.contrib.pyopenssl (top-level)
|
||||
missing module named chardet - imported by requests (optional)
|
||||
missing module named 'pyodide.ffi' - imported by urllib3.contrib.emscripten.fetch (delayed, optional)
|
||||
missing module named pyodide - imported by urllib3.contrib.emscripten.fetch (top-level)
|
||||
missing module named js - imported by urllib3.contrib.emscripten.fetch (top-level)
|
||||
missing module named vms_lib - imported by platform (delayed, optional)
|
||||
missing module named 'java.lang' - imported by platform (delayed, optional)
|
||||
missing module named java - imported by platform (delayed)
|
||||
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
|
||||
File diff suppressed because it is too large
Load Diff
418
v4/windows_client/client.py
Normal file
418
v4/windows_client/client.py
Normal file
@@ -0,0 +1,418 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import ctypes
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import threading
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from websockets.client import connect
|
||||
|
||||
try:
|
||||
from winotify import Notification, audio
|
||||
except Exception:
|
||||
Notification = None
|
||||
audio = None
|
||||
|
||||
try:
|
||||
import pystray
|
||||
from PIL import Image, ImageDraw, ImageTk
|
||||
except Exception:
|
||||
pystray = None
|
||||
Image = None
|
||||
ImageDraw = None
|
||||
ImageTk = None
|
||||
|
||||
try:
|
||||
import tkinter as tk
|
||||
except Exception:
|
||||
tk = None
|
||||
|
||||
AGENT_VERSION = "4.0.0-alpha"
|
||||
APP_NAME = "NotifyPulseAgent"
|
||||
SPI_SETDESKWALLPAPER = 0x0014
|
||||
SPIF_UPDATEINIFILE = 0x01
|
||||
SPIF_SENDCHANGE = 0x02
|
||||
|
||||
|
||||
def appdata_dir() -> Path:
|
||||
base = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
|
||||
path = base / "NotifyPulseV4"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
(path / "cache").mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
DATA_DIR = appdata_dir()
|
||||
CLIENT_FILE = DATA_DIR / "client.json"
|
||||
SETTINGS_FILE = DATA_DIR / "settings.json"
|
||||
STOP_EVENT = threading.Event()
|
||||
|
||||
|
||||
def load_json(path: Path, fallback: Any) -> Any:
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
return fallback
|
||||
|
||||
|
||||
def save_json(path: Path, payload: Any) -> None:
|
||||
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def app_base_dir() -> Path:
|
||||
if getattr(sys, "frozen", False):
|
||||
return Path(sys.executable).resolve().parent
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def load_agent_config() -> dict[str, Any]:
|
||||
cfg_file = app_base_dir() / "agent_config.json"
|
||||
cfg = load_json(cfg_file, {})
|
||||
return cfg if isinstance(cfg, dict) else {}
|
||||
|
||||
|
||||
def load_or_create_client_id() -> str:
|
||||
raw = load_json(CLIENT_FILE, {})
|
||||
did = raw.get("device_id", "").strip()
|
||||
if did:
|
||||
return did
|
||||
did = f"win_{uuid.uuid4().hex[:12]}"
|
||||
save_json(CLIENT_FILE, {"device_id": did, "created_at": int(time.time())})
|
||||
return did
|
||||
|
||||
|
||||
DEVICE_ID = load_or_create_client_id()
|
||||
_cfg = load_agent_config()
|
||||
SERVER_URL = str(
|
||||
os.getenv("NP4_SERVER_URL", _cfg.get("server_url", "http://127.0.0.1:8080"))
|
||||
).rstrip("/")
|
||||
API_TOKEN = str(os.getenv("NP4_API_TOKEN", _cfg.get("api_token", "")))
|
||||
|
||||
|
||||
def http_headers() -> dict[str, str]:
|
||||
headers: dict[str, str] = {}
|
||||
if API_TOKEN:
|
||||
headers["X-Api-Token"] = API_TOKEN
|
||||
return headers
|
||||
|
||||
|
||||
def ws_url_from_http(base: str) -> str:
|
||||
parsed = urlparse(base)
|
||||
scheme = "wss" if parsed.scheme == "https" else "ws"
|
||||
netloc = parsed.netloc
|
||||
return f"{scheme}://{netloc}/ws/device"
|
||||
|
||||
|
||||
def toast(title: str, message: str) -> None:
|
||||
if Notification is None:
|
||||
print(f"[notify fallback] {title}: {message}")
|
||||
return
|
||||
n = Notification(app_id=APP_NAME, title=title, msg=message, duration="short")
|
||||
if audio:
|
||||
n.set_audio(audio.Default, loop=False)
|
||||
n.show()
|
||||
|
||||
|
||||
def _load_tray_icon_image():
|
||||
if Image is None:
|
||||
return None
|
||||
icon_path = app_base_dir() / "icon.ico"
|
||||
if icon_path.exists():
|
||||
try:
|
||||
return Image.open(icon_path)
|
||||
except Exception:
|
||||
pass
|
||||
png_path = app_base_dir() / "icon.png"
|
||||
if png_path.exists():
|
||||
try:
|
||||
return Image.open(png_path)
|
||||
except Exception:
|
||||
pass
|
||||
img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
d.ellipse((8, 8, 56, 56), fill=(79, 142, 247, 255))
|
||||
d.ellipse((20, 20, 44, 44), fill=(255, 255, 255, 235))
|
||||
return img
|
||||
|
||||
|
||||
def start_tray() -> None:
|
||||
if pystray is None:
|
||||
return
|
||||
|
||||
def on_quit(icon, _item):
|
||||
STOP_EVENT.set()
|
||||
icon.stop()
|
||||
|
||||
image = _load_tray_icon_image()
|
||||
if image is None:
|
||||
return
|
||||
icon = pystray.Icon(
|
||||
"NotifyPulseAgentV4",
|
||||
image,
|
||||
f"NotifyPulse Agent V4 ({DEVICE_ID})",
|
||||
menu=pystray.Menu(pystray.MenuItem("Quit", on_quit)),
|
||||
)
|
||||
t = threading.Thread(target=icon.run, daemon=True)
|
||||
t.start()
|
||||
|
||||
|
||||
def set_wallpaper(path: str) -> bool:
|
||||
try:
|
||||
ok = ctypes.windll.user32.SystemParametersInfoW(
|
||||
SPI_SETDESKWALLPAPER, 0, str(path), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE
|
||||
)
|
||||
return bool(ok)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _download_to_cache(url: str) -> Path:
|
||||
ext = Path(urlparse(url).path).suffix.lower() or ".jpg"
|
||||
out = DATA_DIR / "cache" / f"wallpaper_{int(time.time())}{ext}"
|
||||
r = requests.get(url, headers=http_headers(), timeout=30)
|
||||
r.raise_for_status()
|
||||
out.write_bytes(r.content)
|
||||
return out
|
||||
|
||||
|
||||
def _prepare_overlay_image(pil_img, sw: int, sh: int, stretch: bool):
|
||||
if stretch:
|
||||
return pil_img.resize((sw, sh), Image.Resampling.LANCZOS)
|
||||
img_w, img_h = pil_img.size
|
||||
scale = min(sw / img_w, sh / img_h)
|
||||
nw, nh = int(img_w * scale), int(img_h * scale)
|
||||
resized = pil_img.resize((nw, nh), Image.Resampling.LANCZOS)
|
||||
canvas = Image.new("RGB", (sw, sh), "black")
|
||||
canvas.paste(resized, ((sw - nw) // 2, (sh - nh) // 2))
|
||||
return canvas
|
||||
|
||||
|
||||
def show_overlay(path: str, duration_ms: int, opacity: float = 0.4, stretch: bool = False) -> bool:
|
||||
if tk is None or Image is None or ImageTk is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
pil_img = Image.open(path)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _run():
|
||||
try:
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
sw, sh = root.winfo_screenwidth(), root.winfo_screenheight()
|
||||
win = tk.Toplevel(root)
|
||||
win.attributes("-topmost", True)
|
||||
win.attributes("-alpha", max(0.05, min(1.0, float(opacity))))
|
||||
win.overrideredirect(True)
|
||||
win.geometry(f"{sw}x{sh}+0+0")
|
||||
prepared = _prepare_overlay_image(pil_img, sw, sh, stretch)
|
||||
tk_img = ImageTk.PhotoImage(prepared)
|
||||
label = tk.Label(win, image=tk_img, bg="black")
|
||||
label.image = tk_img
|
||||
label.pack(fill="both", expand=True)
|
||||
win.after(max(500, int(duration_ms)), root.destroy)
|
||||
root.mainloop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
return True
|
||||
|
||||
|
||||
def version_tuple(v: str) -> tuple[int, ...]:
|
||||
nums: list[int] = []
|
||||
for part in v.replace("-", ".").split("."):
|
||||
if part.isdigit():
|
||||
nums.append(int(part))
|
||||
else:
|
||||
break
|
||||
return tuple(nums or [0])
|
||||
|
||||
|
||||
async def send_event(ws, name: str, data: dict[str, Any]) -> None:
|
||||
await ws.send(json.dumps({"type": "event", "name": name, "data": data}))
|
||||
|
||||
|
||||
async def send_ack(ws, command_id: str, status: str = "ok", detail: str = "") -> None:
|
||||
await ws.send(
|
||||
json.dumps(
|
||||
{
|
||||
"type": "ack",
|
||||
"command_id": command_id,
|
||||
"status": status,
|
||||
"detail": detail,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def load_local_settings() -> dict[str, Any]:
|
||||
return load_json(SETTINGS_FILE, {})
|
||||
|
||||
|
||||
def save_local_settings(settings: dict[str, Any]) -> None:
|
||||
save_json(SETTINGS_FILE, settings)
|
||||
|
||||
|
||||
async def apply_command(ws, msg: dict[str, Any]) -> None:
|
||||
cid = str(msg.get("id", ""))
|
||||
action = str(msg.get("action", ""))
|
||||
data = msg.get("data", {}) if isinstance(msg.get("data"), dict) else {}
|
||||
|
||||
try:
|
||||
if action == "notify":
|
||||
title = str(data.get("title", "NotifyPulse"))
|
||||
message = str(data.get("message", ""))
|
||||
toast(title, message)
|
||||
await send_ack(ws, cid, "ok", "notification shown")
|
||||
return
|
||||
|
||||
if action == "wallpaper_set":
|
||||
path = str(data.get("path", "")).strip()
|
||||
url = str(data.get("url", "")).strip()
|
||||
if url:
|
||||
local = _download_to_cache(url)
|
||||
ok = set_wallpaper(str(local))
|
||||
elif path:
|
||||
ok = set_wallpaper(path)
|
||||
else:
|
||||
await send_ack(ws, cid, "error", "missing wallpaper path/url")
|
||||
return
|
||||
await send_ack(ws, cid, "ok" if ok else "error", "wallpaper applied" if ok else "wallpaper failed")
|
||||
return
|
||||
|
||||
if action == "overlay_show":
|
||||
path = str(data.get("path", "")).strip()
|
||||
url = str(data.get("url", "")).strip()
|
||||
duration_ms = int(data.get("duration_ms", 6000))
|
||||
opacity = float(data.get("opacity", 0.4))
|
||||
stretch = bool(data.get("stretch", False))
|
||||
if url:
|
||||
local = _download_to_cache(url)
|
||||
ok = show_overlay(str(local), duration_ms, opacity=opacity, stretch=stretch)
|
||||
elif path:
|
||||
ok = show_overlay(path, duration_ms, opacity=opacity, stretch=stretch)
|
||||
else:
|
||||
await send_ack(ws, cid, "error", "missing overlay path/url")
|
||||
return
|
||||
await send_ack(ws, cid, "ok" if ok else "error", "overlay shown" if ok else "overlay failed")
|
||||
return
|
||||
|
||||
if action == "sync_settings":
|
||||
settings = data.get("settings", {})
|
||||
if isinstance(settings, dict):
|
||||
save_local_settings(settings)
|
||||
await send_ack(ws, cid, "ok", "settings synced")
|
||||
return
|
||||
|
||||
if action == "ping":
|
||||
await send_ack(ws, cid, "ok", "pong")
|
||||
return
|
||||
|
||||
await send_ack(ws, cid, "unsupported", f"unknown action: {action}")
|
||||
except Exception as exc:
|
||||
await send_ack(ws, cid, "error", str(exc))
|
||||
|
||||
|
||||
async def heartbeat_loop(ws) -> None:
|
||||
while not STOP_EVENT.is_set():
|
||||
await ws.send(json.dumps({"type": "heartbeat", "time": int(time.time())}))
|
||||
await asyncio.sleep(15)
|
||||
|
||||
|
||||
def check_update_manifest() -> tuple[bool, dict[str, Any]]:
|
||||
try:
|
||||
r = requests.get(
|
||||
f"{SERVER_URL}/api/v4/update/manifest/windows",
|
||||
headers=http_headers(),
|
||||
timeout=10,
|
||||
)
|
||||
r.raise_for_status()
|
||||
manifest = r.json()
|
||||
remote_v = str(manifest.get("version", "0.0.0"))
|
||||
return version_tuple(remote_v) > version_tuple(AGENT_VERSION), manifest
|
||||
except Exception:
|
||||
return False, {}
|
||||
|
||||
|
||||
async def run_client() -> None:
|
||||
ws_url = ws_url_from_http(SERVER_URL)
|
||||
backoff_s = 2
|
||||
while not STOP_EVENT.is_set():
|
||||
try:
|
||||
async with connect(ws_url, ping_interval=20, ping_timeout=20, open_timeout=15) as ws:
|
||||
hello = {
|
||||
"type": "hello",
|
||||
"token": API_TOKEN,
|
||||
"payload": {
|
||||
"device_id": DEVICE_ID,
|
||||
"platform": "windows",
|
||||
"version": AGENT_VERSION,
|
||||
"hostname": socket.gethostname(),
|
||||
"capabilities": ["notify", "wallpaper_set", "overlay_show", "sync_settings"],
|
||||
},
|
||||
}
|
||||
await ws.send(json.dumps(hello))
|
||||
|
||||
heartbeat_task = asyncio.create_task(heartbeat_loop(ws))
|
||||
try:
|
||||
update_available, manifest = check_update_manifest()
|
||||
if update_available:
|
||||
await send_event(
|
||||
ws,
|
||||
"update_available",
|
||||
{
|
||||
"current_version": AGENT_VERSION,
|
||||
"target_version": manifest.get("version", ""),
|
||||
"download_url": manifest.get("download_url", ""),
|
||||
},
|
||||
)
|
||||
|
||||
while True:
|
||||
raw = await ws.recv()
|
||||
msg = json.loads(raw)
|
||||
mtype = msg.get("type", "")
|
||||
if mtype == "welcome":
|
||||
incoming = msg.get("settings")
|
||||
if isinstance(incoming, dict):
|
||||
save_local_settings(incoming)
|
||||
elif mtype == "command":
|
||||
await apply_command(ws, msg)
|
||||
elif mtype == "heartbeat_ack":
|
||||
pass
|
||||
finally:
|
||||
heartbeat_task.cancel()
|
||||
with contextlib.suppress(Exception):
|
||||
await heartbeat_task
|
||||
|
||||
backoff_s = 2
|
||||
except Exception as exc:
|
||||
print(f"[agent] disconnected: {exc}")
|
||||
await asyncio.sleep(backoff_s)
|
||||
backoff_s = min(backoff_s * 2, 30)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print(f"{APP_NAME} {AGENT_VERSION}")
|
||||
print(f"Server: {SERVER_URL}")
|
||||
print(f"Device: {DEVICE_ID}")
|
||||
print(f"Host: {platform.node()}")
|
||||
start_tray()
|
||||
toast("NotifyPulse Agent", "Agent started and connecting to server")
|
||||
asyncio.run(run_client())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
v4/windows_client/dist/NotifyPulseAgent-V4.exe
vendored
Normal file
BIN
v4/windows_client/dist/NotifyPulseAgent-V4.exe
vendored
Normal file
Binary file not shown.
BIN
v4/windows_client/icon.ico
Normal file
BIN
v4/windows_client/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
BIN
v4/windows_client/icon.png
Normal file
BIN
v4/windows_client/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
6
v4/windows_client/requirements.txt
Normal file
6
v4/windows_client/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
websockets>=12.0
|
||||
requests>=2.32
|
||||
winotify>=1.1
|
||||
pystray>=0.19
|
||||
Pillow>=10.0
|
||||
pyinstaller>=6.0
|
||||
Reference in New Issue
Block a user