diff options
| -rw-r--r-- | flatbot.go | 103 | ||||
| -rw-r--r-- | sent.go | 3 | 
2 files changed, 91 insertions, 15 deletions
| @@ -1,46 +1,119 @@  package main  import ( +	"flag"  	"fmt"  	"io"  	"log"  	"net/http"  	"os" +	"time"  ) +var ( +	apiToken = flag.String("api-token", +		os.Getenv("FLATBOT_TELEGRAM_BOT_API_TOKEN"), +		"Telegram Bot API token") +	chatID = flag.String("chat-id", +		os.Getenv("FLATBOT_TELEGRAM_CHAT_ID"), +		"Telegram chat id where to send notification messages") +	state = flag.String("state", "/tmp/flatbot-sent.json", +		"Filename to save and load already sent flats") +	interval = flag.Duration("frequency", 5*time.Minute, +		"Frequency interval to fetch new data") +	once   = flag.Bool("once", false, "Run fetch and message loop only once") +	dryRun = flag.Bool("dry-run", false, +		"Run entire flow, but print new flats to stdout instead of "+ +			"sending them to Telegram") +) + +func usage() { +	fmt.Fprintf(os.Stderr, +		"usage: flatbot [-api-token token] [-chat-id id] "+ +			"[-state file] [-interval duration] [-once] [-send] "+ +			"URL...\n") +	flag.PrintDefaults() +	os.Exit(2) +} +  func main() { -	url := "http://localhost:8000/2025-02-19-isle-of-dogs.html" -	body, err := fetch(url) +	flag.Usage = usage +	flag.Parse() +	if !*dryRun { +		if len(*apiToken) == 0 { +			fmt.Fprintf(os.Stderr, +				"Going to send messages to Telegram, "+ +					"but no API token was provided\n") +			os.Exit(1) +		} +		if len(*chatID) == 0 { +			fmt.Fprintf(os.Stderr, +				"Going to send messages to Telegram, "+ +					"but no chat id was provided\n") +			os.Exit(1) +		} +	} +	if flag.NArg() == 0 { +		usage() +		os.Exit(1) +	} + +	for { +		err := loopOnce() +		if err != nil { +			log.Fatal(err) +		} +		if *once { +			break +		} +		log.Printf("Going to sleep for %v", *interval) +		time.Sleep(*interval) +	} +} + +func loopOnce() error { +	sent, err := readSent(*state)  	if err != nil { -		log.Fatal(err) +		return err  	} -	fetched, err := parse(body) +	for _, url := range flag.Args() { +		sent, err = doOneURL(url, sent) +		if err != nil { +			log.Print(err) +			continue +		} +	} +	// TODO: trim sent file here? +	return writeSent(sent, *state) +} + +func doOneURL(url string, sent []flat) ([]flat, error) { +	body, err := fetch(url)  	if err != nil { -		log.Fatal(err) +		return sent, err  	} -	sent, err := readSent("sent/sent.json") +	fetched, err := parse(body)  	if err != nil { -		log.Fatal(err) +		return sent, err  	}  	newFlats := removeAlreadySent(fetched, sent)  	m := messenger{ -		Token:  os.Getenv("FLATBOT_TELEGRAM_BOT_API_TOKEN"), -		ChatID: os.Getenv("FLATBOT_TELEGRAM_CHANNEL_ID"), +		Token:  *apiToken, +		ChatID: *chatID,  	}  	for _, f := range newFlats { -		if false { +		if !*dryRun {  			err = m.Send(f)  			if err != nil { -				// TODO: what to do with it?  				log.Print(err) +				continue  			}  		} +		log.Printf("Should have been sent to chat: %v, %v", +			f.Price, f.URL())  		sent = append(sent, f)  	} -	// Remove flats from sent that are no longer in the search response to -	// prevent indefinite grow of sent file. -	sent = removeDelisted(sent, fetched) -	writeSent(sent, "/tmp/sent.json") +	return sent, nil  }  func fetch(url string) ([]byte, error) { @@ -54,6 +54,9 @@ func removeDelisted(sent []flat, allFlats []flat) []flat {  }  func writeSent(sent []flat, filename string) error { +	if !slices.IsSortedFunc(sent, compareID) { +		slices.SortFunc(sent, compareID) +	}  	jsonData, err := json.Marshal(sent)  	if err != nil {  		return err |