Telegram Bots als eigenständige Entität mit eigener Page

- Neue Tabelle 'telegram_bots' (bot_id, name, bot_token, webhook_secret, is_public)
- Automatische Migration bestehender Session-Telegram-Configs in telegram_bots
- Sessions verweisen via telegram_bot_id auf Bot (mehrere Sessions pro Bot möglich)
- Webhook-URL: /api/telegram/webhook/{botId} statt {sessionId}
- Eigene Telegram-Bot-Page (wie Agent/Cronjob/Session) statt Modal
- Bot-Page: Name, Token, Secret, Webhook-Verwaltung (registrieren/status/entfernen), Löschen
- Dashboard: Telegram Bots Section mit Cards (Name, maskierter Token, Session-Count)
- Session-Page: Bot-Picker (wie Agent-Picker) statt Token-Input, Chat-ID readonly + Unlink-Button
- Public-Flag: Bot kann von anderen Usern in ihren Sessions ausgewählt werden
- Vereinfacht: 1 Session = max. 1 Telegram Chat, allowed_chat_ids entfällt
- /start zeigt Chat-ID an, /unlink entfernt (Unlink nur über Web-UI)
- Nachrichten-Routing: Bot + Chat-ID → Session Lookup
This commit is contained in:
Meleki committed 2026-03-08 19:27:05 +01:00
1 parent 3b7bdb7a20
commit ec73ccd71e
13 files changed
+907 -371

No files matched your search

