- 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
135 lines
6.6 KiB
JavaScript
135 lines
6.6 KiB
JavaScript
// ── Avatar Transform Helper ──
|
|
|
|
function _buildAvatarTransformStyle(obj) {
|
|
const parts = [];
|
|
if (obj.avatarScale && obj.avatarScale !== 1) parts.push(`scale(${obj.avatarScale})`);
|
|
if (obj.avatarOffsetX || obj.avatarOffsetY) parts.push(`translate(${obj.avatarOffsetX || 0}%, ${obj.avatarOffsetY || 0}%)`);
|
|
return parts.length ? ` style="transform: ${parts.join(' ')}"` : '';
|
|
}
|
|
|
|
// ── Session Card Helper ──
|
|
// Erzeugt ein .card-Element für eine Session (wiederverwendbar für Dashboard + Picker).
|
|
|
|
function buildSessionCard(session, agent) {
|
|
const card = document.createElement('div');
|
|
card.className = 'card';
|
|
|
|
const transformStyle = _buildAvatarTransformStyle(session);
|
|
const imageHtml = session.image ? `<img src="${session.image}" alt=""${transformStyle}>` : '';
|
|
|
|
// Badges
|
|
const badges = [];
|
|
if (agent) badges.push(`<span class="badge">${esc(agent.name)}</span>`);
|
|
if (session.reviewAgentId) badges.push(`<span class="badge badge-feature" title="Review-Agent"><i class="fa fa-check-circle-o"></i></span>`);
|
|
if (session.finalizerAgentId) badges.push(`<span class="badge badge-feature" title="Finalizer-Agent"><i class="fa fa-flag-checkered"></i></span>`);
|
|
if (session.context) badges.push(`<span class="badge badge-feature" title="${esc(session.context)}"><i class="fa fa-file-text-o"></i></span>`);
|
|
if (session.telegramConfigured) badges.push(`<span class="badge ${session.telegramLinked ? 'badge-feature' : 'badge-off'}" title="Telegram ${session.telegramLinked ? 'verbunden' : 'konfiguriert'}"><i class="fa fa-paper-plane"></i></span>`);
|
|
|
|
// Meta
|
|
const metaParts = [];
|
|
if (session.messageCount) metaParts.push(`<span class="card-meta-item"><i class="fa fa-comment"></i> ${esc(session.messageCount + ' Nachrichten')}</span>`);
|
|
if (session.archiveMessageCount) metaParts.push(`<span class="card-meta-item"><i class="fa fa-archive"></i> ${esc(session.archiveMessageCount + ' Nachrichten archiviert')}</span>`);
|
|
if (session.workspace) metaParts.push(`<span class="card-meta-item card-workspace" title="${esc(session.workspace)}"><i class="fa fa-folder"></i> ${esc(session.workspace)}</span>`);
|
|
|
|
card.innerHTML = `
|
|
<div class="card-image">${imageHtml}</div>
|
|
<div class="card-body">
|
|
<div class="card-title-row">
|
|
<span class="card-title">${esc(session.name || 'Unbenannt')}</span>
|
|
${badges.join('')}
|
|
</div>
|
|
${metaParts.length ? `<div class="card-meta">${metaParts.join('')}</div>` : ''}
|
|
</div>
|
|
`;
|
|
|
|
return card;
|
|
}
|
|
|
|
|
|
// ── 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).
|
|
|
|
function buildAgentCard(agent, skills) {
|
|
const card = document.createElement('div');
|
|
card.className = 'card';
|
|
|
|
const agentTransformStyle = _buildAvatarTransformStyle(agent);
|
|
const imageHtml = agent.avatar ? `<img src="${agent.avatar}" alt=""${agentTransformStyle}>` : '';
|
|
|
|
// Badges
|
|
const badges = [];
|
|
if (agent.toolChoice === 'any') badges.push('<span class="badge badge-forced-feature" title="Tool-Nutzung erzwingen"><i class="fa fa-wrench"></i> Erzwungen</span>');
|
|
if (agent.allowUserChoice) badges.push('<span class="badge badge-feature" title="Rückfragen per Auswahl erlaubt"><i class="fa fa-question-circle"></i> Rückfragen</span>');
|
|
if (agent.autoCommit === 'mention') badges.push('<span class="badge badge-feature" title="Git-Commits nur auf Anweisung"><i class="fa fa-code-fork"></i> Anmerken</span>');
|
|
if (agent.autoCommit === 'enforce') badges.push('<span class="badge badge-forced-feature" title="Git-Commits nach jeder Änderung erzwungen"><i class="fa fa-code-fork"></i> Erzwungen</span>');
|
|
if (agent.autoCommit === 'forbid') badges.push('<span class="badge badge-forced-feature" title="Git-Commits strikt verboten"><i class="fa fa-code-fork"></i> Verboten</span>');
|
|
if (agent.useSoul) badges.push('<span class="badge badge-feature" title="Hat eine Seele (SOUL.md)"><i class="fa fa-heart"></i> Seele</span>');
|
|
if (agent.useIdentity) badges.push('<span class="badge badge-feature" title="Hat eine Identität (IDENTITY.md)"><i class="fa fa-id-badge"></i> Ich</span>');
|
|
if (agent.useUserInfo) badges.push('<span class="badge badge-feature" title="Kennt sein Gegenüber (Benutzer-Persona)"><i class="fa fa-user"></i> Du</span>');
|
|
if (agent.useMemory) badges.push('<span class="badge badge-feature" title="Zugriff auf Erinnerungen (Memory-Tools)"><i class="fa fa-database"></i> Erinnerungen</span>');
|
|
if (agent.webSearchVersion) {
|
|
const wsYear = agent.webSearchVersion.includes('2025') ? 'Version 2025' : 'v2026';
|
|
badges.push(`<span class="badge badge-feature" title="Web-Suche - ${wsYear}"><i class="fa fa-search"></i> Web-Suche</span>`);
|
|
}
|
|
|
|
if (skills) {
|
|
for (const skill of skills) {
|
|
badges.push(`<span class="badge badge-skill" title="${esc(skill.description || skill.slug)}"><i class="fa fa-puzzle-piece"></i> ${esc(skill.name)}</span>`);
|
|
}
|
|
}
|
|
|
|
card.innerHTML = `
|
|
<div class="card-image">${imageHtml}</div>
|
|
<div class="card-body">
|
|
<div class="card-title-row">
|
|
<span class="card-title">${esc(agent.name)}</span>
|
|
<span class="badge">${prettyModelName(agent.model)}</span>
|
|
</div>
|
|
${badges.length ? `<div class="card-badges">${badges.join('')}</div>` : ''}
|
|
</div>
|
|
`;
|
|
|
|
return card;
|
|
}
|