package main import ( "encoding/json" "fmt" "log" "os" "strings" ) type GmailFilter struct { Name string `json:"name"` Criteria string `json:"criteria"` Actions string `json:"actions"` Description string `json:"description"` } type FilterSuggestions struct { Filters []GmailFilter `json:"filters"` UnsubscribeCandidates []string `json:"unsubscribe_candidates"` ArchiveCandidates []string `json:"archive_candidates"` SearchQueries []SearchQuery `json:"search_queries"` } type SearchQuery struct { Purpose string `json:"purpose"` Query string `json:"query"` Description string `json:"description"` } func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run filters.go [output_format]") fmt.Println(" analysis_json_file: JSON output from analyze.go") fmt.Println(" output_format: filters (default), queries, or all") os.Exit(1) } jsonFile := os.Args[1] outputFormat := "filters" if len(os.Args) > 2 { outputFormat = strings.ToLower(os.Args[2]) } stats, err := loadAnalysisFromJSON(jsonFile) if err != nil { log.Fatalf("Error loading analysis JSON: %v", err) } suggestions := generateFilterSuggestions(stats) switch outputFormat { case "filters": printFilters(suggestions.Filters) case "queries": printSearchQueries(suggestions.SearchQueries) case "all": printAllSuggestions(suggestions) case "json": outputJSON(suggestions) default: fmt.Printf("Unknown output format: %s\n", outputFormat) os.Exit(1) } } func loadAnalysisFromJSON(filename string) (EmailStats, error) { var stats EmailStats file, err := os.Open(filename) if err != nil { return stats, err } defer file.Close() decoder := json.NewDecoder(file) err = decoder.Decode(&stats) return stats, err } func generateFilterSuggestions(stats EmailStats) FilterSuggestions { suggestions := FilterSuggestions{ Filters: []GmailFilter{}, UnsubscribeCandidates: []string{}, ArchiveCandidates: []string{}, SearchQueries: []SearchQuery{}, } // Generate filters based on top domains suggestions.Filters = append(suggestions.Filters, generateDomainFilters(stats.TopDomains)...) // Generate filters based on subject patterns suggestions.Filters = append(suggestions.Filters, generatePatternFilters(stats.SubjectPatterns)...) // Generate filters based on categories suggestions.Filters = append(suggestions.Filters, generateCategoryFilters(stats.Categories)...) // Generate unsubscribe candidates suggestions.UnsubscribeCandidates = generateUnsubscribeCandidates(stats.TopSenders, stats.TopDomains) // Generate archive candidates suggestions.ArchiveCandidates = generateArchiveCandidates(stats.TopDomains) // Generate search queries suggestions.SearchQueries = generateSearchQueries(stats) return suggestions } func generateDomainFilters(domains []DomainInfo) []GmailFilter { var filters []GmailFilter for _, domain := range domains { if domain.Count < 10 { continue } switch domain.Type { case "social": filters = append(filters, GmailFilter{ Name: fmt.Sprintf("Auto-label %s", domain.Domain), Criteria: fmt.Sprintf("from:@%s", domain.Domain), Actions: "Apply label: Social, Skip inbox", Description: fmt.Sprintf("Auto-process social media emails from %s (%d emails)", domain.Domain, domain.Count), }) case "commerce": filters = append(filters, GmailFilter{ Name: fmt.Sprintf("Auto-label %s", domain.Domain), Criteria: fmt.Sprintf("from:@%s", domain.Domain), Actions: "Apply label: Shopping, Skip inbox", Description: fmt.Sprintf("Auto-process shopping emails from %s (%d emails)", domain.Domain, domain.Count), }) case "automated": filters = append(filters, GmailFilter{ Name: fmt.Sprintf("Auto-archive %s", domain.Domain), Criteria: fmt.Sprintf("from:@%s", domain.Domain), Actions: "Apply label: Automated, Skip inbox, Mark as read", Description: fmt.Sprintf("Auto-archive automated emails from %s (%d emails)", domain.Domain, domain.Count), }) } } return filters } func generatePatternFilters(patterns []PatternInfo) []GmailFilter { var filters []GmailFilter for _, pattern := range patterns { if pattern.Count < 5 { continue } switch pattern.Pattern { case "newsletter": filters = append(filters, GmailFilter{ Name: "Auto-label newsletters", Criteria: `subject:(newsletter OR weekly OR monthly OR digest)`, Actions: "Apply label: Newsletters, Skip inbox", Description: fmt.Sprintf("Auto-process newsletter emails (%d found)", pattern.Count), }) case "promotional": filters = append(filters, GmailFilter{ Name: "Auto-label promotions", Criteria: `subject:(sale OR deal OR offer OR discount OR "%" OR free)`, Actions: "Apply label: Promotions, Skip inbox", Description: fmt.Sprintf("Auto-process promotional emails (%d found)", pattern.Count), }) case "notification": filters = append(filters, GmailFilter{ Name: "Auto-label notifications", Criteria: `subject:(notification OR alert OR reminder)`, Actions: "Apply label: Notifications", Description: fmt.Sprintf("Auto-label notification emails (%d found)", pattern.Count), }) case "transactional": filters = append(filters, GmailFilter{ Name: "Auto-label receipts", Criteria: `subject:(receipt OR confirmation OR invoice OR payment)`, Actions: "Apply label: Receipts, Star it", Description: fmt.Sprintf("Auto-label important transactional emails (%d found)", pattern.Count), }) } } return filters } func generateCategoryFilters(categories map[string]int) []GmailFilter { var filters []GmailFilter for category, count := range categories { if count < 20 { continue } switch category { case "newsletters": filters = append(filters, GmailFilter{ Name: "Auto-handle newsletters", Criteria: `from:(noreply OR no-reply) OR subject:unsubscribe`, Actions: "Apply label: Newsletters, Skip inbox", Description: fmt.Sprintf("Auto-process newsletter-type emails (%d found)", count), }) case "work": filters = append(filters, GmailFilter{ Name: "Auto-label work tools", Criteria: `from:(slack.com OR github.com OR jira OR atlassian)`, Actions: "Apply label: Work", Description: fmt.Sprintf("Auto-label work-related tool emails (%d found)", count), }) case "finance": filters = append(filters, GmailFilter{ Name: "Auto-label finance", Criteria: `subject:(payment OR invoice OR receipt) OR from:(paypal OR stripe OR bank)`, Actions: "Apply label: Finance, Star it", Description: fmt.Sprintf("Auto-label financial emails (%d found)", count), }) } } return filters } func generateUnsubscribeCandidates(senders []SenderInfo, domains []DomainInfo) []string { var candidates []string // High-volume newsletter senders for _, sender := range senders { if sender.Count > 20 && (strings.Contains(strings.ToLower(sender.Email), "noreply") || strings.Contains(strings.ToLower(sender.Email), "no-reply") || strings.Contains(strings.ToLower(sender.Email), "newsletter")) { candidates = append(candidates, fmt.Sprintf("%s (%d emails)", sender.Email, sender.Count)) } } // High-volume automated domains for _, domain := range domains { if domain.Count > 30 && domain.Type == "automated" { candidates = append(candidates, fmt.Sprintf("All emails from @%s (%d emails)", domain.Domain, domain.Count)) } } return candidates } func generateArchiveCandidates(domains []DomainInfo) []string { var candidates []string for _, domain := range domains { if domain.Count > 50 && (domain.Type == "automated" || domain.Type == "social") { candidates = append(candidates, fmt.Sprintf("@%s (%d emails, %s)", domain.Domain, domain.Count, domain.Type)) } } return candidates } func generateSearchQueries(stats EmailStats) []SearchQuery { queries := []SearchQuery{ { Purpose: "Find old newsletters", Query: `subject:(newsletter OR weekly OR monthly) older_than:6m`, Description: "Find newsletters older than 6 months that can be safely deleted", }, { Purpose: "Find promotional emails", Query: `subject:(sale OR deal OR offer OR discount) older_than:3m`, Description: "Find promotional emails older than 3 months", }, { Purpose: "Find social notifications", Query: `from:(facebook OR twitter OR linkedin OR instagram) older_than:1m`, Description: "Find social media notifications older than 1 month", }, { Purpose: "Find automated emails", Query: `from:(noreply OR no-reply) older_than:1y`, Description: "Find automated emails older than 1 year", }, { Purpose: "Find large attachments", Query: `has:attachment larger:10M`, Description: "Find emails with attachments larger than 10MB", }, { Purpose: "Find unread old emails", Query: `is:unread older_than:1m`, Description: "Find unread emails older than 1 month that might need attention", }, } // Add domain-specific queries for top domains for i, domain := range stats.TopDomains { if i >= 3 || domain.Count < 50 { break } queries = append(queries, SearchQuery{ Purpose: fmt.Sprintf("Review %s emails", domain.Domain), Query: fmt.Sprintf(`from:@%s`, domain.Domain), Description: fmt.Sprintf("Review all %d emails from %s", domain.Count, domain.Domain), }) } return queries } func printFilters(filters []GmailFilter) { fmt.Printf("\n=== GMAIL FILTER SUGGESTIONS ===\n\n") fmt.Printf("To create these filters in Gmail:\n") fmt.Printf("1. Go to Gmail Settings > Filters and Blocked Addresses\n") fmt.Printf("2. Click 'Create a new filter'\n") fmt.Printf("3. Use the criteria below, then apply the suggested actions\n\n") for i, filter := range filters { fmt.Printf("%d. %s\n", i+1, filter.Name) fmt.Printf(" Criteria: %s\n", filter.Criteria) fmt.Printf(" Actions: %s\n", filter.Actions) fmt.Printf(" Description: %s\n\n", filter.Description) } } func printSearchQueries(queries []SearchQuery) { fmt.Printf("\n=== GMAIL SEARCH QUERIES ===\n\n") fmt.Printf("Copy and paste these queries into Gmail's search box:\n\n") for i, query := range queries { fmt.Printf("%d. %s\n", i+1, query.Purpose) fmt.Printf(" Query: %s\n", query.Query) fmt.Printf(" Description: %s\n\n", query.Description) } } func printAllSuggestions(suggestions FilterSuggestions) { printFilters(suggestions.Filters) fmt.Printf("\n=== UNSUBSCRIBE CANDIDATES ===\n") fmt.Printf("Consider unsubscribing from these high-volume senders:\n\n") for i, candidate := range suggestions.UnsubscribeCandidates { fmt.Printf("%d. %s\n", i+1, candidate) } fmt.Printf("\n=== BULK ARCHIVE CANDIDATES ===\n") fmt.Printf("Consider bulk archiving emails from these domains:\n\n") for i, candidate := range suggestions.ArchiveCandidates { fmt.Printf("%d. %s\n", i+1, candidate) } printSearchQueries(suggestions.SearchQueries) } func outputJSON(suggestions FilterSuggestions) { encoder := json.NewEncoder(os.Stdout) encoder.SetIndent("", " ") encoder.Encode(suggestions) }