Telegram: Bot=Session statt User-Level, Chat-Code-Refactoring

- Telegram-Config (Bot-Token, Webhook-Secret, ChatId) von User-Settings
  in sessions-Tabelle verschoben → jede Session hat ihren eigenen Bot
- Link-Code-System komplett entfernt (TelegramLinkRecord gelöscht)
- /start verknüpft automatisch, kein /link mehr nötig
- Alle Endpoints session-basiert: /api/sessions/{id}/telegram/*
- Webhook-URL pro Session: /api/telegram/webhook/{sessionId}

Refactoring: Doppelten Chat-Code eliminiert
- Neuer ChatService: PrepareContext, BuildHistoryFromDb, ResolveModelId
- Neues ChatContext-Model als gemeinsame Datenstruktur
- AnthropicService.ChatNonStreaming() für Telegram (statt eigenem Loop)
- TelegramService um ~180 Zeilen reduziert, kein eigener Claude-Loop mehr
- ChatEndpoint nutzt ChatService.PrepareContext() statt eigener Setup-Logik
- Model-Mapping nur noch an einer Stelle (3x → 1x)

Frontend
- Telegram-Section von Settings-Page in Session-Bearbeitungsseite verschoben
- Webhook-Secret readonly, wird einmalig generiert und persistent gespeichert
- Status wird automatisch geladen, Register/Delete je nach Webhook-Status
- Toast-Notification-System (showToast) in ui.js
- Link-Code-Modal und Telegram-Menüpunkt im Dashboard entfernt
- Telegram-Badge auf Session-Cards (konfiguriert/verbunden)

Bugfix: Bearer statt x-api-key für OAuth-Tokens im TelegramService
This commit is contained in:
Meleki committed 2026-03-03 21:46:59 +01:00
1 parent a0752ea492
commit 0120bb8f73
21 files changed
+994 -1272

No files matched your search

+3 -56
View File
@@ -17,8 +17,7 @@ registerPage('dashboard', {
// Globaler Click-Handler zum Schließen aller Card-Menüs
document.addEventListener('click', this._closeCardMenus = () => closeAllCardMenus());
// Telegram-Status parallel laden
const telegramStatusPromise = apiTelegramStatus().catch(() => ({ configured: false }));
// Telegram-Config parallel pro Session laden (sessions werden gleich geladen)
const agents = await apiGetAgents();
const agentMap = {};
@@ -41,6 +40,7 @@ registerPage('dashboard', {
const sessionBadges = [];
if (agent) sessionBadges.push(`<span class="badge">${esc(agent.name)}</span>`);
if (s.context) sessionBadges.push(`<span class="badge badge-feature" title="${esc(s.context)}"><i class="fa fa-file-text-o"></i></span>`);
if (s.telegramConfigured) sessionBadges.push(`<span class="badge ${s.telegramLinked ? 'badge-feature' : 'badge-off'}" title="Telegram ${s.telegramLinked ? 'verbunden' : 'konfiguriert'}"><i class="fa fa-paper-plane"></i></span>`);
card.innerHTML = `
<div class="card-image">${s.image ? `<img src="${s.image}" alt="">` : ''}</div>
@@ -52,7 +52,6 @@ registerPage('dashboard', {
<button class="card-menu-trigger icon-btn"><i class="fa fa-ellipsis-v"></i></button>
<div class="card-menu-dropdown" hidden>
<button class="card-menu-item" data-action="chat"><i class="fa fa-comment"></i> Chat öffnen</button>
<button class="card-menu-item" data-action="telegram" hidden><i class="fa fa-paper-plane"></i> Telegram verknüpfen</button>
<button class="card-menu-item" data-action="edit"><i class="fa fa-pencil"></i> Bearbeiten</button>
</div>
</div>
@@ -98,18 +97,6 @@ registerPage('dashboard', {
menuDropdown.hidden = true;
navigateTo('chat', { id: s.sessionId });
});
const telegramBtn = menuDropdown.querySelector('[data-action="telegram"]');
telegramBtn.addEventListener('click', async (e) => {
e.stopPropagation();
menuDropdown.hidden = true;
try {
const result = await apiTelegramLink(s.sessionId);
showTelegramLinkCode(result.code, s.name || 'Unbenannt');
} catch (err) {
showToast(err.message, 'error');
}
});
menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => {
e.stopPropagation();
menuDropdown.hidden = true;
@@ -156,12 +143,7 @@ registerPage('dashboard', {
}
}
// Telegram-Buttons einblenden wenn konfiguriert
telegramStatusPromise.then(status => {
if (status.configured) {
grid.querySelectorAll('[data-action="telegram"]').forEach(btn => btn.hidden = false);
}
});
// Neue Session Button
document.getElementById('newSessionBtn').addEventListener('click', () => navigateTo('session'));
@@ -262,42 +244,7 @@ registerPage('dashboard', {
}
});
function showTelegramLinkCode(code, sessionName) {
// Bestehendes Modal entfernen
const existing = document.getElementById('telegramLinkModal');
if (existing) existing.remove();
const modal = document.createElement('div');
modal.id = 'telegramLinkModal';
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal telegram-link-modal">
<div class="modal-header">
<h3><i class="fa fa-paper-plane"></i> Telegram verknüpfen</h3>
<button class="icon-btn modal-close"><i class="fa fa-times"></i></button>
</div>
<div class="modal-body">
<p>Sende diesen Code an deinen Telegram-Bot um die Session <strong>${esc(sessionName)}</strong> zu verknüpfen:</p>
<div class="telegram-code-display">
<code>/link ${esc(code)}</code>
<button class="icon-btn" title="Kopieren" id="copyTelegramCode"><i class="fa fa-copy"></i></button>
</div>
<p class="telegram-code-hint"><i class="fa fa-clock-o"></i> Der Code ist 5 Minuten gültig.</p>
</div>
</div>
`;
document.body.appendChild(modal);
modal.querySelector('.modal-close').addEventListener('click', () => modal.remove());
modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
document.getElementById('copyTelegramCode').addEventListener('click', () => {
navigator.clipboard.writeText('/link ' + code).then(() => {
showToast('In Zwischenablage kopiert', 'success');
});
});
}
function closeAllCardMenus() {
document.querySelectorAll('.card-menu-dropdown').forEach(d => d.hidden = true);