summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile20
-rw-r--r--app/app.go125
-rw-r--r--app/config.go25
-rw-r--r--go.mod20
-rw-r--r--go.sum35
-rw-r--r--internal/rss/config.go9
-rw-r--r--internal/telegram/config.go6
-rw-r--r--main.go28
-rw-r--r--store/seq.txt1
-rw-r--r--templates/fs.go6
-rw-r--r--templates/telegram.gotmpl5
11 files changed, 280 insertions, 0 deletions
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..828f625
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+# syntax=docker/dockerfile:1
+
+FROM golang:1.22.4
+
+WORKDIR /app
+
+COPY go.mod go.sum ./
+RUN go mod download
+
+COPY . .
+
+RUN CGO_ENABLED=0 GOOS=linux go build -o /rss2world
+
+ENV RSS_URL="https://neonxp.ru/feed.atom"
+ENV CHECK_INTERVAL="5m"
+ENV SEQ_FILE="/store/seq.txt"
+ENV TELEGRAM_TOKEN="279146841:AAE9Yd2W........_gAhbO3usM"
+ENV TELEGRAM_GROUPS="-1001003209449"
+
+CMD ["/rss2world"] \ No newline at end of file
diff --git a/app/app.go b/app/app.go
new file mode 100644
index 0000000..1ea0ee8
--- /dev/null
+++ b/app/app.go
@@ -0,0 +1,125 @@
+package app
+
+import (
+ "bytes"
+ "context"
+ "os"
+ "text/template"
+ "time"
+
+ tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
+ "github.com/mmcdole/gofeed"
+ "gitrepo.ru/neonxp/rss2world/templates"
+)
+
+type App struct {
+ config *Config
+ telegram *tgbotapi.BotAPI
+ templates *template.Template
+}
+
+func New(cfg *Config) (*App, error) {
+ bot, err := tgbotapi.NewBotAPI(cfg.Telegram.BotToken)
+ if err != nil {
+ return nil, err
+ }
+
+ tpl, err := template.ParseFS(templates.Templates, "*.gotmpl")
+ if err != nil {
+ return nil, err
+ }
+
+ return &App{
+ config: cfg,
+ telegram: bot,
+ templates: tpl,
+ }, nil
+}
+
+func (a *App) Run(ctx context.Context) error {
+ ticker := time.NewTicker(a.config.RSS.CheckInterval)
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-ticker.C:
+ if err := a.iteration(); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (a *App) iteration() error {
+
+ seq, err := a.readSeqFile()
+ if os.IsNotExist(err) {
+ seq = ""
+ err = nil
+ }
+ if err != nil {
+ return err
+ }
+
+ items, err := a.findNewItems(seq)
+ if err != nil || len(items) == 0 {
+ return err
+ }
+
+ for i := len(items) - 1; i >= 0; i-- {
+ if err := a.processItem(items[i]); err != nil {
+ return err
+ }
+ if err := a.writeSeqFile(items[i].GUID); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (a *App) processItem(item *gofeed.Item) error {
+ buf := bytes.NewBufferString("")
+ if err := a.templates.ExecuteTemplate(buf, "telegram", item); err != nil {
+ return err
+ }
+ for _, group := range a.config.Telegram.TargetGroups {
+ msg := tgbotapi.NewMessage(group, buf.String())
+ msg.ParseMode = tgbotapi.ModeHTML
+ if _, err := a.telegram.Send(msg); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (a *App) readSeqFile() (string, error) {
+ seqVal, err := os.ReadFile(a.config.RSS.SeqFile)
+ if err != nil {
+ return "", err
+ }
+
+ return string(seqVal), nil
+}
+
+func (a *App) writeSeqFile(seqVal string) error {
+ return os.WriteFile(a.config.RSS.SeqFile, []byte(seqVal), 0644)
+}
+
+func (a *App) findNewItems(from string) ([]*gofeed.Item, error) {
+ fp := gofeed.NewParser()
+ feed, err := fp.ParseURL(a.config.RSS.URL)
+ if err != nil {
+ return nil, err
+ }
+ out := make([]*gofeed.Item, 0, len(feed.Items))
+ for _, item := range feed.Items {
+ if item.GUID == from {
+ break
+ }
+ out = append(out, item)
+ }
+
+ return out, nil
+}
diff --git a/app/config.go b/app/config.go
new file mode 100644
index 0000000..66531aa
--- /dev/null
+++ b/app/config.go
@@ -0,0 +1,25 @@
+package app
+
+import (
+ "gitrepo.ru/neonxp/rss2world/internal/rss"
+ "gitrepo.ru/neonxp/rss2world/internal/telegram"
+
+ "github.com/caarlos0/env/v11"
+)
+
+type Config struct {
+ RSS *rss.Config
+ Telegram *telegram.Config
+}
+
+func NewConfig() (*Config, error) {
+ cfg := &Config{RSS: &rss.Config{}, Telegram: &telegram.Config{}}
+ if err := env.Parse(cfg.RSS); err != nil {
+ return nil, err
+ }
+ if err := env.Parse(cfg.Telegram); err != nil {
+ return nil, err
+ }
+
+ return cfg, nil
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ea89cc8
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,20 @@
+module gitrepo.ru/neonxp/rss2world
+
+go 1.22.4
+
+require (
+ github.com/caarlos0/env/v11 v11.1.0
+ github.com/mmcdole/gofeed v1.3.0
+)
+
+require (
+ github.com/PuerkitoBio/goquery v1.8.0 // indirect
+ github.com/andybalholm/cascadia v1.3.1 // indirect
+ github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ golang.org/x/net v0.4.0 // indirect
+ golang.org/x/text v0.5.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..1e4dd4c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,35 @@
+github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
+github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
+github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
+github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
+github.com/caarlos0/env/v11 v11.1.0 h1:a5qZqieE9ZfzdvbbdhTalRrHT5vu/4V1/ad1Ka6frhI=
+github.com/caarlos0/env/v11 v11.1.0/go.mod h1:LwgkYk1kDvfGpHthrWWLof3Ny7PezzFwS4QrsJdHTMo=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
+github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
+github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
+github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 h1:Zr92CAlFhy2gL+V1F+EyIuzbQNbSgP4xhTODZtrXUtk=
+github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
+golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/internal/rss/config.go b/internal/rss/config.go
new file mode 100644
index 0000000..e1fd34c
--- /dev/null
+++ b/internal/rss/config.go
@@ -0,0 +1,9 @@
+package rss
+
+import "time"
+
+type Config struct {
+ URL string `env:"RSS_URL"`
+ CheckInterval time.Duration `env:"CHECK_INTERVAL"`
+ SeqFile string `env:"SEQ_FILE"`
+}
diff --git a/internal/telegram/config.go b/internal/telegram/config.go
new file mode 100644
index 0000000..9fd6029
--- /dev/null
+++ b/internal/telegram/config.go
@@ -0,0 +1,6 @@
+package telegram
+
+type Config struct {
+ BotToken string `env:"TELEGRAM_TOKEN"`
+ TargetGroups []int64 `env:"TELEGRAM_GROUPS"`
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..017772e
--- /dev/null
+++ b/main.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+ "context"
+ "os"
+ "os/signal"
+
+ "gitrepo.ru/neonxp/rss2world/app"
+)
+
+func main() {
+ ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
+ defer cancel()
+
+ cfg, err := app.NewConfig()
+ if err != nil {
+ panic(err)
+ }
+
+ a, err := app.New(cfg)
+ if err != nil {
+ panic(err)
+ }
+
+ if err := a.Run(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/store/seq.txt b/store/seq.txt
new file mode 100644
index 0000000..aeb20ea
--- /dev/null
+++ b/store/seq.txt
@@ -0,0 +1 @@
+https://neonxp.ru/posts/2024/06/02/%D0%BA%D0%BD%D0%B8%D0%B6%D0%BD%D1%8B%D0%B5_%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B8_1/ \ No newline at end of file
diff --git a/templates/fs.go b/templates/fs.go
new file mode 100644
index 0000000..197a5e1
--- /dev/null
+++ b/templates/fs.go
@@ -0,0 +1,6 @@
+package templates
+
+import "embed"
+
+//go:embed *.gotmpl
+var Templates embed.FS
diff --git a/templates/telegram.gotmpl b/templates/telegram.gotmpl
new file mode 100644
index 0000000..9e2b91a
--- /dev/null
+++ b/templates/telegram.gotmpl
@@ -0,0 +1,5 @@
+{{define "telegram"}}
+<b>Новый пост в блоге Уголок NeonXP:</b>
+
+<a href="{{.Link}}">{{.Title}}</a>
+{{end}} \ No newline at end of file