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
|
.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
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/joho/godotenv v1.5.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
|
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/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 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
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 h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs=
|
||||||
github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
|
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() {
|
func WaitUntilNextUpdate() {
|
||||||
now := time.Now()
|
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))
|
log.Println("sleeping for", dur.Round(time.Second))
|
||||||
time.Sleep(dur)
|
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() {
|
func main() {
|
||||||
var oldRss *Rss
|
defer db.Close()
|
||||||
for {
|
for {
|
||||||
rss, err := FetchStackerNewsRssFeed()
|
rss, err := FetchStackerNewsRssFeed()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -35,9 +23,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range rss.Channel.Items {
|
for _, item := range rss.Channel.Items {
|
||||||
// TODO: We ignore errors during sending items to telegram.
|
if ItemExists(item) {
|
||||||
// This means these items will be missed since they will be skipped in the next run
|
log.Printf("item %s already exists. skipping.\n", item.Guid)
|
||||||
if contains(oldRss, item) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err := SendItemToTelegram(&item)
|
err := SendItemToTelegram(&item)
|
||||||
@ -45,9 +32,9 @@ func main() {
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
SaveItem(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldRss = rss
|
|
||||||
WaitUntilNextUpdate()
|
WaitUntilNextUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
rss.go
5
rss.go
@ -14,6 +14,7 @@ type Item struct {
|
|||||||
Link string `xml:"link"`
|
Link string `xml:"link"`
|
||||||
Description string `xml:"description"`
|
Description string `xml:"description"`
|
||||||
PubDate RssDate `xml:"pubDate"`
|
PubDate RssDate `xml:"pubDate"`
|
||||||
|
Author Author `xml:"author"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
@ -32,6 +33,10 @@ type RssDate struct {
|
|||||||
time.Time
|
time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Author struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RssDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (c *RssDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
var v string
|
var v string
|
||||||
dateFormat := "Mon, 02 Jan 2006 15:04:05 GMT"
|
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 {
|
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
|
linkItem := item.Link != item.Guid
|
||||||
if linkItem {
|
if linkItem {
|
||||||
text += fmt.Sprintf("[Link](%s) ", item.Link)
|
text += fmt.Sprintf("[Link](%s) ", item.Link)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user