mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-18 10:02:16 +00:00
Update notification service
This commit is contained in:
@@ -1673,7 +1673,11 @@ class PollingCollector:
|
|||||||
self._poll_interval = poll_interval
|
self._poll_interval = poll_interval
|
||||||
self._hostname = _hostname()
|
self._hostname = _hostname()
|
||||||
self._last_update_check = 0
|
self._last_update_check = 0
|
||||||
|
self._last_proxmenux_check = 0
|
||||||
self._last_ai_model_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
|
# In-memory cache: error_key -> last notification timestamp
|
||||||
self._last_notified: Dict[str, float] = {}
|
self._last_notified: Dict[str, float] = {}
|
||||||
# Track known error keys + metadata so we can detect new ones AND emit recovery
|
# Track known error keys + metadata so we can detect new ones AND emit recovery
|
||||||
@@ -1707,6 +1711,7 @@ class PollingCollector:
|
|||||||
try:
|
try:
|
||||||
self._check_persistent_health()
|
self._check_persistent_health()
|
||||||
self._check_updates()
|
self._check_updates()
|
||||||
|
self._check_proxmenux_updates()
|
||||||
self._check_ai_model_availability()
|
self._check_ai_model_availability()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[PollingCollector] Error: {e}")
|
print(f"[PollingCollector] Error: {e}")
|
||||||
@@ -2137,6 +2142,95 @@ class PollingCollector:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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 ────────────────────────────
|
# ── AI Model availability check ────────────────────────────
|
||||||
|
|
||||||
def _check_ai_model_availability(self):
|
def _check_ai_model_availability(self):
|
||||||
|
|||||||
@@ -1178,6 +1178,13 @@ class NotificationManager:
|
|||||||
if isinstance(ai_enabled, str):
|
if isinstance(ai_enabled, str):
|
||||||
ai_enabled = ai_enabled.lower() == 'true'
|
ai_enabled = ai_enabled.lower() == 'true'
|
||||||
ai_language = self._config.get('ai_language', 'en')
|
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
|
# ProxMenux logo for welcome message
|
||||||
logo_url = 'https://proxmenux.com/telegram.png'
|
logo_url = 'https://proxmenux.com/telegram.png'
|
||||||
@@ -1195,10 +1202,10 @@ class NotificationManager:
|
|||||||
# Build status indicators for icons and AI, adapted to channel format
|
# Build status indicators for icons and AI, adapted to channel format
|
||||||
if use_rich_format:
|
if use_rich_format:
|
||||||
icon_status = '✅ Icons: enabled'
|
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:
|
else:
|
||||||
icon_status = 'Icons: disabled'
|
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
|
# Base test message — shows current channel config
|
||||||
# NOTE: narrative lines are intentionally unlabeled so the AI
|
# NOTE: narrative lines are intentionally unlabeled so the AI
|
||||||
|
|||||||
@@ -787,6 +787,20 @@ TEMPLATES = {
|
|||||||
),
|
),
|
||||||
'label': 'AI model auto-updated',
|
'label': 'AI model auto-updated',
|
||||||
'group': 'system',
|
'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,
|
'default_enabled': True,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1121,8 +1135,9 @@ EVENT_EMOJI = {
|
|||||||
'update_summary': '\U0001F4E6',
|
'update_summary': '\U0001F4E6',
|
||||||
'pve_update': '\U0001F195', # NEW
|
'pve_update': '\U0001F195', # NEW
|
||||||
'update_complete': '\u2705',
|
'update_complete': '\u2705',
|
||||||
|
'proxmenux_update': '\U0001F195', # NEW
|
||||||
# AI
|
# AI
|
||||||
'ai_model_migrated': '\U0001F916', # robot
|
'ai_model_migrated': '\U0001F504', # arrows counterclockwise (refresh/update)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Decorative field-level icons for body text enrichment
|
# Decorative field-level icons for body text enrichment
|
||||||
|
|||||||
Reference in New Issue
Block a user