- Introduced a new sync pipeline for IMAP that integrates a rules engine and webhook execution. - Enhanced the `SyncWorker` to support attachment management and folder synchronization. - Added functionality to detect special folder types (Sent, Drafts, Trash, Archive, Spam) during sync. - Implemented a database schema for tracking rule executions and their outcomes. - Created unit tests for the new rules engine and webhook execution logic. - Updated migration scripts to accommodate new database structures for rule executions and folder states. - Enhanced error handling and logging throughout the sync process for better observability.
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package imap
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/emersion/go-imap/v2"
|
|
)
|
|
|
|
var folderDisplayNames = map[string]string{
|
|
"inbox": "Inbox",
|
|
"sent": "Sent",
|
|
"drafts": "Drafts",
|
|
"trash": "Trash",
|
|
"archive": "Archive",
|
|
"spam": "Spam",
|
|
}
|
|
|
|
var folderNameHeuristics = map[string]string{
|
|
"inbox": "inbox",
|
|
"sent": "sent",
|
|
"sent items": "sent",
|
|
"sent messages": "sent",
|
|
"[gmail]/sent mail": "sent",
|
|
"inbox.sent": "sent",
|
|
"drafts": "drafts",
|
|
"[gmail]/drafts": "drafts",
|
|
"trash": "trash",
|
|
"deleted": "trash",
|
|
"[gmail]/trash": "trash",
|
|
"archive": "archive",
|
|
"all mail": "archive",
|
|
"[gmail]/all mail": "archive",
|
|
"junk": "spam",
|
|
"spam": "spam",
|
|
"[gmail]/spam": "spam",
|
|
}
|
|
|
|
// DetectFolderType classifies an IMAP mailbox for sync using SPECIAL-USE
|
|
// attributes when present, otherwise name heuristics. NoSelect mailboxes
|
|
// return "" so callers can skip sync.
|
|
func DetectFolderType(mailbox string, attrs []imap.MailboxAttr) string {
|
|
for _, attr := range attrs {
|
|
if attr == imap.MailboxAttrNoSelect {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
if folderType := folderTypeFromAttrs(attrs); folderType != "" {
|
|
return folderType
|
|
}
|
|
|
|
return folderTypeFromName(mailbox)
|
|
}
|
|
|
|
// DisplayName returns a user-facing folder label from the detected type,
|
|
// or the last path segment of mailbox when type is custom or unknown.
|
|
func DisplayName(mailbox, folderType string) string {
|
|
folderType = strings.ToLower(strings.TrimSpace(folderType))
|
|
if name, ok := folderDisplayNames[folderType]; ok {
|
|
return name
|
|
}
|
|
return mailboxLeaf(mailbox)
|
|
}
|
|
|
|
func folderTypeFromAttrs(attrs []imap.MailboxAttr) string {
|
|
for _, attr := range attrs {
|
|
switch attr {
|
|
case imap.MailboxAttrSent:
|
|
return "sent"
|
|
case imap.MailboxAttrDrafts:
|
|
return "drafts"
|
|
case imap.MailboxAttrTrash:
|
|
return "trash"
|
|
case imap.MailboxAttrArchive:
|
|
return "archive"
|
|
case imap.MailboxAttrJunk:
|
|
return "spam"
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func folderTypeFromName(mailbox string) string {
|
|
full := strings.ToLower(strings.TrimSpace(mailbox))
|
|
if folderType, ok := folderNameHeuristics[full]; ok {
|
|
return folderType
|
|
}
|
|
|
|
leaf := strings.ToLower(mailboxLeaf(mailbox))
|
|
if folderType, ok := folderNameHeuristics[leaf]; ok {
|
|
return folderType
|
|
}
|
|
|
|
return "custom"
|
|
}
|
|
|
|
func mailboxLeaf(mailbox string) string {
|
|
mailbox = strings.TrimSpace(mailbox)
|
|
if mailbox == "" {
|
|
return ""
|
|
}
|
|
|
|
leaf := mailbox
|
|
for _, sep := range []string{"/", "."} {
|
|
if idx := strings.LastIndex(leaf, sep); idx >= 0 && idx < len(leaf)-len(sep) {
|
|
leaf = leaf[idx+len(sep):]
|
|
}
|
|
}
|
|
return leaf
|
|
}
|