initial commit of fafomo, the FAFO Matrix Bot
This commit is contained in:
commit
385f5dda97
5
dotenv.template
Normal file
5
dotenv.template
Normal file
|
@ -0,0 +1,5 @@
|
|||
FAFOMO_HOMESERVER=matrix.org
|
||||
FAFOMO_USERNAME=@fafomo:matrix.org
|
||||
FAFOMO_PASSWORD=
|
||||
FAFOMO_ROOM=
|
||||
FAFOMO_BACKEND=
|
193
fafomo.go
Normal file
193
fafomo.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Copyright (C) 2024 Leah Neukirchen
|
||||
//
|
||||
// based on mautrix-go example code:
|
||||
//
|
||||
// Copyright (C) 2017 Tulir Asokan
|
||||
// Copyright (C) 2018-2020 Luca Weiss
|
||||
// Copyright (C) 2023 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
var backend string
|
||||
|
||||
var folks map[string]struct{}
|
||||
|
||||
type JSONTop struct {
|
||||
Users []JSONUser `json:"users"`
|
||||
}
|
||||
|
||||
type JSONUser struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
func ircquote(s string) string {
|
||||
if len(s) < 1 {
|
||||
return s // bad luck, V
|
||||
}
|
||||
|
||||
ZWJ := "\u200d"
|
||||
return s[0:1] + ZWJ + s[1:]
|
||||
}
|
||||
|
||||
func listFmt(list []string) string {
|
||||
return "[ " + strings.Join(list, " ") + " ]"
|
||||
}
|
||||
|
||||
func update(ctx context.Context) string {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", backend, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var users JSONTop
|
||||
|
||||
json.NewDecoder(res.Body).Decode(&users)
|
||||
|
||||
var left []string
|
||||
var arrived []string
|
||||
var unchanged []string
|
||||
|
||||
new_folks := make(map[string]struct{})
|
||||
for _, user := range users.Users {
|
||||
new_folks[user.Login] = struct{}{}
|
||||
if _, ok := folks[user.Login]; !ok {
|
||||
arrived = append(arrived, user.Login)
|
||||
} else {
|
||||
unchanged = append(unchanged, user.Login)
|
||||
}
|
||||
}
|
||||
for user, _ := range folks {
|
||||
if _, ok := new_folks[user]; !ok {
|
||||
left = append(left, user)
|
||||
}
|
||||
}
|
||||
|
||||
folks = new_folks
|
||||
|
||||
// no change
|
||||
if len(arrived) == 0 && len(left) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
message := ""
|
||||
if len(arrived) > 0 {
|
||||
message += "arrived: " + listFmt(arrived) + "; "
|
||||
}
|
||||
if len(left) > 0 {
|
||||
message += "left: " + listFmt(left) + "; "
|
||||
}
|
||||
if len(unchanged) > 0 {
|
||||
message += "also there: " + listFmt(unchanged) + "; "
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func main() {
|
||||
homeserver := os.Getenv("FAFOMO_HOMESERVER")
|
||||
username := os.Getenv("FAFOMO_USERNAME")
|
||||
password := os.Getenv("FAFOMO_PASSWORD")
|
||||
room := os.Getenv("FAFOMO_ROOM")
|
||||
backend = os.Getenv("FAFOMO_BACKEND")
|
||||
|
||||
startCtx, _ := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
|
||||
update(startCtx)
|
||||
|
||||
client, err := mautrix.NewClient(homeserver, id.UserID(username), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Println("Logging in...")
|
||||
|
||||
login, err := client.Login(startCtx, &mautrix.ReqLogin{
|
||||
Type: mautrix.AuthTypePassword,
|
||||
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: username},
|
||||
Password: password,
|
||||
})
|
||||
|
||||
client.AccessToken = login.AccessToken
|
||||
|
||||
syncer := client.Syncer.(*mautrix.DefaultSyncer)
|
||||
syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) {
|
||||
// log.Info().
|
||||
// Str("sender", evt.Sender.String()).
|
||||
// Str("type", evt.Type.String()).
|
||||
// Str("id", evt.ID.String()).
|
||||
// Str("body", evt.Content.AsMessage().Body).
|
||||
// Msg("Received message")
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Println("Now running...")
|
||||
|
||||
syncCtx, _ := context.WithCancel(context.Background())
|
||||
syncCtx, stop := signal.NotifyContext(syncCtx, os.Interrupt)
|
||||
defer stop()
|
||||
|
||||
var syncStopWait sync.WaitGroup
|
||||
syncStopWait.Add(1)
|
||||
|
||||
go func() {
|
||||
err = client.SyncWithContext(syncCtx)
|
||||
defer syncStopWait.Done()
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ctx, _ := context.WithTimeout(syncCtx, 5*time.Second)
|
||||
|
||||
message := update(ctx)
|
||||
if message != "" {
|
||||
log.Printf("Sent update: %s\n", message)
|
||||
_, err := client.SendText(ctx, id.RoomID(room), message)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send event: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
case <-syncCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stop()
|
||||
syncStopWait.Wait()
|
||||
|
||||
log.Println("Exited")
|
||||
}
|
21
go.mod
Normal file
21
go.mod
Normal file
|
@ -0,0 +1,21 @@
|
|||
module fa-fo.de/fafomo
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require maunium.net/go/mautrix v0.21.0
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/tidwall/gjson v1.17.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
go.mau.fi/util v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
)
|
45
go.sum
Normal file
45
go.sum
Normal file
|
@ -0,0 +1,45 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
|
||||
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
go.mau.fi/util v0.8.0 h1:MiSny8jgQq4XtCLAT64gDJhZVhqiDeMVIEBDFVw+M0g=
|
||||
go.mau.fi/util v0.8.0/go.mod h1:1Ixb8HWoVbl3rT6nAX6nV4iMkzn7KU/KXwE0Rn5RmsQ=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mautrix v0.21.0 h1:Z6nVu+clkJgj6ANwFYQQ1BtYeVXZPZ9lRgwuFN57gOY=
|
||||
maunium.net/go/mautrix v0.21.0/go.mod h1:qm9oDhcHxF/Xby5RUuONIGpXw1SXXqLZj/GgvMxJxu0=
|
Loading…
Reference in a new issue