fafomo/presence.go
2025-06-16 10:27:06 +02:00

136 lines
2.8 KiB
Go

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
}