zwischenstand
This commit is contained in:
@@ -3,9 +3,12 @@ package tool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"my-brain-importer/internal/agents"
|
||||
"my-brain-importer/internal/agents/tool/email"
|
||||
"my-brain-importer/internal/brain"
|
||||
"my-brain-importer/internal/config"
|
||||
)
|
||||
|
||||
// Agent verteilt Tool-Anfragen an spezialisierte Sub-Agenten.
|
||||
@@ -41,8 +44,14 @@ func (a *Agent) handleEmail(req agents.Request) agents.Response {
|
||||
result, err = email.SummarizeUnread()
|
||||
case agents.ActionEmailRemind:
|
||||
result, err = email.ExtractReminders()
|
||||
case agents.ActionEmailIngest:
|
||||
return a.handleEmailIngest(req)
|
||||
case agents.ActionEmailMove:
|
||||
return a.handleEmailMove(req)
|
||||
case agents.ActionEmailTriage:
|
||||
return a.handleEmailTriage()
|
||||
default:
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Unbekannte Email-Aktion `%s`. Verfügbar: summary, unread, remind", subAction)}
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Unbekannte Email-Aktion `%s`. Verfügbar: summary, unread, remind, ingest, move, triage", subAction)}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -50,3 +59,144 @@ func (a *Agent) handleEmail(req agents.Request) agents.Response {
|
||||
}
|
||||
return agents.Response{Text: "📧 **Email-Analyse:**\n\n" + result}
|
||||
}
|
||||
|
||||
// handleEmailIngest importiert Emails aus einem IMAP-Ordner in Qdrant.
|
||||
// Args[1] = Ordnername (Standard: "Archiv")
|
||||
func (a *Agent) handleEmailIngest(req agents.Request) agents.Response {
|
||||
folder := "Archiv"
|
||||
if len(req.Args) > 1 && req.Args[1] != "" {
|
||||
folder = req.Args[1]
|
||||
}
|
||||
|
||||
accounts := config.AllEmailAccounts()
|
||||
if len(accounts) == 0 {
|
||||
return agents.Response{Text: "❌ Kein Email-Account konfiguriert."}
|
||||
}
|
||||
|
||||
total := 0
|
||||
var errs []string
|
||||
for _, acc := range accounts {
|
||||
n, err := brain.IngestEmailFolder(acc, folder, 500)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", acc.Name, err))
|
||||
continue
|
||||
}
|
||||
total += n
|
||||
}
|
||||
|
||||
if len(errs) > 0 && total == 0 {
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Email-Ingest fehlgeschlagen:\n%s", joinLines(errs))}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("✅ **Email-Ingest abgeschlossen:** %d Emails aus `%s` in die Wissensdatenbank importiert.", total, folder)
|
||||
if len(errs) > 0 {
|
||||
msg += "\n⚠️ Fehler bei einigen Accounts:\n" + joinLines(errs)
|
||||
}
|
||||
return agents.Response{Text: msg}
|
||||
}
|
||||
|
||||
// handleEmailMove verschiebt alle ungelesenen Emails in einen konfigurierten Archivordner.
|
||||
// Args[1] = Zielordner-Name (aus archive_folders in config oder Legacy: 2Jahre/5Jahre/Archiv)
|
||||
func (a *Agent) handleEmailMove(req agents.Request) agents.Response {
|
||||
if len(req.Args) < 2 || req.Args[1] == "" {
|
||||
return agents.Response{Text: "❌ Zielordner fehlt. " + buildMoveFoldersHint()}
|
||||
}
|
||||
dest := req.Args[1]
|
||||
|
||||
imapFolder, ok := resolveArchiveFolder(dest)
|
||||
if !ok {
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Unbekannter Ordner `%s`. %s", dest, buildMoveFoldersHint())}
|
||||
}
|
||||
|
||||
accounts := config.AllEmailAccounts()
|
||||
if len(accounts) == 0 {
|
||||
return agents.Response{Text: "❌ Kein Email-Account konfiguriert."}
|
||||
}
|
||||
|
||||
total := 0
|
||||
var errs []string
|
||||
for _, acc := range accounts {
|
||||
n, err := email.MoveUnread(acc, imapFolder)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", acc.Name, err))
|
||||
continue
|
||||
}
|
||||
total += n
|
||||
}
|
||||
|
||||
if len(errs) > 0 && total == 0 {
|
||||
return agents.Response{Text: fmt.Sprintf("❌ Verschieben fehlgeschlagen:\n%s", joinLines(errs))}
|
||||
}
|
||||
if total == 0 {
|
||||
return agents.Response{Text: fmt.Sprintf("📭 Keine ungelesenen Emails zum Verschieben nach `%s`.", imapFolder)}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("✅ **%d Email(s)** nach `%s` verschoben.", total, imapFolder)
|
||||
if len(errs) > 0 {
|
||||
msg += "\n⚠️ Fehler:\n" + joinLines(errs)
|
||||
}
|
||||
return agents.Response{Text: msg}
|
||||
}
|
||||
|
||||
// handleEmailTriage klassifiziert die letzten 10 Emails aller Accounts und verschiebt sie.
|
||||
func (a *Agent) handleEmailTriage() agents.Response {
|
||||
result, err := email.TriageRecentAllAccounts(10)
|
||||
if err != nil {
|
||||
return agents.Response{Error: err, Text: fmt.Sprintf("❌ Triage fehlgeschlagen: %v", err)}
|
||||
}
|
||||
return agents.Response{Text: "🗂️ **Email-Triage (letzte 10 Emails):**\n\n" + result}
|
||||
}
|
||||
|
||||
// ResolveArchiveFolder ist die exportierte Version von resolveArchiveFolder für den Discord-Layer.
|
||||
func ResolveArchiveFolder(name string) (imapFolder string, ok bool) {
|
||||
return resolveArchiveFolder(name)
|
||||
}
|
||||
|
||||
// resolveArchiveFolder sucht den IMAP-Ordnernamen für einen Anzeigenamen aus der Config.
|
||||
// Fallback: Legacy-Hardcoding für 2Jahre/5Jahre/Archiv wenn keine archive_folders konfiguriert.
|
||||
func resolveArchiveFolder(name string) (imapFolder string, ok bool) {
|
||||
for _, acc := range config.AllEmailAccounts() {
|
||||
for _, af := range acc.ArchiveFolders {
|
||||
if strings.EqualFold(af.Name, name) || strings.EqualFold(af.IMAPFolder, name) {
|
||||
return af.IMAPFolder, true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Legacy-Fallback für Konfigurationen ohne archive_folders
|
||||
legacy := map[string]string{
|
||||
"2jahre": "2Jahre",
|
||||
"5jahre": "5Jahre",
|
||||
"archiv": "Archiv",
|
||||
}
|
||||
if canonical, found := legacy[strings.ToLower(name)]; found {
|
||||
return canonical, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// buildMoveFoldersHint gibt eine Hinweis-Nachricht mit verfügbaren Archivordnern zurück.
|
||||
func buildMoveFoldersHint() string {
|
||||
seen := map[string]bool{}
|
||||
var names []string
|
||||
for _, acc := range config.AllEmailAccounts() {
|
||||
for _, af := range acc.ArchiveFolders {
|
||||
key := strings.ToLower(af.Name)
|
||||
if !seen[key] {
|
||||
seen[key] = true
|
||||
names = append(names, fmt.Sprintf("`%s`", af.Name))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return "Verfügbar: `2Jahre`, `5Jahre`, `Archiv`"
|
||||
}
|
||||
return fmt.Sprintf("Verfügbar: %s", strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
func joinLines(lines []string) string {
|
||||
result := ""
|
||||
for _, l := range lines {
|
||||
result += "• " + l + "\n"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user