first commit
This commit is contained in:
commit
12ef16cbee
11 changed files with 280 additions and 0 deletions
20
Dockerfile
Normal file
20
Dockerfile
Normal file
|
@ -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"]
|
125
app/app.go
Normal file
125
app/app.go
Normal file
|
@ -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
|
||||||
|
}
|
25
app/config.go
Normal file
25
app/config.go
Normal file
|
@ -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
|
||||||
|
}
|
20
go.mod
Normal file
20
go.mod
Normal file
|
@ -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
|
||||||
|
)
|
35
go.sum
Normal file
35
go.sum
Normal file
|
@ -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=
|
9
internal/rss/config.go
Normal file
9
internal/rss/config.go
Normal file
|
@ -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"`
|
||||||
|
}
|
6
internal/telegram/config.go
Normal file
6
internal/telegram/config.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package telegram
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
BotToken string `env:"TELEGRAM_TOKEN"`
|
||||||
|
TargetGroups []int64 `env:"TELEGRAM_GROUPS"`
|
||||||
|
}
|
28
main.go
Normal file
28
main.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
1
store/seq.txt
Normal file
1
store/seq.txt
Normal file
|
@ -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/
|
6
templates/fs.go
Normal file
6
templates/fs.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed *.gotmpl
|
||||||
|
var Templates embed.FS
|
5
templates/telegram.gotmpl
Normal file
5
templates/telegram.gotmpl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "telegram"}}
|
||||||
|
<b>Новый пост в блоге Уголок NeonXP:</b>
|
||||||
|
|
||||||
|
<a href="{{.Link}}">{{.Title}}</a>
|
||||||
|
{{end}}
|
Loading…
Reference in a new issue