diff --git a/Endpoints/TelegramEndpoint.cs b/Endpoints/TelegramEndpoint.cs index 6dd2fea..f7dc212 100644 --- a/Endpoints/TelegramEndpoint.cs +++ b/Endpoints/TelegramEndpoint.cs @@ -27,6 +27,9 @@ public static class TelegramEndpoint // Webhook-Info (Admin) app.MapGet("/api/telegram/webhook-info", HandleWebhookInfo); + + // Webhook löschen (Admin) + app.MapDelete("/api/telegram/webhook", HandleDeleteWebhook); } private static async Task HandleWebhook(HttpRequest req, HttpResponse res, TelegramService telegram) @@ -136,4 +139,18 @@ public static class TelegramEndpoint res.ContentType = "application/json"; await res.WriteAsync(result); } + + private static async Task HandleDeleteWebhook(HttpRequest req, HttpResponse res, TelegramService telegram) + { + if (!req.HttpContext.IsAdmin()) + { + res.StatusCode = 403; + await res.WriteAsJsonAsync(new { error = "Admin required" }); + return; + } + + var result = await telegram.DeleteWebhook(); + res.ContentType = "application/json"; + await res.WriteAsync(result); + } } diff --git a/Services/TelegramService.cs b/Services/TelegramService.cs index 000a56d..4653d5c 100644 --- a/Services/TelegramService.cs +++ b/Services/TelegramService.cs @@ -835,6 +835,15 @@ public class TelegramService var resp = await client.GetAsync($"https://api.telegram.org/bot{_botToken}/getWebhookInfo"); return await resp.Content.ReadAsStringAsync(); } + + public async Task DeleteWebhook() + { + if (!IsConfigured) return "Telegram Bot nicht konfiguriert."; + + var client = _httpFactory.CreateClient(); + var resp = await client.PostAsync($"https://api.telegram.org/bot{_botToken}/deleteWebhook", null); + return await resp.Content.ReadAsStringAsync(); + } } // TokenUsage und ToolResult werden aus AnthropicService.cs verwendet diff --git a/wwwroot/css/dashboard.css b/wwwroot/css/dashboard.css index 38a60f6..045afa4 100644 --- a/wwwroot/css/dashboard.css +++ b/wwwroot/css/dashboard.css @@ -375,3 +375,62 @@ justify-content: center; gap: var(--badge-spacing); } + +/* ── Telegram Settings Status Box ── */ + +.telegram-status-box { + margin: 0 0 var(--box-spacing) 0; + border: var(--border-width) solid var(--f1-border); + border-radius: var(--border-radius); + overflow: hidden; +} + +.tg-status-row { + display: flex; + align-items: baseline; + gap: var(--box-spacing); + padding: calc(var(--box-padding) * 0.6) var(--box-padding); + border-bottom: var(--border-width) solid var(--f1-border); + font-size: var(--font-size); +} + +.tg-status-row:last-child { + border-bottom: none; +} + +.tg-status-row.tg-error { + background: color-mix(in srgb, var(--danger) 8%, transparent); +} + +.tg-label { + flex-shrink: 0; + width: 160px; + color: var(--text-muted, var(--semi-transparent)); + opacity: var(--semi-transparent); + font-size: calc(var(--font-size) * 0.9); +} + +.tg-value { + flex: 1; + word-break: break-all; + font-family: var(--font-mono, monospace); + font-size: calc(var(--font-size) * 0.9); +} + +.tg-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 99px; + font-family: var(--font-sans, sans-serif); + font-size: calc(var(--font-size) * 0.85); +} + +.tg-badge-ok { + background: color-mix(in srgb, var(--success, #22c55e) 15%, transparent); + color: var(--success, #22c55e); +} + +.tg-badge-off { + background: color-mix(in srgb, var(--warning, #f59e0b) 15%, transparent); + color: var(--warning, #f59e0b); +} diff --git a/wwwroot/index.html b/wwwroot/index.html index 2229aac..e7786c1 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -284,6 +284,37 @@ +
diff --git a/wwwroot/js/api.js b/wwwroot/js/api.js index e3e734b..81678be 100644 --- a/wwwroot/js/api.js +++ b/wwwroot/js/api.js @@ -274,6 +274,28 @@ async function apiTelegramLink(sessionId) { return resp.json(); } +async function apiTelegramWebhookInfo() { + const resp = await fetch('/api/telegram/webhook-info'); + if (!resp.ok) throw new Error('Webhook-Info fehlgeschlagen'); + return resp.json(); +} + +async function apiTelegramRegisterWebhook(baseUrl) { + const resp = await fetch('/api/telegram/register-webhook', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ baseUrl }) + }); + if (!resp.ok) throw new Error('Webhook-Registrierung fehlgeschlagen'); + return resp.json(); +} + +async function apiTelegramDeleteWebhook() { + const resp = await fetch('/api/telegram/webhook', { method: 'DELETE' }); + if (!resp.ok) throw new Error('Webhook-Löschung fehlgeschlagen'); + return resp.json(); +} + async function apiBackupDb() { const resp = await fetch('/api/backup', { method: 'POST' }); if (!resp.ok) throw new Error('Backup fehlgeschlagen'); diff --git a/wwwroot/js/pages/settings-page.js b/wwwroot/js/pages/settings-page.js index 03d12d1..546db1a 100644 --- a/wwwroot/js/pages/settings-page.js +++ b/wwwroot/js/pages/settings-page.js @@ -40,10 +40,12 @@ registerPage('settings', { const chainMetaSelect = document.getElementById('settingChainMetaDisplay'); const resetBtn = document.getElementById('settingsResetBtn'); const backupSection = document.getElementById('settingsBackupSection'); + const telegramSection = document.getElementById('settingsTelegramSection'); - // Backup nur für Admins + // Backup + Telegram nur für Admins if (currentUser && currentUser.isAdmin) { backupSection.style.display = ''; + initTelegramSection(); } const s = loadSettings(); @@ -99,6 +101,97 @@ registerPage('settings', { toolCallSelect.value = SETTINGS_DEFAULTS.toolCallDisplay; chainMetaSelect.value = SETTINGS_DEFAULTS.chainMetaDisplay; }); + + function initTelegramSection() { + // Erst prüfen ob Telegram überhaupt konfiguriert ist + apiTelegramStatus().then(s => { + if (!s.configured) return; // Bot-Token nicht gesetzt → Section versteckt lassen + telegramSection.style.display = ''; + + const statusBtn = document.getElementById('settingsTelegramStatusBtn'); + const statusBox = document.getElementById('settingsTelegramStatusBox'); + const registerBtn = document.getElementById('settingsTelegramRegisterBtn'); + const deleteBtn = document.getElementById('settingsTelegramDeleteBtn'); + + // Status abrufen + statusBtn.addEventListener('click', async () => { + statusBtn.disabled = true; + statusBtn.textContent = 'Wird abgerufen...'; + statusBox.style.display = 'none'; + try { + const info = await apiTelegramWebhookInfo(); + const result = info.result || {}; + const url = result.url || '(kein Webhook registriert)'; + const pending = result.pending_update_count ?? 0; + const lastError = result.last_error_message; + let html = `
Webhook-URL${escapeHtml(url)}
`; + html += `
Ausstehende Updates${pending}
`; + if (lastError) { + html += `
Letzter Fehler${escapeHtml(lastError)}
`; + } + const active = !!result.url; + html += `
Status${active ? '✅ Aktiv' : '⚠️ Nicht registriert'}
`; + statusBox.innerHTML = html; + statusBox.style.display = ''; + } catch (e) { + statusBox.innerHTML = `
Fehler: ${escapeHtml(e.message)}
`; + statusBox.style.display = ''; + } finally { + statusBtn.disabled = false; + statusBtn.textContent = 'Status abrufen'; + } + }); + + // Webhook registrieren + registerBtn.addEventListener('click', async () => { + const baseUrl = prompt('Base-URL der App (z.B. https://deine-domain.de):', window.location.origin); + if (!baseUrl) return; + registerBtn.disabled = true; + registerBtn.textContent = 'Wird registriert...'; + try { + const res = await apiTelegramRegisterWebhook(baseUrl.trim()); + if (res.result && res.result.ok) { + alert('✅ Webhook erfolgreich registriert!'); + } else { + alert('Antwort: ' + JSON.stringify(res.result)); + } + } catch (e) { + alert('Fehler: ' + e.message); + } finally { + registerBtn.disabled = false; + registerBtn.textContent = 'Registrieren'; + } + }); + + // Webhook löschen + deleteBtn.addEventListener('click', async () => { + if (!confirm('Webhook wirklich entfernen? Der Bot empfängt dann keine Nachrichten mehr.')) return; + deleteBtn.disabled = true; + deleteBtn.textContent = 'Wird entfernt...'; + try { + const res = await apiTelegramDeleteWebhook(); + if (res.ok) { + alert('✅ Webhook erfolgreich entfernt.'); + } else { + alert('Antwort: ' + JSON.stringify(res)); + } + } catch (e) { + alert('Fehler: ' + e.message); + } finally { + deleteBtn.disabled = false; + deleteBtn.textContent = 'Webhook entfernen'; + } + }); + }); + } }, destroy() {} }); + +function escapeHtml(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}