From 632a6c253a76356fd413ac77bccec8ab56da0c57 Mon Sep 17 00:00:00 2001 From: pdyde Date: Sat, 21 Feb 2026 14:01:41 +0100 Subject: [PATCH] feat: Team-Member Lernsystem & Automatische Vorstellung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Team-Member Updates: - Korrigierte Rollen und Verantwortlichkeiten: * Eric Fischer: Lead Programming & Tech * Georg Tschare: CEO & Kundenkommunikation * Piotr Dyderski: SysAdmin, TechLead, 3D Artist & R&D Neue Funktionen: - update_team_member() - Aktualisiert Team-Member-Daten per Email/Name - is_known_team_member() - Prüft ob Email in Team-DB existiert - @UPDATE_TEAM_MEMBER Kommando für Orchestrator - @ADD_TEAM_MEMBER Kommando für Orchestrator Intelligentes Lernsystem: - Erkennt neue Email-Absender automatisch - Leitet unbekannte Absender automatisch an Orchestrator - Orchestrator bittet um Vorstellung (Name, Rolle, Aufgaben) - Informationen werden in Team-DB gespeichert - Verbessert zukünftige Koordination Email-Workflow für neue Absender: 1. Email von unbekanntem @diversityball.at kommt an 2. System erkennt: "Nicht in Team-DB" 3. Orchestrator übernimmt automatisch 4. Fragt freundlich nach Vorstellung 5. Beantwortet auch die eigentliche Anfrage 6. Speichert Infos mit @ADD_TEAM_MEMBER Vorteile: ✓ Orchestrator baut automatisch Wissensdatenbank auf ✓ Bessere Koordination durch bekannte Verantwortlichkeiten ✓ Keine manuellen Team-Member Einträge nötig ✓ Freundlicher, professioneller Erstkontakt ✓ Lernfähiges System das besser wird Context-Weiterleitung: - extra_context aus Tasks wird jetzt durchgereicht - Kombiniert Email-Kontext + Team-Member-Status - Agents haben volle Informationen über Absender --- app.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/app.py b/app.py index 6d65331..8368537 100644 --- a/app.py +++ b/app.py @@ -515,6 +515,23 @@ TelegramID: [Telegram-ID des Team-Members] Message: [Nachricht] @END +**Team-Member Informationen aktualisieren:** +@UPDATE_TEAM_MEMBER +Identifier: [Email oder Name des Team-Members] +Role: [Neue Rolle] (optional) +Responsibilities: [Neue Verantwortlichkeiten] (optional) +TelegramID: [Telegram-ID] (optional) +Phone: [Telefon] (optional) +@END + +**Neuen Team-Member hinzufügen:** +@ADD_TEAM_MEMBER +Name: [Vollständiger Name] +Role: [Rolle/Position] +Responsibilities: [Verantwortlichkeiten] +Email: [Email-Adresse] +@END + Der Orchestrator kümmert sich um die Zuweisung und Kommunikation! ## Wichtig: @@ -746,6 +763,47 @@ def add_team_member(name, role, responsibilities='', email='', telegram_id=None, return member_id +def update_team_member(identifier, **kwargs): + """Aktualisiert ein Team-Mitglied (per Email oder Name).""" + con = sqlite3.connect(EMAIL_JOURNAL_DB) + + # Finde Member per Email oder Name + member = con.execute( + "SELECT id FROM team_members WHERE email = ? OR name = ? LIMIT 1", + (identifier, identifier) + ).fetchone() + + if not member: + con.close() + logging.warning(f"[DB] Team-Member '{identifier}' nicht gefunden für Update") + return False + + member_id = member[0] + + # Baue UPDATE query + updates = [] + params = [] + + for key, value in kwargs.items(): + if key in ['name', 'role', 'responsibilities', 'email', 'telegram_id', 'phone', 'active']: + updates.append(f"{key} = ?") + params.append(value) + + if not updates: + con.close() + return False + + params.append(member_id) + query = f"UPDATE team_members SET {', '.join(updates)} WHERE id = ?" + + con.execute(query, params) + con.commit() + con.close() + + logging.info(f"[DB] Team-Member '{identifier}' aktualisiert") + return True + + def get_team_member_summary(): """Erstellt eine Text-Zusammenfassung aller Team-Members für den Orchestrator.""" members = get_team_members(active_only=True) @@ -754,6 +812,8 @@ def get_team_member_summary(): return "Keine Team-Mitglieder definiert." summary = "## Reale Team-Mitglieder:\n\n" + summary += "Du kannst Team-Member-Informationen jederzeit mit @UPDATE_TEAM_MEMBER aktualisieren!\n\n" + for member in members: summary += f"**{member['name']}** ({member['role']})\n" if member['responsibilities']: @@ -1172,6 +1232,22 @@ def is_whitelisted(sender_address): return False +def is_known_team_member(sender_address): + """Prüft ob Absender ein bekanntes Team-Mitglied ist.""" + match = re.search(r'<([^>]+)>', sender_address) + addr = match.group(1).lower() if match else sender_address.lower().strip() + + # Prüfe ob Email in Team-Members DB + con = sqlite3.connect(EMAIL_JOURNAL_DB) + result = con.execute( + "SELECT name, role FROM team_members WHERE LOWER(email) = ? AND active = 1", + (addr,) + ).fetchone() + con.close() + + return result # Returns (name, role) or None + + def decode_email_header_value(value): """Dekodiert Email-Header (z.B. encoded Subject/From).""" if not value: @@ -1487,9 +1563,39 @@ def poll_emails(): addr_match = re.search(r'<([^>]+)>', sender) sender_email = addr_match.group(1) if addr_match else sender.strip() - # Agent bestimmen - full_prompt_for_routing = f"{subject} {body}" - agent_key = delegate_to_agent(full_prompt_for_routing) + # Prüfe ob Absender bekannt ist (in Team-Members DB) + known_member = is_known_team_member(sender) + + # Wenn unbekannt: Bitte um Vorstellung + extra_context = "" + if not known_member: + extra_context = f""" + +## ⚠️ WICHTIG - Neuer Absender: +Diese Email kommt von **{sender}** - diese Person ist noch nicht in der Team-Datenbank! + +Bitte in deiner Antwort: +1. Freundlich begrüßen +2. Um Vorstellung bitten: Name, Rolle, Verantwortlichkeiten +3. Fragen was sie im Projekt macht +4. Dann ihre eigentliche Anfrage beantworten + +Der Orchestrator wird die Informationen dann mit @ADD_TEAM_MEMBER oder @UPDATE_TEAM_MEMBER in die Datenbank eintragen. +""" + else: + extra_context = f""" + +## Team-Member erkannt: +Diese Email kommt von **{known_member[0]}** ({known_member[1]}) +""" + + # Agent bestimmen - bei unbekannten Sendern immer Orchestrator + if not known_member: + agent_key = 'orchestrator' + logger.info("[EmailPoller] Unbekannter Absender → Orchestrator übernimmt") + else: + full_prompt_for_routing = f"{subject} {body}" + agent_key = delegate_to_agent(full_prompt_for_routing) # Journal: Status 'queued' – \Seen wird NICHT gesetzt (erst nach Verarbeitung) journal_insert(message_id, email_id.decode(), sender, subject, 'queued', agent_key) @@ -1511,6 +1617,7 @@ def poll_emails(): 'original_body': body, 'message_id': message_id, # für Journal-Update durch TaskWorker 'imap_uid': email_id.decode(), + 'extra_context': extra_context, # Für unbekannte Absender } tasks.append(task) with task_queue_lock: @@ -1570,14 +1677,19 @@ Bitte bearbeite diese Anfrage vollständig: 3. Formuliere eine vollständige, hilfreiche Antwort 4. Die Antwort wird automatisch an {sender_email} zurückgeschickt""" - extra_context = f""" + email_context = f""" ## Email-Kontext: - Diese Anfrage kommt per Email von: {sender_email} - Die Antwort wird automatisch per Email zurückgeschickt - Formuliere die Antwort daher als Email-Text (freundlich, professionell) - Wenn gebeten wird, eine Email an jemanden zu schicken: gib die Adresse in der Form "An: adresse@example.com" oder "To: adresse@example.com" an""" - agent_response = execute_agent_task(agent_key, full_prompt, extra_context=extra_context) + # Kombiniere Email-Kontext mit Task-Kontext (z.B. unbekannter Absender) + combined_context = email_context + if task.get('extra_context'): + combined_context += "\n\n" + task['extra_context'] + + agent_response = execute_agent_task(agent_key, full_prompt, extra_context=combined_context) # Zusätzliche Empfänger aus der Agenten-Antwort extrahieren extra_recipients = re.findall( @@ -2762,24 +2874,24 @@ def init_default_team_members(): if len(existing) == 0: add_team_member( name="Eric Fischer", - role="Projektleiter & Event-Koordinator", - responsibilities="Gesamtkoordination, Budget-Verwaltung, Location-Management", + role="Lead Programming & Tech", + responsibilities="Technische Entwicklung, Programmierung, System-Architektur", email="eric.fischer@signtime.media", - telegram_id=None, # Wird bei Bedarf ergänzt + telegram_id=None, phone="" ) add_team_member( name="Georg Tschare", - role="Content & Marketing", - responsibilities="Marketing, Social Media, Presse, Kommunikation", + role="CEO & Kundenkommunikation", + responsibilities="Geschäftsführung, Kundenkontakt, Kommunikation, Strategie", email="georg.tschare@signtime.media", telegram_id=None, phone="" ) add_team_member( name="Piotr Dyderski", - role="System-Administrator & Tech-Lead", - responsibilities="Technische Infrastruktur, AI-Agenten, Automatisierung", + role="SysAdmin, TechLead, 3D Artist & R&D", + responsibilities="System-Administration, Technische Leitung, 3D-Visualisierung, AI-Agenten, Research & Development", email="p.dyderski@live.at", telegram_id=None, phone=""