diff --git a/dotenv.template b/dotenv.template new file mode 100644 index 0000000..b0719c9 --- /dev/null +++ b/dotenv.template @@ -0,0 +1,5 @@ +FAFOMO_HOMESERVER=matrix.org +FAFOMO_USERNAME=@fafomo:matrix.org +FAFOMO_PASSWORD= +FAFOMO_ROOM= +FAFOMO_BACKEND= diff --git a/fafomo.go b/fafomo.go index 2c519c8..e1d9067 100644 --- a/fafomo.go +++ b/fafomo.go @@ -2,7 +2,7 @@ // // based on mautrix-go example code: // -// Copyright (C) 2017 Tulir Asokan +// Copyright (C) 2017 Tulir Asokan // Copyright (C) 2018-2020 Luca Weiss // Copyright (C) 2023 Tulir Asokan // @@ -16,20 +16,21 @@ import ( "context" "encoding/json" "errors" - "flag" - "fmt" + "log" "net/http" "os" "os/signal" "strings" + "sync" "time" - "k8s.io/klog" "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 { @@ -50,40 +51,31 @@ func ircquote(s string) string { } func listFmt(list []string) string { - var listQuoted []string - for _, l := range list { - listQuoted = append(listQuoted, ircquote(l)) - } - return "[ " + strings.Join(listQuoted, " ") + " ]" + return "[ " + strings.Join(list, " ") + " ]" } -func update(ctx context.Context) (string, error) { - req, err := http.NewRequestWithContext(ctx, "GET", flagBackendURL, nil) +func update(ctx context.Context) string { + req, err := http.NewRequestWithContext(ctx, "GET", backend, nil) if err != nil { - return "", err + panic(err) } res, err := http.DefaultClient.Do(req) if err != nil { - return "", err + panic(err) } defer res.Body.Close() var users JSONTop - if res.StatusCode != 200 { - return "", fmt.Errorf("unexpected API status code %d", res.StatusCode) - } - if err := json.NewDecoder(res.Body).Decode(&users); err != nil { - return "", fmt.Errorf("could not decode API response: %v", err) - } + json.NewDecoder(res.Body).Decode(&users) var left []string var arrived []string var unchanged []string - newFolks := make(map[string]struct{}) + new_folks := make(map[string]struct{}) for _, user := range users.Users { - newFolks[user.Login] = struct{}{} + new_folks[user.Login] = struct{}{} if _, ok := folks[user.Login]; !ok { arrived = append(arrived, user.Login) } else { @@ -91,137 +83,111 @@ func update(ctx context.Context) (string, error) { } } for user, _ := range folks { - if _, ok := newFolks[user]; !ok { + if _, ok := new_folks[user]; !ok { left = append(left, user) } } - folks = newFolks + folks = new_folks // no change if len(arrived) == 0 && len(left) == 0 { - return "", nil + return "" } - var parts []string + message := "" if len(arrived) > 0 { - parts = append(parts, fmt.Sprintf("arrived: %s", listFmt(arrived))) + message += "arrived: " + listFmt(arrived) + "; " } if len(left) > 0 { - parts = append(parts, fmt.Sprintf("left: %s", listFmt(left))) + message += "left: " + listFmt(left) + "; " } if len(unchanged) > 0 { - adjective := "also" - if len(left) > 0 && len(arrived) == 0 { - adjective = "still" - } - parts = append(parts, fmt.Sprintf("%s there: %s", adjective, listFmt(unchanged))) + message += "also there: " + listFmt(unchanged) + "; " } - return strings.Join(parts, "; "), nil + return message } -var ( - flagMatrixHomeserver = "matrix.org" - flagMatrixUsername = "@fafomo:matrix.org" - flagMatrixPassword string - flagMatrixRoom string - flagBackendURL string -) - func main() { - flag.StringVar(&flagMatrixHomeserver, "matrix_homeserver", flagMatrixHomeserver, "Address of Matrix homeserver") - flag.StringVar(&flagMatrixUsername, "matrix_username", flagMatrixUsername, "Matrix login username") - flag.StringVar(&flagMatrixPassword, "matrix_password", flagMatrixPassword, "Matrix login password") - flag.StringVar(&flagMatrixRoom, "matrix_room", flagMatrixRoom, "Matrix room MXID") - flag.StringVar(&flagBackendURL, "backend_url", flagBackendURL, "Checkinator (backend) addresss") - flag.Parse() + 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") - for _, s := range []struct { - value string - name string - }{ - {flagMatrixHomeserver, "matrix_homeserver"}, - {flagMatrixUsername, "matrix_username"}, - {flagMatrixPassword, "matrix_password"}, - {flagMatrixRoom, "matrix_room"}, - {flagBackendURL, "backend_url"}, - } { - if s.value == "" { - klog.Exitf("-%s must be set", s.name) - } - } + startCtx, _ := context.WithTimeout(context.Background(), 20*time.Second) - startCtx, startCtxC := context.WithTimeout(context.Background(), 20*time.Second) - defer startCtxC() + update(startCtx) - if _, err := update(startCtx); err != nil { - klog.Exitf("Initial update failed: %v", err) - } - - client, err := mautrix.NewClient(flagMatrixHomeserver, id.UserID(flagMatrixUsername), "") + client, err := mautrix.NewClient(homeserver, id.UserID(username), "") if err != nil { - klog.Exitf("NewClient failed: %v", err) + panic(err) } - klog.Infof("Logging in...") + log.Println("Logging in...") login, err := client.Login(startCtx, &mautrix.ReqLogin{ Type: mautrix.AuthTypePassword, - Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: flagMatrixUsername}, - Password: flagMatrixPassword, + Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: username}, + Password: password, }) - if err != nil { - klog.Exitf("Failed to log in: %v", err) - } client.AccessToken = login.AccessToken syncer := client.Syncer.(*mautrix.DefaultSyncer) syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) { - klog.V(1).Infof("Sender: %s, type: %s, id: %s, body: %q", evt.Sender, evt.Type, evt.ID, evt.Content.AsMessage().Body) + // 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") }) - klog.Infof("Now running...") + if err != nil { + panic(err) + } - ctx, ctxC := signal.NotifyContext(context.Background(), os.Interrupt) - defer ctxC() + 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) - // Run Matrix sync process in the background. - syncDone := make(chan struct{}) go func() { - err = client.SyncWithContext(ctx) - defer close(syncDone) - if err != nil && !errors.Is(err, ctx.Err()) { - klog.Exitf("Sync failed: %v", err) + err = client.SyncWithContext(syncCtx) + defer syncStopWait.Done() + if err != nil && !errors.Is(err, context.Canceled) { + panic(err) } }() - - // Update space members every minute. + ticker := time.NewTicker(60 * time.Second) -process: for { select { case <-ticker.C: - ctx, _ := context.WithTimeout(ctx, 5*time.Second) + ctx, _ := context.WithTimeout(syncCtx, 5*time.Second) - message, err := update(ctx) - if err != nil { - klog.Errorf("Update failed: %v", err) - } else if message != "" { - klog.Infof("Sent update: %s\n", message) - _, err := client.SendText(ctx, id.RoomID(flagMatrixRoom), message) + message := update(ctx) + if message != "" { + log.Printf("Sent update: %s\n", message) + _, err := client.SendText(ctx, id.RoomID(room), message) if err != nil { - klog.Errorf("Failed to send event: %v\n", err) + log.Printf("Failed to send event: %v\n", err) } } - case <-ctx.Done(): - break process + case <-syncCtx.Done(): + return } } - klog.Infof("Waiting for graceful sync before exiting...") - <-syncDone - klog.Infof("Done.") + stop() + syncStopWait.Wait() + + log.Println("Exited") } diff --git a/go.mod b/go.mod index 6dc1278..9e6cc4e 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,7 @@ module fa-fo.de/fafomo go 1.23.0 -require ( - k8s.io/klog v1.0.0 - maunium.net/go/mautrix v0.21.0 -) +require maunium.net/go/mautrix v0.21.0 require ( filippo.io/edwards25519 v1.1.0 // indirect diff --git a/go.sum b/go.sum index 83ea584..530232c 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 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/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 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= @@ -42,7 +41,5 @@ 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= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= maunium.net/go/mautrix v0.21.0 h1:Z6nVu+clkJgj6ANwFYQQ1BtYeVXZPZ9lRgwuFN57gOY= maunium.net/go/mautrix v0.21.0/go.mod h1:qm9oDhcHxF/Xby5RUuONIGpXw1SXXqLZj/GgvMxJxu0=