mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-05 20:03:48 +00:00
Update notification service
This commit is contained in:
@@ -1673,7 +1673,11 @@ class PollingCollector:
|
||||
self._poll_interval = poll_interval
|
||||
self._hostname = _hostname()
|
||||
self._last_update_check = 0
|
||||
self._last_proxmenux_check = 0
|
||||
self._last_ai_model_check = 0
|
||||
# Track notified ProxMenux versions to avoid duplicates
|
||||
self._notified_proxmenux_version: str | None = None
|
||||
self._notified_proxmenux_beta_version: str | None = None
|
||||
# In-memory cache: error_key -> last notification timestamp
|
||||
self._last_notified: Dict[str, float] = {}
|
||||
# Track known error keys + metadata so we can detect new ones AND emit recovery
|
||||
@@ -1707,6 +1711,7 @@ class PollingCollector:
|
||||
try:
|
||||
self._check_persistent_health()
|
||||
self._check_updates()
|
||||
self._check_proxmenux_updates()
|
||||
self._check_ai_model_availability()
|
||||
except Exception as e:
|
||||
print(f"[PollingCollector] Error: {e}")
|
||||
@@ -2137,6 +2142,95 @@ class PollingCollector:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ── ProxMenux update check ────────────────────────────────
|
||||
|
||||
PROXMENUX_VERSION_FILE = '/usr/local/share/proxmenux/version.txt'
|
||||
PROXMENUX_BETA_VERSION_FILE = '/usr/local/share/proxmenux/beta_version.txt'
|
||||
REPO_MAIN_VERSION_URL = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/main/version.txt'
|
||||
REPO_DEVELOP_VERSION_URL = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/develop/version.txt'
|
||||
|
||||
def _check_proxmenux_updates(self):
|
||||
"""Check for ProxMenux updates (main and beta channels).
|
||||
|
||||
Compares local version files with remote GitHub repository versions
|
||||
and emits notifications when updates are available.
|
||||
Uses same 24h interval as system updates.
|
||||
"""
|
||||
import urllib.request
|
||||
|
||||
now = time.time()
|
||||
if now - self._last_proxmenux_check < self.UPDATE_CHECK_INTERVAL:
|
||||
return
|
||||
|
||||
self._last_proxmenux_check = now
|
||||
|
||||
def read_local_version(path: str) -> str | None:
|
||||
"""Read version from local file."""
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
with open(path, 'r') as f:
|
||||
return f.read().strip()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def read_remote_version(url: str) -> str | None:
|
||||
"""Fetch version from remote URL."""
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'ProxMenux-Monitor/1.0'})
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
return resp.read().decode('utf-8').strip()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def version_tuple(v: str) -> tuple:
|
||||
"""Convert version string to tuple for comparison."""
|
||||
try:
|
||||
return tuple(int(x) for x in v.split('.'))
|
||||
except Exception:
|
||||
return (0,)
|
||||
|
||||
try:
|
||||
# Check main version
|
||||
local_main = read_local_version(self.PROXMENUX_VERSION_FILE)
|
||||
if local_main:
|
||||
remote_main = read_remote_version(self.REPO_MAIN_VERSION_URL)
|
||||
if remote_main and version_tuple(remote_main) > version_tuple(local_main):
|
||||
# Only notify if we haven't already notified for this version
|
||||
if self._notified_proxmenux_version != remote_main:
|
||||
self._notified_proxmenux_version = remote_main
|
||||
data = {
|
||||
'hostname': self._hostname,
|
||||
'current_version': local_main,
|
||||
'new_version': remote_main,
|
||||
}
|
||||
self._queue.put(NotificationEvent(
|
||||
'proxmenux_update', 'INFO', data,
|
||||
source='polling', entity='node', entity_id='',
|
||||
))
|
||||
|
||||
# Check beta version (only if user has beta file)
|
||||
local_beta = read_local_version(self.PROXMENUX_BETA_VERSION_FILE)
|
||||
if local_beta:
|
||||
remote_beta = read_remote_version(self.REPO_DEVELOP_VERSION_URL)
|
||||
if remote_beta and version_tuple(remote_beta) > version_tuple(local_beta):
|
||||
# Only notify if we haven't already notified for this version
|
||||
if self._notified_proxmenux_beta_version != remote_beta:
|
||||
self._notified_proxmenux_beta_version = remote_beta
|
||||
data = {
|
||||
'hostname': self._hostname,
|
||||
'current_version': local_beta,
|
||||
'new_version': f'{remote_beta} (Beta)',
|
||||
}
|
||||
# Use same event_type - single toggle controls both
|
||||
self._queue.put(NotificationEvent(
|
||||
'proxmenux_update', 'INFO', data,
|
||||
source='polling', entity='node', entity_id='',
|
||||
))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ── AI Model availability check ────────────────────────────
|
||||
|
||||
def _check_ai_model_availability(self):
|
||||
|
||||
@@ -1178,6 +1178,13 @@ class NotificationManager:
|
||||
if isinstance(ai_enabled, str):
|
||||
ai_enabled = ai_enabled.lower() == 'true'
|
||||
ai_language = self._config.get('ai_language', 'en')
|
||||
ai_prompt_mode = self._config.get('ai_prompt_mode', 'default')
|
||||
|
||||
# Determine AI info string based on prompt mode
|
||||
if ai_prompt_mode == 'custom':
|
||||
ai_info = f'{ai_provider} / custom prompt'
|
||||
else:
|
||||
ai_info = f'{ai_provider} / {ai_language}'
|
||||
|
||||
# ProxMenux logo for welcome message
|
||||
logo_url = 'https://proxmenux.com/telegram.png'
|
||||
@@ -1195,10 +1202,10 @@ class NotificationManager:
|
||||
# Build status indicators for icons and AI, adapted to channel format
|
||||
if use_rich_format:
|
||||
icon_status = '✅ Icons: enabled'
|
||||
ai_status = f'✅ AI: enabled ({ai_provider} / {ai_language})' if ai_enabled else '❌ AI: disabled'
|
||||
ai_status = f'✅ AI: enabled ({ai_info})' if ai_enabled else '❌ AI: disabled'
|
||||
else:
|
||||
icon_status = 'Icons: disabled'
|
||||
ai_status = f'AI: enabled ({ai_provider} / {ai_language})' if ai_enabled else 'AI: disabled'
|
||||
ai_status = f'AI: enabled ({ai_info})' if ai_enabled else 'AI: disabled'
|
||||
|
||||
# Base test message — shows current channel config
|
||||
# NOTE: narrative lines are intentionally unlabeled so the AI
|
||||
|
||||
@@ -787,6 +787,20 @@ TEMPLATES = {
|
||||
),
|
||||
'label': 'AI model auto-updated',
|
||||
'group': 'system',
|
||||
'severity': 'info',
|
||||
'default_enabled': True,
|
||||
},
|
||||
|
||||
# ── ProxMenux updates ──
|
||||
'proxmenux_update': {
|
||||
'title': '{hostname}: ProxMenux {new_version} available',
|
||||
'body': (
|
||||
'A new version of ProxMenux is available.\n'
|
||||
'Current: {current_version}\n'
|
||||
'New: {new_version}'
|
||||
),
|
||||
'label': 'ProxMenux update available',
|
||||
'group': 'updates',
|
||||
'default_enabled': True,
|
||||
},
|
||||
|
||||
@@ -1121,8 +1135,9 @@ EVENT_EMOJI = {
|
||||
'update_summary': '\U0001F4E6',
|
||||
'pve_update': '\U0001F195', # NEW
|
||||
'update_complete': '\u2705',
|
||||
'proxmenux_update': '\U0001F195', # NEW
|
||||
# AI
|
||||
'ai_model_migrated': '\U0001F916', # robot
|
||||
'ai_model_migrated': '\U0001F504', # arrows counterclockwise (refresh/update)
|
||||
}
|
||||
|
||||
# Decorative field-level icons for body text enrichment
|
||||
|
||||
Reference in New Issue
Block a user