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 @@
1ircrss
LICENSE+1−0
@@ -0,0 +1 @@
1Public domain.
README.md+14−0
@@ -0,0 +1,14 @@
1# ircrss
2
3An extremely simple RSS-to-IRC bot.
4
5## Running
6
7Multiple 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 @@
1module git.sr.ht/~ft/ircrss
2
3go 1.26.5
4
5require (
6 github.com/fluffle/goirc v1.3.5
7 github.com/mmcdole/gofeed v1.4.0
8)
9
10require (
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 @@
1github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
2github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
3github.com/fluffle/goirc v1.3.5 h1:MKs1hzIwKfquqsCYk/U1Z7OFlQzgTJss/2ZlWckJXgM=
4github.com/fluffle/goirc v1.3.5/go.mod h1:u5jzFHvmESPQ6pzcT/OVSKKd/WOh5pKDM28XJn15cO0=
5github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
6github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
7github.com/mmcdole/gofeed v1.4.0 h1:+efDmI/yJXJgTfa8we5zg9GAKsU+2d7tnpt9QZwvjLQ=
8github.com/mmcdole/gofeed v1.4.0/go.mod h1:ngV5MTB7UJko6fH3/fG5AkB/ABUGK1ZTePF9iRhzu/c=
9github.com/mmcdole/goxpp/v2 v2.0.0 h1:HrSCflxerUEqZQNq3u7ldtmE/XkwnTx4Zpq2DW4i5rQ=
10github.com/mmcdole/goxpp/v2 v2.0.0/go.mod h1:CUduYMnO9JB6Z/uqDn9Ormk/r8E9BsLQxHPWDZ961Os=
11github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
12golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
13golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
14golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
15golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
16golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
17golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
18golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
19golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
20golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
21golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
22golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
23golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
24golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
25golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
26golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
27golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
28golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
29golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
30golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
31golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
32golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
33golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
34golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
35golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
36golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
37golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
main.go+113−0
@@ -0,0 +1,113 @@
1package main
2
3import (
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
20var (
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
28func 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