Files
gott/wwwroot/js/pages/session-page.js
T
Meleki ec73ccd71e 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
2026-03-08 19:27:05 +01:00

280 lines
11 KiB
JavaScript

// ── Session Edit Page ──
let _sessionAvatarEditor = null;
let _sessionContextMde = null;
registerPage('session', {
async init(params) {
const sessionId = params.id;
const isNew = !sessionId;
let session = null;
if (!isNew) {
session = await apiGetSession(sessionId);
if (!session) return navigateTo('dashboard');
}
const titleEl = document.getElementById('sessionPageTitle');
const nameEl = document.getElementById('sessionName');
const descEl = document.getElementById('sessionDescription');
const contextEl = document.getElementById('sessionContext');
const workspaceEl = document.getElementById('sessionWorkspace');
const agentSelect = document.getElementById('agentSelect');
const agentSelectCard = document.getElementById('agentSelectCard');
const preparerAgentSelect = document.getElementById('preparerAgentSelect');
const preparerAgentSelectCard = document.getElementById('preparerAgentSelectCard');
const reviewAgentSelect = document.getElementById('reviewAgentSelect');
const reviewAgentSelectCard = document.getElementById('reviewAgentSelectCard');
const reviewIterationsRow = document.getElementById('reviewIterationsRow');
const maxReviewIterationsEl = document.getElementById('maxReviewIterations');
const finalizerAgentSelect = document.getElementById('finalizerAgentSelect');
const finalizerAgentSelectCard = document.getElementById('finalizerAgentSelectCard');
const imagePreview = document.getElementById('sessionImagePreview');
const saveBtn = document.getElementById('sessionSaveBtn');
const cancelBtn = document.getElementById('sessionCancelBtn');
const deleteBtn = document.getElementById('sessionDeleteBtn');
const commandsList = document.getElementById('customCommandsList');
const addCommandBtn = document.getElementById('addCommandBtn');
const more = document.querySelector('.more');
// Avatar Editor initialisieren
_sessionAvatarEditor = AvatarEditor.init({
previewEl: imagePreview,
initialImage: (session && session.image) || null,
initialScale: (session && session.avatarScale) || 1,
initialOffsetX: (session && session.avatarOffsetX) || 0,
initialOffsetY: (session && session.avatarOffsetY) || 0
});
if (session) {
titleEl.textContent = session.name || 'Unbenannt';
nameEl.value = session.name || '';
descEl.value = session.description || '';
workspaceEl.value = session.workspace || '';
deleteBtn.hidden = false;
// Custom Commands laden
loadCommandsIntoUI(commandsList, session.customCommands);
} else {
titleEl.textContent = 'Neue Session';
deleteBtn.hidden = true;
}
// EasyMDE für Kontext
_sessionContextMde = createMDE(contextEl, {
initialValue: (session && session.context) || '',
placeholder: 'Zusätzlicher Kontext für den System-Prompt (optional)'
});
// Befehl hinzufügen
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
// Telegram-Section nur für existierende Sessions
if (!isNew) {
initSessionTelegram(sessionId, session);
}
// Agents laden + Skills parallel
const agents = await apiGetAgents();
const agentMap = {};
for (const a of agents) agentMap[a.agentId] = a;
const agentSkillsMap = {};
for (const a of agents) {
agentSkillsMap[a.agentId] = a.skills || [];
}
function renderAgentInCard(cardEl, agent, emptyIcon, emptyText) {
const inner = agent ? buildAgentCard(agent, agentSkillsMap[agent.agentId] || []) : null;
_renderCardPreview(cardEl, inner, emptyIcon, emptyText, 'agent-select-change');
}
function updateReviewVisibility() {
reviewIterationsRow.hidden = !reviewAgentSelect.value;
}
// Helper: Agent-Picker für eine Rolle einrichten (Initial-Rendering + Click-Handler)
function setupAgentPicker(cardEl, selectEl, initialId, emptyIcon, emptyText, onChange) {
if (initialId) {
selectEl.value = initialId;
renderAgentInCard(cardEl, agentMap[initialId] || null, emptyIcon, emptyText);
} else {
renderAgentInCard(cardEl, null, emptyIcon, emptyText);
}
cardEl.addEventListener('click', () => {
openAgentPickerModal(agents, agentSkillsMap, selectEl.value, (selectedId) => {
selectEl.value = selectedId || '';
renderAgentInCard(cardEl, selectedId ? agentMap[selectedId] : null, emptyIcon, emptyText);
if (onChange) onChange(selectedId);
});
});
}
setupAgentPicker(agentSelectCard, agentSelect, session && session.agentId, 'fa-user-circle-o', 'Kein Agent ausgewählt');
setupAgentPicker(preparerAgentSelectCard, preparerAgentSelect, session && session.preparerAgentId, 'fa-filter', 'Kein Vorbereiter-Agent');
setupAgentPicker(reviewAgentSelectCard, reviewAgentSelect, session && session.reviewAgentId, 'fa-check-circle-o', 'Kein Review-Agent', updateReviewVisibility);
if (session && session.reviewAgentId) maxReviewIterationsEl.value = session.maxReviewIterations || 3;
updateReviewVisibility();
setupAgentPicker(finalizerAgentSelectCard, finalizerAgentSelect, session && session.finalizerAgentId, 'fa-flag-checkered', 'Kein Finalizer-Agent');
initMoreButton(more);
// Workspace Browse
const workspaceBrowse = document.getElementById('sessionWorkspaceBrowse');
if (workspaceBrowse) {
workspaceBrowse.addEventListener('click', () => {
const defaultPath = (currentUser && currentUser.defaultPath) || '';
const currentVal = workspaceEl.value.trim();
// Für den Folder-Browser den absoluten Startpfad zusammenbauen
let startPath = '/';
if (defaultPath && currentVal) {
startPath = defaultPath.replace(/\/+$/, '') + '/' + currentVal;
} else if (defaultPath) {
startPath = defaultPath;
} else if (currentVal) {
startPath = currentVal;
}
openFolderBrowser({
startPath,
basePath: defaultPath || null,
onSelect(absolutePath) {
// Relativen Pfad zum DefaultPath berechnen
if (defaultPath && absolutePath.startsWith(defaultPath)) {
let rel = absolutePath.slice(defaultPath.length).replace(/^\/+/, '');
workspaceEl.value = rel || '';
} else {
// Kein DefaultPath oder Pfad außerhalb → absoluten Pfad beibehalten (Legacy)
workspaceEl.value = absolutePath;
}
}
});
});
}
// Speichern
saveBtn.addEventListener('click', async () => {
const commands = getCommandsFromUI(commandsList);
const data = {
name: nameEl.value.trim() || 'Unbenannt',
description: descEl.value.trim(),
image: _sessionAvatarEditor.getImage(),
agentId: agentSelect.value || null,
reviewAgentId: reviewAgentSelect.value || null,
maxReviewIterations: parseInt(maxReviewIterationsEl.value) || 3,
finalizerAgentId: finalizerAgentSelect.value || null,
preparerAgentId: preparerAgentSelect.value || null,
context: _sessionContextMde.value().trim() || null,
workspace: workspaceEl.value.trim() || null,
customCommands: commands,
avatarScale: _sessionAvatarEditor.getScale(),
avatarOffsetX: _sessionAvatarEditor.getOffsetX(),
avatarOffsetY: _sessionAvatarEditor.getOffsetY()
};
if (isNew) {
await apiCreateSession(data);
} else {
await apiUpdateSession(sessionId, data);
// 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');
});
// Abbrechen
cancelBtn.addEventListener('click', () => navigateTo('dashboard'));
// Löschen
deleteBtn.addEventListener('click', async () => {
if (!confirm('Session wirklich löschen?')) return;
await apiDeleteSession(sessionId);
navigateTo('dashboard');
});
},
destroy() {
if (_sessionContextMde) {
_sessionContextMde.toTextArea();
_sessionContextMde = null;
}
if (_sessionAvatarEditor) {
_sessionAvatarEditor.destroy();
_sessionAvatarEditor = null;
}
}
});
function openAgentPickerModal(agents, agentSkillsMap, currentId, onSelect) {
const items = agents.map(a => {
a._pickerId = a.agentId;
return a;
});
_openPickerModal('Agent auswählen', items, currentId, onSelect, {
noneLabel: 'Kein Agent',
buildCard: (a) => buildAgentCard(a, agentSkillsMap[a.agentId] || [])
});
}
function initSessionTelegram(sessionId, session) {
var botSelectCard = document.getElementById('telegramBotSelectCard');
var botSelect = document.getElementById('telegramBotSelect');
var chatIdInput = document.getElementById('sessionTelegramChatId');
var unlinkBtn = document.getElementById('sessionTelegramUnlink');
// 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() {
initTelegramBotPicker(botSelectCard, botSelect, null);
});
}
async function initTelegramBotPicker(cardEl, selectEl, currentBotId) {
var bots = [];
try {
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];
function renderBotInCard(bot) {
var inner = bot ? buildTelegramBotCard(bot) : null;
_renderCardPreview(cardEl, inner, 'fa-paper-plane', 'Kein Bot zugewiesen', 'tg-bot-select-change');
}
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);
});
});
}