+14
View File
@@ -714,3 +714,17 @@ label.skill-item {
.form .fe-mde-wrap .editor-toolbar {
border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
}
/* ── Inline Actions (Buttons nebeneinander) ── */
.form-actions-inline {
display: flex;
gap: var(--badge-spacing);
flex-wrap: wrap;
margin-top: var(--badge-spacing);
}
.form-actions-inline button {
font-size: 0.85em;
padding: 0.4em 0.8em;
}
+62 -14
View File
@@ -83,6 +83,13 @@
</div>
<div class="card-grid" id="cronjobCards"></div>
</section>
<section>
<div class="section-header">
<h2><i class="fa fa-paper-plane"></i> Telegram Bots</h2>
<button id="newTelegramBotBtn" class="section-add-btn"><i class="fa fa-plus"></i> Neuer Bot</button>
</div>
<div class="card-grid" id="telegramBotCards"></div>
</section>
<section id="sharedLinksSection" hidden>
<div class="section-header">
<h2>Geteilte Links</h2>
@@ -143,21 +150,18 @@
<label>Benutzerdefinierte Befehle</label>
<div class="custom-commands" id="customCommandsList"></div>
<button type="button" id="addCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
<label><i class="fa fa-paper-plane"></i> <b>Telegram Bot</b></label>
<span>Erstelle einen Bot via <a href="https://t.me/BotFather" target="_blank">@BotFather</a></span>
<label>Bot-Token</label>
<input type="text" id="sessionTelegramToken" placeholder="123456:ABC-DEF..." autocomplete="off">
<label>Webhook-Secret</label>
<input type="text" id="sessionTelegramSecret" readonly style="opacity: var(--semi-transparent); ">
<label>Erlaubte Chat-IDs</label>
<input type="text" id="sessionTelegramAllowed" placeholder="Kommagetrennt, z.B. 123456,789012">
<small class="form-hint">Der verknüpfte Owner ist automatisch erlaubt. Weitere Chat-IDs hier eintragen.</small>
<label>Status</label>
<div id="sessionTelegramStatusBox" class="telegram-status-box">Kein Status verfügbar</div>
<div class="telegram-actions">
<button type="button" id="sessionTelegramRegisterBtn" hidden>Webhook registrieren</button>
<button type="button" id="sessionTelegramDeleteBtn" class="danger-btn" hidden>Webhook entfernen</button>
<label><i class="fa fa-paper-plane"></i> <b>Telegram</b></label>
<label>Telegram Bot</label>
<div id="telegramBotSelectCard" class="agent-select-card">
<div class="agent-select-empty"><i class="fa fa-paper-plane"></i> Kein Bot zugewiesen</div>
</div>
<input type="hidden" id="telegramBotSelect" value="">
<label>Chat-ID</label>
<div class="input-with-btn">
<input type="text" id="sessionTelegramChatId" placeholder="Wird automatisch bei /start gesetzt" autocomplete="off" readonly>
<button type="button" id="sessionTelegramUnlink" class="icon-btn danger-btn" title="Verknüpfung lösen"><i class="fa fa-chain-broken"></i></button>
</div>
<small class="form-hint">Wird automatisch gesetzt wenn jemand /start an den Bot sendet.</small>
</div>
</div>
<div class="form-actions">
@@ -306,6 +310,49 @@
</div>
</template>
<template id="tpl-telegram-bot">
<div class="page form-page">
<header>
<button class="back-btn icon-btn" data-page="dashboard"><i class="fa fa-arrow-left"></i></button>
<h1 id="telegramBotPageTitle">Neuer Telegram Bot</h1>
</header>
<div class="page-content">
<div class="form">
<label>Name</label>
<input type="text" id="tgBotName" placeholder="z.B. Mein Hauptbot">
<label>Bot-Token</label>
<input type="text" id="tgBotToken" placeholder="123456:ABC-DEF..." autocomplete="off">
<small class="form-hint">Erstelle einen Bot via <a href="https://t.me/BotFather" target="_blank">@BotFather</a></small>
<label class="checkbox-label">
<input type="checkbox" id="tgBotPublic">
<span>Public – andere User können diesen Bot in ihren Sessions verwenden</span>
</label>
<div id="tgBotSecretRow" hidden>
<label>Webhook-Secret</label>
<div class="input-with-btn">
<input type="text" id="tgBotSecret" readonly>
<button type="button" id="tgBotCopySecret" class="icon-btn" title="Kopieren"><i class="fa fa-clipboard"></i></button>
</div>
</div>
<div id="tgBotWebhookSection" hidden>
<label><i class="fa fa-plug"></i> <b>Webhook</b></label>
<div id="tgBotWebhookStatus" class="form-status-box"></div>
<div class="form-actions-inline">
<button type="button" id="tgBotWebhookRegister" class="positive-btn"><i class="fa fa-plug"></i> Registrieren</button>
<button type="button" id="tgBotWebhookInfo"><i class="fa fa-info-circle"></i> Status prüfen</button>
<button type="button" id="tgBotWebhookDelete" class="danger-btn"><i class="fa fa-unlink"></i> Entfernen</button>
</div>
</div>
<div class="form-actions">
<button id="tgBotSaveBtn">Speichern</button>
<button id="tgBotCancelBtn">Abbrechen</button>
<button id="tgBotDeleteBtn" class="danger-btn" hidden>Löschen</button>
</div>
</div>
</div>
</div>
</template>
<template id="tpl-chat">
<div class="page chat-page">
<header>
@@ -575,6 +622,7 @@
<script src="js/avatar-editor.js"></script>
<script src="js/pages/dashboard.js"></script>
<script src="js/pages/cronjob-page.js"></script>
<script src="js/pages/telegram-bot-page.js"></script>
<script src="js/pages/agent-page.js"></script>
<script src="js/pages/session-page.js"></script>
<script src="js/pages/chat-page.js"></script>
+68 -15
View File
@@ -284,29 +284,61 @@ async function apiChangePassword(currentPassword, newPassword) {
return resp.json();
}
// ── Telegram API (pro Session) ──
// ── Telegram Bot API ──
async function apiSessionTelegramGet(sessionId) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram`);
if (!resp.ok) return { configured: false };
async function apiGetTelegramBots() {
const resp = await fetch('/api/telegram/bots');
return resp.json();
}
async function apiSessionTelegramSave(sessionId, botToken, webhookSecret, allowedChatIds) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram`, {
method: 'PUT',
async function apiGetTelegramBot(botId) {
const resp = await fetch(`/api/telegram/bots/${botId}`);
if (!resp.ok) return null;
return resp.json();
}
async function apiCreateTelegramBot(name, botToken) {
const resp = await fetch('/api/telegram/bots', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ botToken, webhookSecret, allowedChatIds })
body: JSON.stringify({ name, botToken })
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Telegram-Config speichern fehlgeschlagen');
throw new Error(err.error || 'Bot erstellen fehlgeschlagen');
}
return resp.json();
}
async function apiSessionTelegramRegisterWebhook(sessionId, baseUrl) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram/register-webhook`, {
async function apiGetAvailableTelegramBots() {
const resp = await fetch('/api/telegram/bots/available');
return resp.json();
}
async function apiUpdateTelegramBot(botId, name, botToken, isPublic) {
const resp = await fetch(`/api/telegram/bots/${botId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, botToken, isPublic })
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Bot speichern fehlgeschlagen');
}
return resp.json();
}
async function apiDeleteTelegramBot(botId) {
const resp = await fetch(`/api/telegram/bots/${botId}`, { method: 'DELETE' });
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Bot löschen fehlgeschlagen');
}
return resp.json();
}
async function apiTelegramBotRegisterWebhook(botId, baseUrl) {
const resp = await fetch(`/api/telegram/bots/${botId}/register-webhook`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseUrl })
@@ -315,18 +347,39 @@ async function apiSessionTelegramRegisterWebhook(sessionId, baseUrl) {
return resp.json();
}
async function apiSessionTelegramWebhookInfo(sessionId) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram/webhook-info`);
async function apiTelegramBotWebhookInfo(botId) {
const resp = await fetch(`/api/telegram/bots/${botId}/webhook-info`);
if (!resp.ok) throw new Error('Webhook-Info fehlgeschlagen');
return resp.json();
}
async function apiSessionTelegramDeleteWebhook(sessionId) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram/webhook`, { method: 'DELETE' });
async function apiTelegramBotDeleteWebhook(botId) {
const resp = await fetch(`/api/telegram/bots/${botId}/webhook`, { method: 'DELETE' });
if (!resp.ok) throw new Error('Webhook-Löschung fehlgeschlagen');
return resp.json();
}
// ── Session Telegram Config ──
async function apiSessionTelegramGet(sessionId) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram`);
if (!resp.ok) return { configured: false };
return resp.json();
}
async function apiSessionTelegramSave(sessionId, telegramBotId, chatId) {
const resp = await fetch(`/api/sessions/${sessionId}/telegram`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ telegramBotId, chatId })
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Telegram-Config speichern fehlgeschlagen');
}
return resp.json();
}
async function apiGetSettings() {
const resp = await fetch('/api/settings');
if (!resp.ok) return {};
+41
View File
@@ -46,6 +46,47 @@ function buildSessionCard(session, agent) {
}
// ── Telegram Bot Card Helper ──
function buildTelegramBotCard(bot) {
const card = document.createElement('div');
card.className = 'card card-compact';
const badges = [];
if (bot.isPublic) {
badges.push('<span class="badge badge-feature"><i class="fa fa-globe"></i> Public</span>');
}
if (bot.isOwner === false) {
badges.push('<span class="badge badge-off"><i class="fa fa-user"></i> Fremd</span>');
}
if (bot.sessionCount > 0) {
badges.push(`<span class="badge badge-feature"><i class="fa fa-comment"></i> ${bot.sessionCount} ${bot.sessionCount === 1 ? 'Session' : 'Sessions'}</span>`);
}
// Meta-Items nur anzeigen wenn Daten vorhanden
const metaParts = [];
if (bot.botToken) {
const maskedToken = '•••' + bot.botToken.slice(-4);
metaParts.push(`<span class="card-meta-item"><i class="fa fa-key"></i> ${esc(maskedToken)}</span>`);
}
if (bot.createdAt) {
metaParts.push(`<span class="card-meta-item"><i class="fa fa-clock-o"></i> ${formatDate(bot.createdAt)}</span>`);
}
card.innerHTML = `
<div class="card-body">
<div class="card-title-row">
<span class="card-title"><i class="fa fa-paper-plane"></i> ${esc(bot.name)}</span>
${badges.join('')}
</div>
${metaParts.length ? `<div class="card-meta">${metaParts.join('')}</div>` : ''}
</div>
`;
return card;
}
// ── Agent Card Helper ──
// Erzeugt ein .card-Element für einen Agent (wiederverwendbar für Dashboard + Picker).
+14
View File
@@ -172,6 +172,20 @@ function _openPickerModal(title, items, currentId, onSelect, options) {
});
}
// ── Telegram Bot Picker Modal ──
function openTelegramBotPickerModal(bots, currentBotId, onSelect) {
var items = bots.map(function(b) {
b._pickerId = b.botId;
return b;
});
_openPickerModal('Telegram Bot auswählen', items, currentBotId, onSelect, {
noneLabel: 'Kein Bot',
noneIcon: 'fa-paper-plane',
buildCard: function(b) { return buildTelegramBotCard(b); }
});
}
// ── OpenClaw Import Modal ──
async function showOpenClawImportModal(sessionId) {
+20 -2
View File
@@ -35,8 +35,8 @@ registerPage('dashboard', {
// Telegram-Config parallel pro Session laden (sessions werden gleich geladen)
const [agents, sessions, cronjobs, shares] = await Promise.all([
apiGetAgents(), apiGetSessions(), apiGetCronjobs(), apiListShares()
const [agents, sessions, cronjobs, shares, telegramBots] = await Promise.all([
apiGetAgents(), apiGetSessions(), apiGetCronjobs(), apiListShares(), apiGetTelegramBots()
]);
const agentMap = {};
for (const a of agents) agentMap[a.agentId] = a;
@@ -253,6 +253,24 @@ registerPage('dashboard', {
// Neuer Cronjob Button
document.getElementById('newCronjobBtn').addEventListener('click', () => navigateTo('cronjob'));
// Telegram Bots
const tgGrid = document.getElementById('telegramBotCards');
tgGrid.innerHTML = '';
for (const bot of telegramBots) {
const card = buildTelegramBotCard(bot);
// Card klickbar → direkt zur Edit-Seite
card.classList.add('clickable');
card.addEventListener('click', () => navigateTo('telegram-bot', { id: bot.botId }));
tgGrid.appendChild(card);
}
document.getElementById('newTelegramBotBtn').addEventListener('click', () => {
navigateTo('telegram-bot');
});
// Shared Links
const sharedSection = document.getElementById('sharedLinksSection');
const sharedGrid = document.getElementById('sharedLinksCards');
+53 -117
View File
@@ -71,7 +71,7 @@ registerPage('session', {
// Telegram-Section nur für existierende Sessions
if (!isNew) {
initSessionTelegram(sessionId);
initSessionTelegram(sessionId, session);
}
// Agents laden + Skills parallel
@@ -175,16 +175,14 @@ registerPage('session', {
await apiCreateSession(data);
} else {
await apiUpdateSession(sessionId, data);
// Telegram-Config mitspeichern (falls Section sichtbar)
var tgToken = document.getElementById('sessionTelegramToken');
var tgSecret = document.getElementById('sessionTelegramSecret');
if (tgToken) {
try {
await saveTelegramConfig(sessionId, tgToken, tgSecret);
} catch(e) {
showToast('Session gespeichert, aber Telegram-Fehler: ' + e.message, 'error');
return;
}
// Telegram-Config mitspeichern
try {
var tgBotId = document.getElementById('telegramBotSelect').value || '';
var tgChatId = document.getElementById('sessionTelegramChatId').value.trim();
await apiSessionTelegramSave(sessionId, tgBotId, tgChatId);
} catch(e) {
showToast('Session gespeichert, aber Telegram-Fehler: ' + e.message, 'error');
return;
}
}
navigateTo('dashboard');
@@ -223,121 +221,59 @@ function openAgentPickerModal(agents, agentSkillsMap, currentId, onSelect) {
});
}
function initSessionTelegram(sessionId) {
var tokenInput = document.getElementById('sessionTelegramToken');
var secretInput = document.getElementById('sessionTelegramSecret');
var allowedInput = document.getElementById('sessionTelegramAllowed');
var registerBtn = document.getElementById('sessionTelegramRegisterBtn');
var deleteBtn = document.getElementById('sessionTelegramDeleteBtn');
var statusBox = document.getElementById('sessionTelegramStatusBox');
function initSessionTelegram(sessionId, session) {
var botSelectCard = document.getElementById('telegramBotSelectCard');
var botSelect = document.getElementById('telegramBotSelect');
var chatIdInput = document.getElementById('sessionTelegramChatId');
var unlinkBtn = document.getElementById('sessionTelegramUnlink');
// Vorhandene Config laden + Status abrufen
apiSessionTelegramGet(sessionId).then(async function(config) {
if (config.botToken) tokenInput.value = config.botToken;
if (config.allowedChatIds) allowedInput.value = config.allowedChatIds;
if (config.webhookSecret) {
secretInput.value = config.webhookSecret;
} else {
// Secret einmalig generieren und sofort persistent speichern
var newSecret = crypto.randomUUID().replace(/-/g, '');
secretInput.value = newSecret;
await apiSessionTelegramSave(sessionId, config.botToken || '', newSecret);
}
// Status automatisch abrufen wenn Token konfiguriert ist
if (config.botToken) {
refreshTelegramStatus(sessionId, statusBox, registerBtn, deleteBtn);
} else {
showTelegramUnconfigured(statusBox, registerBtn, deleteBtn);
}
// Unlink-Button
unlinkBtn.addEventListener('click', function() {
if (!chatIdInput.value) return;
if (!confirm('Telegram-Verknüpfung wirklich lösen?')) return;
chatIdInput.value = '';
showToast('Chat-ID entfernt. Speichern nicht vergessen.', 'info');
});
// Config laden
apiSessionTelegramGet(sessionId).then(function(config) {
if (config.chatId) chatIdInput.value = config.chatId;
if (config.telegramBotId) botSelect.value = config.telegramBotId;
// Bot-Picker initialisieren
initTelegramBotPicker(botSelectCard, botSelect, config.telegramBotId);
}).catch(function() {
showTelegramUnconfigured(statusBox, registerBtn, deleteBtn);
});
// Webhook registrieren
registerBtn.addEventListener('click', async function() {
var baseUrl = prompt('Base-URL der App:', window.location.origin);
if (!baseUrl) return;
await withButtonLoading(registerBtn, 'Wird registriert...', async () => {
try {
await saveTelegramConfig(sessionId, tokenInput, secretInput);
var res = await apiSessionTelegramRegisterWebhook(sessionId, baseUrl.trim());
if (res.ok) {
showToast('Webhook registriert!', 'success');
} else {
showToast('Antwort: ' + JSON.stringify(res), 'error');
}
} catch(e) {
showToast('Fehler: ' + e.message, 'error');
}
});
refreshTelegramStatus(sessionId, statusBox, registerBtn, deleteBtn);
});
// Webhook entfernen
deleteBtn.addEventListener('click', async function() {
if (!confirm('Webhook wirklich entfernen?')) return;
await withButtonLoading(deleteBtn, 'Wird entfernt...', async () => {
try {
var res = await apiSessionTelegramDeleteWebhook(sessionId);
if (res.ok) {
showToast('Webhook entfernt', 'success');
} else {
showToast('Antwort: ' + JSON.stringify(res), 'error');
}
} catch(e) {
showToast('Fehler: ' + e.message, 'error');
}
});
refreshTelegramStatus(sessionId, statusBox, registerBtn, deleteBtn);
initTelegramBotPicker(botSelectCard, botSelect, null);
});
}
function showTelegramUnconfigured(statusBox, registerBtn, deleteBtn) {
statusBox.innerHTML = '<p>⚠️ Kein Bot-Token konfiguriert. Speichere zuerst die Session mit einem Token.</p>';
statusBox.hidden = false;
registerBtn.hidden = true;
deleteBtn.hidden = true;
}
async function refreshTelegramStatus(sessionId, statusBox, registerBtn, deleteBtn) {
statusBox.hidden = true;
async function initTelegramBotPicker(cardEl, selectEl, currentBotId) {
var bots = [];
try {
var info = await apiSessionTelegramWebhookInfo(sessionId);
var result = info.result || {};
var url = result.url || '';
var pending = result.pending_update_count || 0;
var lastError = result.last_error_message;
var active = !!url;
var result = await apiGetAvailableTelegramBots();
if (Array.isArray(result)) bots = result;
} catch(e) { /* ignore */ }
var botMap = {};
for (var i = 0; i < bots.length; i++) botMap[bots[i].botId] = bots[i];
var html = '';
if (active) {
html += '<p>✅ <strong>Webhook aktiv</strong></p>';
html += '<p><strong>URL:</strong> ' + esc(url) + '</p>';
html += '<p><strong>Ausstehend:</strong> ' + pending + '</p>';
if (lastError) html += '<p class="tg-error"><strong>Letzter Fehler:</strong> ' + esc(lastError) + '</p>';
} else {
html += '<p>⚠️ <strong>Webhook nicht registriert</strong></p>';
}
statusBox.innerHTML = html;
statusBox.hidden = false;
// Buttons ein/ausblenden je nach Status
registerBtn.hidden = active;
deleteBtn.hidden = !active;
} catch(e) {
statusBox.innerHTML = '<p class="tg-error">Status konnte nicht abgerufen werden: ' + esc(e.message) + '</p>';
statusBox.hidden = false;
registerBtn.hidden = false;
deleteBtn.hidden = true;
function renderBotInCard(bot) {
var inner = bot ? buildTelegramBotCard(bot) : null;
_renderCardPreview(cardEl, inner, 'fa-paper-plane', 'Kein Bot zugewiesen', 'tg-bot-select-change');
}
}
async function saveTelegramConfig(sessionId, tokenInput, secretInput) {
var token = tokenInput.value.trim();
var secret = secretInput.value.trim();
var allowedInput = document.getElementById('sessionTelegramAllowed');
var allowed = allowedInput ? allowedInput.value.trim() : '';
await apiSessionTelegramSave(sessionId, token, secret, allowed);
if (currentBotId && botMap[currentBotId]) {
selectEl.value = currentBotId;
renderBotInCard(botMap[currentBotId]);
} else {
renderBotInCard(null);
}
cardEl.addEventListener('click', function() {
openTelegramBotPickerModal(bots, selectEl.value, function(selectedBotId) {
selectEl.value = selectedBotId || '';
renderBotInCard(selectedBotId ? botMap[selectedBotId] : null);
});
});
}
+184
View File
@@ -0,0 +1,184 @@
// ── Telegram Bot Page ──
registerPage('telegram-bot', {
async init(params) {
var isEdit = params && params.id;
var bot = null;
var titleEl = document.getElementById('telegramBotPageTitle');
var nameInput = document.getElementById('tgBotName');
var tokenInput = document.getElementById('tgBotToken');
var secretRow = document.getElementById('tgBotSecretRow');
var secretInput = document.getElementById('tgBotSecret');
var copySecretBtn = document.getElementById('tgBotCopySecret');
var webhookSection = document.getElementById('tgBotWebhookSection');
var webhookStatus = document.getElementById('tgBotWebhookStatus');
var webhookRegisterBtn = document.getElementById('tgBotWebhookRegister');
var webhookInfoBtn = document.getElementById('tgBotWebhookInfo');
var webhookDeleteBtn = document.getElementById('tgBotWebhookDelete');
var publicCheckbox = document.getElementById('tgBotPublic');
var saveBtn = document.getElementById('tgBotSaveBtn');
var cancelBtn = document.getElementById('tgBotCancelBtn');
var deleteBtn = document.getElementById('tgBotDeleteBtn');
if (isEdit) {
bot = await apiGetTelegramBot(params.id);
if (!bot) {
showToast('Bot nicht gefunden', 'error');
navigateTo('dashboard');
return;
}
titleEl.textContent = bot.name || 'Telegram Bot';
nameInput.value = bot.name || '';
tokenInput.value = bot.botToken || '';
publicCheckbox.checked = bot.isPublic || false;
deleteBtn.hidden = false;
// Webhook-Secret anzeigen
if (bot.webhookSecret) {
secretRow.hidden = false;
secretInput.value = bot.webhookSecret;
}
// Webhook-Section anzeigen
webhookSection.hidden = false;
_loadWebhookStatus(bot.botId, webhookStatus);
} else {
titleEl.textContent = 'Neuer Telegram Bot';
deleteBtn.hidden = true;
}
// Copy Secret
copySecretBtn.addEventListener('click', async function() {
try {
await navigator.clipboard.writeText(secretInput.value);
showToast('Kopiert!');
} catch(e) {
showToast('Kopieren fehlgeschlagen', 'error');
}
});
// Webhook registrieren
webhookRegisterBtn.addEventListener('click', async function() {
var baseUrl = prompt('Base-URL der App:', window.location.origin);
if (!baseUrl) return;
await withButtonLoading(webhookRegisterBtn, 'Registriere...', async function() {
try {
var res = await apiTelegramBotRegisterWebhook(bot.botId, baseUrl.trim());
if (res.ok) {
showToast('Webhook registriert!', 'success');
_loadWebhookStatus(bot.botId, webhookStatus);
} else {
showToast('Fehler: ' + JSON.stringify(res), 'error');
}
} catch(err) {
showToast('Fehler: ' + err.message, 'error');
}
});
});
// Webhook-Status prüfen
webhookInfoBtn.addEventListener('click', async function() {
await withButtonLoading(webhookInfoBtn, 'Prüfe...', async function() {
await _loadWebhookStatus(bot.botId, webhookStatus);
});
});
// Webhook entfernen
webhookDeleteBtn.addEventListener('click', async function() {
if (!confirm('Webhook wirklich entfernen?')) return;
await withButtonLoading(webhookDeleteBtn, 'Entferne...', async function() {
try {
var res = await apiTelegramBotDeleteWebhook(bot.botId);
if (res.ok) {
showToast('Webhook entfernt', 'success');
_loadWebhookStatus(bot.botId, webhookStatus);
} else {
showToast('Fehler: ' + JSON.stringify(res), 'error');
}
} catch(err) {
showToast('Fehler: ' + err.message, 'error');
}
});
});
// Speichern
saveBtn.addEventListener('click', async function() {
var name = nameInput.value.trim();
var token = tokenInput.value.trim();
var isPublic = publicCheckbox.checked;
if (!name || !token) {
showToast('Name und Bot-Token sind erforderlich', 'error');
return;
}
await withButtonLoading(saveBtn, 'Speichern...', async function() {
try {
if (isEdit) {
await apiUpdateTelegramBot(bot.botId, name, token, isPublic);
showToast('Bot gespeichert', 'success');
// Titel aktualisieren
titleEl.textContent = name;
} else {
var created = await apiCreateTelegramBot(name, token);
showToast('Bot erstellt', 'success');
navigateTo('telegram-bot', { id: created.botId });
}
} catch(e) {
showToast('Fehler: ' + e.message, 'error');
}
});
});
// Abbrechen
cancelBtn.addEventListener('click', function() {
navigateTo('dashboard');
});
// Löschen
deleteBtn.addEventListener('click', async function() {
if (!confirm('Bot "' + (bot.name || '') + '" wirklich löschen? Alle Session-Verknüpfungen werden entfernt.')) return;
try {
await apiDeleteTelegramBot(bot.botId);
showToast('Bot gelöscht', 'success');
navigateTo('dashboard');
} catch(e) {
showToast('Fehler: ' + e.message, 'error');
}
});
}
});
async function _loadWebhookStatus(botId, statusEl) {
try {
var info = await apiTelegramBotWebhookInfo(botId);
var result = info.result || {};
var url = result.url || '';
var pending = result.pending_update_count || 0;
var lastError = result.last_error_message || '';
var lastErrorDate = result.last_error_date ? new Date(result.last_error_date * 1000).toLocaleString('de-AT') : '';
var parts = [];
if (url) {
parts.push('<span class="status-active"><i class="fa fa-check-circle"></i> Webhook aktiv</span>');
parts.push('<span><i class="fa fa-link"></i> ' + esc(url) + '</span>');
} else {
parts.push('<span class="status-inactive"><i class="fa fa-times-circle"></i> Kein Webhook registriert</span>');
}
if (pending > 0) {
parts.push('<span><i class="fa fa-clock-o"></i> ' + pending + ' ausstehende Updates</span>');
}
if (lastError) {
parts.push('<span class="status-error"><i class="fa fa-exclamation-triangle"></i> ' + esc(lastError) + (lastErrorDate ? ' (' + lastErrorDate + ')' : '') + '</span>');
}
statusEl.innerHTML = parts.join('');
statusEl.hidden = false;
} catch(e) {
statusEl.innerHTML = '<span class="status-error"><i class="fa fa-exclamation-triangle"></i> Status konnte nicht geladen werden</span>';
statusEl.hidden = false;
}
}