Da Hugo ein statischer Website-Generator ist, generiert er die Seiten im Voraus. Um einen Echtzeit-Ticker für Event-Absagen einzubinden, müssen wir die Statik von Hugo aufbrechen und die Daten im Browser des Nutzers via JavaScript live von Mastodon streamen. [1, 2] Da Absagen sofort sichtbar sein müssen (ohne dass der Nutzer die Seite neu lädt), nutzen wir hierfür die Mastodon Streaming API (via WebSockets). [3] Hier ist die Schritt-für-Schritt-Anleitung, wie du den Ticker nahtlos und sauber in dein Hugo-Projekt integrierst.
Schritt 1: Das HTML-Layout anlegen (Partial)
Erstelle ein neues Partial-Layout für deinen Ticker. Dadurch bleibt dein Code modular. Erstelle die Datei layouts/partials/live-ticker.html und füge folgenden Code ein:
Schritt 2: Das Echtzeit-JavaScript erstellen (Asset)
Erstelle die Skript-Datei unter assets/js/mastodon-stream.js. Dieses Skript verbindet sich per WebSocket direkt mit deiner Mastodon-Instanz. Es lauscht im Hintergrund. Sobald du auf Mastodon einen neuen Post absetzt, wird dieser sekundenaktuell oben im Ticker eingehängt. [3, 4, 5]
document.addEventListener(“DOMContentLoaded”, () => { // — KONFIGURATION — const INSTANCE = “mastodon.social”; // Deine Mastodon-Instanz (z.B. mastodon.social, fediv.de etc.) const ACCOUNT_ID = “123456789012345678”; // Deine numerische Mastodon-Account-ID const FILTER_HASHTAG = “absage”; // Nur Posts mit diesem Hashtag anzeigen (Kleingeschrieben!)
const tickerElement = document.getElementById(“mastodon-ticker”);
// Initial einmal die letzten Posts via REST-API laden, damit der Ticker nicht leer startet
fetch(https://${INSTANCE}/api/v1/accounts/${ACCOUNT_ID}/statuses?limit=3)
.then(response => response.json())
.then(posts => {
if(posts.length > 0) tickerElement.innerHTML = ‘’;
posts.forEach(post => {
if (hasHashtag(post, FILTER_HASHTAG)) {
addTickerItem(post, false);
}
});
// Falls nach dem Filtern immer noch leer
if(tickerElement.children.length === 0) {
tickerElement.innerHTML = ‘Aktuell keine Absagen.’;
}
});
// STREAMING: WebSocket-Verbindung für echte Echtzeit-Updates aufbauen
const wsUrl = wss://${INSTANCE}/api/v1/streaming?stream=user;
// Hinweis: Für öffentliche Feeds reicht oft die Verbindung ohne Token,
// falls deine Instanz ein Token verlangt, hänge &access_token=DEIN_TOKEN an.
const socket = new WebSocket(wsUrl);
socket.onmessage = (event) => { const data = JSON.parse(event.data);
// Wir lauschen auf neue "update"-Events (neue Posts)
if (data.event === "update") {
const post = JSON.parse(data.payload);
// Nur Posts von der eigenen ID und mit dem gewünschten Ticker-Hashtag zulassen
if (post.account.id === ACCOUNT_ID && hasHashtag(post, FILTER_HASHTAG)) {
// Platzhalter löschen, falls vorhanden
const placeholder = tickerElement.querySelector('.ticker-placeholder, .ticker-empty');
if(placeholder) placeholder.remove();
addTickerItem(post, true); // true = oben einschieben mit Animation
}
}
};
// Hilfsfunktion: Prüft, ob der Post das gewünschte Hashtag enthält function hasHashtag(post, tag) { return post.tags.some(t => t.name.toLowerCase() === tag.toLowerCase()); }
// Hilfsfunktion: HTML-Element generieren und einfügen function addTickerItem(post, isLive) { const li = document.createElement(“li”); li.className = “ticker-item” + (isLive ? " anim-flash" : “”);
const date = new Date(post.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
li.innerHTML = `
<span class="ticker-time">${date} Uhr</span>
<div class="ticker-content">${post.content}</div>
`;
if (isLive) {
tickerElement.insertBefore(li, tickerElement.firstChild);
} else {
tickerElement.appendChild(li);
}
} });
Tipp zur Account-ID: Deine numerische ID findest du heraus, indem du im Browser https:///api/v1/accounts/lookup?acct= aufrufst.
Schritt 3: Styling hinzufügen (CSS)
Damit der Ticker wie ein echter Alarm-Ticker aussieht und neu reinkommende Nachrichten kurz signalisiert werden, kannst du folgendes CSS in deine Stylesheets (z. B. unter assets/scss/ oder im Head) einbinden:
.live-ticker-container { background: #fff5f5; border: 1px solid #feb2b2; border-radius: 6px; padding: 15px; margin: 20px 0; font-family: sans-serif; } .live-ticker-header { display: flex; align-items: center; color: #c53030; margin-bottom: 10px; } .ticker-dot { height: 10px; width: 10px; background-color: #f56565; border-radius: 50%; display: inline-block; margin-right: 8px; animation: blink 1.5s infinite; } .live-ticker-list { list-style: none; padding: 0; margin: 0; } .ticker-item { padding: 8px 0; border-bottom: 1px dashed #fed7d7; display: flex; gap: 15px; } .ticker-item:last-child { border-bottom: none; } .ticker-time { font-weight: bold; color: #718096; min-width: 60px; } .ticker-content p { margin: 0; } /* Mastodon liefert Content in Tags / @keyframes blink { 0% { transform: scale(0.95); opacity: 0.5; } 50% { transform: scale(1.1); opacity: 1; } 100% { transform: scale(0.95); opacity: 0.5; } }/ Animation für live reinkommende Nachrichten */ .anim-flash { animation: flashHighlight 2s ease-out; }@keyframes flashHighlight { 0% { background-color: #fffaf0; transform: translateY(-10px); } 100% { background-color: transparent; transform: translateY(0); } }
Schritt 4: In die Homepage einbauen
Öffne das Template deiner Homepage (meistens unter layouts/index.html oder layouts/partials/home.html) und füge das Partial an der gewünschten Stelle ein:
{{ partial “live-ticker.html” . }}
Wie du den Ticker im Alltag bedienst
- Wenn ein Event abgesagt wird, schreibst du ganz normal einen Post (Toot) auf Mastodon.
- Setze zwingend das Hashtag #absage (oder das von dir im JS definierte Wort) in den Text.
- Stelle die Sichtbarkeit des Posts auf Öffentlich (oder Nicht gelistet, falls es nicht in den Timelines deiner Follower spamen soll).
- Das Ergebnis: Nutzer, die gerade deine Homepage geöffnet haben, sehen die Absage ohne Neuladen innerhalb einer Sekunde aufblinken. [3, 4]
Um das Skript optimal einzustellen, bräuchte ich noch ein Detail:
- Auf welcher Mastodon-Instanz liegt dein Account (z. B. mastodon.social, chaos.social)? Manche Instanzen erzwingen striktere Token-Abfragen beim WebSocket-Streaming.
[1] https://github.com [2] https://johnmu.com [3] https://docs.joinmastodon.org [4] https://github.com [5] https://github.com