package main import ( "database/sql" "flag" "fmt" "log" "os" "os/signal" "sync" "syscall" "time" _ "github.com/joho/godotenv/autoload" _ "github.com/mattn/go-sqlite3" ) var ( runStart = time.Now() logPath = runStart.Format("2006-01-02.15:04:05") + ".gomeshalerter.json" logFile *os.File logData []LogEntry logBuffer *LogRingBuffer db *sql.DB logMutex sync.Mutex // Mutex for thread-safe logging ) func main() { fmt.Fprintf(os.Stderr, "[%s] starting gomeshalerter\n", runStart.Format("15:04:05")) setupLogging() defer flushLog() var err error db, err = sql.Open("sqlite3", dbPath+"?_journal=WAL") // Use WAL mode for better concurrency if err != nil { log.Fatalf("Failed to open database: %v", err) } // Initialize log buffer logBuffer = NewLogRingBuffer(MAX_LOG_ENTRIES) // Define a cleanup function to properly close resources cleanup := func() { fmt.Fprintf(os.Stderr, "[shutdown] Closing database...\n") if err := db.Close(); err != nil { fmt.Fprintf(os.Stderr, "[shutdown] Error closing database: %v\n", err) } flushLog() fmt.Fprintf(os.Stderr, "[shutdown] Cleanup complete\n") } // Ensure cleanup runs on normal exit defer cleanup() if err := setupDatabase(); err != nil { log.Fatalf("Failed to setup database: %v", err) } ollamaModel := "qwen3:32b" ollamaURL := os.Getenv("OLLAMA_URL") if ollamaURL == "" { ollamaURL = "http://localhost:11434" // Default Ollama server URL } fmt.Fprintf(os.Stderr, "[ollama] Using model: %s at %s\n", ollamaModel, ollamaURL) // Replace --broadcast flag with --dry-run flag (default is to broadcast) dryRun := flag.Bool("dry-run", false, "don't actually send to meshtastic, just print what would be sent") flag.Parse() // Create a WaitGroup to manage goroutines var wg sync.WaitGroup // Create a channel to signal shutdown shutdown := make(chan struct{}) // Set up signal handling for immediate exit with cleanup sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigChan fmt.Fprintf(os.Stderr, "[shutdown] Received signal, performing cleanup before exit...\n") cleanup() fmt.Fprintf(os.Stderr, "[shutdown] Exiting...\n") os.Exit(0) // Exit after cleanup }() // Start RSS feed checker goroutine wg.Add(1) go func() { defer wg.Done() rssFeedChecker(shutdown, ollamaURL, ollamaModel) }() // Start article summarizer goroutine wg.Add(1) go func() { defer wg.Done() articleSummarizer(shutdown, ollamaURL, ollamaModel) }() // Start broadcaster goroutine wg.Add(1) go func() { defer wg.Done() broadcaster(shutdown, *dryRun) }() // Start web server goroutine wg.Add(1) go func() { defer wg.Done() webServer(shutdown) }() // Wait for all goroutines to finish wg.Wait() fmt.Fprintf(os.Stderr, "[shutdown] All goroutines stopped, exiting...\n") }