inital v4
This commit is contained in:
@@ -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'],
|
||||
)
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"server_url": "http://192.168.178.122:8080",
|
||||
"api_token": "replace-with-your-token"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"server_url": "http://192.168.178.122:8080",
|
||||
"api_token": "123456789"
|
||||
}
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
@@ -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
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@@ -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