fafomo/fafomo.go

194 lines
3.9 KiB
Go
Raw Normal View History

// Copyright (C) 2024 Leah Neukirchen
//
// based on mautrix-go example code:
//
2024-09-30 20:22:23 +00:00
// 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)
}
}()
2024-09-30 20:22:23 +00:00
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")
}