176 lines
3.7 KiB
Go
176 lines
3.7 KiB
Go
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)
|
|
}
|