This commit is contained in:
orejav
2025-07-28 03:03:24 +03:00
commit b65c0956d2
10 changed files with 2092 additions and 0 deletions

500
cleanup.go Normal file
View File

@@ -0,0 +1,500 @@
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"sort"
"strings"
)
type CleanupRecommendations struct {
SafeToDelete []DeleteRecommendation `json:"safe_to_delete"`
BulkArchive []ArchiveRecommendation `json:"bulk_archive"`
UnsubscribeTargets []UnsubscribeTarget `json:"unsubscribe_targets"`
AttentionNeeded []AttentionItem `json:"attention_needed"`
StorageStats StorageStats `json:"storage_stats"`
}
type DeleteRecommendation struct {
Category string `json:"category"`
Query string `json:"query"`
Count int `json:"estimated_count"`
Description string `json:"description"`
Risk string `json:"risk"`
Examples []string `json:"examples"`
}
type ArchiveRecommendation struct {
Category string `json:"category"`
Query string `json:"query"`
Count int `json:"estimated_count"`
Description string `json:"description"`
Reason string `json:"reason"`
}
type UnsubscribeTarget struct {
Sender string `json:"sender"`
Count int `json:"count"`
LastEmail string `json:"last_email"`
Category string `json:"category"`
Priority string `json:"priority"`
Reason string `json:"reason"`
}
type AttentionItem struct {
Category string `json:"category"`
Query string `json:"query"`
Count int `json:"count"`
Description string `json:"description"`
Action string `json:"recommended_action"`
}
type StorageStats struct {
TotalEmails int `json:"total_emails"`
DeletionPotential int `json:"deletion_potential"`
ArchivePotential int `json:"archive_potential"`
PercentageReduction float64 `json:"percentage_reduction"`
CategoryBreakdown map[string]int `json:"category_breakdown"`
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run cleanup.go <analysis_json_file> [output_format]")
fmt.Println(" analysis_json_file: JSON output from analyze.go")
fmt.Println(" output_format: summary (default), detailed, or json")
os.Exit(1)
}
jsonFile := os.Args[1]
outputFormat := "summary"
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)
}
recommendations := generateCleanupRecommendations(stats)
switch outputFormat {
case "summary":
printSummary(recommendations)
case "detailed":
printDetailed(recommendations)
case "json":
outputJSON(recommendations)
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 generateCleanupRecommendations(stats EmailStats) CleanupRecommendations {
recommendations := CleanupRecommendations{
SafeToDelete: []DeleteRecommendation{},
BulkArchive: []ArchiveRecommendation{},
UnsubscribeTargets: []UnsubscribeTarget{},
AttentionNeeded: []AttentionItem{},
StorageStats: StorageStats{
TotalEmails: stats.TotalEmails,
CategoryBreakdown: stats.Categories,
},
}
// Generate safe delete recommendations
recommendations.SafeToDelete = generateDeleteRecommendations(stats)
// Generate bulk archive recommendations
recommendations.BulkArchive = generateArchiveRecommendations(stats)
// Generate unsubscribe targets
recommendations.UnsubscribeTargets = generateUnsubscribeTargets(stats)
// Generate attention needed items
recommendations.AttentionNeeded = generateAttentionItems(stats)
// Calculate storage impact
recommendations.StorageStats = calculateStorageImpact(stats, recommendations)
return recommendations
}
func generateDeleteRecommendations(stats EmailStats) []DeleteRecommendation {
var recommendations []DeleteRecommendation
// Old promotional emails
if count, ok := stats.Categories["newsletters"]; ok && count > 50 {
recommendations = append(recommendations, DeleteRecommendation{
Category: "Old Newsletters",
Query: `subject:(newsletter OR weekly OR monthly) older_than:6m`,
Count: count / 2, // Estimate half are old
Description: "Newsletter emails older than 6 months",
Risk: "Low",
Examples: []string{"Weekly digest emails", "Monthly newsletters", "Company updates"},
})
}
// Old social media notifications
if socialCount := countSocialEmails(stats.TopDomains); socialCount > 30 {
recommendations = append(recommendations, DeleteRecommendation{
Category: "Social Media Notifications",
Query: `from:(facebook OR twitter OR linkedin OR instagram) older_than:2m`,
Count: socialCount * 2 / 3, // Estimate 2/3 are old
Description: "Social media notifications older than 2 months",
Risk: "Low",
Examples: []string{"Facebook notifications", "Twitter alerts", "LinkedIn updates"},
})
}
// Old promotional emails
promotionalCount := 0
for _, pattern := range stats.SubjectPatterns {
if pattern.Pattern == "promotional" {
promotionalCount = pattern.Count
break
}
}
if promotionalCount > 20 {
recommendations = append(recommendations, DeleteRecommendation{
Category: "Old Promotions",
Query: `subject:(sale OR deal OR offer OR discount) older_than:3m`,
Count: promotionalCount * 3 / 4, // Estimate 3/4 are old
Description: "Promotional/sales emails older than 3 months",
Risk: "Low",
Examples: []string{"Sales announcements", "Discount offers", "Flash sales"},
})
}
// Automated system emails
automatedCount := countAutomatedEmails(stats.TopDomains)
if automatedCount > 40 {
recommendations = append(recommendations, DeleteRecommendation{
Category: "Old System Notifications",
Query: `from:(noreply OR no-reply) subject:(notification OR alert) older_than:1y`,
Count: automatedCount / 2,
Description: "System notifications and alerts older than 1 year",
Risk: "Medium",
Examples: []string{"System alerts", "Automated notifications", "Service updates"},
})
}
return recommendations
}
func generateArchiveRecommendations(stats EmailStats) []ArchiveRecommendation {
var recommendations []ArchiveRecommendation
// Archive newsletters
if count, ok := stats.Categories["newsletters"]; ok && count > 30 {
recommendations = append(recommendations, ArchiveRecommendation{
Category: "All Newsletters",
Query: `from:(noreply OR no-reply) OR subject:(newsletter OR unsubscribe)`,
Count: count,
Description: "Move all newsletter-type emails out of inbox",
Reason: "Newsletters rarely require immediate action",
})
}
// Archive social media
if count, ok := stats.Categories["social"]; ok && count > 20 {
recommendations = append(recommendations, ArchiveRecommendation{
Category: "Social Media",
Query: `from:(facebook OR twitter OR linkedin OR instagram OR youtube)`,
Count: count,
Description: "Move social media notifications out of inbox",
Reason: "Social notifications can be checked on the platforms directly",
})
}
// Archive automated emails
automatedCount := countAutomatedEmails(stats.TopDomains)
if automatedCount > 25 {
recommendations = append(recommendations, ArchiveRecommendation{
Category: "Automated Emails",
Query: `from:(noreply OR no-reply OR automated)`,
Count: automatedCount,
Description: "Move automated system emails out of inbox",
Reason: "Automated emails are usually informational only",
})
}
return recommendations
}
func generateUnsubscribeTargets(stats EmailStats) []UnsubscribeTarget {
var targets []UnsubscribeTarget
// High-volume newsletter senders
for _, sender := range stats.TopSenders {
if sender.Count <= 10 {
break
}
priority := "medium"
reason := ""
category := ""
email := strings.ToLower(sender.Email)
if strings.Contains(email, "noreply") || strings.Contains(email, "no-reply") {
category = "automated"
if sender.Count > 50 {
priority = "high"
reason = "Very high volume automated sender"
} else if sender.Count > 25 {
priority = "medium"
reason = "High volume automated sender"
} else {
priority = "low"
reason = "Moderate volume automated sender"
}
} else if strings.Contains(email, "newsletter") || strings.Contains(email, "marketing") {
category = "newsletter"
priority = "medium"
reason = "Newsletter or marketing emails"
} else if containsSocialDomain(sender.Domain) {
category = "social"
priority = "low"
reason = "Social media notifications"
} else {
category = "other"
priority = "low"
reason = "High volume sender - review manually"
}
if sender.Count > 15 {
targets = append(targets, UnsubscribeTarget{
Sender: sender.Email,
Count: sender.Count,
Category: category,
Priority: priority,
Reason: reason,
})
}
}
// Sort by priority and count
sort.Slice(targets, func(i, j int) bool {
if targets[i].Priority != targets[j].Priority {
priorityOrder := map[string]int{"high": 3, "medium": 2, "low": 1}
return priorityOrder[targets[i].Priority] > priorityOrder[targets[j].Priority]
}
return targets[i].Count > targets[j].Count
})
return targets
}
func generateAttentionItems(stats EmailStats) []AttentionItem {
var items []AttentionItem
// High volume personal senders
for _, sender := range stats.TopSenders {
if sender.Count > 50 && !strings.Contains(strings.ToLower(sender.Email), "noreply") &&
!containsAutomatedKeywords(sender.Email) {
items = append(items, AttentionItem{
Category: "High Volume Personal",
Query: fmt.Sprintf(`from:%s`, sender.Email),
Count: sender.Count,
Description: fmt.Sprintf("Very high email volume from %s", sender.Email),
Action: "Review relationship or create filters",
})
}
}
// Old unread emails
items = append(items, AttentionItem{
Category: "Old Unread",
Query: `is:unread older_than:1m`,
Count: 0, // Would need additional analysis
Description: "Unread emails older than 1 month",
Action: "Review and either read, archive, or delete",
})
// Large attachments
items = append(items, AttentionItem{
Category: "Large Attachments",
Query: `has:attachment larger:10M`,
Count: 0, // Would need additional analysis
Description: "Emails with attachments larger than 10MB",
Action: "Review and download important files, then delete emails",
})
return items
}
func calculateStorageImpact(stats EmailStats, recommendations CleanupRecommendations) StorageStats {
deletionPotential := 0
archivePotential := 0
for _, rec := range recommendations.SafeToDelete {
deletionPotential += rec.Count
}
for _, rec := range recommendations.BulkArchive {
archivePotential += rec.Count
}
totalReduction := deletionPotential + archivePotential
percentageReduction := float64(totalReduction) / float64(stats.TotalEmails) * 100
return StorageStats{
TotalEmails: stats.TotalEmails,
DeletionPotential: deletionPotential,
ArchivePotential: archivePotential,
PercentageReduction: percentageReduction,
CategoryBreakdown: stats.Categories,
}
}
func countSocialEmails(domains []DomainInfo) int {
count := 0
for _, domain := range domains {
if domain.Type == "social" {
count += domain.Count
}
}
return count
}
func countAutomatedEmails(domains []DomainInfo) int {
count := 0
for _, domain := range domains {
if domain.Type == "automated" {
count += domain.Count
}
}
return count
}
func containsSocialDomain(domain string) bool {
socialDomains := []string{"facebook", "twitter", "linkedin", "instagram", "youtube", "tiktok"}
domain = strings.ToLower(domain)
for _, social := range socialDomains {
if strings.Contains(domain, social) {
return true
}
}
return false
}
func containsAutomatedKeywords(email string) bool {
keywords := []string{"noreply", "no-reply", "automated", "system", "admin"}
email = strings.ToLower(email)
for _, keyword := range keywords {
if strings.Contains(email, keyword) {
return true
}
}
return false
}
func printSummary(recommendations CleanupRecommendations) {
fmt.Printf("\n=== INBOX CLEANUP RECOMMENDATIONS ===\n\n")
stats := recommendations.StorageStats
fmt.Printf("📊 CURRENT STATE:\n")
fmt.Printf(" Total emails: %d\n", stats.TotalEmails)
fmt.Printf(" Potential for deletion: %d emails\n", stats.DeletionPotential)
fmt.Printf(" Potential for archiving: %d emails\n", stats.ArchivePotential)
fmt.Printf(" Total inbox reduction: %.1f%%\n\n", stats.PercentageReduction)
fmt.Printf("🗑️ SAFE TO DELETE (%d emails):\n", stats.DeletionPotential)
for i, rec := range recommendations.SafeToDelete {
fmt.Printf(" %d. %s (%d emails, %s risk)\n", i+1, rec.Category, rec.Count, rec.Risk)
fmt.Printf(" Query: %s\n", rec.Query)
}
fmt.Printf("\n📦 BULK ARCHIVE (%d emails):\n", stats.ArchivePotential)
for i, rec := range recommendations.BulkArchive {
fmt.Printf(" %d. %s (%d emails)\n", i+1, rec.Category, rec.Count)
fmt.Printf(" Query: %s\n", rec.Query)
}
fmt.Printf("\n✋ UNSUBSCRIBE TARGETS:\n")
for i, target := range recommendations.UnsubscribeTargets {
if i >= 5 {
fmt.Printf(" ... and %d more (use 'detailed' output for full list)\n", len(recommendations.UnsubscribeTargets)-5)
break
}
fmt.Printf(" %d. %s (%d emails, %s priority)\n", i+1, target.Sender, target.Count, target.Priority)
}
fmt.Printf("\n⚠ NEEDS ATTENTION:\n")
for i, item := range recommendations.AttentionNeeded {
fmt.Printf(" %d. %s\n", i+1, item.Description)
fmt.Printf(" Action: %s\n", item.Action)
}
fmt.Printf("\n💡 NEXT STEPS:\n")
fmt.Printf(" 1. Review deletion candidates (start with low-risk items)\n")
fmt.Printf(" 2. Set up bulk archive operations\n")
fmt.Printf(" 3. Unsubscribe from high-priority senders\n")
fmt.Printf(" 4. Create Gmail filters to prevent future buildup\n")
fmt.Printf(" 5. Address attention items\n\n")
fmt.Printf("Use 'detailed' output format for complete Gmail queries and instructions.\n")
}
func printDetailed(recommendations CleanupRecommendations) {
printSummary(recommendations)
fmt.Printf("\n=== DETAILED CLEANUP INSTRUCTIONS ===\n\n")
fmt.Printf("🗑️ DELETION INSTRUCTIONS:\n")
for i, rec := range recommendations.SafeToDelete {
fmt.Printf("\n%d. %s (%s risk)\n", i+1, rec.Category, rec.Risk)
fmt.Printf(" Gmail Query: %s\n", rec.Query)
fmt.Printf(" Description: %s\n", rec.Description)
fmt.Printf(" Estimated Count: %d emails\n", rec.Count)
fmt.Printf(" Examples: %s\n", strings.Join(rec.Examples, ", "))
fmt.Printf(" Instructions:\n")
fmt.Printf(" 1. Paste query into Gmail search\n")
fmt.Printf(" 2. Review a few emails to confirm they're safe to delete\n")
fmt.Printf(" 3. Select all → Delete\n")
}
fmt.Printf("\n📦 ARCHIVE INSTRUCTIONS:\n")
for i, rec := range recommendations.BulkArchive {
fmt.Printf("\n%d. %s\n", i+1, rec.Category)
fmt.Printf(" Gmail Query: %s\n", rec.Query)
fmt.Printf(" Description: %s\n", rec.Description)
fmt.Printf(" Reason: %s\n", rec.Reason)
fmt.Printf(" Estimated Count: %d emails\n", rec.Count)
fmt.Printf(" Instructions:\n")
fmt.Printf(" 1. Paste query into Gmail search\n")
fmt.Printf(" 2. Select all → Archive\n")
}
fmt.Printf("\n✋ UNSUBSCRIBE DETAILS:\n")
for i, target := range recommendations.UnsubscribeTargets {
fmt.Printf("\n%d. %s (%s priority)\n", i+1, target.Sender, target.Priority)
fmt.Printf(" Email Count: %d\n", target.Count)
fmt.Printf(" Category: %s\n", target.Category)
fmt.Printf(" Reason: %s\n", target.Reason)
fmt.Printf(" Gmail Query: from:%s\n", target.Sender)
}
}
func outputJSON(recommendations CleanupRecommendations) {
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
encoder.Encode(recommendations)
}