feat: initial commit – Frankenbot Multi-Agent Orchestration System

- Flask Web-App mit Dashboard, Chat, Orchestrator, Tasks, Dateien, Emails, Agenten, Settings
- Email-Poller (IMAP) mit SQLite-Journal als Failsafe (kein Emailverlust bei Absturz)
- Failsafe-Fenster und Poll-Intervall zur Laufzeit via /settings konfigurierbar
- TaskWorker: IMAP Seen-Flag erst nach erfolgreichem Task-Abschluss
- Whitelist-Filter: eric.fischer, p.dyderski, georg.tschare (gmail + signtime.media), *@diversityball.at
- 9 Agenten: researcher, tax_advisor, document_editor, location_manager, program_manager,
  catering_manager, musik_rechte_advisor, zusammenfasser, orchestration_ui
- Diversity Ball Wien 2026 – Wissensdatenbank, Sponsoringverträge, Email-Vorlagen
This commit is contained in:
Pjot 2026-02-20 17:31:16 +01:00
commit 56d9bc2c76
71 changed files with 5953 additions and 0 deletions

119
templates/settings.html Normal file
View file

@ -0,0 +1,119 @@
{% extends "base.html" %}
{% block title %}Einstellungen{% endblock %}
{% block content %}
<div class="page-header mb-4">
<h1 class="page-title">Einstellungen</h1>
<p class="page-subtitle text-muted">Poller-Konfiguration &amp; System-Status</p>
</div>
<div class="row g-4">
<!-- Poller-Einstellungen -->
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">
<span class="me-2"></span> Email-Poller
</div>
<div class="card-body">
<form method="POST">
<div class="mb-4">
<label class="form-label fw-semibold">Poll-Intervall <span class="text-muted fw-normal">(Sekunden)</span></label>
<input type="number" class="form-control" name="poll_interval"
value="{{ poller_settings.poll_interval }}" min="10" max="3600" required>
<div class="form-text">Wie oft der Poller den IMAP-Eingang prüft. Aktuell: <strong>{{ poller_settings.poll_interval }}s</strong> ({{ (poller_settings.poll_interval / 60) | round(1) }} min)</div>
</div>
<div class="mb-4">
<label class="form-label fw-semibold">Failsafe-Fenster <span class="text-muted fw-normal">(Sekunden)</span></label>
<input type="number" class="form-control" name="failsafe_window"
value="{{ poller_settings.failsafe_window }}" min="30" max="86400" required>
<div class="form-text">
Wie lange ein Task laufen darf bevor er als hängend gilt und erneut verarbeitet wird.
Aktuell: <strong>{{ poller_settings.failsafe_window }}s</strong> ({{ (poller_settings.failsafe_window / 60) | round(1) }} min).
<span class="text-warning">Muss größer als das Poll-Intervall sein.</span>
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">Speichern</button>
<a href="/settings" class="btn btn-outline-secondary">Zurücksetzen</a>
</div>
</form>
</div>
</div>
</div>
<!-- Journal-Status -->
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">
<span class="me-2">📋</span> Email-Journal Status
</div>
<div class="card-body">
{% if journal_stats %}
<table class="table table-sm">
<thead>
<tr>
<th>Status</th>
<th class="text-end">Anzahl</th>
</tr>
</thead>
<tbody>
{% for status, count in journal_stats.items() %}
<tr>
<td>
{% if status == 'completed' %}
<span class="badge" style="background:var(--success)">✓ {{ status }}</span>
{% elif status == 'queued' %}
<span class="badge" style="background:var(--warning);color:#000">⏳ {{ status }}</span>
{% elif status == 'skipped' %}
<span class="badge" style="background:var(--text-muted)">— {{ status }}</span>
{% elif status == 'error' %}
<span class="badge" style="background:var(--danger)">✗ {{ status }}</span>
{% else %}
<span class="badge bg-secondary">{{ status }}</span>
{% endif %}
</td>
<td class="text-end fw-semibold">{{ count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">Noch keine Einträge im Journal.</p>
{% endif %}
<hr class="my-3" style="border-color:var(--border)">
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Journal-Datenbank: <code>email_journal.db</code></small>
<form method="POST" action="/settings/journal-clear"
onsubmit="return confirm('Alle abgeschlossenen Journal-Einträge löschen?')">
<button type="submit" class="btn btn-sm btn-outline-danger">Abgeschlossene löschen</button>
</form>
</div>
</div>
</div>
</div>
<!-- Whitelist -->
<div class="col-12">
<div class="card">
<div class="card-header">
<span class="me-2">🔒</span> Email-Whitelist
</div>
<div class="card-body">
<p class="text-muted mb-2">Nur Emails von diesen Absendern werden verarbeitet (aktuell hardcoded in <code>app.py</code>):</p>
<div class="d-flex flex-wrap gap-2">
{% set whitelist = ['eric.fischer@signtime.media', 'p.dyderski@live.at', 'georg.tschare@gmail.com', 'georg.tschare@signtime.media'] %}
{% for addr in whitelist %}
<code class="px-2 py-1 rounded" style="background:var(--bg-elevated);color:var(--accent-light)">{{ addr }}</code>
{% endfor %}
<code class="px-2 py-1 rounded" style="background:var(--bg-elevated);color:var(--accent2)">*@diversityball.at</code>
</div>
</div>
</div>
</div>
</div>
{% endblock %}