History

a first

Sigrid Solveig Haflínudóttir committed 3 months ago

Commit 7c54a4aNo parent (first commit)6 files · +182 −0Browse files

Unified · Split

6 files
  1. .gitignore +1 −0
  2. LICENSE +1 −0
  3. README.md +14 −0
  4. go.mod +16 −0
  5. go.sum +37 −0
  6. main.go +113 −0
.gitignore+1−0
@@ -0,0 +1 @@
1+ircrss
LICENSE+1−0
@@ -0,0 +1 @@
1+Public domain.
README.md+14−0
@@ -0,0 +1,14 @@
1+# ircrss
2+
3+An extremely simple RSS-to-IRC bot.
4+
5+## Running
6+
7+Multiple URLs may be used.
8+
9+ ircrss -server irc.libera.chat \
10+ -channel '#my-channel' \
11+ -seen ~/my-channel-rss-links \
12+ -nick rssclanker \
13+ https://git.sr.ht/user/repo/log/rss.xml
14+ ...
go.mod+16−0
@@ -0,0 +1,16 @@
1+module git.sr.ht/~ft/ircrss
2+
3+go 1.26.5
4+
5+require (
6+ github.com/fluffle/goirc v1.3.5
7+ github.com/mmcdole/gofeed v1.4.0
8+)
9+
10+require (
11+ github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
12+ github.com/golang/mock v1.6.0 // indirect
13+ github.com/mmcdole/goxpp/v2 v2.0.0 // indirect
14+ golang.org/x/net v0.57.0 // indirect
15+ golang.org/x/text v0.40.0 // indirect
16+)
go.sum+37−0
@@ -0,0 +1,37 @@
1+github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
2+github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
3+github.com/fluffle/goirc v1.3.5 h1:MKs1hzIwKfquqsCYk/U1Z7OFlQzgTJss/2ZlWckJXgM=
4+github.com/fluffle/goirc v1.3.5/go.mod h1:u5jzFHvmESPQ6pzcT/OVSKKd/WOh5pKDM28XJn15cO0=
5+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
6+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
7+github.com/mmcdole/gofeed v1.4.0 h1:+efDmI/yJXJgTfa8we5zg9GAKsU+2d7tnpt9QZwvjLQ=
8+github.com/mmcdole/gofeed v1.4.0/go.mod h1:ngV5MTB7UJko6fH3/fG5AkB/ABUGK1ZTePF9iRhzu/c=
9+github.com/mmcdole/goxpp/v2 v2.0.0 h1:HrSCflxerUEqZQNq3u7ldtmE/XkwnTx4Zpq2DW4i5rQ=
10+github.com/mmcdole/goxpp/v2 v2.0.0/go.mod h1:CUduYMnO9JB6Z/uqDn9Ormk/r8E9BsLQxHPWDZ961Os=
11+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
12+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
13+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
14+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
15+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
16+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
17+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
18+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
19+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
20+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
21+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
22+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
23+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
24+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
25+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
26+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
27+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
28+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
29+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
30+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
31+golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
32+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
33+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
34+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
35+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
36+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
37+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
main.go+113−0
@@ -0,0 +1,113 @@
1+package main
2+
3+import (
4+ "bufio"
5+ "crypto/md5"
6+ "errors"
7+ "flag"
8+ "fmt"
9+ "io"
10+ "log"
11+ "os"
12+ "sort"
13+ "strings"
14+ "time"
15+
16+ irc "github.com/fluffle/goirc/client"
17+ "github.com/mmcdole/gofeed"
18+)
19+
20+var (
21+ server = flag.String("server", "", "IRC server")
22+ channel = flag.String("channel", "", "IRC channel")
23+ nick = flag.String("nick", "rssbot", "IRC nickname")
24+ period = flag.String("period", "10m", "Update period")
25+ seenPath = flag.String("seen", "", "\"Seen\" entries store")
26+)
27+
28+func main() {
29+ flag.Parse()
30+ if !flag.Parsed() || *server == "" || *channel == "" || *nick == "" || *seenPath == "" || len(flag.Args()) == 0 {
31+ flag.Usage()
32+ os.Exit(1)
33+ }
34+
35+ updatePeriod, err := time.ParseDuration(*period)
36+ if err != nil {
37+ fmt.Fprintln(os.Stderr, "update period: %s", err)
38+ os.Exit(1)
39+ }
40+
41+ seenFile, err := os.OpenFile(*seenPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
42+ if err != nil {
43+ fmt.Fprintln(os.Stderr, "seen file: %s", err)
44+ os.Exit(1)
45+ }
46+ defer seenFile.Close()
47+ seenReader := bufio.NewReader(seenFile)
48+ seen := make(map[[md5.Size]byte]struct{})
49+ for {
50+ if url, err := seenReader.ReadString('\n'); err != nil {
51+ if errors.Is(err, io.EOF) {
52+ break
53+ }
54+ fmt.Fprintln(os.Stderr, "seen file: %s", err)
55+ os.Exit(1)
56+ } else {
57+ url := []byte(strings.TrimSpace(url))
58+ seen[md5.Sum(url)] = struct{}{}
59+ }
60+ }
61+
62+ cfg := irc.NewConfig(*nick, "ircrssbot", "RSS bot")
63+ c := irc.Client(cfg)
64+ c.EnableStateTracking()
65+ c.HandleFunc(irc.CONNECTED, func(*irc.Conn, *irc.Line) {
66+ log.Printf("irc: connected")
67+ c.Join(*channel)
68+ })
69+ c.HandleFunc(irc.DISCONNECTED, func(*irc.Conn, *irc.Line) {
70+ log.Printf("irc: disconnected")
71+ os.Exit(1)
72+ })
73+
74+ joinedChan := make(chan bool, 1)
75+ c.HandleFunc(irc.JOIN, func(c *irc.Conn, line *irc.Line) {
76+ if c.Me().Nick == line.Nick {
77+ log.Printf("irc: joined")
78+ close(joinedChan)
79+ }
80+ })
81+ if err := c.ConnectTo(*server); err != nil {
82+ fmt.Fprintln(os.Stderr, "irc: %s", err)
83+ os.Exit(1)
84+ }
85+
86+ <-joinedChan
87+ for {
88+ for _, url := range flag.Args() {
89+ p := gofeed.NewParser()
90+ if feed, err := p.ParseURL(url); err != nil {
91+ log.Printf("%s: %s", url, err)
92+ } else {
93+ sort.Sort(feed)
94+ for _, item := range feed.Items {
95+ sum := md5.Sum([]byte(item.Link))
96+ if _, have := seen[sum]; !have {
97+ if _, err := fmt.Fprintln(seenFile, item.Link); err != nil {
98+ log.Printf("seen file: %s", err)
99+ os.Exit(1)
100+ }
101+ seenFile.Sync()
102+ s := fmt.Sprintf("%s | %s\n", item.Title, item.Link)
103+ c.Privmsgln(*channel, s)
104+ seen[sum] = struct{}{}
105+ }
106+ }
107+ }
108+ }
109+ time.Sleep(updatePeriod)
110+ }
111+ log.Printf("quitting")
112+ c.Quit()
113+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t