From c8681ba96cd6853812cd28b15088cedfe145739f Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Sun, 5 Apr 2020 21:28:18 +0200 Subject: [PATCH] Add initial version --- .gitignore | 1 + Dockerfile | 13 ++++++++ Feed.go | 35 +++++++++++++++++++++ JsonFeedToTelegram.go | 73 +++++++++++++++++++++++++++++++++++++++++++ Telegram.go | 31 ++++++++++++++++++ go.mod | 3 ++ 6 files changed, 156 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Feed.go create mode 100644 JsonFeedToTelegram.go create mode 100644 Telegram.go create mode 100644 go.mod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..33f8240 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.14-alpine as build +ADD . /app +WORKDIR /app +RUN go build + +FROM alpine:3.11 +RUN apk add --no-cache tzdata ca-certificates +COPY --from=build /app/JsonFeedToTelegram /bin/ +WORKDIR /app +VOLUME /app/storage +EXPOSE 8080 +ENV LAST_ARTICLE_FILE /app/storage/lastarticle +CMD ["JsonFeedToTelegram"] \ No newline at end of file diff --git a/Feed.go b/Feed.go new file mode 100644 index 0000000..7707462 --- /dev/null +++ b/Feed.go @@ -0,0 +1,35 @@ +package main + +import ( + "encoding/json" + "errors" + "net/http" +) + +type Article struct { + Title string `json:"title"` + Url string `json:"url"` +} + +func LatestArticle(url string) (*Article, error) { + jsonFeed := &struct { + Items []Article `json:"items"` + }{} + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, errors.New("failed to create req to get json feed") + } + req.Header.Add("User-Agent", "JsonFeedToTelegram") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, errors.New("failed to get json feed") + } + err = json.NewDecoder(resp.Body).Decode(&jsonFeed) + if err != nil { + return nil, errors.New("failed to parse json feed") + } + if len(jsonFeed.Items) < 1 { + return nil, errors.New("no articles in feed") + } + return &jsonFeed.Items[0], nil +} diff --git a/JsonFeedToTelegram.go b/JsonFeedToTelegram.go new file mode 100644 index 0000000..75291d6 --- /dev/null +++ b/JsonFeedToTelegram.go @@ -0,0 +1,73 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "time" +) + +func main() { + lastArticleFile, lastArticleFileSet := os.LookupEnv("LAST_ARTICLE_FILE") + feed, feedSet := os.LookupEnv("FEED") + botToken, botTokenSet := os.LookupEnv("BOT_TOKEN") + channel, channelSet := os.LookupEnv("CHANNEL") + if lastArticleFileSet && feedSet && botTokenSet && channelSet { + telegram := Telegram{botToken: botToken, channel: channel} + http.HandleFunc("/hook", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Wrong HTTP method", http.StatusMethodNotAllowed) + return + } + fmt.Println("Fetch feed: ", time.Now().Format(time.RFC3339)) + article, err := LatestArticle(feed) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if lastArticle := lastArticleUrl(lastArticleFile); lastArticle != article.Url { + err = telegram.Post(createMessage(article)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = updateLastArticleUrl(lastArticleFile, article.Url) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } else { + http.Error(w, errors.New("no new article").Error(), http.StatusInternalServerError) + return + } + }) + log.Fatal(http.ListenAndServe(":8080", nil)) + } else { + log.Fatal("Not configured") + } +} + +func lastArticleUrl(filename string) string { + fileContent, _ := ioutil.ReadFile(filename) + return string(fileContent) +} + +func updateLastArticleUrl(filename, url string) error { + return ioutil.WriteFile(filename, []byte(url), 0644) +} + +func createMessage(article *Article) string { + var message bytes.Buffer + message.WriteString("🔔 Something new was published") + message.WriteString("\n\n") + if article.Title != "" { + message.WriteString(article.Title) + message.WriteString("\n\n") + } + message.WriteString(article.Url) + return message.String() +} diff --git a/Telegram.go b/Telegram.go new file mode 100644 index 0000000..63bfa74 --- /dev/null +++ b/Telegram.go @@ -0,0 +1,31 @@ +package main + +import ( + "errors" + "net/http" + "net/url" +) + +type Telegram struct { + channel string + botToken string +} + +var telegramBaseUrl = "https://api.telegram.org/bot" + +func (t *Telegram) Post(message string) error { + params := url.Values{} + params.Add("chat_id", t.channel) + params.Add("text", message) + tgUrl, err := url.Parse(telegramBaseUrl + t.botToken + "/sendMessage") + if err != nil { + return errors.New("failed to create Telegram request") + } + tgUrl.RawQuery = params.Encode() + req, _ := http.NewRequest(http.MethodPost, tgUrl.String(), nil) + resp, err := http.DefaultClient.Do(req) + if err != nil || resp.StatusCode != 200 { + return errors.New("failed to send Telegram message") + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..655f63a --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.jlel.se/jlelse/JsonFeedToTelegram + +go 1.14