示例#1
0
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()
}