commit 8030a5ee2aa2ec6c8d442f84dd6f885c379547b2
Author: ugrnm <ultrageranium@bleu255.com>
Date: Thu, 14 Nov 2024 23:12:36 +0100
init
Diffstat:
A | .gitignore | | | 2 | ++ |
A | go.mod | | | 18 | ++++++++++++++++++ |
A | warawara.go | | | 155 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 175 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+go.sum
+warawara
diff --git a/go.mod b/go.mod
@@ -0,0 +1,18 @@
+module television
+
+go 1.22.0
+
+toolchain go1.23.2
+
+require (
+ golang.org/x/crypto v0.27.0 // indirect
+ golang.org/x/mod v0.21.0 // indirect
+ golang.org/x/net v0.29.0 // indirect
+ golang.org/x/sync v0.8.0 // indirect
+ golang.org/x/text v0.18.0 // indirect
+ golang.org/x/tools v0.25.0 // indirect
+ mellium.im/reader v0.1.0 // indirect
+ mellium.im/sasl v0.3.2 // indirect
+ mellium.im/xmlstream v0.15.4 // indirect
+ mellium.im/xmpp v0.22.0 // indirect
+)
diff --git a/warawara.go b/warawara.go
@@ -0,0 +1,155 @@
+/*
+ warawara
+
+ The Warawara are unborn human souls who reside in the Sea World. Once they
+ mature, they fly up into the sky to be born as humans. Sometimes they
+ bring news from questionnable RSS feeds.
+
+*/
+
+package main
+
+import (
+ "context"
+ "crypto/tls"
+ "encoding/xml"
+ "flag"
+ "fmt"
+ "mellium.im/sasl"
+ "mellium.im/xmpp"
+ "mellium.im/xmpp/dial"
+ "mellium.im/xmpp/jid"
+ "mellium.im/xmpp/muc"
+ "mellium.im/xmpp/stanza"
+ "net/http"
+ "os"
+ "time"
+)
+
+var(
+ feedURL string
+ ctx context.Context
+ cancel context.CancelFunc
+ botJID string
+ botServer string
+ botPassword string
+ session *xmpp.Session
+ MUCJID string
+)
+
+type itemXML struct {
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ Description string `xml:"description"`
+ Date string `xml:"pubDate"`
+}
+
+type channelXML struct {
+ Items []itemXML `xml:"channel>item"`
+}
+
+type textMessage struct {
+ stanza.Message
+ Body string `xml:"body"`
+}
+
+func sendMUCMessage(text string) {
+ to := jid.MustParse(MUCJID)
+ var msg textMessage = textMessage{
+ Message: stanza.Message{
+ From: jid.MustParse(botJID),
+ To: to,
+ Type:"groupchat"},
+ Body: text}
+ session.Encode(ctx, msg)
+}
+
+func joinXMPP() {
+ ctx, cancel = context.WithCancel(context.Background())
+ //defer cancel()
+ var err error
+ // TLS disabled to avoid a 3min timeout before connecting - whyyyyyyy?
+ dialer := dial.Dialer{NoTLS: true,}
+ conn, err := dialer.Dial(ctx, "tcp", jid.MustParse(botJID))
+ if err != nil {
+ fmt.Println("Error:", botServer, err)
+ }
+ negotiator := xmpp.NewNegotiator(func(*xmpp.Session, *xmpp.StreamConfig) xmpp.StreamConfig {
+ return xmpp.StreamConfig{
+ Features: []xmpp.StreamFeature{
+ xmpp.StartTLS(&tls.Config{
+ ServerName: botServer,
+ }),
+ xmpp.SASL("", botPassword, sasl.ScramSha256Plus, sasl.ScramSha1Plus, sasl.ScramSha256, sasl.ScramSha1, sasl.Plain),
+ xmpp.BindResource(),
+ },
+ }
+ })
+ session, err = xmpp.NewSession(context.TODO(), jid.MustParse(botServer), jid.MustParse(botJID), conn, 0, negotiator)
+ if err != nil {
+ fmt.Println("Error:", botJID, err)
+ os.Exit(1)
+ }
+ fmt.Println("Connected to", botServer, "as", botJID)
+ err = session.Send(ctx, stanza.Presence{Type: stanza.AvailablePresence}.Wrap(nil))
+ if err != nil {
+ fmt.Println("Error:", err)
+ }
+}
+
+// TODO: this is in a goroutine so this won't display errors
+// and won't exit, need to use channels or something...
+func joinMUC() {
+ MUC := jid.MustParse(MUCJID + "/warawara")
+ mucClient := muc.Client{}
+ _, err := mucClient.Join(ctx, MUC, session)
+ if err != nil {
+ fmt.Println("Error:", err)
+ os.Exit(1)
+ }
+}
+
+func fetchFeed() {
+ pDateOld := ""
+ for {
+ fmt.Println("trying to fetch feed")
+ if feed, err := http.Get(feedURL); err != nil {
+ fmt.Println("Error:", err)
+ } else {
+ channel := channelXML{}
+ if err := xml.NewDecoder(feed.Body).Decode(&channel); err != nil {
+ fmt.Println("Error:", err)
+ } else if len(channel.Items) != 0 {
+ item := channel.Items[0]
+ pTitle := item.Title
+ pLink := item.Link
+ pDescription := item.Description
+ pDate := item.Date
+ if pDate != pDateOld {
+ breakingNews := "🗨️ " + pTitle + " " + pDescription + "\n" + pLink
+ fmt.Println(breakingNews)
+ sendMUCMessage(breakingNews)
+ pDateOld = pDate
+ }
+ }
+ }
+ time.Sleep(16 * time.Minute) // cerca's limiter kicks in at 15min
+ }
+}
+
+func main() {
+ flag.StringVar(&feedURL, "url", "", "RSS feed URL")
+ flag.StringVar(&botJID, "bot", "", "bot JID")
+ flag.StringVar(&botServer, "server", "", "server")
+ flag.StringVar(&botPassword, "password", "", "bot JID password")
+ flag.StringVar(&MUCJID, "muc", "", "MUC JID")
+ flag.Parse()
+
+ fmt.Println("🫧 w a r a w a r a 🫧\n")
+ fmt.Println("ctrl-c for emergency shutdown\n")
+
+ joinXMPP()
+ go joinMUC()
+
+ fetchFeed()
+}