package main import ( "fmt" "sync" "time" ) // ANSI color codes const ( ColorReset = "\033[0m" ColorRed = "\033[31m" ColorGreen = "\033[32m" ColorYellow = "\033[33m" ColorBlue = "\033[34m" ColorPurple = "\033[35m" ColorCyan = "\033[36m" ColorWhite = "\033[37m" ColorBold = "\033[1m" ) // ProgressTracker tracks checking progress type ProgressTracker struct { Total int Checked int WithBalance int TotalValue float64 StartTime time.Time mu sync.Mutex } // NewProgressTracker creates a new progress tracker func NewProgressTracker(total int) *ProgressTracker { return &ProgressTracker{ Total: total, StartTime: time.Now(), } } // Increment increments progress counters func (pt *ProgressTracker) Increment(balance float64) { pt.mu.Lock() defer pt.mu.Unlock() pt.Checked++ pt.TotalValue += balance if balance > 0 { pt.WithBalance++ } } // GetStats returns current statistics func (pt *ProgressTracker) GetStats() (int, int, int, float64, time.Duration) { pt.mu.Lock() defer pt.mu.Unlock() elapsed := time.Since(pt.StartTime) return pt.Total, pt.Checked, pt.WithBalance, pt.TotalValue, elapsed } // CalculateETA calculates estimated time to completion func (pt *ProgressTracker) CalculateETA() string { pt.mu.Lock() defer pt.mu.Unlock() if pt.Checked == 0 { return "calculating..." } elapsed := time.Since(pt.StartTime) avgTimePerKey := elapsed / time.Duration(pt.Checked) remaining := pt.Total - pt.Checked eta := avgTimePerKey * time.Duration(remaining) if eta < time.Second { return "< 1s" } else if eta < time.Minute { return fmt.Sprintf("%ds", int(eta.Seconds())) } else if eta < time.Hour { mins := int(eta.Minutes()) secs := int(eta.Seconds()) % 60 return fmt.Sprintf("%dm %ds", mins, secs) } else { hours := int(eta.Hours()) mins := int(eta.Minutes()) % 60 return fmt.Sprintf("%dh %dm", hours, mins) } } type StatusDisplay struct { tracker *ProgressTracker stopChan chan bool wg sync.WaitGroup } // NewStatusDisplay creates new status display func NewStatusDisplay(tracker *ProgressTracker) *StatusDisplay { return &StatusDisplay{ tracker: tracker, stopChan: make(chan bool), } } // Start starts the display update loop func (sd *StatusDisplay) Start() { sd.wg.Add(1) go sd.updateLoop() } // Stop stops the display func (sd *StatusDisplay) Stop() { sd.stopChan <- true sd.wg.Wait() sd.clearLine() } func (sd *StatusDisplay) clearLine() { fmt.Print("\r\033[K") } func (sd *StatusDisplay) updateLoop() { defer sd.wg.Done() ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-sd.stopChan: return case <-ticker.C: sd.update() } } } func (sd *StatusDisplay) update() { total, checked, withBalance, totalValue, _ := sd.tracker.GetStats() eta := sd.tracker.CalculateETA() percentage := 0 if total > 0 { percentage = (checked * 100) / total } progressColor := ColorYellow if percentage >= 100 { progressColor = ColorGreen } else if percentage >= 75 { progressColor = ColorCyan } status := fmt.Sprintf("%s%s[%d%%]%s %s%d/%d%s checked | %s%d%s with balance | %s$%.2f%s total | ETA: %s%s%s", ColorBold, progressColor, percentage, ColorReset, ColorCyan, checked, total, ColorReset, ColorGreen, withBalance, ColorReset, ColorYellow, totalValue, ColorReset, ColorPurple, eta, ColorReset, ) sd.clearLine() fmt.Print("\r" + status) } func ColorPrint(color, text string) { fmt.Print(color + text + ColorReset) } func ColorPrintln(color, text string) { fmt.Println(color + text + ColorReset) } func colorPrint(color, text string) { fmt.Print(color + text + ColorReset) } func colorPrintln(color, text string) { fmt.Println(color + text + ColorReset) }