1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
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() {
flag.Usage = usage
flag.Parse()
if flag.NArg() == 0 {
usage()
os.Exit(1)
}
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)
}
}
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 {
return err
}
fetched := make([]flat, 0)
for _, url := range flag.Args() {
sent, fetched, err = doOneURL(url, sent, fetched)
if err != nil {
log.Print(err)
continue
}
}
sent = removeDelisted(sent, fetched)
return writeSent(sent, *state)
}
func doOneURL(url string, sent, fetched []flat) ([]flat, []flat, error) {
body, err := fetch(url)
if err != nil {
return sent, fetched, err
}
parsed, err := parse(body)
if err != nil {
return sent, fetched, err
}
fetched = append(fetched, parsed...)
newFlats := removeAlreadySent(parsed, sent)
m := messenger{
Token: *apiToken,
ChatID: *chatID,
}
for _, f := range newFlats {
if !*dryRun {
err = m.Send(f)
if err != nil {
log.Print(err)
continue
}
}
log.Printf("Should have been sent to chat: %v, %v",
f.Price, f.URL())
sent = append(sent, f)
}
return sent, fetched, nil
}
func fetch(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return make([]byte, 0), err
}
if resp.StatusCode != http.StatusOK {
return make([]byte, 0),
fmt.Errorf("Bad response status: %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return make([]byte, 0), err
}
return body, nil
}
|