Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4865e2e300 | ||
|
|
35c7baa854 | ||
|
|
0e1a3fe25d | ||
|
|
2693ef2378 | ||
|
|
21364b5c35 | ||
|
|
c96d4a8be2 | ||
|
|
0b64e64ceb |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.env
|
||||
stackernews_rss-to-tg
|
||||
sn-rss2tg
|
||||
sn-rss2tg.sqlite3
|
||||
|
||||
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@ -1,15 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "sn-rss-to-tg",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
69
db.go
Normal file
69
db.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
db, err = sql.Open("sqlite3", "sn-rss2tg.sqlite3")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
migrate(db)
|
||||
}
|
||||
|
||||
func migrate(db *sql.DB) {
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
link TEXT NOT NULL,
|
||||
pub_date TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error during migration: %w", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func extractIdFromItem(item Item) int {
|
||||
parts := strings.Split(item.Guid, "/")
|
||||
id, err := strconv.Atoi(parts[len(parts)-1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error during item id extraction: %w", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func ItemExists(item Item) bool {
|
||||
id := extractIdFromItem(item)
|
||||
var count int
|
||||
err := db.QueryRow(`SELECT COUNT(1) FROM items WHERE id = ?`, id).Scan(&count)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error during item check: %w", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func SaveItem(item Item) {
|
||||
id := extractIdFromItem(item)
|
||||
_, err := db.Exec(`INSERT INTO items(id, title, link, pub_date) VALUES (?, ?, ?, ?)`, id, item.Title, item.Link, item.PubDate.Time)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error during item insert: %w", err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
3
go.mod
3
go.mod
@ -1,9 +1,10 @@
|
||||
module gitlab.com/ekzyis/stackernews_rss-to-tg
|
||||
module gitlab.com/ekzyis/sn-rss2tg
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/namsral/flag v1.7.4-pre // indirect
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@ -2,5 +2,7 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs=
|
||||
github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
|
||||
|
||||
23
main.go
23
main.go
@ -7,25 +7,13 @@ import (
|
||||
|
||||
func WaitUntilNextUpdate() {
|
||||
now := time.Now()
|
||||
dur := now.Truncate(time.Hour).Add(time.Hour).Sub(now)
|
||||
dur := now.Truncate(time.Minute).Add(time.Minute).Sub(now)
|
||||
log.Println("sleeping for", dur.Round(time.Second))
|
||||
time.Sleep(dur)
|
||||
}
|
||||
|
||||
func contains(rss *Rss, a Item) bool {
|
||||
if rss == nil {
|
||||
return false
|
||||
}
|
||||
for _, x := range rss.Channel.Items {
|
||||
if x.Title == a.Title {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
var oldRss *Rss
|
||||
defer db.Close()
|
||||
for {
|
||||
rss, err := FetchStackerNewsRssFeed()
|
||||
if err != nil {
|
||||
@ -35,9 +23,8 @@ func main() {
|
||||
}
|
||||
|
||||
for _, item := range rss.Channel.Items {
|
||||
// TODO: We ignore errors during sending items to telegram.
|
||||
// This means these items will be missed since they will be skipped in the next run
|
||||
if contains(oldRss, item) {
|
||||
if ItemExists(item) {
|
||||
log.Printf("item %s already exists. skipping.\n", item.Guid)
|
||||
continue
|
||||
}
|
||||
err := SendItemToTelegram(&item)
|
||||
@ -45,9 +32,9 @@ func main() {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
SaveItem(item)
|
||||
}
|
||||
|
||||
oldRss = rss
|
||||
WaitUntilNextUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
5
rss.go
5
rss.go
@ -14,6 +14,7 @@ type Item struct {
|
||||
Link string `xml:"link"`
|
||||
Description string `xml:"description"`
|
||||
PubDate RssDate `xml:"pubDate"`
|
||||
Author Author `xml:"author"`
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
@ -32,6 +33,10 @@ type RssDate struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
type Author struct {
|
||||
Name string `xml:"name"`
|
||||
}
|
||||
|
||||
func (c *RssDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
var v string
|
||||
dateFormat := "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
|
||||
2
tg.go
2
tg.go
@ -33,7 +33,7 @@ func init() {
|
||||
}
|
||||
|
||||
func SendItemToTelegram(item *Item) error {
|
||||
text := fmt.Sprintf("%s (%s)\n", item.Title, humanize.Time(item.PubDate.Time))
|
||||
text := fmt.Sprintf("%s\n_%s by_ [%s](https://stacker.news/%s)\n", item.Title, humanize.Time(item.PubDate.Time), item.Author.Name, item.Author.Name)
|
||||
linkItem := item.Link != item.Guid
|
||||
if linkItem {
|
||||
text += fmt.Sprintf("[Link](%s) ", item.Link)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user