package main import ( "context" "encoding/json" "fmt" "net/http" "strings" "time" "k8s.io/klog" "maunium.net/go/mautrix" "maunium.net/go/mautrix/id" ) func presence(ctx context.Context, client *mautrix.Client) { if _, err := update(ctx); err != nil { klog.Exitf("Initial update failed: %v", 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) 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(flagPresenceMatrixRoom), message) if err != nil { klog.Errorf("Failed to send event: %v\n", err) } } case <-ctx.Done(): break process } } } 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 { var listQuoted []string for _, l := range list { listQuoted = append(listQuoted, ircquote(l)) } return "[ " + strings.Join(listQuoted, " ") + " ]" } func update(ctx context.Context) (string, error) { req, err := http.NewRequestWithContext(ctx, "GET", flagPresenceBackendURL, nil) if err != nil { return "", err } res, err := http.DefaultClient.Do(req) if err != nil { return "", 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) } var left []string var arrived []string var unchanged []string newFolks := make(map[string]struct{}) for _, user := range users.Users { newFolks[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 := newFolks[user]; !ok { left = append(left, user) } } folks = newFolks // no change if len(arrived) == 0 && len(left) == 0 { return "", nil } var parts []string if len(arrived) > 0 { parts = append(parts, fmt.Sprintf("arrived: %s", listFmt(arrived))) } if len(left) > 0 { parts = append(parts, fmt.Sprintf("left: %s", 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))) } return strings.Join(parts, "; "), nil }