You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
3.6 KiB

package main
import (
"context"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/matrix-org/gomatrix"
"github.com/sirupsen/logrus"
"git.hacknology.de/projekte/leakybot/internal/config"
"git.hacknology.de/projekte/leakybot/internal/httputil"
"git.hacknology.de/projekte/leakybot/internal/power"
"git.hacknology.de/projekte/leakybot/internal/ruler"
)
const (
idSeparator = "--"
)
var (
signals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
log = &logrus.Logger{
Out: os.Stderr,
Formatter: &logrus.TextFormatter{
DisableTimestamp: true,
},
Hooks: make(logrus.LevelHooks),
Level: logrus.InfoLevel,
ExitFunc: os.Exit,
ReportCaller: false,
}
)
func formatID(user, room string) string {
return strings.Join([]string{user, room}, idSeparator)
}
func splitID(id string) (user, room string) {
tokens := strings.SplitN(id, idSeparator, 2)
return tokens[0], tokens[1]
}
func main() {
cfg, err := config.Get(os.Args[0], os.Args[1:], os.Getenv)
if err != nil {
log.Fatalf("Error getting config: %s", err)
}
log.SetLevel(cfg.LogLevel)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg := &sync.WaitGroup{}
log.Infof("Homeserver: %s User: %s", cfg.Account.HomeServer, cfg.Account.Username)
client, err := gomatrix.NewClient(cfg.Account.HomeServer, cfg.Account.Username, cfg.Account.AccessToken)
if err != nil {
log.Fatalf("Error creating client: %s", err)
}
client.Client = &http.Client{
Timeout: 60 * time.Second,
Transport: httputil.ContextTransport(ctx, http.DefaultTransport),
}
rooms, err := client.JoinedRooms()
if err != nil {
log.Fatalf("Error retrieving rooms: %s", err)
}
for _, r := range rooms.JoinedRooms {
log.Debugf("Found room: %s", r)
}
rulerFuncs := ruler.Funcs{
Warn: func(id string) error {
user, room := splitID(id)
return power.ModifyLevel(log, client, room, user, -1)
},
Unwarn: func(id string) error {
user, room := splitID(id)
return power.RemoveLevel(log, client, room, user)
},
Ban: func(id string) error {
user, room := splitID(id)
_, err := client.BanUser(room, &gomatrix.ReqBanUser{
Reason: "spam",
UserID: user,
})
return err
},
Unban: func(string) error {
return nil
},
}
r, err := ruler.New(log, cfg.Rules, time.Now, rulerFuncs)
if err != nil {
log.Fatalf("Error creating ruler: %s", err)
}
eventCallback := func(evt *gomatrix.Event) {
id := formatID(evt.Sender, evt.RoomID)
timeStamp := time.Unix(evt.Timestamp/1000, 0)
if err := r.PushEvent(timeStamp, id); err != nil {
log.Errorf("Error pushing event to ruler: %s", err)
return
}
// Using MarkRead from the client library results in an error ("content not JSON")
// TODO revert to using MarkRead once fixed in library
urlPath := client.BuildURL("rooms", evt.RoomID, "receipt", "m.read", evt.ID)
if err := client.MakeRequest(http.MethodPost, urlPath, struct{}{}, nil); err != nil {
log.WithFields(logrus.Fields{
"event": evt.ID,
"room": evt.RoomID,
}).WithError(err).Error("Can not mark event as read.")
}
}
syncer := client.Syncer.(*gomatrix.DefaultSyncer)
syncer.OnEventType("m.room.encrypted", eventCallback)
syncer.OnEventType("m.room.message", eventCallback)
sigCh := make(chan os.Signal)
signal.Notify(sigCh, signals...)
go func() {
sig := <-sigCh
signal.Reset(signals...)
log.Debugf("Got signal %q. Terminating...", sig)
cancel()
}()
r.Start(ctx, wg)
log.Debug("Starting synchronization loop...")
for {
if ctx.Err() != nil {
break
}
if err := client.Sync(); err != nil {
log.Errorf("error during sync: %s", err)
}
}
}