feat(scheduler): Variablen-Badges theme-aware + neue Variablen nodes_online, version

- Badge-Styling: bg-secondary-subtle/text-secondary-emphasis statt bg-secondary
  → lesbar in Light- und Dark-Mode
- {nodes_online}: Nodes < 15 Min last_seen (DB-Abfrage in get_stats())
- {version}: Bot-Version aus config.yaml via config.get()
Closes #15.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ppfeiffer 2026-02-20 14:31:38 +01:00
parent 30960a194d
commit 33c05c0a32
5 changed files with 34 additions and 14 deletions

View file

@ -1,5 +1,15 @@
# Changelog # Changelog
## [0.08.13] - 2026-02-20
### Added
- **Scheduler: neue Template-Variablen** (closes #15):
- `{nodes_online}` Nodes mit `last_seen < 15 Min` (Live-Status, analog Dashboard-Schwellwert)
- `{version}` aktuelle Bot-Version aus `config.yaml`
- **Scheduler Variablen-Badges theme-aware**: Badge-Klasse von `bg-secondary` auf
`bg-secondary-subtle text-secondary-emphasis border-secondary-subtle` umgestellt
korrekte Lesbarkeit in Hell- und Dunkel-Theme.
## [0.08.12] - 2026-02-20 ## [0.08.12] - 2026-02-20
### Changed ### Changed

View file

@ -1,4 +1,4 @@
version: "0.08.12" version: "0.08.13"
bot: bot:
name: "MeshDD-Bot" name: "MeshDD-Bot"

View file

@ -207,6 +207,10 @@ class Database:
"SELECT COUNT(*) FROM nodes WHERE last_seen >= ?", (day_ago,) "SELECT COUNT(*) FROM nodes WHERE last_seen >= ?", (day_ago,)
) as c: ) as c:
stats["nodes_24h"] = (await c.fetchone())[0] stats["nodes_24h"] = (await c.fetchone())[0]
async with self.db.execute(
"SELECT COUNT(*) FROM nodes WHERE last_seen >= ?", (now - 900,)
) as c:
stats["nodes_online"] = (await c.fetchone())[0]
async with self.db.execute( async with self.db.execute(
"SELECT COUNT(*) FROM commands WHERE timestamp >= ?", (day_ago,) "SELECT COUNT(*) FROM commands WHERE timestamp >= ?", (day_ago,)
) as c: ) as c:

View file

@ -5,6 +5,8 @@ from datetime import datetime
import yaml import yaml
from meshbot import config
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
SCHEDULER_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "scheduler.yaml") SCHEDULER_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "scheduler.yaml")
@ -95,12 +97,14 @@ class Scheduler:
logger.exception("Error fetching stats for scheduler vars") logger.exception("Error fetching stats for scheduler vars")
weekdays = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] weekdays = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
replacements = { replacements = {
"{time}": now.strftime("%H:%M"), "{time}": now.strftime("%H:%M"),
"{date}": now.strftime("%d.%m.%Y"), "{date}": now.strftime("%d.%m.%Y"),
"{datetime}": now.strftime("%d.%m.%Y %H:%M"), "{datetime}": now.strftime("%d.%m.%Y %H:%M"),
"{weekday}": weekdays[now.weekday()], "{weekday}": weekdays[now.weekday()],
"{nodes}": str(stats.get("total_nodes", "?")), "{nodes}": str(stats.get("total_nodes", "?")),
"{nodes_24h}": str(stats.get("nodes_24h", "?")), "{nodes_24h}": str(stats.get("nodes_24h", "?")),
"{nodes_online}": str(stats.get("nodes_online", "?")),
"{version}": config.get("version", "?"),
} }
for key, val in replacements.items(): for key, val in replacements.items():
text = text.replace(key, val) text = text.replace(key, val)

View file

@ -8,12 +8,14 @@ initPage({ onAuth: (user) => { currentUser = user; } });
// Template variables available in message jobs // Template variables available in message jobs
const MSG_VARS = [ const MSG_VARS = [
{ key: '{time}', label: '{time}', desc: 'Uhrzeit (HH:MM)' }, { key: '{time}', label: '{time}', desc: 'Uhrzeit (HH:MM)' },
{ key: '{date}', label: '{date}', desc: 'Datum (TT.MM.JJJJ)' }, { key: '{date}', label: '{date}', desc: 'Datum (TT.MM.JJJJ)' },
{ key: '{datetime}', label: '{datetime}', desc: 'Datum + Uhrzeit' }, { key: '{datetime}', label: '{datetime}', desc: 'Datum + Uhrzeit' },
{ key: '{weekday}', label: '{weekday}', desc: 'Wochentag (Montag…)' }, { key: '{weekday}', label: '{weekday}', desc: 'Wochentag (Montag…)' },
{ key: '{nodes}', label: '{nodes}', desc: 'Anzahl Nodes gesamt' }, { key: '{nodes}', label: '{nodes}', desc: 'Anzahl Nodes gesamt' },
{ key: '{nodes_24h}', label: '{nodes_24h}', desc: 'Aktive Nodes (24h)' }, { key: '{nodes_24h}', label: '{nodes_24h}', desc: 'Aktive Nodes (24h)' },
{ key: '{nodes_online}', label: '{nodes_online}', desc: 'Nodes online (< 15 Min)' },
{ key: '{version}', label: '{version}', desc: 'Bot-Version' },
]; ];
// Type toggle label + variable hints // Type toggle label + variable hints
@ -25,7 +27,7 @@ function updateCommandLabel() {
const badges = document.getElementById('varBadges'); const badges = document.getElementById('varBadges');
if (isMsg) { if (isMsg) {
badges.innerHTML = MSG_VARS.map(v => badges.innerHTML = MSG_VARS.map(v =>
`<span class="badge bg-secondary me-1 mb-1" role="button" title="${escapeHtml(v.desc)}" data-var="${escapeHtml(v.key)}" style="cursor:pointer;font-size:.7rem">${escapeHtml(v.label)}</span>` `<span class="badge bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle me-1 mb-1" role="button" title="${escapeHtml(v.desc)}" data-var="${escapeHtml(v.key)}" style="cursor:pointer;font-size:.7rem">${escapeHtml(v.label)}</span>`
).join(''); ).join('');
hints.classList.remove('d-none'); hints.classList.remove('d-none');
} else { } else {