func newFlightFetcher(searches []*app.FlightSearch) app.FlightsFetcher { return func() ([]*model.Flight, error) { swClient := client.NewClient(nil) allFlights := make([]*model.Flight, 0) for _, search := range searches { filters := make([]client.FlightFilter, 0) if search.MaxFareCents != nil { filters = append(filters, &client.MaxAvailableFareFilter{*search.MaxFareCents}) } if search.MaxNumberStops != nil { filters = append(filters, &client.MaxStopsFilter{int(*search.MaxNumberStops)}) } flights, err := swClient.SearchFlights( search.OriginAirports, search.DestinationAirports, search.MinDepartureTime, search.MaxArrivalTime, filters) if err != nil { return nil, err } allFlights = append(allFlights, flights...) } return allFlights, nil } }
func main() { searchesFlag := flag.String(searchesFlagStr, "", "filename of flight searches JSON") fromFlag := flag.String(fromFlagStr, "", "email address from which updates are sent") toFlag := flag.String(toFlagStr, "", "email address to which updates are sent") smtpFlag := flag.String(smtpFlagStr, "", fmt.Sprintf("SMTP host for mail delivery. Port %i is used.", smtpPort)) smtpPasswordFileFlag := flag.String(smtpPasswordFileStr, "", "File containing SMTP password. Must have 0700 permissions.") // Check all the flags flag.Parse() if *searchesFlag == "" { fmt.Fprintf(os.Stderr, "%v flag must be set\n", searchesFlagStr) return } if *fromFlag == "" { fmt.Fprintf(os.Stderr, "%v flag must be set\n", fromFlagStr) return } if *smtpFlag == "" { fmt.Fprintf(os.Stderr, "%v flag must be set\n", smtpFlagStr) return } if *smtpPasswordFileFlag == "" { fmt.Fprintf(os.Stderr, "%v flag must be set\n", smtpPasswordFileStr) return } // Load SMTP password password, err := loadPassword(*smtpPasswordFileFlag) if err != nil { fmt.Fprintf(os.Stderr, "Unable to load password from %v: %v\n", *smtpPasswordFileFlag, err) return } searches, err := app.FlightSearchesFromFile(*searchesFlag) if err != nil { fmt.Fprintf(os.Stderr, "Invalid searches file contents: %v\n", err) return } // Set up channel for OS signals, which are used to shutdown the app sigChannel := make(chan os.Signal, 1) signal.Notify(sigChannel, os.Interrupt, os.Kill) // Create an email notifier to := *toFlag if len(to) == 0 { to = *fromFlag } emailNotifier := &app.EmailFlightsNotifier{ SmtpAddress: fmt.Sprintf("%v:%v", *smtpFlag, smtpPort), Auth: smtp.PlainAuth("", *fromFlag, password, *smtpFlag), From: *fromFlag, To: to, } notifier := app.SearchUpdateNotifierChain{&app.StdoutNotifier{}, emailNotifier} // Create container for state with the function to update it state := app.NewSearchStateUpdater(searches, app.NewFlightFetcher(client.NewClient(nil)), notifier) logger := log.New(os.Stdout, "", log.LstdFlags) updater := func() { logger.Print("Updating flights") if err := state.Update(); err != nil { logger.Printf("Error updating flights: %v\n", err) } } // Run immediately, and then every hour // TODO(alec): Determine if sharing state variable across goroutines is bad in this case ticker := time.NewTicker(1 * time.Hour) go func() { for _ = range ticker.C { updater() } }() updater() // Keep running until signal to shutdown <-sigChannel ticker.Stop() }